Rewrite模块

现在 Nginx 已经成为很多公司作为前端反向代理服务器的首选,在实际工作中往往会 遇到很多跳转(重写 URL)的需求。比如更换域名后需要保持旧的域名能跳转到新的域名 上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。如果在后端使用的 Apache 服务器,虽然也能做跳转,规则库也很强大,但是用 Nginx 跳转效率会更高

Rewrite功能

rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标记位实现URL重写以及重定向。比如:更换域名后需要保持旧的域名能跳转到新的域名上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。

rewrite 只能在
server {}
location {}
if {}
中,并且默认只能对域名后边的除去传递的参数外的字符串起作用
例如:京东登录账号,就是一个跳转

只针对“?前域名后”重写

Rewrite跳转场景

  1. 可以调整用户浏览的 URL,看起来更规范,合乎开发及产品人员的需求
  2. 为了让搜索引擎搜录网站内容及用户体验更好,企业会将动态 URL 地址伪装成静态地址提供服务
  3. 网址换新域名后,让旧的访问跳转到新的域名上。例如,访问京东的 360buy.com会跳转到 jd.com
  4. 根据特殊变量、目录、客户端的信息进行 URL 调整等。

Rewrite跳转实现

​ Nginx 是通过 ngx_http_rewrite_module 模块支持 url 重写、支持 if 条件判断,但不支持 else。

​ 另外该模块需要 PCRE 支持,应在编译 Nginx 时指定 PCRE 支持,默认已经安装。

​ 根据相关变量重定向和选择不同的配置,从一个 location 跳转到另一个 location,不过这样 的循环最多可以执行 10 次,超过后 Nginx 将返回 500 错误。

​ 同时,重写模块包含 set 指令,来创建新的变量并设其值,这在有些情景下非常有用的,如记录条件标识、传递参数到其他location、记录做了什么等等。

rewrite执行顺序如下:

  1. 执行server块里面的rewrite指令。
  2. 执行location 匹配。
  3. 执行选定的location中的rewrite指令。

语法格式

1
2
3
4
5
6
7
8
9
10
rewrite <regex> <replacement> [flag];
regex:表示正则匹配规则
replacement:表示跳转后的内容
flag:表示rewrite 支持的 flag 标记

flag 标记说明:
1. last:本条规则匹配完成后,继续向下匹配新的location URL规则,一般用在server和if中
2. break:本条规则匹配完成即终止,不再匹配后面的任何规则。一般用在location中
3. redirect:返回 302 临时重定向,浏览器地址会显示跳转后的 URL 地址,爬虫不会更新 url(因为是临时)。
4. permanent:返回 301 永久重定向,浏览器地址栏会显示跳转后的 URL 地址,爬虫更新 url。

基于旧域名跳转到新域名

例如现在访问的是www.xiao.com,现在需要将这个域名下面的发帖都跳转到 www.bin.com,后面的参数不变

创建目录

1
2
3
4
5
6
7
[root@localhost /usr/local/nginx/html]# ls
50x.html image index.html test
[root@localhost /usr/local/nginx/html]# cd test/
[root@localhost /usr/local/nginx/html/test]# ls
index.html
[root@localhost /usr/local/nginx/html/test]# cat index.html
this is test web

修改配置文件

1
[root@localhost /usr/local/nginx/conf]# vim nginx.conf

输入www.xiao.com就会转到www.bin.com

基于客户端IP访问跳转

在/var目录下创建一个站点文件

1
2
3
4
5
[root@localhost /var]# mkdir -p www/html
[root@localhost /var/www/html]# vim weihu.html
[root@localhost /usr/local/nginx/html]# cat /var/www/html/weihu.html
<head><h1>This page is under maintenance, please come back later<h1></head>
[root@localhost /usr/local/nginx/conf]# vim nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
###设置是否合法的IP标识###
set $rewrite true;

###判断是否合法的ip###
if ($remote_addr = "192.168.91.103") {
#当客户端IP为192.168.91.103时将变量值设为false,不进行重写
set $rewrite false;
}
if ($rewrite = true) {
rewrite (.+) /weihu.html;
}
location = /weihu.html {
root /var/www.html;
}

location / {
root html;
index index.html index.htm;
}

这样在域名后瞎打或打错都会跳到这个错误页面中

基于参数匹配的跳转

http://www.xiao.com/(100|200)-100.html跳转到http://www.xiao.com页面

1
[root@localhost /usr/local/nginx/conf]# vim nginx.conf

在网页测试输入www.xiao.com/100-100-任意/html

基于旧域名跳转到新域名

比如我们用惯了一个网址,服务商想把地址改变,用户输入原来的网址,可以跳到新的地址

比如,我们访问bbs.xiao.com/post它会跳到新的地址,www.xiao.com/bbs/post

1
[root@localhost /usr/local/nginx/conf]# vim nginx.conf

$1为位置变量,代表/post

创建站点文件

1
2
3
4
5
6
7
[root@localhost /usr/local/nginx/html]# mkdir -p bbs/post

[root@localhost /usr/local/nginx/html/bbs/post]# vim index.html

[root@localhost /usr/local/nginx/html/bbs/post]# cat index.html

<h1>This is mylove</h1>

在网页输入bbs.xiao.com/post就会跳转到www.xiao.com/bbs/post

记得hosts文件要加bbs.xiao.com哦,不然解析不出来

基于目录下所有的php文件

访问任何php文件,都会返回首页

在网址输入www.xiao.com/upload/*(任何).php都会返回到首页

基于普通的一条url

这个是最简单的,就利用正则表达式,以什么开头以什么结尾,改改不管什么都会跳到主页面

正则location中希望proxy到带有uri部分的路径

https://www.e-learn.cn/topic/1707149

如果在正则表达式中使用带有proxy_pass语句的 URI,则需要使用一个或多个变量来构建整个 URI。有关详细信息,请参阅此文档location

所以替代方案是 (1),从location表达式中捕获 URI 并将其添加到proxy_pass语句中。例如:

1
2
3
location ~ ^/([A-Za-z0-9]+) {
proxy_pass http://api/urlshortener/v1/$1;
}

或者 (2),proxy_pass不使用 URI 部分,并使用rewrite...break. 例如:

1
2
3
4
location ~ ^/([A-Za-z0-9]+) {
rewrite ^/([A-Za-z0-9]+) /urlshortener/v1/$1 break;
proxy_pass http://api;
}

用rewrite去除URL中的特定参数

日常服务中经常会用Nginx做一层代理转发,把Nginx当做前置机

比如,以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
# 对外暴露 80 端口
listen 80;
server_name 192.168.10.231;

# 后端API地址暴露为:http://192.168.10.231/apis
location /apis {
proxy_pass http://127.0.0.1:8000/;
proxy_pass_request_headers on;
# 重写URL 去除apis
rewrite "^/apis/(.*)$" /$1 break;
}

}

这里的rewrite 就是为了去除URL中的/apis,实际的后端api中是没有这个参数的,但是为了做到在Nginx转发请求,前端需要加上这个参数,以便于区别

比如前端的请求地址是

1
http://192.168.10.231/apis/user

那么实际上经过Nginx转发后请求的地址是

1
http://127.0.0.1:8000/user

rewrite “^/api/(.)”/1 break,路径重写:

1
2
(1)"^/api/(.)$":匹配路径的正则表达式,用了分组语法就是*(.)**,把/api/以后的所有部分当做1组;
2)/$1:重写的目标路径,这里用$1引用前面正则表达式匹配到的分组(组编号从1开始,也就是api),即/api/后面的所有。这样新的路径就是除去/api/以外的所有,就达到了去除/api前缀的目的

break:指令,常用的有2个,分别是:last、break;

1
2
1)last:重写路径结束后,将得到的路径重新进行一次路径匹配;
2break:重写路径结束后,不再重新匹配路径。

使用rewrite,在if模块中结合root实现alias的效果

if模块中不允许写alias,所以我们可以用rewrite重写url + root实现alias的效果

  • 先写一个单域名下多服务配置,访问aaa/bbb会打到index.html
1
2
3
4
5
6
7
8
9
location ~ ^/aaa/$ {
alias /data/web/aaa/dist/;
index index.html;
}

location ~ ^/bbb/$ {
alias /data/web/bbb/dist/;
index index.html;
}
  • 访问静态资源的时候,在location中根据uri来判断属于哪个服务,让我们去掉base路径,来实现alias的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
location ~* ^.+.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|mp3|wav|bmp|rtf|js|flv|swf|map|ico|svg|woff2|woff|ttf|map|json)$ {
root /data/web/t_sopei_cn/dist;
if ($request_uri ~ "/aaa/") {
# 去掉aaa
rewrite ^/aaa/(.*) /$1;
root /data/web/aaa/dist;
}
if ($request_uri ~ "/bbb/") {
# 去掉bbb
rewrite ^/bbb/(.*) /$1;
root /data/web/bbb/dist;
}
access_log off;
add_header Cache-Control max-age=31536000;
}