Nginx内置变量及案例详解
好的,Nginx 内置变量是 Nginx 配置中极其强大且常用的功能。它们提供了关于当前请求、响应、连接状态等丰富的信息,使你能够实现动态配置、日志定制、访问控制、重写规则、代理设置等复杂逻辑。下面我将详细介绍常用的内置变量并辅以实际案例详解。
核心概念:
- 来源: 这些变量由 Nginx 核心或模块(如
ngx_http_core_module
,ngx_http_proxy_module
,ngx_http_log_module
等)在请求处理的不同阶段自动设置。 - 作用域: 大多数变量在请求处理期间都是可用的,可以在
location
,server
,http
块以及access_log
,rewrite
,proxy_pass
等指令中使用。 - 值类型: 通常是字符串。有些变量在某些上下文中可能为空。
常用内置变量分类及详解:
一、请求行与请求头相关变量
-
$uri
/$document_uri
- 含义: 当前请求的 规范化后的 URI(不包括查询参数
?
后面的部分)。Nginx 会对其进行解码、移除多余的斜杠、解析相对路径.
和..
。 - 案例:
location /images/ {# 记录请求的图片路径 (如 /images/cat.jpg)access_log /var/log/nginx/image_access.log '$uri'; }
- 含义: 当前请求的 规范化后的 URI(不包括查询参数
-
$request_uri
- 含义: 客户端发来的 原始完整的请求 URI(包括查询参数)。未经任何规范化处理。
- 案例:
# 重写规则:如果请求URI包含 'oldpath',则重定向到新路径,但保留原始查询参数 if ($request_uri ~* "^/oldpath/(.*)") {return 301 /newpath/$1?$args; }
-
$args
/$query_string
- 含义: 请求 URI 中的 查询参数(
?
后面的部分)。两者通常等价。 - 案例:
# 根据查询参数做不同处理 location /search {if ($args ~ "q=([^&]+)") {# 提取搜索关键词 (需要小心 if 的使用陷阱,通常建议用 map 或 split_clients)set $search_term $1;proxy_pass http://search_backend?query=$search_term;}# ... 其他处理 }
- 含义: 请求 URI 中的 查询参数(
-
$is_args
- 含义: 如果请求 URI 包含查询参数,则值为
?
;否则为空字符串。常用于拼接 URL。 - 案例:
# 在重写或代理时,优雅地添加或保留查询参数 location /api/ {rewrite ^/api/(.*)$ /internal_api/$1$is_args$args break;proxy_pass http://api_backend; }
- 含义: 如果请求 URI 包含查询参数,则值为
-
$request_method
- 含义: 客户端请求的 HTTP 方法(如
GET
,POST
,PUT
,DELETE
等)。 - 案例:
# 限制特定 location 只允许 GET 和 HEAD 方法 location /static/ {if ($request_method !~ ^(GET|HEAD)$) {return 405; # Method Not Allowed}# ... 静态文件服务配置 }
- 含义: 客户端请求的 HTTP 方法(如
-
$http_HEADER_NAME
- 含义: 获取 任意请求头 的值。将请求头名称转换为小写,用下划线
_
代替连字符-
,并加上前缀$http_
。 - 案例:
# 获取 User-Agent set $user_agent $http_user_agent;# 获取 X-Forwarded-For (常用于获取真实客户端IP,当Nginx前有代理时) set $real_client_ip $http_x_forwarded_for;# 获取 Authorization 头 (用于基础认证或JWT) set $auth_header $http_authorization;# 记录特定请求头到日志 log_format custom_log '$remote_addr - [$time_local] "$request" $status "$http_referer" "$http_user_agent"';
- 含义: 获取 任意请求头 的值。将请求头名称转换为小写,用下划线
-
$host
- 含义: 按以下优先级确定:
- 请求行中的主机名 (HTTP/1.0)。
Host
请求头的值。- 与请求匹配的
server_name
配置块中的第一个名称。
- 重要: 不包含端口号。最常用且推荐用于配置基于域名的虚拟主机逻辑。
- 案例:
# 基于不同域名提供不同内容 server {listen 80;server_name site1.com;root /var/www/site1; } server {listen 80;server_name site2.com;root /var/www/site2; } # 在通用配置中使用 $host location / {add_header X-Served-By $host always; }
- 含义: 按以下优先级确定:
-
$http_host
- 含义: 直接取自
Host
请求头的值,包含端口号(如果客户端指定了端口,如Host: example.com:8080
)。 - 与
$host
区别:$host
不包含端口,且回退机制更强。通常$host
更安全通用,除非明确需要端口信息。 - 案例:
# 需要精确包含端口信息的情况 (较少见) if ($http_host != "www.example.com:443") {# ... 做一些处理 }
- 含义: 直接取自
二、客户端信息相关变量
-
$remote_addr
- 含义: 客户端 IP 地址。这是直接与 Nginx 建立 TCP 连接的客户端的 IP。重要提示: 如果 Nginx 前面有代理(如负载均衡器、CDN),这个地址是代理的 IP,而不是最终用户的 IP!获取真实用户 IP 通常用
$http_x_forwarded_for
。 - 案例:
# 基础访问日志通常包含客户端IP log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent"'; access_log /var/log/nginx/access.log main;# 简单的 IP 访问限制 (生产环境建议用 `allow`/`deny` 或 `ngx_http_geo_module`) if ($remote_addr = "123.45.67.89") {return 403; }
- 含义: 客户端 IP 地址。这是直接与 Nginx 建立 TCP 连接的客户端的 IP。重要提示: 如果 Nginx 前面有代理(如负载均衡器、CDN),这个地址是代理的 IP,而不是最终用户的 IP!获取真实用户 IP 通常用
-
$remote_port
- 含义: 客户端用于建立连接的 端口号。
- 案例: 较少单独使用,有时用于调试或详细日志记录。
log_format detailed ... '$remote_addr:$remote_port ...';
-
$remote_user
- 含义: 如果请求使用了 HTTP 基本认证 (Basic Auth),此变量包含认证中提供的 用户名。否则为空。
- 案例:
# 记录谁访问了受保护区域 location /admin/ {auth_basic "Admin Area";auth_basic_user_file /etc/nginx/.htpasswd;access_log /var/log/nginx/admin_access.log '$remote_user - $remote_addr [$time_local] "$request"';# ... 其他配置 }
三、响应相关变量
-
$status
- 含义: 服务器返回给客户端的 HTTP 响应状态码 (如
200
,404
,500
,301
等)。极其常用。 - 案例:
# 在访问日志中记录状态码 log_format main ... '$status' ...;# 只记录错误状态 (4xx, 5xx) 到单独日志 map $status $loggable {~^[23] 0; # 2xx, 3xx 不记录default 1; # 4xx, 5xx 记录 } access_log /var/log/nginx/error_access.log main if=$loggable;# 根据状态码添加响应头 (例如标识错误来源) location @backend {proxy_pass http://backend;proxy_intercept_errors on;error_page 404 = @fallback;add_header X-Backend-Status $status always; # 记录后端实际状态 }
- 含义: 服务器返回给客户端的 HTTP 响应状态码 (如
-
$body_bytes_sent
- 含义: 发送给客户端的 响应体字节数,不包括响应头。近似于
Content-Length
的值。 - 案例:
# 记录传输的数据量 (用于流量分析、监控) log_format download '$remote_addr - [$time_local] "$request" $status $body_bytes_sent'; access_log /var/log/nginx/download.log download;
- 含义: 发送给客户端的 响应体字节数,不包括响应头。近似于
-
$bytes_sent
- 含义: 发送给客户端的 总字节数(包括响应头)。
- 案例: 需要精确计算总传输量时使用。
-
$sent_http_HEADER_NAME
- 含义: 获取 Nginx 发送给客户端的 任意响应头 的值。将响应头名称转换为小写,用下划线
_
代替连字符-
,并加上前缀$sent_http_
。 - 案例:
# 记录后端设置的自定义响应头 (如 X-Backend-ID) log_format backend_log ... '$sent_http_x_backend_id' ...;# 检查是否设置了某个响应头 (例如 Cache-Control) if ($sent_http_cache_control ~ "max-age=(\d+)") {set $cache_max_age $1; }
- 含义: 获取 Nginx 发送给客户端的 任意响应头 的值。将响应头名称转换为小写,用下划线
四、连接与请求处理相关变量
-
$connection
/$connection_requests
$connection
: 当前连接的序列号(唯一标识一个连接)。在连接复用时,多个请求共享同一个$connection
。$connection_requests
: 当前连接上已经处理过的 请求数量(对于 keepalive 连接)。- 案例:
# 记录连接信息 (用于分析长连接复用情况) log_format connection_log '$remote_addr - $connection [$connection_requests] "$request"';
-
$request_time
- 含义: 处理请求所花费的 总时间(秒),精度为毫秒。从读取客户端请求的第一个字节开始,到将响应最后一个字节发送给客户端结束。关键性能指标!
- 案例:
# 记录慢请求 (例如超过 1 秒的请求) log_format slow_log '$remote_addr - [$time_local] "$request" $status $request_time'; access_log /var/log/nginx/slow.log slow_log if=$slow_condition;map $request_time $slow_condition {~^\d\.\d{3}$ 0; # 小于1秒default 1; # 大于等于1秒 }
-
$request_length
- 含义: 客户端请求的 总长度(包括请求行、请求头和请求体)。可用于检测异常大的请求。
- 案例:
# 限制非常大的请求 (防止DoS) client_max_body_size 10m; # 限制请求体大小 # 在日志中记录请求长度 log_format ... '$request_length' ...;
-
$request
- 含义: 完整的 原始请求行(如
GET /index.html?param=value HTTP/1.1
)。 - 案例:
# 标准访问日志格式的核心部分 log_format main ... '"$request"' ...;
- 含义: 完整的 原始请求行(如
五、时间相关变量
-
$time_iso8601
- 含义: 服务器时间的 ISO 8601 标准格式(如
2023-10-27T14:31:15+08:00
)。日志记录推荐格式,便于解析和排序。 - 案例:
# 使用标准时间格式的日志 log_format iso_log '$remote_addr - [$time_iso8601] "$request" $status'; access_log /var/log/nginx/access.log iso_log;
- 含义: 服务器时间的 ISO 8601 标准格式(如
-
$time_local
- 含义: 服务器时间的 本地格式(如
27/Oct/2023:14:31:15 +0800
)。格式由log_format
指令的默认值或配置决定。 - 案例: 经典日志格式常用。
- 含义: 服务器时间的 本地格式(如
-
$msec
- 含义: 当前时间戳(秒),精度到毫秒(如
1698388275.123
)。用于需要高精度时间戳的场景。 - 案例: 自定义计时、唯一ID生成(需结合其他信息)。
- 含义: 当前时间戳(秒),精度到毫秒(如
六、代理与上游服务器相关变量(需启用相应模块)
-
$proxy_host
- 含义: 在
proxy_pass
指令中指定的 代理目标服务器的主机名或 IP 地址和端口。如果proxy_pass
只指定了 URI 部分,此变量可能为空。 - 案例: 记录代理目标。
- 含义: 在
-
$proxy_port
- 含义:
proxy_pass
指令中指定的 代理目标服务器的端口。
- 含义:
-
$proxy_add_x_forwarded_for
- 含义: 一个非常有用的变量。用于构造
X-Forwarded-For
请求头以传递给上游服务器。它的值是 现有的$http_x_forwarded_for
值(如果存在)后面加上逗号和$remote_addr
。如果不存在$http_x_forwarded_for
,则直接等于$remote_addr
。这是设置proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
的标准做法。 - 案例:
location / {proxy_pass http://backend;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 关键!传递真实客户端IP链 }
- 含义: 一个非常有用的变量。用于构造
-
$upstream_addr
- 含义: 处理请求的 上游服务器的 IP 地址和端口(或 Unix-domain socket 路径)。如果使用了负载均衡,它反映的是实际处理请求的那台后端服务器。调试后端问题的关键变量!
- 案例:
# 在日志中记录请求被转发到了哪个后端服务器 log_format upstream_log ... '$upstream_addr' ...; access_log /var/log/nginx/upstream.log upstream_log;
-
$upstream_status
- 含义: 上游服务器返回的 HTTP 响应状态码。如果 Nginx 无法连接到上游,此变量通常为
502
(Bad Gateway) 或504
(Gateway Timeout)。 - 案例: 区分是后端服务错误 (
5xx
) 还是 Nginx 本身无法连接后端。log_format backend_error ... '$upstream_status' ...;
- 含义: 上游服务器返回的 HTTP 响应状态码。如果 Nginx 无法连接到上游,此变量通常为
-
$upstream_response_time
- 含义: 从 Nginx 开始向上游服务器建立连接到接收完上游服务器响应头的 时间(秒),精度为毫秒。衡量后端服务器性能的关键指标!
- 案例:
# 记录后端响应时间 log_format backend_perf ... '$upstream_response_time' ...;
-
$upstream_http_HEADER_NAME
- 含义: 从上游服务器响应中获取 任意响应头 的值。将响应头名称转换为小写,用下划线
_
代替连字符-
,并加上前缀$upstream_http_
。 - 案例:
# 获取后端设置的 Cache-Control 头来决定是否缓存 proxy_cache_valid 200 302 10m if=($upstream_http_cache_control ~ "max-age=(\d+)"); # 记录后端自定义头 log_format ... '$upstream_http_x_backend_version' ...;
- 含义: 从上游服务器响应中获取 任意响应头 的值。将响应头名称转换为小写,用下划线
七、其他实用变量
-
$scheme
- 含义: 请求使用的 协议方案(
http
或https
)。 - 案例:
# 强制 HTTPS 重定向 if ($scheme != "https") {return 301 https://$host$request_uri; }
- 含义: 请求使用的 协议方案(
-
$server_name
- 含义: 匹配当前请求的 server 块中
server_name
指令的第一个名称。 - 案例: 在需要精确知道配置块匹配的 server name 时使用。
- 含义: 匹配当前请求的 server 块中
-
$server_addr
/$server_port
$server_addr
: 接受请求的服务器的 IP 地址。$server_port
: 接受请求的服务器的 端口号。- 案例: 构造绝对 URL 或记录服务器信息。
-
$nginx_version
- 含义: 当前运行的 Nginx 版本号。
- 案例: 在响应头中标识服务器版本(注意安全风险,通常建议隐藏)或条件配置。
# 根据版本启用不同特性 (谨慎使用) if ($nginx_version ~ "^1\.18\.") {# 针对 1.18.x 的特定配置 }
综合案例详解:
-
动态访问日志:
log_format dynamic_log '$remote_addr - $remote_user [$time_iso8601] ''"$request" $status $body_bytes_sent ''"$http_referer" "$http_user_agent" ''rt=$request_time uct="$upstream_connect_time" urt="$upstream_response_time" ''ups_addr=$upstream_addr ups_status=$upstream_status ''Host="$host" Real_IP="$http_x_forwarded_for"'; access_log /var/log/nginx/access.log dynamic_log;
- 这个日志格式包含了客户端信息、时间戳、请求信息、响应状态、传输大小、来源、User Agent、关键时间指标(总时间、连接后端时间、后端响应时间)、后端服务器地址和状态码、请求的Host以及真实客户端IP链。非常适合调试和性能分析。
-
基于 User-Agent 的访问控制:
map $http_user_agent $bad_bot {default 0;~*(googlebot|bingbot|yahoo) 0; # 允许好的爬虫~*(scrapers|spammer|malicious) 1; # 阻止坏的用户代理~*curl 1; # 阻止 curl (根据需求调整) } server {...if ($bad_bot) {return 403 "Access Forbidden";# 或者限制速率: limit_req zone=bot_zone burst=5 nodelay;} }
-
安全的文件下载限制:
location ~* \.(sql|conf|env|key)$ {# 结合 $uri/$request_uri 匹配特定文件类型if ($args !~ "downloadkey=secret123") { # 简单示例,生产环境用更安全方式return 403 "Unauthorized access to sensitive files.";}# 或者只允许特定IP访问allow 192.168.1.0/24;deny all;# ... 文件服务配置 }
-
负载均衡调试日志:
log_format lb_debug '$time_iso8601 | CLIENT: $remote_addr | REQ: "$request" | ''UPSTREAM: $upstream_addr | STATUS: $upstream_status | ''TIME: $request_time / $upstream_response_time'; access_log /var/log/nginx/lb_debug.log lb_debug;
- 清晰展示请求时间线、客户端信息、具体请求、最终到达的后端、后端状态码以及关键时间指标,便于排查负载均衡或后端问题。
-
自定义错误页面带信息:
error_page 404 /404.html; location = /404.html {internal; # 只能内部访问# 在错误页面中嵌入变量 (需要SSI支持或动态生成)ssi on;add_header Content-Type text/html;return 200 '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Oops! Page Not Found</h1><p>The requested URL <code>$uri</code> was not found on this server.</p><p>Error occurred at: $time_iso8601</p></body></html>'; }
重要注意事项:
if
指令的陷阱:if
在location
上下文中有许多限制和令人惊讶的行为(有时会破坏继承关系)。尽量避免在if
块中进行复杂的配置修改,优先使用map
,try_files
,return
,rewrite
(带last
/break
) 或split_clients
等更安全的指令。如果必须用if
,主要用于检查变量值然后return
或rewrite
。- 变量作用域: 大多数内置变量在请求范围内有效。使用
set
指令创建的自定义变量作用域是其所在的配置块(http
,server
,location
)。 - 性能: 频繁使用某些变量(如正则匹配)或复杂的
map
块可能会影响性能。在高流量环境中需谨慎优化。 - 安全性: 不要将未经处理的用户输入(如
$args
,$request_uri
,$http_*
)直接用于敏感操作(如文件路径、命令执行)。始终进行验证、过滤或转义。 - 空值: 某些变量在某些上下文中可能为空(例如
$remote_user
未认证时,$upstream_*
未使用代理时)。在逻辑判断时要考虑这种情况。 - 文档: 最权威的文档始终是 Nginx 官方文档:
http://nginx.org/en/docs/varindex.html
。不同模块提供的变量在其模块文档中列出。
通过熟练理解和灵活运用这些内置变量,你可以极大地提升 Nginx 配置的灵活性、可观测性和功能性,构建出更强大、更智能的 Web 服务器配置。