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_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';

📚 相关链接


标签: nginx 变量 配置 map 动态