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 -lExpected:
200, then1or 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. -
curlandjqare 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; }; doneADC resolves
--gateway-groupby display name; the compatibility Admin API uses the group's UUID. Enter the display name of the group identified byAPI7_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}"
doneExpected: 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.
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-rewriteExpected: 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:
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.yamlExpected: 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 acceptsPUTpublicly even though the plugin sendsPOSTupstream. - 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-rewriteselector, labels, and that the file contains the complete desired state.