本文清理整理了 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;优先用map、try_files、error_page、allow/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/deny 与 error_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
map 在 http 级别计算变量,避免到处写 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 的访问控制失效。
维护模式、白名单访问、特定脚本管控都可以通过更清晰的指令组合实现,可读性和可维护性更高。