Orbit

Configuring Redirects and Rewrites

Orbit has a built-in redirect and rewrite engine that runs before your project is asked for anything, configured either in KPanel or as a file in your repository. This guide covers both, the pattern syntax and its captures, how rules are ordered, the limits, and the behaviour that catches people out.

Where To Configure Rules

There are two places, and they are evaluated in a fixed order.

  1. In KPanel. Open your project in Orbit, go to Settings, and scroll to the redirects and rewrites section for the environment. Each environment has its own independent set of rules, so production and staging are configured separately.
  2. In your repository, as a kaps.json file. This is covered further down.

Panel rules are evaluated first. If none of them matches, the kaps.json rules are tried.

Redirects and rewrites section in Orbit project settings

Adding a Rule In KPanel

  1. Click Add rule.
  2. Fill in the source, the path pattern to match incoming requests against. The placeholder shows the two shapes it expects: /old-path or /blog/:slug.
  3. Fill in the destination. The placeholder shows /new-path or https://..., so both a local path and a full external URL are valid.
  4. Choose the rule type:
    • 301 Permanent: the URL has moved for good. Browsers and search engines cache this.
    • 302 Temporary: moved for now, not cached. Use for campaigns and experiments.
    • Rewrite: serve the destination path without changing the URL in the browser's address bar.
  5. Click Save rules.

Rules take effect from the next deployment, not immediately. Saving a rule does not change what the currently live deployment serves. Redeploy after saving, or your rules will appear not to work.

A 301 is cached by browsers, sometimes for a very long time, and there is nothing you can do from the server to clear it. If you are not certain a move is permanent, use a 302 first and switch it to 301 once you are. Getting this wrong on a high-traffic path is genuinely hard to walk back.

Source Path Patterns

PatternMatchesCaptures
/old-pageExactly /old-pageNothing
/blog/*Anything starting with /blog/The rest of the path, referenced as * in the destination
/posts/:id/posts/ plus one path segmentThat segment, as :id
/files/:rest*/files/ plus everything after it, slashes includedThe whole remainder, as :rest

The difference between :id and :rest* is the important one. A named parameter matches a single segment and stops at the next slash. A splat matches everything remaining, including slashes.

Using Captures In the Destination

Reference a named parameter by name, and a bare wildcard as *:

SourceDestinationResult
/blog/:slug/articles/:slug/blog/hello-world becomes /articles/hello-world
/docs/:rest*/help/:rest*/docs/a/b/c becomes /help/a/b/c
/old/*/new/*/old/a/b becomes /new/a/b

Rule Order

Rules are tested from the top of the list down and the first match wins. Once a rule matches, no later rule is considered. The panel says so under the list: "Rules are tested in order. First match wins."

Put the specific rules above the general ones. A /blog/* rule placed above /blog/2023/:slug will swallow every request the more specific rule was meant to handle, and it will look like the specific rule is simply broken.

If no rule matches, the request is served normally.

Common Use Cases

Renaming a Page

You renamed /about-us to /about and want old links to keep working.

  • Source: /about-us
  • Destination: /about
  • Type: 301 Permanent

Moving a Whole Section

Your blog moved from /news/:slug to /blog/:slug.

  • Source: /news/:slug
  • Destination: /blog/:slug
  • Type: 301 Permanent

Proxying an API Path Silently

You want /api/v1/* served from a different internal path without exposing the change.

  • Source: /api/v1/:path*
  • Destination: /api/internal/:path*
  • Type: Rewrite

A Temporary Holding Page

  • Source: /checkout
  • Destination: /maintenance
  • Type: 302 Temporary

Sending Traffic To Another Domain

The destination may be an absolute URL, so a rule can point at a different site entirely.

  • Source: /shop/:rest*
  • Destination: https://shop.example.com/:rest*
  • Type: 301 Permanent

Configuration As Code With kaps.json

Rules can live in your repository instead of the panel. Add a kaps.json file, and make sure your build copies it into your output directory, because Orbit reads it from the root of the built artifact rather than from the repository root.

{
  "redirects": [
    { "source": "/about-us", "destination": "/about", "permanent": true },
    { "source": "/news/:slug", "destination": "/blog/:slug", "permanent": true },
    { "source": "/promo", "destination": "/spring-sale", "permanent": false }
  ],
  "rewrites": [
    { "source": "/api/v1/:path*", "destination": "/api/internal/:path*" }
  ],
  "headers": [
    {
      "source": "/*",
      "headers": [
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-Content-Type-Options", "value": "nosniff" }
      ]
    }
  ]
}

permanent: true produces a 301 and permanent: false a 302. Omitting it gives a 301.

kaps.json applies to static deployments only. If Server mode is on, your app handles its own routing and the file is ignored. It is also evaluated only after the environment's panel rules have failed to match, so a panel rule always beats a file rule for the same path.

Use kaps.json when the rules belong with the code, so they are reviewed in a pull request and move with a rollback. Use the panel when you need a rule live now without a deploy. Do not maintain the same rule in both places: the panel rule will always win and the file rule will look like it is being ignored, which it is.

Limits

  • Up to 100 redirect and rewrite rules per environment, and 100 in a kaps.json.
  • Up to 200 custom header rules per environment.

Rules beyond the limit are dropped silently rather than raising an error, so stay well under it.

Custom Response Headers

Alongside redirects, each environment has a response headers section in Settings for injecting HTTP headers on matching paths. It uses the same path pattern syntax, and there are quick-add presets for HSTS, CSP, no-embed, no-sniff, referrer policy and CORS.

Unlike redirects, all matching header rules are applied, not just the first, and a later rule overwrites an earlier one when they set the same header. Content-Length, Transfer-Encoding and Connection are blocked, because setting them would corrupt the response.

Behaviour Worth Knowing Before You Rely On It

Query strings are not carried across a redirect. A rule matches whether or not the request has a query string, but the destination is built from your template and the captured path segments only. A request to /old?utm_source=email redirects to /new, with the parameters dropped. If you depend on tracking parameters surviving, handle the redirect in an edge function instead, where you control the destination URL completely.

  • Rules match the path only. Fragments (#section) never reach the server at all; the browser reattaches them after a redirect.
  • A rewrite to a path that does not exist produces a 404, rather than silently falling through to the original path. Rewrite targets must exist in your build output.
  • Rules run before your project is asked for anything, so they apply to static files and to server-mode requests alike.
  • Each environment is independent. Rules on production do not apply to staging or previews. Copy them deliberately.

Deleting a Rule

Click the trash icon on the rule row, then click Save rules to apply the change. As with adding one, the change takes effect from the next deployment.

When To Use an Edge Function Instead

The redirect engine handles path-to-path rules. Reach for an edge function when you need logic the rule engine cannot express: branching on a header or cookie, preserving or rewriting query parameters, weighted A/B routing, or anything conditional.

Related Reading

Still need help?

Email us at support@kapsulehost.com or open a chat in KPanel.

Open KPanel
Configuring Redirects and Rewrites