Docs

Built-In Variables

Discover the built-in variables available in API7 Gateway, including NGINX and APISIX variables, which can be used for route matching, log customization, and plugin configurations.

Built-in variables in API7 Gateway are pre-defined variables that can be directly referenced in configurations. They are often used in plugin configurations, route matching, and log customization.

API7 Gateway supports three types of built-in variables:

  • NGINX Variables
  • APISIX Variables
  • Custom Variables

These variables are evaluated in a given order.

NGINX Variables

NGINX provides variables that expose request and response information.

Commonly used variables include:

VariableDescription
upstream_addrIP address and port, or UNIX-domain socket path, of the upstream server.
remote_addrClient address.
request_methodRequest method, such as GET or POST.
request_uriFull original request URI, including arguments.
server_nameName of the server that accepted the request.
statusResponse status. It can be 000 until NGINX establishes the response status. Use it in response-phase contexts, such as Debug Session sampling rules or logging plugins; do not use it for route matching.
uriCurrent normalized request URI, which can change during request processing.
http_user_agentValue of the User-Agent request header.

See the complete list of NGINX variables for more information.

APISIX Variables

In addition to NGINX variables, APISIX offers a variety of built-in variables:

Variable NameDescription
post_arg_*HTTP POST form data when the content type is application/x-www-form-urlencoded. The asterisk is to be replaced with the actual name of the POST form data.
post_arg.*HTTP POST body parameter when the content type is application/json, application/x-www-form-urlencoded, or multipart/form-data. The asterisk is to be replaced with the actual name of the POST parameter. Supports JSON path-like selection, such as post_arg.model.version and post_arg.messages[*].content[*].type.
arg_*URL query string. The asterisk is to be replaced with the actual query parameter name.
http_*HTTP request header. The asterisk is to be replaced with the actual name of the header.
cookie_*Request cookie. The asterisk is to be replaced with the actual name of the cookie.
methodHTTP request method, such as GET or POST. The equivalent NGINX variable is request_method.
balancer_ipUpstream server IP.
balancer_portUpstream server port.
consumer_nameConsumer username.
consumer_group_idConsumer group ID.
graphql_nameGraphQL operation name.
graphql_operationGraphQL operation type.
graphql_root_fieldsGraphQL root fields.
route_idRoute ID.
route_nameRoute name.
service_idService ID.
service_nameService name.
resp_bodyHTTP response body.
mqtt_client_idClient ID in MQTT protocol.
redis_cmd_lineRedis command.
rpc_timeRPC request round-trip time.
external_user.*External user information. This variable can be populated by authentication plugins such as openid-connect, making it available to other plugins. For example, limit-count-advanced can enforce rate limiting by username when key is set to ${external_user.preferred_username}.
upstream_unresolved_hostThe configured upstream host or domain name before DNS resolution (the upstream node's domain or host). Available in API7 Enterprise from version 3.9.15.

http_* reads an HTTP request header. It is not a prefix for other built-in variables. For example, http_uri, http_method, and http_status read request headers named Uri, Method, and Status. Use uri, method, and status for the request path, request method, and response status.

API7 Gateway does not verify that such a header exists when compiling an expression. If a client does not send the header, a condition that expects it to have a value can fail to match without an error.

Forwarded Header Variables

API7 Gateway rewrites the X-Forwarded-* headers before proxying a request, so a client cannot claim to have arrived over a scheme, host or port it did not use. X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Port are replaced with values the gateway observed, Forwarded is cleared, and the connection address is appended to X-Forwarded-For.

That leaves two versions of each value, and which one a variable gives you depends on where you read it.

VariableHolds
schemeThe scheme the client actually connected over. This is the sanitized X-Forwarded-Proto.
var_x_forwarded_hostThe sanitized X-Forwarded-Host: the Host header including its port, falling back to $host.
var_x_forwarded_portThe sanitized X-Forwarded-Port: the port in the Host header, falling back to the listener's port.
original_x_forwarded_protoThe X-Forwarded-Proto the client sent.
original_x_forwarded_hostThe X-Forwarded-Host the client sent.
original_x_forwarded_portThe X-Forwarded-Port the client sent.
original_x_forwarded_forThe X-Forwarded-For the client sent, before the connection address was appended.
original_forwardedThe Forwarded header the client sent, before it was cleared.

$http_x_forwarded_proto, $http_x_forwarded_host, $http_x_forwarded_port and $http_forwarded return the client's raw value when read from the NGINX configuration — in an access log format, an if, or a map. They are read while the original headers are still in place, and NGINX caches that first read for the rest of the request.

An access log that names $http_x_forwarded_proto is therefore logging a client-controlled string, not the scheme the request arrived on. Use $scheme, $var_x_forwarded_host and $var_x_forwarded_port to log what the gateway forwarded, and the $original_* variables when you deliberately want what the client claimed.

Plugins are not affected for the three headers above: ctx.var.http_x_forwarded_* and core.request.header always see the sanitized X-Forwarded-Proto, -Host and -Port. $http_x_forwarded_for is different and is covered below.

X-Forwarded-For Is Not Sanitized by Default

X-Forwarded-For is handled differently from the other three, and the difference matters.

Out of the box, the gateway does not touch the inbound X-Forwarded-For chain. It appends the connection address and forwards everything the client sent ahead of it. trusted_addresses is commented out in the shipped configuration, and the code that clears an untrusted chain returns immediately when it is unset.

So on a default install, every entry but the last is attacker-supplied — in plugins, in ctx.var.http_x_forwarded_for, in logs, and in whatever your upstream application does with the header. Anything that takes "the client IP" from the front of that chain, including IP allow-lists and rate limits keyed on it, is trusting a value the client chose.

This is the compatible default, not a bug, but it is only safe once you set the trust boundary below.

Trusted Proxies

If the gateway sits behind a load balancer or CDN that sets these headers legitimately, list its addresses under apisix.trusted_addresses in config.yaml:

config.yaml
apisix:
  trusted_addresses:
    - 127.0.0.1
    - 172.18.0.0/16

The key is read from under apisix:. Written at the top level it is never seen: the gateway starts normally, logs trusted_addresses is not configured at info level, and behaves as though you had configured nothing.

Once it is set, behavior changes on both sides of the boundary:

  • A request from a trusted address has its X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port and Forwarded headers put back to what that peer sent, from the $original_* copies. Its X-Forwarded-For chain is preserved, with the connection address appended. A header the peer did not send is left at the gateway's own observed value rather than being blanked.
  • A request from an untrusted address has its inbound X-Forwarded-For dropped entirely, so the upstream sees only the address the gateway observed. The other three are overwritten as usual.

AI Traffic Variables

These are populated on requests proxied to an LLM provider and are nil on other traffic. They are most useful in log formats and in rate limiting keyed by token consumption.

VariableDescription
request_llm_modelThe model named in the request.
llm_modelThe model the gateway sent upstream: the AI instance's configured model when it overrides one, otherwise the requested model.
llm_prompt_tokensTokens in the prompt.
llm_completion_tokensTokens in the completion.
llm_total_tokensPrompt and completion tokens combined.
llm_reasoning_tokensTokens spent on reasoning, for models that report them separately.
llm_cache_read_input_tokensPrompt tokens served from the provider's cache.
llm_cache_creation_input_tokensPrompt tokens written to the provider's cache.
llm_time_to_first_tokenTime until the first token of the response, in milliseconds.
llm_stream"true" or "false", from whether the request was a streaming one.
llm_has_tool_calls"true" when the response contained tool calls. Unset otherwise — it is never "false".
llm_tool_countNumber of tools the request offered, from its tools array. Despite the adjacent name, this counts what was made available, not what the model called.
llm_end_user_idThe end-user identifier extracted from the request, for protocols that define one.
llm_content_risk_levelThe risk level assigned by content inspection, where it ran.

apisix_upstream_response_time is set alongside these, in milliseconds, and is the total time the provider took rather than the time to the first token.

Billing follows llm_prompt_tokens and llm_completion_tokens separately, and cached input tokens are usually billed at a different rate from fresh ones — a cost report built on llm_total_tokens alone cannot distinguish them.

Evaluation Order

API7 Gateway evaluates variables in the given order:

  1. Custom Variables
  2. APISIX Variables
  3. NGINX Variables

If a variable is successfully sourced in custom variables, API7 Gateway will not continue to look in APISIX variables or NGINX variables.

In other words, custom variables will overwrite variables of the same names defined in APISIX variables or NGINX variables, to better meet requirements of your specific use cases.

Variable Syntax

A valid variable name can include letters, digits, underscores (_), and periods (.).

Escaped variables with a backslash (\) are not treated as variables, for example, \$variable_name.

$var and ${var} forms

A variable can be referenced in two forms:

  • $variable_name
  • ${variable_name}

Both forms are supported; however, the braced form is required in certain contexts to ensure correct parsing.

The unbraced form $variable is valid when the following character cannot be part of a variable name. For example, the following forms are equivalent:

  • $http_host and ${http_host}
  • $arg_username-$arg_userid and ${arg_username}-${arg_userid}

When a variable is followed by a character that could belong to a variable name, the parser will incorrectly interpret it as a longer variable name. For example, with https://$http_baseurl.com, the parser would treat the entire string http_baseurl.com as the variable, which is incorrect. In this case, you should use the braced form https://${http_baseurl}.com to clearly delimit the variable.

You would also need the braced form if you use the ?? operator.

Default Values with ??

You can specify a default value for a variable using the ?? operator. If the variable is not defined, the value after the operator will be used.

ExampleBehavior
${http_username ?? anonymous}If the HTTP request header username is defined, its value is used; otherwise the string anonymous is used.
${http_count ?? 10}If the HTTP request header count is defined, its value is used; otherwise the string 10 is used.