Nginx 变量使用
Nginx 变量的定义、使用和作用域
📋 目录
内置变量
HTTP 请求相关变量
# 客户端信息
$remote_addr # 客户端 IP 地址
$remote_port # 客户端端口
$remote_user # 客户端用户名(基本认证)
$http_user_agent # 用户代理字符串
$http_referer # 来源页面
# 请求信息
$request # 完整请求行(GET / HTTP/1.1)
$request_method # 请求方法(GET、POST 等)
$request_uri # 完整 URI(带参数)
$uri # 请求 URI(不带参数,可能改变)
$document_uri # 同 $uri
$args # 查询字符串
$query_string # 同 $args
$server_protocol # 协议版本(HTTP/1.1、HTTP/2.0)
# 服务器信息
$server_name # 服务器名称
$server_addr # 服务器 IP
$server_port # 服务器端口
$hostname # 主机名
$https # 是否 HTTPS(on 或空)
$scheme # 协议(http 或 https)HTTP 响应相关变量
$status # 响应状态码
$body_bytes_sent # 响应体大小(字节)
$bytes_sent # 响应总大小(字节)
$content_length # 响应头中的 Content-Length
$content_type # 响应头中的 Content-Type
$sent_http_NAME # 发送的响应头(如 $sent_http_content_type)时间相关变量
$time_local # 本地时间格式(02/Jan/2024:12:34:56 +0800)
$time_iso8601 # ISO 8601 时间格式(2024-01-02T12:34:56+08:00)
$msec # 毫秒级时间戳
$request_time # 请求处理时间(秒,毫秒精度)
$upstream_response_time # 上游响应时间代理相关变量
$proxy_host # 代理主机名
$proxy_port # 代理端口
$proxy_add_x_forwarded_for # X-Forwarded-For 值
# 反向代理相关
$upstream_addr # 上游服务器地址
$upstream_status # 上游响应状态码
$upstream_response_time # 上游响应时间Location 相关变量
$document_root # 当前请求的根目录
$realpath_root # 根目录的规范路径
$request_filename # 请求的文件路径(root + uri)Cookie 相关变量
$cookie_NAME # 获取指定名称的 cookie 值
$http_cookie # 所有 cookie 字符串特殊变量
$is_args # 是否有查询字符串(? 或空)
$args_value # 查询字符串的值
$limit_rate # 限速值
# 地理位置变量(需要 GeoIP 模块)
$geoip_country_code
$geoip_country_name
$geoip_city
$geoip_latitude
$geoip_longitude自定义变量
Set 指令
# 基本语法
set $variable value;
# 示例
server {
listen 80;
server_name example.com;
location / {
set $my_var "hello world";
return 200 $my_var;
}
}变量赋值
# 字符串赋值
set $name "John";
# 变量引用
set $greeting "Hello, $name";
# 组合变量
set $full_path "/data$uri";
# 条件赋值(配合 if)
set $cache_control "";
if ($request_uri ~* "\.(jpg|jpeg|png|gif|css|js)$") {
set $cache_control "public, max-age=31536000, immutable";
}
# 在响应头中使用
add_header Cache-Control $cache_control;变量作用域规则
# 变量在定义后可用
server {
listen 80;
server_name example.com;
# 在这里不能使用 $my_var
location / {
set $my_var "value";
# 在这里可以使用 $my_var
location /sub/ {
# 子 location 中也可以使用 $my_var
}
}
location /other/ {
# 在这里不能使用 $my_var(在不同 location 中定义)
}
}变量作用域
请求级变量
# 每个请求独立
server {
location / {
set $request_id $request_id; # 每个请求都不同
return 200 $request_id;
}
}配置级变量
# 在配置加载时确定
http {
# 这些变量在所有请求中相同
set $server_name "my-server";
server {
# 可以使用 $server_name
add_header X-Server $server_name;
}
}Location 级变量
server {
location /api/ {
set $api_version "v1";
proxy_set_header X-API-Version $api_version;
proxy_pass http://backend;
}
location /api/v2/ {
set $api_version "v2";
proxy_set_header X-API-Version $api_version;
proxy_pass http://backend;
}
}Map 模块
Map 基本用法
# 语法
map $source_variable $target_variable {
default value; # 默认值
pattern1 value1; # 匹配模式
pattern2 value2;
...
}
# 示例: 根据用户代理判断设备类型
http {
map $http_user_agent $device_type {
default "desktop";
~*mobile|android|iphone|ipad "mobile";
~*bot|spider|crawl "bot";
}
server {
listen 80;
server_name example.com;
location / {
add_header X-Device-Type $device_type;
root /var/www/$device_type;
}
}
}Map 匹配规则
# 字符串匹配
map $uri $page_type {
default "other";
/ "home";
/about "about";
/contact "contact";
}
# 正则表达式匹配
map $uri $cache_control {
default "private, no-cache";
~^/static/ "public, max-age=31536000, immutable";
~*\.(jpg|jpeg|png|gif|css|js)$ "public, max-age=86400";
}
# Hostname 匹配
map $host $backend {
default backend_default;
hostnames;
example.com backend_1;
*.example.com backend_2;
~^www\.(.+)$ backend_www;
}
upstream backend_default { server 127.0.0.1:8000; }
upstream backend_1 { server 127.0.0.1:8001; }
upstream backend_2 { server 127.0.0.1:8002; }
upstream backend_www { server 127.0.0.1:8003; }复杂 Map 示例
# 根据 IP 地址设置限制
geo $limit {
default 1;
192.168.1.0/24 0; # 内网不限
10.0.0.0/8 0; # 内网不限
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
}
}
# 根据请求方法设置缓存时间
map $request_method $cache_time {
default 0; # 不缓存
GET 1h; # GET 请求缓存 1 小时
HEAD 1h; # HEAD 请求缓存 1 小时
}
server {
location / {
proxy_cache_valid $cache_time;
proxy_pass http://backend;
}
}变量使用示例
日志中使用变量
# 自定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format detailed '$remote_addr|$time_iso8601|$request_method|$uri|$status|'
'$body_bytes_sent|$request_time|$upstream_response_time|'
'$http_user_agent|$http_referer';
log_format json escape=json '{'
'"timestamp":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request_method":"$request_method",'
'"uri":"$uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"user_agent":"$http_user_agent",'
'"referer":"$http_referer"'
'}';
access_log /var/log/nginx/access.log main;反向代理中使用变量
# 动态设置后端
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
}
server {
listen 80;
server_name example.com;
location / {
# 设置请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
# 超时设置
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 错误页面
proxy_intercept_errors on;
# 传递后端
proxy_pass http://backend;
}
}重定向中使用变量
# 强制 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
# 根据条件重定向
server {
listen 80;
server_name example.com;
if ($http_user_agent ~* "mobile") {
return 302 http://m.example.com$request_uri;
}
location / {
root /var/www/html;
}
}
# 动态重定向
map $uri $redirect_url {
default "";
/old-page /new-page;
/old-blog/ /new-blog/;
}
server {
if ($redirect_url != "") {
return 301 $redirect_url;
}
}缓存控制中使用变量
# 根据文件类型设置缓存
map $request_uri $cache_control {
default "private, no-cache";
~^/static/ "public, max-age=31536000, immutable";
~*\.(jpg|jpeg|png|gif|css|js|woff|woff2|ttf|eot)$ "public, max-age=86400";
~*\.html$ "public, max-age=3600";
}
server {
location / {
add_header Cache-Control $cache_control;
try_files $uri $uri/ =404;
}
location ~* \.(jpg|jpeg|png|gif|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# 根据状态码设置缓存
map $status $cache_time {
200 1h; # 成功响应缓存 1 小时
404 1m; # 404 缓存 1 分钟
500 0; # 500 不缓存
default 0; # 其他不缓存
}
server {
location / {
proxy_cache_valid $cache_time;
proxy_pass http://backend;
}
}A/B 测试中使用变量
# 基于 IP 的 A/B 测试
split_clients "${remote_addr}" $variant {
50% "a";
50% "b";
}
server {
listen 80;
server_name example.com;
location / {
root /var/www/$variant;
add_header X-Variant $variant;
}
}
# 基于 Cookie 的 A/B 测试
map $cookie_variant $variant {
default "a";
~*^b$ "b";
}
server {
listen 80;
server_name example.com;
location / {
if ($variant = "b") {
add_header Set-Cookie "variant=b; Path=/; Max-Age=86400";
}
root /var/www/$variant;
}
}🔧 注意事项
变量定义限制
# 错误: 在 if 中定义变量
server {
location / {
if ($uri = "/test") {
set $test "value"; # 不推荐
}
# $test 可能在其他情况下未定义
}
}
# 正确: 先定义变量
server {
location / {
set $test "";
if ($uri = "/test") {
set $test "value";
}
# $test 总是有值
}
}变量性能考虑
# 变量解析有开销,不要滥用
# 错误:过多变量计算
server {
set $a "value1";
set $b "${a}_suffix";
set $c "${b}_final";
return 200 $c;
}
# 正确:直接使用
server {
return 200 "value1_suffix_final";
}变量安全性
# 不要在日志中记录敏感信息
# 错误:
log_format sensitive '$remote_addr|$http_authorization|$cookie_sessionid';
# 正确:
log_format safe '$remote_addr|$request_method|$uri|$status';