# URL Rewriting

The AISIX gateway can rewrite the request path before all routing, including host-matched passthrough routes. An ordered list of rewrite rules runs at the entry of the proxy listener: the first rule whose optional `hosts` condition and `match` regex both match rewrites the path. The request then flows through the normal endpoint — authentication, access control, rate limits, and telemetry apply exactly as if the client had sent the rewritten path.

Use URL rewriting when existing clients are configured with URL shapes AISIX does not serve natively — for example, gateways that expose one URL per MCP server, or internal conventions like a health path an external monitor already probes.

## Configure Rewrite Rules[​](#configure-rewrite-rules "Direct link to Configure Rewrite Rules")

Rewrite rules are startup configuration in `config.yaml`, under the `proxy` block:

```
proxy:

  addr: "0.0.0.0:3000"

  url_rewrites:

    - name: per-server-mcp-compat

      match: "^/mcp-servers/([^/]+)/mcp$"

      rewrite: "/mcp/$1"
```

| Field     | Required | Description                                                                                                                                                                                                                                                                                                                          |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`    | No       | Label used in gateway logs when the rule fires.                                                                                                                                                                                                                                                                                      |
| `hosts`   | No       | Inbound host names that limit the rule's scope. Omitted means all hosts; an explicit list must be non-empty. Matching ignores case and the request's port. Supports exact names and a leading `*.` matching exactly one additional label, such as `*.example.com`. Entries contain no scheme, port, or path.                         |
| `match`   | Yes      | Regex tested against the raw, percent-encoded request path (never the query string), with no decoding or normalization. Anchor with `^` and `$` to match the whole path.                                                                                                                                                             |
| `rewrite` | Yes      | Replacement for the matched portion of the path. `$1` … expand numbered capture groups, and `${server}` expands a named group defined as `(?P<server>…)` in `match`. Use the braced form (`${1}text`) when literal text follows a reference. Must not contain `?`, `#`, or whitespace — the query string is preserved automatically. |

Changing rules requires a gateway restart, like any other startup configuration. Startup validation rejects broken rules with an error naming the rule — invalid regexes, references to capture groups the pattern does not define, forbidden characters in the template, and patterns that would match the empty string — so a typo surfaces immediately instead of as silently mis-routed traffic.

When the gateway is configured purely through environment variables, set the whole list as one JSON array:

```
export AISIX_PROXY__URL_REWRITES='[{"name":"per-server-mcp-compat","match":"^/mcp-servers/([^/]+)/mcp$","rewrite":"/mcp/$1"}]'
```

## How Rules Are Applied[​](#how-rules-are-applied "Direct link to How Rules Are Applied")

* Rules run in declaration order; the **first** rule matching both host and path wins and is applied **once** — a rewritten path is never fed back through the rule list. A host mismatch continues to the next rule. Put a scoped rule before any broader rule that could also match.
* `rewrite` replaces the matched portion of the path. With an unanchored `match`, the unmatched prefix and suffix are preserved.
* The query string is preserved as sent.
* A request no rule matches is passed through untouched, and canonical paths keep working alongside the rewritten shapes.
* Rewriting runs before all business routing on the proxy listener. Both host-based and path-based passthrough routes see the rewritten path. The admin and metrics listeners are not affected.

Rewriting selects which gateway endpoint serves the request; it never bypasses governance. The rewritten request is authenticated and authorized by its target endpoint, and metrics and access logs record the rewritten route.

Rewrite rules change the path, not the `Host` or the route-selection policy. A matching host-based passthrough route still takes precedence over the gateway's built-in endpoints, even if the rewritten path is `/v1/chat/completions`.

Compared with 1.2.0, host-matched passthrough requests now also run through URL rewriting. Review existing global rules and add `hosts` when they should apply only to the gateway's own hostname.

## Example: Scope a Chat Alias to One Host[​](#example-scope-a-chat-alias-to-one-host "Direct link to Example: Scope a Chat Alias to One Host")

To serve `/chat` as the standard Chat Completions endpoint on one hostname while preserving other services' `/chat` paths:

```
proxy:

  url_rewrites:

    - name: chat-alias

      hosts: ["gateway.example.com"]

      match: "^/chat$"

      rewrite: "/v1/chat/completions"
```

Requests to `gateway.example.com/chat` enter the standard Chat pipeline, provided that hostname is not claimed by a host-based passthrough route. Requests to `agent.example.com/chat` are unchanged by this rule and can use their configured passthrough route. To alias `/pjt/chat` instead, change `match` to `"^/pjt/chat$"`. The request body must still use the standard Chat Completions format, including a configured model name and `messages`.

`hosts` uses the request URI authority when present, including for absolute-form requests from another proxy. Otherwise it reads the inbound `Host` header. It does not use `X-Forwarded-Host`. For example, `GATEWAY.EXAMPLE.COM:8443` matches `gateway.example.com`. A wildcard such as `*.example.com` matches `agent.example.com`, but neither `example.com` nor `one.agent.example.com`.

For the AISIX Helm chart, set the same list under `config.proxy.url_rewrites`:

```
config:

  proxy:

    url_rewrites:

      - name: chat-alias

        hosts: ["gateway.example.com"]

        match: "^/chat$"

        rewrite: "/v1/chat/completions"
```

## Example: Serve Per-Server MCP URLs[​](#example-serve-per-server-mcp-urls "Direct link to Example: Serve Per-Server MCP URLs")

Some gateways expose each MCP server at its own URL, such as `/mcp-servers/github/mcp`, and clients call tools by their original names. AISIX serves that contract natively on its [per-server MCP endpoint](https://docs.api7.ai/ai-gateway/mcp-gateway/overview.md#per-server-endpoints) `/mcp/{server}` — one rewrite rule connects the legacy URL shape to it:

```
proxy:

  url_rewrites:

    - name: per-server-mcp-compat

      match: "^/mcp-servers/([^/]+)/mcp$"

      rewrite: "/mcp/$1"
```

A client configured with `https://gateway.example.com/mcp-servers/github/mcp` now reaches `/mcp/github`. AISIX normally lists the `github` server's tools under their original names and accepts calls with those names, so no client changes are needed. If an original name would be ambiguous with a registered server prefix, AISIX advertises the namespaced spelling that remains callable. The caller API key's tool access, rate limits, and guardrails apply unchanged. Any matching AISIX Enterprise or AISIX Cloud budgets also continue to apply.

Only the URL shapes you declare are served: with the rule above, `/mcp-servers/github/sse` matches nothing and returns 404 rather than being silently routed.

## Example: Alias an Arbitrary Path[​](#example-alias-an-arbitrary-path "Direct link to Example: Alias an Arbitrary Path")

Rules are not MCP-specific. Any path can map onto any proxy endpoint:

```
proxy:

  url_rewrites:

    - name: legacy-health

      match: "^/healthz-compat$"

      rewrite: "/livez"
```
