[toc]
nginx rewrite
1.rewrite基本概述
1.1 什么是rewrite
rewrite即URL重写, 主要实现url地址重写以及重定向, 就是把传入Web的请求重定向到其他URL的过程。
1.2 rewrite使用场景
1.URL地址跳转,例如用户访问aaa.com将其跳转到bbb.com , 或者当用户通过http的方式访问aaa.com时,将其跳转至https的方式访问bbb.com
2.URL伪静态, 将动态页面显示为静态页面方式的一种技术, 便于搜索引擎的录入, 同时减少动态URL地址对外暴露过多的参数, 提升更高的安全性。
3.搜索引擎SEO优化依赖于url路径, 以便支持搜索引擎录入
2.rewrite配置语法
Syntax: rewrite regex replacement [flag];
Default: —
Context: server, location, if
在匹配过程中可以引用一些Nginx的全局变量
变量 | 说明 |
---|---|
$document_root | 针对当前请求的根路径设置值 |
$host | 请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名 |
$request_filename | 当前请求的文件路径名(带网站的主目录/code/images/test.jpg) |
$request_uri | 当前请求的文件路径名(不带网站的主目录/images/test.jpg) |
$scheme | 请求用的协议,比如http或者https |
rewrite匹配优先级
1.执行server块的rewrite指令
2.执行location匹配
3.执行选定的location中的rewrite
3.flag
rewrite指令根据表达式来重定向URI, 或者修改字符串。 可以应用于server、location、if环境下,每行rewrite指令最后跟一个flag标记,支持的flag标记有如下表格所示
flag | 说明 |
---|---|
last | 本条规则匹配完成后,停止匹配,不在匹配后面的规则 |
break | 本条规则匹配完成后,停止匹配,不在匹配后面的规则 |
redirect | 返回302临时重定向, 地址栏会显示跳转后的地址 |
permanent | 返回301永久重定向,地址栏会显示跳转后的地址 |
last
官方解释
停止处理当前
ngx_http_rewrite_module
指令集, 并开始搜索与更改后的URI相匹配的新位置
如果匹配的URI,rewrite在server块中,并且last做为flag,匹配到此rewrite URI时,不再向下匹配server块中的rewrite,进而继续下面location URI的查找匹配;
如果匹配的URI,rewrite在location块中,last做为flag,匹配到此rewrite URI时,会跳出此location块,继续从上到下查找其它的location块URI,但不会再匹配server块中的rewrite中的URI
break
官方解释
ngx_http_rewrite_module
与break指令一样, 停止处理当前的指令集
如果匹配的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
官方解释
返回带有302代码的临时重定向;如果替换字符串不是以 "
http://
","https://
"或"$scheme
" 开头,则使用该字符串
permanent
官方解释
返回301永久重定向,地址栏会显示跳转后的地址,即表示如果客户端不清理浏览器缓存,那么返回的结果将永久保存在客户端浏览器中了
4.nginx处理http请求的11个阶段
-
ngx_http_post_read_phase
- 读取请求头,接收到http头部后处理阶段
-
ngx_http_server_rewrite_phase
- 执行server块中rewrite,独立http阶段
-
ngx_http_find_config_phase
- 根据uri查找替换location,uri寻找匹配的location阶段
-
ngx_http_rewrite_phase
- 根据替换结果,继续执行rewrite,寻找到匹配的location之后再修改请求的uri
-
ngx_http_post_rewrite_phase
- 执行rewrite后处理,在rewrite重写url后,防止错误的nginx.conf配置导致死循环(递归的修改uri)
-
ngx_http_preaccess_phase
- 认证预处理,请求限制,连接限制,表示在处理ngx_http_access_phase阶段请求访问限制前,http模块可以介入处理的阶段
-
ngx_http_access_phase
- 认证处理,用于让http模块判断是否允许这个请求访问nginx服务
-
ngx_http_post_access_phase
- 认证后处理,认证不通过,丢包,在ngx_http_access_phase阶段中,当http模块的handler处理函数返回不允许访问的错误码时(ngx_htp_forbidden或者ngx_http_unauhorized),这里将负责向用户发送拒绝服务的错误相应
-
ngx_http_try_files_phase
- 尝试try标签,此阶段专门为try_files配置预设立,当http请求访问静态资源时,try_files配置项可以使这个配置顺序的访问多个静态资源
-
ngx_http_content_phase
- 内容处理,用于处理http请求内容的阶段,这是大部分http模块最愿意介入的阶段
-
ngx_http_log_phase
- 日志处理,处理完请求后记录日志的阶段
5.rewrite配置实例
5.1 无flag测试
在nginx中引入echo、sleep、time等功能 github地址
无flag测试用例1
server {
listen 80;
rewrite ^/(.*)$ /nginx_one/$1;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
}
测试结果
$ curl 127.0.0.1/abc
This is nginx_two location
uri: /nginx_two/abc
无 flag 时,rewrite 会依次向下匹配,根据nginx在http请求处理的阶段中,我们 会先匹配server块中的rewrite规则;
第一次匹配rewrite ^/(.*)$ /nginx_one/$1;
URI变成:/nginx_one/abc
,无flag继续向下匹配;
第二次匹配rewrite ^/nginx_one/(.*)$ /nginx_two/$1
; URI变成/nginx_two/abc
;
再向下FIND_CONFIG阶段,查找location进行匹配,正好找到location /nginx_two/
所以 response 如上;
无flag测试用例2
server {
listen 80;
rewrite ^/(.*)$ /nginx_one/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
}
测试结果
$ curl 127.0.0.1/abc
This is nginx_two location
uri: /nginx_two/abc
无flag,处理server块阶段,匹配rewrite ^/(.*)$ /nginx_one/$1
,URI为:/nginx_one/abc;
到FIND_CONFIG阶段,匹配location, location /nginx_one/
,这个location块中有rewrite再次匹配^/nginx_one/(.*)$ /nginx_two/$1;
URI变为:/nginx_two/abc
,这里没有flag,会跳出继续FIND_CONFIG阶段,而不会到 SERVER_REWRITE 阶段;而是匹配location /nginx_two/
,所以response如上。
5.2 有flag测试
redirect和permanent的区别就是 redirect
是临时重定向302,而permanent
是永久重定向301
5.2.1 flag redirect测试用例1
server {
listen 80;
#rewrite ^/(.*)$ /nginx_one/$1 redirect;
rewrite ^/(.*)$ https://www.baidu.com/ redirect;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}
测试结果
浏览器访问 虚拟机IP地址/abc 会跳转到百度首页 状态码为临时重定向302
这里的flag是redirect,说明需要重定向到replacement,正好这 里的replacement有"https://",此时会直接跳转并返回给客户端;
如果打开#rewrite ^/(.*)$ /nginx_one/$1 redirect;
的注释,浏览器中访问会提示重定向次数过多
5.2.2 flag redirect测试用例2
server {
listen 80;
rewrite_log on;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1 redirect;
rewrite ^/(.*)$ /nginx_one/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}
测试结果
$ curl -I 127.0.0.1/nginx_one/abc
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.16.1
Date: Mon, 22 Jun 2020 12:05:16 GMT
Content-Type: text/html
Content-Length: 145
Location: http://127.0.0.1/nginx_three/abc
Connection: keep-alive
1.匹配rewrite ^/nginx_one/(.*)$ /nginx_two/$1;
后,URI变为:/nginx_two/abc ;
无flag,继续向下;
2.继续匹配 rewrite ^/nginx_two/(.*)$ /nginx_three/$1 redirect;
302临时
重定向,URI: http://127.0.0.1/nginx_three/abc
,
3.再次访问,此时会从SERVER_REWRITE这个阶段开始,此时匹配的是 rewrite ^/(.*)$ /nginx_one/$1;
URI变为:/nginx_one/nginx_three/abc
,无flag,向 >
下FIND_CONFIG阶段;
4.最后查看location 匹配location /nginx_one/
,所以回显如上;
5.2.3 flag last测试用例1
server {
listen 80;
rewrite_log on;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1 last;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}
测试结果
$ curl 127.0.0.1/nginx_one/abc
This is nginx_two location
uri: /nginx_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结果;
5.2.4 flag last测试用例2
server {
listen 80;
rewrite_log on;
rewrite ^/(.*)$ /nginx_one/$1;
rewrite ^/nginx_three/(.*)$ /nginx_four/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
rewrite ^/(.*)$ /nginx_three/$1 last;
rewrite ^/nginx_three/(.*)$ /;
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
location /nginx_four/ {
echo "This is nginx_four location";
echo "uri: ${uri}";
}
}
测试结果
$ curl 127.0.0.1/abc
This is nginx_three location
uri: /nginx_three/nginx_one/abc
1.匹配rewrite ^/(.*)$ /k8svip_one/$1;
URI变为:/nginx_one/abc;
2.匹配location /nginx_one/
进而匹配location块中的rewrite ^/(.*)$ /nginx_three/$1 last;
因为是last,会跳出location继续FIND_CONFIG阶段
3.URI为:/nginx_three/nginx_one/abc;
而不会到SERVER_WRITE阶段;
匹配 location /nginx_three/
,所以看到上面的response;
5.2.5 flag break测试用例1
server {
listen 80;
rewrite_log on;
rewrite ^/nginx_one/(.*)$ /nginx_two/$1 break;
rewrite ^/nginx_two/(.*)$ /nginx_three/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}
测试结果
$ curl 127.0.0.1/nginx_one/abc
This is nginx_two location
uri: /nginx_two/abc
1.匹配rewrite ^/nginx_one/(.*)$ /nginx_two/$1 break;
flag为break,结束本层级的rewirte匹配,URI变为:/nginx_two/abc
2.继续FIND_CONFIG阶段,匹配location /nginx_two/;
所以response 如上;
5.2.6 flag break测试用例2
server {
listen 80;
rewrite_log on;
rewrite ^/(.*)$ /nginx_one/$1;
location / {
echo "This is default location";
echo "uri: ${uri}";
}
location /nginx_one/ {
rewrite ^/(.*)$ /nginx_three/$1 break;
rewrite ^/nginx_two/(.*)$ /;
echo "This is nginx_one location";
echo "uri: ${uri}";
}
location /nginx_two/ {
echo "This is nginx_two location";
echo "uri: ${uri}";
}
location /nginx_three/ {
echo "This is nginx_three location";
echo "uri: ${uri}";
}
}