Nginx内置变量及案例详解

好的,Nginx 内置变量是 Nginx 配置中极其强大且常用的功能。它们提供了关于当前请求、响应、连接状态等丰富的信息,使你能够实现动态配置、日志定制、访问控制、重写规则、代理设置等复杂逻辑。下面我将详细介绍常用的内置变量并辅以实际案例详解。

核心概念:

  • 来源: 这些变量由 Nginx 核心或模块(如 ngx_http_core_module, ngx_http_proxy_module, ngx_http_log_module 等)在请求处理的不同阶段自动设置。
  • 作用域: 大多数变量在请求处理期间都是可用的,可以在 location, server, http 块以及 access_log, rewrite, proxy_pass 等指令中使用。
  • 值类型: 通常是字符串。有些变量在某些上下文中可能为空。

常用内置变量分类及详解:

一、请求行与请求头相关变量

  1. $uri / $document_uri

    • 含义: 当前请求的 规范化后的 URI(不包括查询参数 ? 后面的部分)。Nginx 会对其进行解码、移除多余的斜杠、解析相对路径 ...
    • 案例:
      location /images/ {
          # 记录请求的图片路径 (如 /images/cat.jpg)
          access_log /var/log/nginx/image_access.log '$uri';
      }
      
  2. $request_uri

    • 含义: 客户端发来的 原始完整的请求 URI(包括查询参数)。未经任何规范化处理。
    • 案例:
      # 重写规则:如果请求URI包含 'oldpath',则重定向到新路径,但保留原始查询参数
      if ($request_uri ~* "^/oldpath/(.*)") {
          return 301 /newpath/$1?$args;
      }
      
  3. $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;
          }
          # ... 其他处理
      }
      
  4. $is_args

    • 含义: 如果请求 URI 包含查询参数,则值为 ?;否则为空字符串。常用于拼接 URL。
    • 案例:
      # 在重写或代理时,优雅地添加或保留查询参数
      location /api/ {
          rewrite ^/api/(.*)$ /internal_api/$1$is_args$args break;
          proxy_pass http://api_backend;
      }
      
  5. $request_method

    • 含义: 客户端请求的 HTTP 方法(如 GET, POST, PUT, DELETE 等)。
    • 案例:
      # 限制特定 location 只允许 GET 和 HEAD 方法
      location /static/ {
          if ($request_method !~ ^(GET|HEAD)$) {
              return 405; # Method Not Allowed
          }
          # ... 静态文件服务配置
      }
      
  6. $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"';
      
  7. $host

    • 含义: 按以下优先级确定:
      1. 请求行中的主机名 (HTTP/1.0)。
      2. Host 请求头的值。
      3. 与请求匹配的 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;
      }
      
  8. $http_host

    • 含义: 直接取自 Host 请求头的值,包含端口号(如果客户端指定了端口,如 Host: example.com:8080)。
    • $host 区别: $host 不包含端口,且回退机制更强。通常 $host 更安全通用,除非明确需要端口信息。
    • 案例:
      # 需要精确包含端口信息的情况 (较少见)
      if ($http_host != "www.example.com:443") {
          # ... 做一些处理
      }
      

二、客户端信息相关变量

  1. $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;
      }
      
  2. $remote_port

    • 含义: 客户端用于建立连接的 端口号
    • 案例: 较少单独使用,有时用于调试或详细日志记录。
      log_format detailed ... '$remote_addr:$remote_port ...';
      
  3. $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"';
          # ... 其他配置
      }
      

三、响应相关变量

  1. $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; # 记录后端实际状态
      }
      
  2. $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;
      
  3. $bytes_sent

    • 含义: 发送给客户端的 总字节数(包括响应头)。
    • 案例: 需要精确计算总传输量时使用。
  4. $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;
      }
      

四、连接与请求处理相关变量

  1. $connection / $connection_requests

    • $connection: 当前连接的序列号(唯一标识一个连接)。在连接复用时,多个请求共享同一个 $connection
    • $connection_requests: 当前连接上已经处理过的 请求数量(对于 keepalive 连接)。
    • 案例:
      # 记录连接信息 (用于分析长连接复用情况)
      log_format connection_log '$remote_addr - $connection [$connection_requests] "$request"';
      
  2. $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秒
      }
      
  3. $request_length

    • 含义: 客户端请求的 总长度(包括请求行、请求头和请求体)。可用于检测异常大的请求。
    • 案例:
      # 限制非常大的请求 (防止DoS)
      client_max_body_size 10m; # 限制请求体大小
      # 在日志中记录请求长度
      log_format ... '$request_length' ...;
      
  4. $request

    • 含义: 完整的 原始请求行(如 GET /index.html?param=value HTTP/1.1)。
    • 案例:
      # 标准访问日志格式的核心部分
      log_format main ... '"$request"' ...;
      

五、时间相关变量

  1. $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;
      
  2. $time_local

    • 含义: 服务器时间的 本地格式(如 27/Oct/2023:14:31:15 +0800)。格式由 log_format 指令的默认值或配置决定。
    • 案例: 经典日志格式常用。
  3. $msec

    • 含义: 当前时间戳(秒),精度到毫秒(如 1698388275.123)。用于需要高精度时间戳的场景。
    • 案例: 自定义计时、唯一ID生成(需结合其他信息)。

六、代理与上游服务器相关变量(需启用相应模块)

  1. $proxy_host

    • 含义:proxy_pass 指令中指定的 代理目标服务器的主机名或 IP 地址和端口。如果 proxy_pass 只指定了 URI 部分,此变量可能为空。
    • 案例: 记录代理目标。
  2. $proxy_port

    • 含义: proxy_pass 指令中指定的 代理目标服务器的端口
  3. $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链
      }
      
  4. $upstream_addr

    • 含义: 处理请求的 上游服务器的 IP 地址和端口(或 Unix-domain socket 路径)。如果使用了负载均衡,它反映的是实际处理请求的那台后端服务器。调试后端问题的关键变量!
    • 案例:
      # 在日志中记录请求被转发到了哪个后端服务器
      log_format upstream_log ... '$upstream_addr' ...;
      access_log /var/log/nginx/upstream.log upstream_log;
      
  5. $upstream_status

    • 含义: 上游服务器返回的 HTTP 响应状态码。如果 Nginx 无法连接到上游,此变量通常为 502 (Bad Gateway) 或 504 (Gateway Timeout)。
    • 案例: 区分是后端服务错误 (5xx) 还是 Nginx 本身无法连接后端。
      log_format backend_error ... '$upstream_status' ...;
      
  6. $upstream_response_time

    • 含义: 从 Nginx 开始向上游服务器建立连接到接收完上游服务器响应头的 时间(秒),精度为毫秒。衡量后端服务器性能的关键指标!
    • 案例:
      # 记录后端响应时间
      log_format backend_perf ... '$upstream_response_time' ...;
      
  7. $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' ...;
      

七、其他实用变量

  1. $scheme

    • 含义: 请求使用的 协议方案httphttps)。
    • 案例:
      # 强制 HTTPS 重定向
      if ($scheme != "https") {
          return 301 https://$host$request_uri;
      }
      
  2. $server_name

    • 含义: 匹配当前请求的 server 块中 server_name 指令的第一个名称
    • 案例: 在需要精确知道配置块匹配的 server name 时使用。
  3. $server_addr / $server_port

    • $server_addr: 接受请求的服务器的 IP 地址
    • $server_port: 接受请求的服务器的 端口号
    • 案例: 构造绝对 URL 或记录服务器信息。
  4. $nginx_version

    • 含义: 当前运行的 Nginx 版本号
    • 案例: 在响应头中标识服务器版本(注意安全风险,通常建议隐藏)或条件配置。
      # 根据版本启用不同特性 (谨慎使用)
      if ($nginx_version ~ "^1\.18\.") {
          # 针对 1.18.x 的特定配置
      }
      

综合案例详解:

  1. 动态访问日志:

    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链。非常适合调试和性能分析。
  2. 基于 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;
        }
    }
    
  3. 安全的文件下载限制:

    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;
        # ... 文件服务配置
    }
    
  4. 负载均衡调试日志:

    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;
    
    • 清晰展示请求时间线、客户端信息、具体请求、最终到达的后端、后端状态码以及关键时间指标,便于排查负载均衡或后端问题。
  5. 自定义错误页面带信息:

    error_page 404 /404.html;
    location = /404.html {
        internal; # 只能内部访问
        # 在错误页面中嵌入变量 (需要SSI支持或动态生成)
        ssi on;
        add_header Content-Type text/html;
        return 200 '404 Not Found
                   

    Oops! Page Not Found

    The requested URL $uri was not found on this server.

    Error occurred at: $time_iso8601

    '; }

重要注意事项:

  1. if 指令的陷阱: iflocation 上下文中有许多限制和令人惊讶的行为(有时会破坏继承关系)。尽量避免在 if 块中进行复杂的配置修改,优先使用 map, try_files, return, rewrite (带 last/break) 或 split_clients 等更安全的指令。如果必须用 if,主要用于检查变量值然后 returnrewrite
  2. 变量作用域: 大多数内置变量在请求范围内有效。使用 set 指令创建的自定义变量作用域是其所在的配置块(http, server, location)。
  3. 性能: 频繁使用某些变量(如正则匹配)或复杂的 map 块可能会影响性能。在高流量环境中需谨慎优化。
  4. 安全性: 不要将未经处理的用户输入(如 $args, $request_uri, $http_*)直接用于敏感操作(如文件路径、命令执行)。始终进行验证、过滤或转义。
  5. 空值: 某些变量在某些上下文中可能为空(例如 $remote_user 未认证时,$upstream_* 未使用代理时)。在逻辑判断时要考虑这种情况。
  6. 文档: 最权威的文档始终是 Nginx 官方文档:http://nginx.org/en/docs/varindex.html。不同模块提供的变量在其模块文档中列出。

通过熟练理解和灵活运用这些内置变量,你可以极大地提升 Nginx 配置的灵活性、可观测性和功能性,构建出更强大、更智能的 Web 服务器配置。

你可能感兴趣的:(nginx,运维,内置变量)