Administrator
发布于 2025-11-05 / 2 阅读
0
0

Nginx中的判断条件

本文清理整理了 Nginx 中 if 的判断条件、注意事项,以及提供更安全、可维护的替代写法和多个可复制的配置样例。

判断条件速查(if 可用)

  • =:等值比较(如:if ($remote_addr = 1.2.3.4)

  • ~:正则匹配,区分大小写(如:if ($request_uri ~ ^/api/)

  • ~*:正则匹配,不区分大小写

  • !~ / !~*:正则不匹配(分别区分/不区分大小写)

  • -f / !-f:路径存在且为文件

  • -d / !-d:路径存在且为目录

  • -e / !-e:路径存在(文件或目录均可)

  • -x / !-x:路径存在且可执行

提示:上述文件/目录判断仅在 if 指令内可用;若目标是“文件是否存在则优先静态返回”,更推荐使用 try_files(见下方样例)。

使用建议与常见坑

  • 避免在复杂场景大量使用 if;优先用 maptry_fileserror_pageallow/deny、更精确的 location 来实现业务逻辑。

  • if 推荐放在 location 中与 return/rewrite 搭配使用;过度使用 if 可能造成难以排查的副作用。

  • 处于反向代理/负载均衡场景时,$remote_addr 通常是上游的地址;若要基于真实客户端 IP 做控制,请正确配置 real ip 模块:

# 信任的反向代理/负载均衡地址段
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from 172.16.0.0/12;

# 按照常见约定从头部中提取真实 IP
real_ip_header X-Forwarded-For;
real_ip_recursive on;

配置样例

1)维护模式:仅允许公司 IP 正常访问,其他用户看到维护页

更推荐使用 allow/deny 配合 error_page,避免复杂 if 逻辑。

http {
  # 若处于反向代理后面,务必配好 real ip(见上文)

  server {
    listen 80;
    server_name example.com;

    # 允许的公司/内网 IP(示例)
    allow 222.222.222.222;
    allow 192.168.1.170;
    allow 192.168.1.169;
    deny  all;  # 其他全部拒绝(403)

    # 将 403 转换为 503,并展示维护页面
    error_page 403 =503 /tingfu.html;
    error_page 503 /tingfu.html;

    # 维护页的静态文件位置
    location = /tingfu.html {
      root /var/www/html;  # /var/www/html/tingfu.html
    }

    # 正常业务入口(白名单 IP 会走这里)
    location / {
      proxy_pass http://app_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;
    }
  }
}

若只想对某个目录/脚本启用维护模式,可将 allow/denyerror_page 放到对应的 location 中。

location ~ ^/design/.*\.php$ {
  allow 222.222.222.222;
  allow 192.168.1.170;
  allow 192.168.1.169;
  deny  all;
  error_page 403 =503 /tingfu.html;
  # 白名单走正常逻辑(示例为静态或上游)
  fastcgi_pass php_fpm;
}

2)仅允许内部 IP 访问某个 PHP,其他用户跳转到另一个页面

将原先的 set + 多重 if 重写为更清晰的 location + 访问控制。

location = /img/test.php {
  # 仅允许某个内部 IP 访问
  allow 222.222.222.222;
  deny  all;

  # 非白名单用户跳转到 55555.php(302 临时重定向,也可用 301/307)
  error_page 403 =302 /img/55555.php;

  # 白名单用户走正常 PHP 逻辑
  fastcgi_pass php_fpm;
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

location = /img/55555.php {
  fastcgi_pass php_fpm;
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

3)基于 User-Agent 的简单访问控制(谨慎使用 if)

location / {
  # 拦截常见爬虫/脚本(示例,按需调整)
  if ($http_user_agent ~* (curl|wget|scrapy|bot)) {
    return 403;
  }

  proxy_pass http://app_backend;
}

4)文件存在优先走静态,否则走后端(用 try_files 替代 if -f)

server {
  listen 80;
  server_name static.example.com;
  root /var/www/static;

  location / {
    # 若请求文件存在则直接返回;不存在则反代到后端
    try_files $uri $uri/ @backend;
  }

  location @backend {
    proxy_pass http://app_backend;
  }
}

5)使用 map 做更复杂条件,再在 location 内用 return/rewrite

maphttp 级别计算变量,避免到处写 if

http {
  # 示例:按 IP 生成是否为可信的标记
  map $remote_addr $is_trusted_ip {
    default 0;
    222.222.222.222 1;
    192.168.1.170   1;
    192.168.1.169   1;
  }

  server {
    listen 80;
    server_name map.example.com;

    error_page 503 /tingfu.html;
    location = /tingfu.html { root /var/www/html; }

    location / {
      # 在 location 内根据变量做快速返回
      if ($is_trusted_ip = 0) { return 503; }
      proxy_pass http://app_backend;
    }
  }
}

6)基于 scheme 的做ssl跳转

 server {
        listen 0.0.0.0:80;
        listen 443 ssl;
        http2 on;
        server_name example.com;
        if ($scheme = http ) {
            return 301 https://$host$request_uri;
        }
        ssl_certificate cert.d/example.com.pem;
        ssl_certificate_key cert.d/example.com.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!3DES:!ADH:!RC4:!DH:!DHE;
        ssl_prefer_server_ciphers on;

        # 通用代理配置
        include proxy.conf;
        location / {
            proxy_pass http://example;
        }
   }

小结

  • 若能用 location/allow/deny/error_page/try_files/map 来表达业务,尽量避免到处写 if

  • 在代理/负载均衡场景下,务必正确配置 real ip,以免基于 IP 的访问控制失效。

  • 维护模式、白名单访问、特定脚本管控都可以通过更清晰的指令组合实现,可读性和可维护性更高。


评论