【10.28】nginx 的 rewrite 说明

1.12 nginx 的 rewrite 配置 - if

  • 域名跳转(重定向)、URL 重写(伪静态)、动静分离(跳转域名,并接入 CDN 实现加速)
    ·依赖 PCRE 库
    ·模块:ngx_http_rewrite_module

  • Rwrite 相关指令
    if (条件) {command}

  • if指令

    格式:if (条件判断) { 具体的rewrite规则 }

    条件举例
    条件判断语句由Nginx内置变量、逻辑判断符号和目标字符串三部分组成。
    其中,内置变量是Nginx固定的非自定义的变量,如,$request_method, $request_uri等。

    逻辑判断符号,有=, !=, ~, ~, !~, !~

    ! 表示相反的意思,~为匹配符号,它右侧为正则表达式,区分大小写,而~*为不区分大小写匹配。

    目标字符串可以是正则表达式,通常不用加引号,但表达式中有特殊符号时,比如空格、花括号、分号等,需要用单引号引起来。

示例1

if ($request_method = POST)  //当请求的方法为POST时,直接返回405状态码
{
    return 405; //在该示例中并未用到rewrite规则,if中支持用return指令。
}

示例2

if ($http_user_agent ~ MSIE) //user_agent带有MSIE字符的请求,直接返回403状态码
{
    return 403;
}

如果想同时限制多个user_agent,还可以写成这样

if ($http_user_agent ~ "MSIE|firefox|spider")
{
    return 403;
}

示例3

if(!-f $request_filename)  //当请求的文件不存在,将会执行下面的rewrite规则
{
    rewrite 语句;
}

示例4

if($request_uri ~* 'gid=\d{9,12}/')  //\d表示数字,{9,12}表示数字出现的次数是9到12次,如gid=123456789/就是符合条件的。
{
    rewrite 语句;
}

参考:
http://note.youdao.com/noteshare?id=f90a325f7067ccf64d38a404831601b8&sub=70FC34A78563430AAB7C372F371673B3

1.13 nginx 中的 break 和 last

1、rewrite 中 的 break 和 last

两个指令用法相同,但含义不同,需要放到rewrite规则的末尾,用来控制重写后的链接是否继续被nginx配置执行(主要是rewrite、return指令)。

  • 示例1(连续两条 rewrite 规则):
[root@alexis-01 conf.d]# vim www.1.com.conf

server{
    listen 80;
    server_name www.1.com;
    index index.html;
    root /data/wwwroot/www.1.com;
        rewrite_log on;
        rewrite /1.html /2.html;
        rewrite /2.html /3.html;
}

rewrite_log on 指打开 rewrite log

在 nginx.conf 中,将 error_log 级别改为 notice; 打开

error_log  /var/log/nginx/error.log notice

访问测试:

[root@alexis-01 ~]# cat /data/wwwroot/www.1.com/1.html
111111
[root@alexis-01 ~]# cat /data/wwwroot/www.1.com/2.html 
222222
[root@alexis-01 ~]# cat /data/wwwroot/www.1.com/3.html 
333333
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/1.html
333333

查看 error log 可以发现,请求被 rewrite 到了 3.html

[root@alexis-01 ~]# tail -4 /var/log/nginx/error.log
2019/10/28 21:13:41 [notice] 8689#8689: *11 "/1.html" matches "/1.html", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:13:41 [notice] 8689#8689: *11 rewritten data: "/2.html", args: "", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:13:41 [notice] 8689#8689: *11 "/2.html" matches "/2.html", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:13:41 [notice] 8689#8689: *11 rewritten data: "/3.html", args: "", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"

当我们请求 /1.html 时,rewrite 到了 /2.html,然后又从 /2.html rewrite 到了 /3.html,最终访问到的是 /3.html,两条 rewrite 规则先后执行。

2、break 和 last 在 location { } 外部

格式: rewrite xxxxx break;

  • 示例2(增加 break ):
server{
    listen 80; 
    server_name test.com;
    root /tmp/123.com;

    rewrite /1.html /2.html break;
    rewrite /2.html /3.html;
}

当我们请求 /1.html 时,最终访问到的是 /2.html,就终止了
说明 break 在此示例中,作用是不再执行 break 以下的rewrite 规则。

访问测试:

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/1.html
222222

查看错误日志,发现只匹配到了一个,后面就没有继续执行了

[root@alexis-01 ~]# !tail
tail -4 /var/log/nginx/error.log
2019/10/28 21:16:33 [notice] 6934#6934: worker process 8689 exited with code 0
2019/10/28 21:16:33 [notice] 6934#6934: signal 29 (SIGIO) received
2019/10/28 21:16:47 [notice] 8698#8698: *12 "/1.html" matches "/1.html", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"
2019/10/28 21:16:47 [notice] 8698#8698: *12 rewritten data: "/2.html", args: "", client: 127.0.0.1, server: www.1.com, request: "GET HTTP://www.1.com/1.html HTTP/1.1", host: "www.1.com"

同理,将 break 改为 last ,效果是一样的

[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.1.com.conf 
server{
    listen 80;
    server_name www.1.com;
    index index.html;
    root /data/wwwroot/www.1.com;
        rewrite_log on;
        rewrite /1.html /2.html last;
        rewrite /2.html /3.html;
}
[root@alexis-01 ~]# nginx -s reload
[root@alexis-01 ~]# !curl
curl -x127.0.0.1:80 www.1.com/1.html
222222

但,当配置文件中有 location 时,它还会去执行 location { } 段的配置(请求要匹配该 location)。

  • 示例3(break 后面还有 location段):
server{
    listen 80; 
    server_name test.com;
    root /tmp/123.com;

    rewrite /1.html /2.html break;
    rewrite /2.html /3.html;
    location /2.html {
        return 403;
    }
}

当请求 /1.html 时,最终会返回 403 状态码,说明它去匹配了 break 后面的 location { } 配置。

以上 2 个示例中,可以把 break 替换为 last ,它们两者起到的效果一模一样。

3、当 break 和 last 在location { } 里面时

  • 示例4(什么都不加):
server{
    listen 80; 
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    rewrite_log on;
    location / {
        rewrite /1.html /2.html;
        rewrite /2.html /3.html;
    }
    location /2.html
    {
        rewrite /2.html /a.html;
    }
    location /3.html
    {
        rewrite /3.html /b.html;
    }
}

访问测试:

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/1.html
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.16.1</center>
</body>
</html>

当请求 /1.html,跳到 /2.html,又跳到 /3.html,跳出 location 后,进入 location /3.html,然后从 /3.html 最终将会访问 /b.html。
到 /b.html 还未结束,由于匹配到了 /,又到了 location / 下发现两条 rewrite 规则都不匹配,最终发回结果
而 /b.html 不存在,所以访问到了 404

  • 示例5(增加 break ):
server{
    listen 80; 
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    rewrite_log on;
    location / {
        rewrite /1.html /2.html break;
        rewrite /2.html /3.html;
    }
    location /2.html
    {
        rewrite /2.html /a.html;
    }
    location /3.html
    {
        rewrite /3.html /b.html;
    }
}

访问测试

[root@alexis-01 ~]# !curl
curl -x127.0.0.1:80 www.1.com/1.html
222222

当请求 /1.html,最终会访问 /2.html
在 location { } 内部,遇到 break ,本 location { } 内以及后面的所有 location { } 内的所有指令都不再执行。

  • 示例6(增加 last):
server{
    listen 80; 
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    
    location / {
        rewrite /1.html /2.html last;
        rewrite /2.html /3.html;
    }
    location /2.html
    {
        rewrite /2.html /a.html;
    }
    location /3.html
    {
        rewrite /3.html /b.html;
    }
}

当请求 /1.html,最终会访问 /a.html
在 location { } 内部,遇到 last,本 location { } 内后续指令不再执行,而 rewrite 后的 url 再次从头开始,从头到尾匹配一遍规则。
在 location / 和 location /2.html 之间,会匹配后者,因为后者更精准,到了后一个 location,匹配了 /a.html,然后 /a.html 回去 location / 下去找,发现都不匹配,就直接返回 a.html 内容,而它不存在,所以 404 了。

  • 结论
    当 rewrite 规则在 location { } 外,break 和 last 作用一样,遇到 break 或 last 后,其后续的 rewrite/return 语句不再执行。但后续有 location { } 的话,还会近一步执行 location { } 里面的语句,当然前提是请求必须要匹配该 location。

    当 rewrite 规则在 location { } 里,遇到 break 后,本 location { } 与其他 location { } 的所有 rewrite/return 规则都不再执行。

    当 rewrite 规则在 location { } 里,遇到 last 后,本 location { } 里后续 rewrite/return 规则不执行,但重写后的 url 再次从头开始执行所有规则,哪个匹配执行哪个。

  • 参考:
    http://note.youdao.com/noteshare?id=65a4d769f41ae9ae034922b3644284b2&sub=819C0913ECCE4B6396FEB120B1DF92CF

1.14 nginx 中的 return 用法

  • nginx 的 return 指令
    该指令一般用于对请求的客户端直接返回响应状态码。在该作用域内 return 后面的所有 nginx 配置都是无效的。
    可以使用在 server、location 以及 if 配置中。

除了支持跟状态码,还可以跟字符串或者 url 链接。

1、直接返回状态码

  • 示例1:
server{
    listen 80;
    server_name www.1.com;
    return 403;
    rewrite /(.*) /abc/$1;  //该行配置不会被执行。
}
  • 示例2:
server {
.....

if ($request_uri ~ "\.htpasswd|\.bak")
{
    return 404;
    rewrite /(.*) /aaa.txt;  //该行配置不会被执行。
}
//如果下面还有其他配置,会被执行。
.....
}

访问测试

[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.1.com.conf
server{
    listen 80;  
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    rewrite_log on;
    if ($request_uri ~ "\.htpasswd|\.bak")
    {
        return 405;
        rewrite /(.*) /aaa.txt;  
    }
}
[root@alexis-01 ~]# nginx -s reload
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 405 Not Allowed
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:16:31 GMT
Content-Type: text/html
Content-Length: 157
Connection: keep-alive
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasssssssswd -I
HTTP/1.1 404 Not Found
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:17:02 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive

因为机器里本来就没有要访问的文件,所以肯定会返回 404,那么把状态码改为 405 测试,如果没有匹配到,那么才是 404

2、返回字符串

  • 示例3:
server{
    listen 80;
    server_name www.1.com;
    return 200 "hello";
}

访问测试

[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.1.com.conf
server{
    listen 80;  
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    rewrite_log on;
    if ($request_uri ~ "\.htpasswd|\.bak")
    {
        return 200 "hello";
        rewrite /(.*) /aaa.txt;  
    }
}
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:21:23 GMT
Content-Type: application/octet-stream
Content-Length: 5
Connection: keep-alive

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
hello

说明: 如果要想返回字符串,必须要加上状态码,否则会报错。
还可以支持 json 数据

  • 示例4:
location ^~ /alexis {
    default_type application/json ;
    return 200  '{"name":"alexis","id":"100"}';
}

访问测试

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
{"name":"alexis","id":"100"}

3、也支持写一个变量

  • 示例5:
location /test {
    return 200 "$host $request_uri";
}

访问测试

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
www.1.com /123/.htpasswd

4、返回 url

  • 示例6:
server{
    listen 80;
    server_name www.1.com;
    return http://www.1.com/12345678.html;
}

访问测试

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:35:40 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: http://www.1.com/12345678.html

连接之前加状态码也可以
·改为 301

return 301 http://www.1.com/12345678.html;
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:38:34 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://www.1.com/12345678.html

·改为 200

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 14:40:10 GMT
Content-Type: application/octet-stream
Content-Length: 30
Connection: keep-alive

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.1.com/123/.htpasswd
http://www.1.com/12345678.html

注意: return 后面的 url 必须是以 http:// 或者 https:// 开头的。

5、生成场景实战,返回一段代码

背景: 网站被黑了,凡是在百度点击到本网站的请求,全部都跳转到了一个赌博网站。

  • 通过nginx解决:
if ($http_referer ~ 'baidu.com') 
{
    return 200 "<html><script>window.location.href='//$host$request_uri';</script></html>";
}

如果写成:

return http://$host$request_uri;

在浏览器中会提示“重定向的次数过多”。

  • 参考:
    http://note.youdao.com/noteshare?id=0dae7f742625a2ff14a0f4f59b894a69&sub=74133821946C498D93A114668C11CF14

1.15 rewrite 规则语法

  • rewrite 规则

格式: rewrite regex replacement [flag]

  • rewrite 配置可以在 server、location 以及 if 配置段内生效

  • regex 是用于匹配 URI 的正则表达式,其不会匹配到 $host(域名)

  • replacement 是目标跳转的 URI,可以 http:// 或者 https:// 开头,也可以省略掉 $host,直接写 $request_uri 部分(即请求的链接)

  • flag,用来设置 rewrite 对 URI 的处理行为,其中有 break、last、rediect、permanent,其中 break 和 last 在前面已经介绍过

    rediect 和 permanent 的区别在于,前者为临时重定向(302),而后者是永久重定向(301),对于用户通过浏览器访问,这两者的效果是一致的。

    但是,对于搜索引擎蜘蛛爬虫来说就有区别了,使用 301 更有利于 SEO。所以,建议 replacemnet 是以 http:// 或者 https:// 开头的 flag 使用 permanent。

  • 示例1
location / {
    rewrite /(.*) http://www.1.com/$1 permanent;
}

说明:.* 为正则表达式,用 () 括起来,在后面的 URI 中可以调用它,第一次出现的 () 用 $1 调用,第二次出现的 () 用 $2 调用,以此类推。

  • 示例2
location / {
    rewrite /.* http://www.1.com$request_uri permanent;
}

说明:在 replacement 中,支持变量,这里的 $request_uri 就是客户端请求的链接

  • 示例3
server{
    listen 80;
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    index index.html;
    rewrite /(.*) /abc/$1 redirect;
}

说明:本例中的 rewrite 规则有问题,会造连续循环,最终会失败,解决该问题有两个方案。
关于循环次数,经测试发现,curl 会循环50次,chrome 会循环80次,IE会循环120次,firefox 会循环20次。

  • 示例4
server{
    listen 80;
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    index index.html;
    rewrite /(.*) /abc/$1 break;
}

说明:在 rewrite 中使用 break,会避免循环。

  • 示例5
server{
    listen 80;
    server_name www.1.com;
    root /data/wwwroot/www.1.com;
    index index.html;
    if ($request_uri !~ '^/abc/')
    {
        rewrite /(.*) /abc/$1 redirect;
    }
}

说明:加一个条件限制,也可以避免产生循环

访问测试

[root@alexis-01 ~]# vim /etc/nginx/conf.d/www.2.com.conf

server{
    listen 80;
    server_name www.2.com;
    root /data/wwwroot/www.2.com;
    location /
    {
        rewrite /(.*) /abc/$1 redirect;
    }
}
[root@alexis-01 ~]# curl -x127.0.0.1:80 www.2.com/1.html -I
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 15:05:56 GMT
Content-Type: text/html
Content-Length: 145
Location: http://www.2.com/abc/1.html
Connection: keep-alive

[root@alexis-01 ~]# curl -x127.0.0.1:80 www.2.com/1.html -L
curl: (47) Maximum (50) redirects followed

//将配置文件中 redirect 改为 break[root@alexis-01 ~]# curl -x127.0.0.1:80 www.2.com/abc/1.html -I -L
HTTP/1.1 404 Not Found
Server: nginx/1.16.1
Date: Mon, 28 Oct 2019 15:10:15 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive

正确写法,一般按照 示例 5 写即可

  • 参考:
    http://note.youdao.com/noteshare?id=309d530e9fbab2b090811b25ec8a565d&sub=EF5AFCDC3C31425F9E88539F832CB914
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值