Skip to main content
Version: 3.20.0

Built-In Variables

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

APISIX 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 a set of variables that can be used to access various request-specific information.

Commonly used variables include:

  • upstream_addr
  • remote_addr
  • request_uri
  • server_name
  • uri
  • http_user_agent

When ext-plugin-post-resp fetches an upstream response, it also populates upstream_addr and upstream_response_time for logging. The response time includes the upstream connection, headers, and body read, but excludes external runner processing and time spent sending the response to the client.

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.
uri_param_*URL parameter when APISIX uses the radixtree_uri_with_parameter router. 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.
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.
rate_limiting_infoJSON rate-limiting evaluation result. Empty before a supported rate-limiting plugin runs. See Rate Limiting Information.
route_idRoute ID.
route_nameRoute name.
service_idService ID.
service_nameService name.
upstream_unresolved_hostHost or IP address of the selected upstream node before DNS resolution. For a node configured as httpbin.org, this variable remains httpbin.org while balancer_ip contains the resolved address.
resp_bodyHTTP response body.
request_typeRequest category used by observability plugins, such as traditional_http, ai_chat, or ai_stream. A successful WebSocket upgrade is classified as websocket.
mqtt_client_idClient ID in MQTT protocol.
redis_cmd_lineRedis command.
rpc_timeRPC request round-trip time.

Custom Variables​

You can also register your own variables and use them as built-in variables. For instance, you can use custom variables to customize log format in logging plugins, or use them as keys in rate limiting plugins.

Example​

The following examples demonstrate three ways to register custom variables. The first two register a6_route_labels and add route labels to a remote log. The third uses _meta.pre_function to register a path parameter and rewrite a URI.

Create a Service​

The following request creates a service with the http-logger plugin and an upstream:

curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
--data-binary @- <<EOF
{
"id":"srv_custom_var",
"plugins": {
"http-logger": {
"uri": "${REMOTE_SERVER_ADDR}"
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
}
}
}
EOF

Register a Custom Variable​

You can register custom variables in a startup hook, in a serverless function plugin, or in a plugin's _meta.pre_function. The following methods demonstrate each approach.

Method 1: At Startup with a Custom Lua Module​

The following example demonstrates how to register a custom variable at startup. Save the module as /opt/apisix-custom/custom_variables.lua. The a6_route_labels variable returns the matched route's labels value when available:

require "apisix"
local core = require "apisix.core"

core.ctx.register_var("a6_route_labels", function(ctx)
local route = ctx.matched_route and ctx.matched_route.value
if route and route.labels then
return route.labels
end
return nil
end)

return true

APISIX loads the hook before its main module during initialization, so the hook requires apisix before apisix.core.

Configure APISIX to find and load the module at startup:

config.yaml
apisix:
extra_lua_path: "/opt/apisix-custom/?.lua"
lua_module_hook: custom_variables

extra_lua_path adds the directory to the Lua module search path. lua_module_hook explicitly loads custom_variables during APISIX initialization. Reload APISIX after changing the module or configuration. The Admin API plugin-reload operation does not reload this startup hook.

For more information, see create Lua plugin guide.

Method 2: In the serverless Plugins​

You can also register a custom variable using the serverless-pre-function or serverless-post-function serverless function plugins. These plugins run serverless functions before or after a specified execution phase, and you can register custom variables in these functions.

The following request adds the serverless-pre-function plugin to the previously created service. The function registers the custom variable a6_route_labels:

curl "http://127.0.0.1:9180/apisix/admin/services/srv_custom_var" -X PATCH \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function() local core = require \"apisix.core\" core.ctx.register_var(\"a6_route_labels\", function(ctx) local route = ctx.matched_route and ctx.matched_route.value if route and route.labels then return route.labels end return nil end) end"
]
}
}
}'
Method 3: In _meta.pre_function of Plugins​

You can use _meta.pre_function to run custom code before a plugin executes in each phase.

The following example demonstrates how to configure _meta.pre_function in proxy-rewrite, register the request path's user_id parameter as a variable, and use the variable in the rewritten path.

To use parameters in the route URI, first set the router to radixtree_uri_with_parameter in the configuration file:

config.yaml
apisix:
router:
http: radixtree_uri_with_parameter

Then reload APISIX for changes to take effect.

Create a route to the httpbin service to examine the rewritten path:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "pre-func-route",
"uri": "/anything/:user_id/hello",
"plugins": {
"proxy-rewrite": {
"_meta": {
"pre_function": "return function(conf, ctx) local core = require \"apisix.core\" core.ctx.register_var(\"user_id\", function(ctx) return ctx.curr_req_matched.user_id end) end"
},
"uri": "/anything/$user_id/world"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

❶ Match requests to /anything/:user_id/hello where user_id is a parameter.

❷ Customize the _meta.pre_function to extract the user_id value from the requested path and save it to a variable of the same name.

❸ Rewrite the request path to /anything/$user_id/world, where $user_id will be replaced by the variable value.

Send a request to the route:

curl -i "http://127.0.0.1:9080/anything/johndoe/hello"

The response should contain the rewritten URL with the value of user_id:

{
"url": "http://127.0.0.1/anything/johndoe/world"
}

Configure Log Format​

Create a plugin metadata object to configure log format for all http-logger instances:

curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/http-logger" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"log_format": {
"host": "$host",
"client_ip": "$remote_addr",
"labels": "$a6_route_labels"
}
}'

❶ host and remote_addr: NGINX variables.

❷ a6_route_labels: custom variable.

Create a Route in Service​

Create a route in the service:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id":"route_custom_var",
"uri":"/get",
"service_id": "srv_custom_var",
"labels": {
"key": "test_a6_route_labels"
}
}'

❶ service_id: corresponds to the previously created service.

❷ labels: route information to be logged with the custom variable.

Verify Custom Variable Registration​

Send a request to verify that the custom variable logs the labels information in the route:

curl "http://127.0.0.1:9080/get"

You should see a log entry in your remote server created by a POST request with a body similar to the following:

[
{
"labels": {
"key": "test_a6_route_labels"
},
"service_id": "srv_custom_var",
"client_ip": "172.21.0.1",
"route_id": "route_custom_var",
"host": "127.0.0.1"
}
]

This verifies the custom variable was registered and it logs the labels information in a route successfully.

Evaluation Order​

APISIX evaluates variables in the given order:

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

If a variable is successfully sourced in custom variables, APISIX 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.

Simple and Braced 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.