编程技术分享平台

网站首页 > 技术教程 正文

看了这篇!再不会 Nginx rewrite 算我输

xnh888 2024-09-09 09:45:24 技术教程 21 ℃ 0 评论


原创 k8svip


写在之前


经常会有人问,nginx rewrite 的事,其实它是很简单的,今天稍微把之前的笔记拿出来分享一下,首先会讲到http请求处理的11个阶段,如果想深入的了解rewrite,就必须对http请求处理的各个阶段有一定的了解,并且如果读nginx源码,也会更加的容易。


(图一)

上图中是nginx处理http请求时,经过的11个阶段,并不是所有的请求都会经过,但大部分都涉及,并且上图中标红的是与rewrite相关的几个阶段;


rewrite 配置说明


语法

Syntax: rewrite regex replacement [flag];

Default: —

Context: server, location, if


rewrite官方解释

If the specified regular expression matches a request URI, URI is changed as specified in the replacement string. The rewrite directives are executed sequentially in order of their appearance in the configuration file. It is possible to terminate further processing of the directives using flags. If a replacement string starts with "http://", "https://", or "$scheme", the processing stops and the redirect is returned to a client.

如果指定的正则表达式匹配一个请求的 URI,则 URI 就按照指定的 replacement 进行重写或重定向,而 rewrite 指令会按照配置文件中出现的顺序执行,并且 flag 标志位可以停止处理;如果 replacement 以"http://"或"https://"或"$scheme"开头,此时停止处理并直接重定向返回客户端;

rewrite语法解释

regex: 对请求的URI做正则匹配,regex匹配的是URI,不包括域名socket和查询参数,默认的查询参数会被追加到replacement末尾,如果不希望在末尾追加query string,可以在replacement的末尾加一个"?";

replacement: 目标URI匹配成功后替换的URL;

flag: 控制指令的执行顺序,如果没有flag,rewrite会根据指令出现的先后顺序,由上到下依次匹配;

rewrite_log指令:,日志文件中会记录rewrite匹配的过程,注意日志是记录在error.log中,可以调试的时候打开 debug 级别查看匹配过程;


flag有哪些

last

stops processing the current set of ngx_http_rewrite_module directives and starts a search for a new location matching the changed URI;

如果匹配的URI,rewrite在server块中,并且last做为flag,匹配到此rewrite URI时,不再向下匹配server块中的rewrite,进而继续下面location URI的查找匹配;

如果匹配的URI,rewrite在location块中,last做为flag,匹配到此rewrite时,会跳出此location块,继续从上到下查找其它的location块URI,但不会再匹配server块中的rewrite中的URI;


break

stops processing the current set of ngx_http_rewrite_module directives as with the break directive;

如果匹配的URI,rewrite在server块中,并且break做为flag,匹配到此rewrite URI时,不再向下匹配server块中的rewrite,进而继续下面location URI的查找匹配;

如果匹配的URI,rewrite在location块中,break做为flag,匹配到此rewrite时,不会跳出此location块,而是继续对location块下面的语句继续运行,不会跳出此location块,并且也不会匹配location 块下面的其它rewrite规则;

完成该rewrite规则的执行后,停止处理后续rewrite指令集,并不再重新查找;但是当前location内剩余非rewrite语句和location外的的非rewrite语句可以执行;


redirect

returns a temporary redirect with the 302 code; used if a replacement string does not start with "http://","https://", 'scheme';(这里的scheme前面有一个¥)

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


permanent

returns a permanent redirect with the 301 code.

返回301永久重定向,地址栏会显示跳转后的地址,即表示如果客户端不清理浏览器缓存,那么返回的结果将永久保存在客户端浏览器中了。


rewrite 配置实例


无 flag 测试用例一

server {

listen 80;


rewrite ^/(.*)$ /k8svip_one/$1;

rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1;


location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}

}


测试及结果

测试:curl http://127.0.0.1/abc

测试结果

This is k8svip_two location

uri: /k8svip_two/abc


无 flag 时,rewrite 会依次向下匹配,根据nginx在http请求处理的阶段中,我们会先匹配server块中的rewrite规则;

第一次匹配rewrite ^/(.*)$ /k8svip_one/$1; URI变成:/k8svip_one/abc,无flag继续向下匹配;

第二次匹配rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1; URI变成/k8svip_two/abc;

再向下FIND_CONFIG阶段,查找location进行匹配,正好找到location /k8svip_two/ 所以 response 如上;


无 flag 测试用例二

server {

listen 80;


rewrite ^/(.*)$ /k8svip_one/$1;

location / {

echo "This is default location";

echo "uri: ${uri}";

}

location /k8svip_one/ {

rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1;

echo "This is k8svip_one location";

echo "uri: ${uri}";

}

location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}

}


测试及结果

测试:curl http://127.0.0.1/abc

测试结果:

This is k8svip_two location

uri: /k8svip_two/abc


无flag,处理server块阶段,匹配rewrite ^/(.*)$ /k8svip_one/$1 ,URI为:/k8svip_one/abc;

到FIND_CONFIG阶段,匹配location, location /k8svip_one/ ,这个location块中有rewrite再次匹配^/k8svip_one/(.*)$ /k8svip_two/$1; URI变为:/k8svip_two/abc,这里没有flag,会跳出继续FIND_CONFIG阶段,而不会到 SERVER_REWRITE 阶段;而是匹配location /k8svip_two/ ,所以response如上。


flag redirect 测试用例一

server {

listen 80;

#rewrite ^/(.*)$ /k8svip_one/$1 redirect;

rewrite ^/(.*)$ https://www.baidu.com/ redirect;

rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1;

rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1;


location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}


location /k8svip_three/ {

echo "This is k8svip_three location";

echo "uri: ${uri}";

}

}


测试及结果

测试:浏览器访问 http://127.0.0.1/abc

response:跳转到百度,状态码为302,临时重定向;


这里的flag是redirect,说明需要重定向到replacement, 正好这里的replacement有“https://”,此时会直接跳转并返回给客户端;

如果这里的#rewrite ^/(.*)$ /k8svip_one/$1 redirect; 这里的注释#打开,会出现问题情况呢?使用浏览器测试下,并自行分析下,告诉你结论,会死循环,如果不明白,可以联系我;


flag redirect 测试用例二

server {

listen 80;

server_name a.com;

rewrite_log on;

rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1;

rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1 redirect;

rewrite ^/(.*)$ /k8svip_one/$1;


location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}


location /k8svip_three/ {

echo "This is k8svip_three location";

echo "uri: ${uri}";

}

}


测试及结果

浏览器访问: http://127.0.0.1/k8svip_one/abc

浏览器跳转为:http://127.0.0.1/k8svip_three/abc


浏览器显示结果如下:

This is k8svip_one location

uri: /k8svip_one/k8svip_three/abc


1. 匹配rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1; 后,URI变为:/k8svip_two/abc ; 无flag,继续向下;

2. 继续匹配 rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1 redirect; 302临时重定向,URI: http://127.0.0.1/k8svip_three/abc,

3. 再次访问,此时会从SERVER_REWRITE这个阶段开始,此时匹配的是 rewrite ^/(.*)$ /k8svip_one/$1; URI变为:/k8svip_one/k8svip_three/abc ,无flag,向下FIND_CONFIG阶段;

4. 最后查看location 匹配location /k8svip_one/ ,所以回显如上;


flag permanent 测试用例


1. 这里的用例与redirect是一模一样的,只是状态码不一样,redirect 302 临时重定向, permanent 301 永久重定向,并且两种重定向,浏览器URL都会发现跳转;

2. 301和302重定向时,也有很多人称为外部重定向,这里的意思是他会重新从nginx http请求处理的第一个阶段开始重新匹配,而相对的内部重定向,是指不会到SERVER_REWRITE阶段的重定向,但有可能是REWRITE和FIND_CONFIG这两个阶段的相互跳转查找,在flag break和last的时候会讲解到;


flag last 测试用例一

server {

listen 80;

rewrite_log on;

rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1 last;

rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1;


location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}


location /k8svip_three/ {

echo "This is k8svip_three location";

echo "uri: ${uri}";

}

}


测试及结果

测试:curl http://127.0.0.1/k8svip_one/abc

测试结果:

This is k8svip_two location

uri: /k8svip_two/abc


1. 匹配 rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1 last;遇到last,停止同级段的匹配,这里的意思是,中止server段向下的匹配,进行FIND_CONFIG阶段,URI变为:/k8svip_two/abc;

这里可以看出,如果last没有中止server段向下的匹配,会匹配rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1,实际结果是没有匹配的;

2. 由上面步骤之后,匹配location /k8svip_two/, 所以会出现上面的response结果;


flag last 测试用例二

server {

listen 80;

server_name a.com;

rewrite_log on;


rewrite ^/(.*)$ /k8svip_one/$1;

rewrite ^/k8svip_three/(.*)$ /k8svip_four/$1;


location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

rewrite ^/(.*)$ /k8svip_three/$1 last;

rewrite ^/k8svip_three/(.*)$ /;


echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}


location /k8svip_three/ {

echo "This is k8svip_three location";

echo "uri: ${uri}";

}


location /k8svip_four/ {

echo "This is k8svip_four location";

echo "uri: ${uri}";

}

}


测试及结果

测试:curl http://127.0.0.1/abc

测试结果:

This is k8svip_three location

uri: /k8svip_three/k8svip_one/abc


1. 匹配rewrite ^/(.*)$ /k8svip_one/$1; URI变为:/k8svip_one/abc;

2. 匹配location /k8svip_one/ 进而匹配location块中的rewrite ^/(.*)$ /k8svip_three/$1 last; 因为是last,会跳出location继续FIND_CONFIG阶段,

3. URI为:/k8svip_three/k8svip_one/abc;而不会到SERVER_WRITE阶段;

匹配 location /k8svip_three/ ,所以看到上面的response;


flag break 测试用例一

server {

listen 80;

rewrite_log on;

rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1 break;

rewrite ^/k8svip_two/(.*)$ /k8svip_three/$1;


location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}


location /k8svip_three/ {

echo "This is k8svip_three location";

echo "uri: ${uri}";

}

}


测试及结果

测试:curl http://127.0.0.1/k8svip_one/abc

测试结果如下:

This is k8svip_two location

uri: /k8svip_two/abc


1. 匹配rewrite ^/k8svip_one/(.*)$ /k8svip_two/$1 break; flag为break结束本层级的rewirte匹配,URI变为:/k8svip_two/abc

2. 继续FIND_CONFIG阶段,匹配location /k8svip_two/ ; 所以response 如上;


flag break 测试用例二

server {

listen 80;

server_name a.com;

rewrite_log on;


rewrite ^/(.*)$ /k8svip_one/$1;

location / {

echo "This is default location";

echo "uri: ${uri}";

}


location /k8svip_one/ {

rewrite ^/(.*)$ /k8svip_three/$1 break;

rewrite ^/k8svip_two/(.*)$ /;


echo "This is k8svip_one location";

echo "uri: ${uri}";

}


location /k8svip_two/ {

echo "This is k8svip_two location";

echo "uri: ${uri}";

}


location /k8svip_three/ {

echo "This is k8svip_three location";

echo "uri: ${uri}";

}

}


测试及结果

测试:curl http://127.0.0.1/abc

测试结果如下:

This is k8svip_one location

uri: /k8svip_three/k8svip_one/abc


1. 匹配rewrite ^/(.*)$ /k8svip_one/$1; URI变为:/k8svip_one/abc

2. 匹配location /k8svip_one/,然后继续 rewrite ^/(.)$ /k8svip_three/$1 break; flag为break,结束本层级的rewrite ^/(.)$ /k8svip_three/$1 break;,并且继续进行本层级的其它操作;

3. 此时的URI:/k8svip_three/k8svip_one/abc,所以response如上;


总结


本文结合理论及实践总结了nginx rewrite规则使用,rewrite可以出现在service段,也可以出现在location 作用域内,它会根据flag标识位的不同做出相应的跳转。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表