Upstream Request Headers
On the standard protocol endpoints, such as /v1/chat/completions, /v1/messages, and /v1/responses, AISIX builds the upstream request from scratch. It selects the upstream credential, rewrites the model name, and sends only the headers the provider's protocol requires. Headers the caller sent are not forwarded, so a caller cannot reach the upstream provider with their own credentials or trace context.
Two provider-key settings extend that default when an upstream needs more context:
request.default_headersadds headers AISIX generates, with values that can reference the current request, such as the calling API key's team.request.forward_client_headersrelays specific inbound client headers, such asanthropic-betaor a trace header, to the upstream.
Both settings live on the provider key, so every model that references it inherits them.
Prerequisites
Before starting, prepare the following:
- A running on-premises AISIX Cloud deployment with an environment and an admin token that has write scope. Follow the On-Premises AISIX Cloud Quickstart to set these up.
- An upstream provider credential and endpoint. The examples create a provider key around them; see Provider Keys for the rest of that resource's fields.
Set the shared variables used by the examples on this page:
export AISIX_CP="http://localhost:8080/api"
export AISIX_TOKEN="aisix_pat_YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
export UPSTREAM_API_KEY="YOUR_UPSTREAM_API_KEY"
# Set after the first example creates the key, for the update example.
export PK_ID="YOUR_PROVIDER_KEY_ID"
# A caller API key allowed on a model that references the provider key,
# and that model's alias — used by the verification request at the end.
export AISIX_API_KEY="YOUR_CALLER_API_KEY"
export MODEL_ALIAS="YOUR_MODEL_ALIAS"
Inject Request-Context Headers
Use request.default_headers when the upstream needs to know who is calling. An internal model service can then apply per-tenant quotas, attribute cost to a team, or join its own access log to the AISIX request id. None of that requires a separate upstream credential per tenant.
A header value is a literal string unless it contains a ${...} reference, which AISIX resolves per request after authenticating the caller and selecting the model:
curl -sS -X POST "$AISIX_CP/provider_keys" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "internal-vllm",
"provider": "byo",
"adapter": "openai",
"api_key": "'"${UPSTREAM_API_KEY}"'",
"api_base": "https://models.internal.example.com/v1",
"allowed_environments": ["'"${ENV_ID}"'"],
"request": {
"default_headers": {
"x-tenant-id": "${request.api_key.team_id}",
"x-cost-center": "${request.api_key.name}",
"x-correlation-id": "${request.id}",
"x-upstream-tier": "premium"
}
}
}'
❶ The team that owns the calling API key. Useful as the tenant identifier for upstream quota or routing decisions.
❷ The calling API key's name, as shown in the dashboard.
❸ This request's correlation id, the same value AISIX sends as x-aisix-request-id and reports in its own logs.
❹ A literal value, sent unchanged on every request.
Available Variables
| Variable | Value |
|---|---|
request.id | Correlation id for this request. |
request.api_key.id | Identifier of the calling API key. |
request.api_key.name | Name of the calling API key. |
request.api_key.team_id | Team that owns the calling API key. |
request.api_key.user_id | Organization member who owns the calling API key. |
model.id | Identifier of the resolved model. |
model.name | Caller-facing name of the resolved model, not the upstream model name. |
provider_key.id | Identifier of this provider key. |
provider_key.name | Name of this provider key. |
Only these variables resolve. On the Cloud Admin API, a value that references any other name is rejected when you save the provider key, so a typo surfaces immediately instead of becoming a header that never arrives. In a resources file the same value is environment-variable interpolation — see Configure in a Resources File.
No variable exposes a secret. Caller API keys, upstream credentials, and signing material are not part of the request context a header value can read.
A header whose variables do not all have a value for a request is dropped from that request rather than sent empty. If the calling API key belongs to no team, a request through the example above carries x-cost-center and x-correlation-id, but no x-tenant-id. An empty x-tenant-id would tell the upstream that the tenant is the empty string.
Forward Client Headers
Use request.forward_client_headers when callers need to pass a header through AISIX to the provider. Common cases are an Anthropic beta feature flag, a distributed-tracing header, and a routing hint an internal model platform reads.
List the headers to relay. Each entry is either an exact header name or a name with a single * wildcard, matched case-insensitively. Both blocks can sit on the same provider key — they are shown separately only to keep each example focused:
curl -sS -X POST "$AISIX_CP/provider_keys" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "internal-vllm-forwarding",
"provider": "byo",
"adapter": "openai",
"api_key": "'"${UPSTREAM_API_KEY}"'",
"api_base": "https://models.internal.example.com/v1",
"allowed_environments": ["'"${ENV_ID}"'"],
"request": {
"forward_client_headers": [
"anthropic-beta",
"traceparent",
"x-trace-*"
]
}
}'
❶ An exact name. A caller sending anthropic-beta reaches the provider with it; a caller who does not send it changes nothing.
❷ The W3C trace context header, so a trace continues into the upstream instead of ending at AISIX.
❸ A wildcard entry, matching x-trace-id, x-trace-parent, and any other name with that prefix. Use x-* to relay every x- header except the ones in Headers AISIX Never Forwards, which no pattern can reach.
An empty or absent list — the default — forwards nothing.
Callers cannot opt themselves in: the list is provider-key configuration, and a header a caller sends that no entry names is dropped exactly as before.
Change the Configuration Later
PATCH /provider_keys/{id} accepts the same request block, and the change reaches every model that references the key.
The block is replaced whole, so send the fields to keep, not only the ones that changed. Read the current block first — GET /provider_keys/{id} returns it — then submit the edited version:
curl -sS -X PATCH "$AISIX_CP/provider_keys/$PK_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"request": {
"default_headers": {"x-tenant-id": "${request.api_key.team_id}"},
"forward_client_headers": ["anthropic-beta", "x-trace-*"]
}
}'
An empty "request": {} clears the block, returning the key to the bundled defaults for its provider. Omitting the field entirely leaves the stored block alone.
In the dashboard, the same block is under Advanced wire-shape overrides on a provider key's edit form, pre-filled with what is stored.
Headers AISIX Never Forwards
Some headers are never taken from the caller, regardless of configuration. Saving a provider key that names one of them in forward_client_headers fails, and a wildcard entry that would otherwise match one skips it at request time.
This is about the caller's copy. AISIX still sends its own value for several of these names — it authenticates with the provider key's credential, sets content-type for the body it builds, and stamps x-aisix-request-id.
| Header group | Headers | Why |
|---|---|---|
| Authentication | authorization, x-api-key, api-key, x-goog-api-key, x-amz-date, x-amz-security-token, x-amz-content-sha256, proxy-authorization | AISIX authenticates to the provider with the provider key's own credential. Forwarding the caller's credential would disclose it to a third party. |
| Session and routing | cookie, set-cookie, host | Prevents session material from crossing into upstream traffic and blocks host-based redirection of the upstream request. |
| Transport | connection, keep-alive, transfer-encoding, content-length, content-type, content-encoding, accept, accept-encoding, te, trailer, upgrade, expect | These describe the caller's connection and a request body AISIX rewrites, not the request AISIX sends. |
| Gateway-owned | x-aisix-* | AISIX asserts these itself, including x-aisix-request-id. A caller's copy is never relayed. |
| Client SDK | x-stainless-* | Version headers the caller's SDK sends about itself. Relaying them to a provider that reads the same headers for its own SDK breaks the call. |
If a request genuinely needs its original headers, use Provider Passthrough instead. Passthrough forwards the request as received, minus the provider key's strip_headers list, and gives up the protocol translation, unified telemetry, and cross-provider features of the standard endpoints.
Passthrough does not relay caller credentials either by default: strip_headers starts as authorization, cookie, set-cookie, x-api-key. Reaching an upstream with the caller's own credential means removing that entry from the list, which discloses the caller's secret to the provider. Do that only for an upstream you control and that the credential is meant for.
Precedence
When the same header name comes from more than one place, the first of these wins:
- Headers AISIX owns, such as the upstream credential,
content-type, andx-aisix-request-id. request.default_headers.- A header relayed by
request.forward_client_headers.
A header is single-valued on the wire: AISIX replaces rather than appends, so an upstream never receives both an operator value and a caller value for the same name.
Configure in a Resources File
A standalone open-source AISIX gateway configures the same fields in its resources file:
provider_keys:
- display_name: internal-vllm
provider: byo
adapter: openai
api_key: ${UPSTREAM_API_KEY}
api_base: https://models.internal.example.com/v1
request:
default_headers:
x-tenant-id: ${request.api_key.team_id}
x-correlation-id: ${request.id}
forward_client_headers:
- anthropic-beta
- x-trace-*
See the Resources File Reference for the full field catalog.
In a resources file, ${...} in a value is also the syntax for environment-variable interpolation at load time. The names in Available Variables are resolved per request by the gateway; any other ${...} is substituted from the environment when the file loads. Keep the two apart when naming environment variables.
Verify
Send a request through a model that references the provider key, including a header the allowlist names:
curl -sS -X POST "http://localhost:9080/v1/chat/completions" \
-H "Authorization: Bearer $AISIX_API_KEY" \
-H "Content-Type: application/json" \
-H "x-trace-id: trace-0001" \
-d '{
"model": "'"${MODEL_ALIAS}"'",
"messages": [{"role": "user", "content": "ping"}]
}'
Then confirm on the upstream side that the request arrived with the expected headers. Check the access log of the upstream service where you can.
If you point api_base at a request-inspection service instead, remember that AISIX sends the provider key's credential and the request body to whatever that api_base names. Use an endpoint you control, a throwaway credential on the test provider key, and a prompt that carries no real data.
If an expected header is missing, check the following:
- The header name is listed in
forward_client_headers, or matches one of its wildcard entries. - The header is not in Headers AISIX Never Forwards.
- For a
default_headersvalue with a variable, the calling API key actually has that attribute. A key with no team drops a header that referencesrequest.api_key.team_id.
Next Steps
- Provider Keys for the rest of the provider-key configuration, including compatibility overrides.
- Resources File Reference for the declarative field catalog.