Skip to content

if 条件判断

当需要判断文件不存在时、对不同的客户端IP执行不同的响应或请求路径符合某些条件时执行不同的逻辑,此时会需要用到 if 指令。

if 能在 server 或 location 段中使用,它的语法为:

if (condition) { 
    # ...
}

if 条件

其中 condition 条件语法可以包括如下:

  • 文件及目录匹配

    • 使用 = 或者 != 直接比较变量内容,注意不是 ==
    • 使用 -f!-f 判断文件是否存在
    • 使用 -d!-d 判断目录是否存在
    • 使用 -e!-e 判断文件或目录是否存在
    • 使用 -x!-x 用来判断文件是否可执行
  • 正则匹配

    • ~ 区分大小写匹配指定正则表达式,当匹配时返回"真"
    • ~* 不区分大小写匹配指定正则表达式,当匹配时返回"真"
    • !~ 区分大小写匹配指定正则表达式,当不匹配时候返回"真"
    • !~* 不区分大小写匹配指定正则表达式,当不匹配时候返回"真"

并且 if 指令不支持多条件、不支持嵌套且不支持 else。

break 指令

遇到 break 则跳出,后面的指令不在执行,比如:

if (!-f $reque_filename) {
    set $id = 1; # 有效的指令
    break;
    limit_rate 10k; # 无效的指令
}

return 指令

完成对请求的处理,直接向客户端返回响应状态码。比如:

nginx
# 格式如下:
return code [text];

# 示例1:直接返回状态码
return 403;
# 示例2:拒绝没有有效身份验证令牌的请求时
return 401 "Access denied because token is expired or invalid";
nginx
# 格式如下: 
return code URL;

# 示例:重定向
return 301 $scheme://www.example.com$request_uri;
nginx
# 格式如下:
return URL;

# 示例:重定向
return $scheme://www.example.com$request_uri;

参数解释如下:

参数描述
code返回给客户端的 HTTP 状态码
URL返回给客户端的 URL 地址
text返回给客户端的响应体内容,支持变量。

return 指令使用简单,适用于重定向满足条件的情况,重写的 URL 适用于匹配 server 或 location 块的每个请求,并且可以使用标准 NGINX 变量构建重写的 URL。

rewrite 指令

nginx
rewrite regex replacement [flag];

rewrite 指令使用指定的正则表达式 regex 来匹配请求的 URI,如果匹配成功,则使用 replacement 更改URI,指令会根据配置文件中的顺序来执行。

同时可以使用 flag 标识来终止指令的进一步处理。

如果替换字符串 replacement 以 http://https://$scheme 开头,则停止处理后续内容并直接重定向返回给客户端。

nginx
location / {
    # 当匹配 正则表达式 /test/(.*)时 请求将被临时重定向到 http://www.$1.com
    # 相当于 flag 写为 redirect
    rewrite /test/(.*) http://www.$1.com;
    return 200 "ok";
}
# 在浏览器中输入 127.0.0.1:8080/test1/baidu 
# 则临时重定向到 http://www.baidu.com
# 后面的 return 指令将没有机会执行了
nginx
location / {
    rewrite /test/(.*) www.$1.com;
    return 200 "ok";
}
# 发送请求如下
# curl 127.0.0.1:8080/test1/baidu
# ok

# 此处没有带 http:// 所以只是简单的重写。请求的 uri 由 /test1/baidu 重写为 www.baidu.com
# 因为会顺序执行 rewrite 指令,所以下一步执行 return 指令响应了 ok 字符串

注意重写表达式只对相对路径有效。如果需要配对主机名,应该使用 if 语句,示例如下:

nginx
if ( $host ~* www\.(.*) ) {
    set      $host_without_www $1;
    rewrite  ^(.*)$  $scheme://$host_without_www$1 permanent;
}

执行顺序

  1. 执行 server 块的 rewrite 指令

  2. 执行 location 匹配

  3. 执行选定的 location 中的 rewrite 指令

如果其中某步中的 URI 被重写,则重新循环执行 1 - 3,直到找到真实存在的文件,如果循环超过10次,则返回 500 Internal Server Error 错误。

flag 标识

  • last: 相当于Apache的[L]标记,表示完成 rewrite

  • break: 停止执行当前虚拟主机的后续 rewrite 指令集

  • redirect: 返回302临时重定向,地址栏会显示跳转后的地址

  • permanent: 返回301永久重定向,地址栏会显示跳转后的地址

lastbreak 的区别

  • last 一般写在 server 代码段和 if 条件判断中,而 break 一般使用在 location 中;
  • last 不终止重写后的 url 匹配,即新的 url 会再从 server 走一遍匹配流程,而 break 终止重写后的匹配;
  • breaklast 都能组织继续执行后面的 rewrite 指令;

set 指令

设置变量,语法如下:

set $variable value;

Nginx 支持的 http 变量实现在 ngx_http_variables.cngx_http_core_variables 存储实现。

变量名简单说明
$arg_PARAMETER这个变量包含GET请求中,如果有变量 PARAMETER 时的值
$args这个变量等于请求行中的参数,同 $query_string
$binary_remote_addr二进制的客户地址
$body_bytes_sent响应时送出的body字节数数量。即使连接中断,这个数据也是精确的
$content_length请求头中的 Content-length 字段。
$content_type请求头中的 Content-Type 字段。
$cookie_COOKIEcookie COOKIE变量的值
$document_root当前请求在 root 指令中指定的值。
$host请求主机头字段,否则为服务器名称。
$http_user_agent客户端 agent 信息
$http_cookie客户端 cookie 信息
$limit_rate这个变量可以限制连接速率。
$request_body_file客户端请求主体信息的临时文件名
$request_method客户端请求的动作,通常为 GET 或 POST 。
$remote_addr客户端的 IP 地址,如果上层使用了负载均衡可以尝试获取 $http_x_forwarded_for 的值
$remote_port客户端的端口。
$remote_user已经经过Auth Basic Module验证的用户名。
$request_completion如果请求结束,设置为OK。当请求未结束或如果该请求不是请求链串的最后一个时为空
$request_filename当前请求的文件路径,由 root 或 alias 指令与URI请求生成。
$schemeHTTP方法(如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 相同。

可以通过查看nginx 中内置的全局变量文档获得更多详情。

举例

如果文件不存在返回 400

if (!-f $request_filename) {
    return 400;
}

当访问的文件和目录不存在时,重定向到某个php文件

nginx
if( !-e $request_filename ) {
    rewrite ^/(.*)$ index.php last;
}

如果主机不是 example.com 则跳转到 example.com

if ($host != example.com) {
    rewrite ^/(.*)$ https://example.com/$1 permanent;
}

如果请求类型不是 POST 则返回 405

if ($request_method = POST) {
    return 405;
}

如果请求参数中有 a=1301 到其他域名

if ($args ~ a=1) {
    rewrite ^ http://example.com/ permanent;
}

禁止访问以.sh,.flv,.mp3为后缀名的文件

nginx
location ~ .*\.(sh|flv|mp3)$ {
    return 403;
}

禁止访问多个目录

禁止访问 cron 和 templates 开头的目录。

nginx
location ~ ^/(cron|templates)/ {
    deny all;
    break;
}

Nginx 多重条件判断

nginx 的配置中不支持 if 条件的逻辑与/逻辑或运算 ,并且不支持 if 的嵌套语法。

  • or 或者

    当站点或页面还没有上线,需要仅允许指定 IP 访问整站或特定URI,其他 IP 都返回 405 状态码。

    针对上面的需求,Nginx 可以使用正则表达式,或者使用 set 指定定义变量。

    比如访问者的 IP 是 8.8.8.8 或者 114.114.114.114时允许访问,其他 IP 访问则返回 405。

    nginx
    if ($remote_ip !~ "^(114|8)\.(114|8)\.(114|8)\.(114|8)$") {
        return 405;
    }
    nginx
    set $allow_ip 0;
    
    if ($remote_addr = 114.114.114.114) {
        set $allow_ip 1;
    }
    if ($remote_addr = 8.8.8.8) {
        set $allow_ip 1;
    }
    
    if ($allow_ip != 1) {
        return 405;
    }

    以上是或者的关系,也就是多个条件中满足其一即可。

  • and 并且

    nginx
    set $and 1;
    
    if (<not condition>) {
      set $and 0;
    }
    
    if (<not condition>) {
      set $and 0;
    }
    
    if ($and) {
      # ...
    }
    nginx
    set $and ""
    
    if (<condition>) {
        set $and "0";
    }
    
    if (<condition>) {
        set $and "${and}1";
    }
    
    if ($and = "01") {
        # ...
    }

参考

if 官方文档