当 rewrite 的重写规则满足不了需求时,比如需要判断当文件不存在时、当路径包含 xx 时等条件,则需要用到 if
if 语法、
if (表达式) { }
表达式语法:
- 当表达式只是一个变量时,如果值为空或任何以 0 开头的字符串都会当做 false
- 直接比较变量和内容时,使用=或!=
- -f 和!-f 用来判断是否存在文件
- -d 和!-d 用来判断是否存在目录
- -e 和!-e 用来判断是否存在文件或目录
- -x 和!-x 用来判断文件是否可执行
为了配置 if 的条件判断,这里需要用到 nginx 中内置的全局变量
$args 这个变量等于请求行中的参数,同$query_string $content_length 请求头中的 Content-length 字段。 $content_type 请求头中的 Content-Type 字段。 $document_root 当前请求在 root 指令中指定的值。 $host 请求主机头字段,否则为服务器名称。 $http_user_agent 客户端 agent 信息 $http_cookie 客户端 cookie 信息 $limit_rate 这个变量可以限制连接速率。 $request_method 客户端请求的动作,通常为 GET 或 POST。 $remote_addr 客户端的 IP 地址。 $remote_port 客户端的端口。 $remote_user 已经经过 Auth Basic Module 验证的用户名。 $request_filename 当前请求的文件路径,由 root 或 alias 指令与 URI 请求生成。 $scheme HTTP 方法(如 http,https)。 $server_protocol 请求使用的协议,通常是 HTTP/1.0 或 HTTP/1.1。 $server_addr 服务器地址,在完成一次系统调用后可以确定这个值。 $server_name 服务器名称。 $server_port 请求到达服务器的端口号。 $request_uri 包含请求参数的原始 URI,不包含主机名,如:”/foo/bar.php?arg=baz”。 $uri 不带请求参数的当前 URI,$uri 不包含主机名,如”/foo/bar.html”。 $document_uri 与$uri 相同。
举例说明
1、如果文件不存在则返回 400
if (!-f $request_filename) { return 400; }
2、如果 host 不是 jouypub.com,则 301 到 jouypub.com 中
if ( $host != 'jouypub.com' ){ rewrite ^/(.*)$ https://jouypub.com/$1 permanent; }
3、如果请求类型不是 POST 则返回 405
if ($request_method = POST) { return 405; }
4、如果参数中有a=1
则 301 到指定域名
if ($args ~ a=1) { rewrite ^ http://example.com/ permanent; }
5、在某种场景下可结合 location 规则来使用,如:
# 访问 /test.html 时 location = /test.html { # 设置默认值为 xiaowu set $name xiaowu; # 如果参数中有 name=xx 则使用该值 if ($args ~* name=(\w+?)(&|$)) { set $name $1; } # 301 rewrite ^ /$name.html permanent; }
上面表示:
/test.html => /xiaowu.html
/test.html?name=ok => /ok.html?name=ok
Nginx 中 if 语句里的 AND,OR 多重判断
最近遇到 Nginx 钟多条件判断,网上查了查资料,这里记录一下。
一、if 语句中的判断条件(nginx)
1、正则表达式匹配:
==:等值比较;
~:与指定正则表达式模式匹配时返回“真”,判断匹配与否时区分字符大小写;
~*:与指定正则表达式模式匹配时返回“真”,判断匹配与否时不区分字符大小写;
!~:与指定正则表达式模式不匹配时返回“真”,判断匹配与否时区分字符大小写;
!~*:与指定正则表达式模式不匹配时返回“真”,判断匹配与否时不区分字符大小写;
2、文件及目录匹配判断:
-f, !-f:判断指定的路径是否为存在且为文件;
-d, !-d:判断指定的路径是否为存在且为目录;
-e, !-e:判断指定的路径是否存在,文件或目录均可;
-x, !-x:判断指定路径的文件是否存在且可执行;
二、场景:
有些公司可能有这样的需求,如:我的网站或者网页游戏需要更新,所有的用户或者玩家访问到的是一个停服更新页面,而本公司的 IP 可以访问,甚至说本公司的某个内网 IP 可以访问,用于确认更新成功与否,针对这个问题写了如下的访问控制规则:
Nginx 多重条件判断(只是一个简单的例子,自己可以更改或者增加更多的判断条件),下面是两个例子和写法:1、可以作为 nginx 的停服更新使用,仅允许 222.222.222.222 或者内网的两个 IP 访问,其他 IP 都 rewrite 到停服页面
Nginx.conf 中加入在你项目的正确位置
set $my_ip ''; #注意这里的$remote_addr 如何用了负载均衡的话,这里应该是$http_x_forwarded_for if ( $remote_addr = 222.222.222.222) { set $my_ip 1; } if ( $remote_addr = 192.168.1.170 ) { set $my_ip 1; } if ( $remote_addr = 192.168.1.169 ) { set $my_ip 1; } #将*.php 转到 tingfu.html if ( $my_ip != 1) { rewrite ^/design/(.*)\.php$ /tingfu.html?$1&; }
2、访问某个 php 应用的时候我只想让内部的某个 IP 访问,其他的 IP 都转到另一个 PHP 上。如下:
访问 test.php,且 IP 不等 222.222.222.222 的跳转到 55555.php:
set $test ''; if ( $request_uri ~* /img/test.php ) { set $test P; } if ( $http_x_forwarded_for !~* ^222\.222\.222\.222.* ) { set $test "${test}C"; } if ( $test = PC ) { #当条件符合 访问 test.php 并且 ip 不是 222.222.222.222 的 转发到 55555.php rewrite ^(.*)$ /img/55555.php permanent; }
例子 1
原理 就是用 SET 变量进行。
AND 就用变量叠加,OR 就用 0 或 1 切换。
nginx 的配置中不支持 if 条件的逻辑与/逻辑或运算 ,并且不支持 if 的嵌套语法,我们可以用变量的方式来实现:
首先是伪代码(即不被 nginx 支持),写在这里只是为了方便理解:
if ($remote_addr ~ "^(12.34|56.78)" && $http_user_agent ~* "spider") { return 403; } 这是等效的,并真实可用的配置 set $flag 0; if ($remote_addr ~ "^(12.34|56.78)") { set $flag "${flag}1"; } if ($http_user_agent ~* "spider") { set $flag "${flag}2"; } if ($flag = "012") { return 403; } ~~~~
nginx 的配置中不支持 if 条件的逻辑与/逻辑或运算 ,并且不支持 if 的嵌套语法,我们可以用变量的方式来实现
set $flag 0; if ($host != 'www.imfeng.com') { set $flag "${flag}1"; } if ($http_user_agent != 'spider') { set $flag "${flag}2"; } if ($flag = "012") { rewrite ^/(.*)$ http://www.imfeng.com/$1 permanent; } 例子 2 location /log { if ($http_user_agent ~ MSIE ) { access_log /var/log/msie.log; } if ($http_user_agent ~ Opera ) { access_log /var/log/opera.log; } if ($http_user_agent ~ Webkit ) { access_log /var/log/webkit.log; } if ($http_user_agent ~ Mozilla ) { access_log /var/log/mozilla.log; } add_header "Content-Type" "text/plain;charset=UTF-8"; return 200 "$http_user_agent"; }