Docs
API7 GatewayManage traffic and APIsTraffic ManagementRewrite Upstream Requests

Rewrite Upstream Requests

Rewrite upstream paths, methods, and request headers without changing the public API.

Applies to:API7 Gateway 3.10.x

Roles:Platform engineer

Goal

Create four independent routes that demonstrate static and regular-expression path rewrites, request-header changes, and an upstream method override. All routes use one HTTPS httpbin service and have different public paths, so the examples can remain active together.

Before you begin

Set the variables used in this guide:

export API7_ADMIN_URL="https://localhost:7443"
export API7_GATEWAY_GROUP_ID="default"   # only the group the quickstart creates; otherwise copy the gateway group's UUID from Gateway Groups in the Dashboard
read -rp "Gateway group name for ADC (if used): " API7_GATEWAY_GROUP_NAME && export API7_GATEWAY_GROUP_NAME
export GATEWAY_URL="http://127.0.0.1:9080"
read -rsp "Dashboard token: " API7_ADMIN_KEY && export API7_ADMIN_KEY && echo
  • Confirm that the control plane and selected gateway group are ready:

    curl -ksS --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}\n' "${API7_ADMIN_URL}/api/gateway_groups" -H "X-API-KEY: ${API7_ADMIN_KEY}"
    curl -ksS --connect-timeout 3 --max-time 10 "${API7_ADMIN_URL}/api/gateway_groups/${API7_GATEWAY_GROUP_ID}/instances" -H "X-API-KEY: ${API7_ADMIN_KEY}" | grep -o '"status": *"Healthy"' | wc -l

    Expected: 200, then 1 or more.

  • The gateway can resolve and reach httpbin.org:443. This is a public demonstration service, so its availability and behavior are outside API7 Gateway's control.

  • curl and jq are available.

  • For ADC, confirm label-selector support and reserve the working files:

    adc --version
    adc sync --help | grep -- '--label-selector'
    for file in proxy-rewrite-adc.yaml proxy-rewrite-cleanup.yaml; do test ! -e "${file}" || { echo "Stop: ${file} already exists"; exit 1; }; done

    ADC resolves --gateway-group by display name; the compatibility Admin API uses the group's UUID. Enter the display name of the group identified by API7_GATEWAY_GROUP_ID.

This guide creates one service and four routes. Check every fixed Admin API identifier before using PUT:

curl -ksS --connect-timeout 3 --max-time 10 -w '\nservice: %{http_code}\n' "${API7_ADMIN_URL}/apisix/admin/services/proxy-rewrite-guide-service-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
for id in proxy-rewrite-static-id proxy-rewrite-regex-id proxy-rewrite-headers-id proxy-rewrite-method-id; do
  curl -ksS --connect-timeout 3 --max-time 10 -w "\nroute ${id}: %{http_code}\n" "${API7_ADMIN_URL}/apisix/admin/routes/${id}?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
done

Expected: every resource is absent. A 200 response containing a resource means the ID is in use; stop or change every occurrence. Correct authentication, group, and request errors until absence is unambiguous because PUT replaces a resource at the same ID. The Admin API URL IDs deliberately differ from the name fields below. ADC also treats a display name and backend ID as separate fields; do not infer one from the other. For ADC, continue only if its diff shows creates for exactly the labeled resources in this guide.

Steps

Complete steps 1 and 2 for the Admin API path, or skip them and complete step 3 for ADC. Do not apply both paths.

Step 1: Create the upstream service

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/services/proxy-rewrite-guide-service-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{
    "name": "proxy-rewrite-guide-service",
    "labels": {"guide": "proxy-rewrite"},
    "upstream": {
      "name": "default", "type": "roundrobin", "scheme": "https", "pass_host": "node",
      "nodes": [{"host": "httpbin.org", "port": 443, "weight": 100}]
    }
  }'

What changed: the service points to the HTTPS demonstration upstream. No public route uses it yet.

Step 2: Create four non-overlapping rewrite routes

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/routes/proxy-rewrite-static-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{"name":"proxy-rewrite-static","labels":{"guide":"proxy-rewrite"},"paths":["/rewrite/static"],"methods":["GET"],"service_id":"proxy-rewrite-guide-service-id","plugins":{"proxy-rewrite":{"uri":"/get"}}}'

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/routes/proxy-rewrite-regex-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{"name":"proxy-rewrite-regex","labels":{"guide":"proxy-rewrite"},"paths":["/rewrite/regex/*"],"methods":["GET"],"service_id":"proxy-rewrite-guide-service-id","plugins":{"proxy-rewrite":{"regex_uri":["^/rewrite/regex/(.*)","/anything/$1"]}}}'

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/routes/proxy-rewrite-headers-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{
    "name":"proxy-rewrite-headers","labels":{"guide":"proxy-rewrite"},"paths":["/rewrite/headers"],"methods":["GET"],"service_id":"proxy-rewrite-guide-service-id",
    "plugins":{"proxy-rewrite":{"uri":"/anything/headers","headers":{"add":{"X-Added-Header":"guide-add"},"set":{"X-Replaced-Header":"guide-set"},"remove":["X-Remove-Header"]}}}
  }'

curl -ksS --connect-timeout 3 --max-time 15 "${API7_ADMIN_URL}/apisix/admin/routes/proxy-rewrite-method-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
  -H "X-API-KEY: ${API7_ADMIN_KEY}" -H "Content-Type: application/json" \
  -d '{"name":"proxy-rewrite-method","labels":{"guide":"proxy-rewrite"},"paths":["/rewrite/method"],"methods":["PUT"],"service_id":"proxy-rewrite-guide-service-id","plugins":{"proxy-rewrite":{"uri":"/anything/method","method":"POST"}}}'

What changed: the gateway now selects each example by its public path. A static uri takes precedence over regex_uri when both are configured. regex_uri is a flat list of pattern/replacement pairs and the first matching pair is used. Request-header actions run in add, remove, then set order; use the plugin's host field rather than headers.set to change the upstream Host header.

Step 3: Apply the complete desired state with ADC

Skip this alternative if you completed steps 1 and 2. Save every resource in one file; separate sync files would describe different desired states and could remove an earlier example.

proxy-rewrite-adc.yaml
services:
  - name: proxy-rewrite-guide-service
    labels: {guide: proxy-rewrite}
    upstream:
      name: default
      scheme: https
      pass_host: node
      nodes:
        - {host: httpbin.org, port: 443, weight: 100}
    routes:
      - name: proxy-rewrite-static
        labels: {guide: proxy-rewrite}
        uris: [/rewrite/static]
        methods: [GET]
        plugins:
          proxy-rewrite: {uri: /get}
      - name: proxy-rewrite-regex
        labels: {guide: proxy-rewrite}
        uris: [/rewrite/regex/*]
        methods: [GET]
        plugins:
          proxy-rewrite:
            regex_uri: ['^/rewrite/regex/(.*)', '/anything/$1']
      - name: proxy-rewrite-headers
        labels: {guide: proxy-rewrite}
        uris: [/rewrite/headers]
        methods: [GET]
        plugins:
          proxy-rewrite:
            uri: /anything/headers
            headers:
              add: {X-Added-Header: guide-add}
              set: {X-Replaced-Header: guide-set}
              remove: [X-Remove-Header]
      - name: proxy-rewrite-method
        labels: {guide: proxy-rewrite}
        uris: [/rewrite/method]
        methods: [PUT]
        plugins:
          proxy-rewrite: {uri: /anything/method, method: POST}

Lint, validate, inspect, and then apply the reviewed diff:

adc lint -f proxy-rewrite-adc.yaml
adc validate -f proxy-rewrite-adc.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-rewrite
adc diff -f proxy-rewrite-adc.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-rewrite
adc sync -f proxy-rewrite-adc.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-rewrite

Expected: lint and validation succeed; the diff creates only the labeled service and its four labeled routes; sync affects only that reviewed set.

Validate

Verify the four transformations:

curl -fsS --connect-timeout 3 --max-time 15 "${GATEWAY_URL}/rewrite/static" | jq -er '.url | endswith("/get")'
curl -fsS --connect-timeout 3 --max-time 15 "${GATEWAY_URL}/rewrite/regex/captured" | jq -er '.url | endswith("/anything/captured")'
curl -fsS --connect-timeout 3 --max-time 15 "${GATEWAY_URL}/rewrite/headers" \
  -H 'X-Replaced-Header: client' -H 'X-Remove-Header: client' \
  | jq -er '.headers as $h | ($h["X-Added-Header"] == "guide-add") and ($h["X-Replaced-Header"] == "guide-set") and (($h | has("X-Remove-Header")) | not)'
curl -fsS --connect-timeout 3 --max-time 15 "${GATEWAY_URL}/rewrite/method" -X PUT | jq -er '.method == "POST"'

Expected: each command prints true.

Roll back / Clean up

For the Admin API path, delete routes before their service:

for id in proxy-rewrite-method-id proxy-rewrite-headers-id proxy-rewrite-regex-id proxy-rewrite-static-id; do
  curl -ksS --connect-timeout 3 --max-time 15 -X DELETE "${API7_ADMIN_URL}/apisix/admin/routes/${id}?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
done
curl -ksS --connect-timeout 3 --max-time 15 -X DELETE "${API7_ADMIN_URL}/apisix/admin/services/proxy-rewrite-guide-service-id?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"

For ADC, save an empty selected state and review the deletion diff before syncing it:

proxy-rewrite-cleanup.yaml
services: []
adc lint -f proxy-rewrite-cleanup.yaml
adc diff -f proxy-rewrite-cleanup.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-rewrite
adc sync -f proxy-rewrite-cleanup.yaml --backend api7ee --server "${API7_ADMIN_URL}" --token "${API7_ADMIN_KEY}" --gateway-group "${API7_GATEWAY_GROUP_NAME}" --tls-skip-verify --label-selector guide=proxy-rewrite
rm -f proxy-rewrite-adc.yaml proxy-rewrite-cleanup.yaml

Expected: the reviewed diff and sync delete only this guide's labeled service and routes.

Troubleshoot

  • A request returns 404: confirm that the public path and method match the selected route. The method example accepts PUT publicly even though the plugin sends POST upstream.
  • A validation returns false: inspect the httpbin JSON to identify whether path, headers, or method remained unchanged, then check the plugin configuration on that route.
  • The upstream returns an error or times out: verify DNS and outbound HTTPS access to httpbin.org:443; retry only after confirming the public service is available.
  • ADC proposes unrelated changes: stop and verify the gateway-group display name, guide=proxy-rewrite selector, labels, and that the file contains the complete desired state.