nginx rewrite
技能目标:
· 了解Rewrite
· 掌握Nginx的正则表达式
· 掌握Rewrite的语法
· 理解常见Rewrite示例
10.1 案例分析
10.1.1 案例概述
现在Nginx已经成为很多公司作为前端反向代理服务器的首选,在实际工作中往往会遇到很多跳转(重写URL)的需求。比如更换域名后需要保持旧的域名能跳转到新的域名上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。如果在后端使用的Apache服务器,虽然也能做跳转,规则库也很强大,但是用Nginx跳转效率会更高,这也是学习本章的目的所在。
10.1.2 案例前置知识点
1.Rewrite跳转场景
Rewrite跳转场景主要包括以下几种:
1)可以调整用户浏览的URL,看起来更规范,合乎开发及产品人员的需求。
2)为了让搜索引擎搜录网站内容及用户体验更好,企业会将动态URL地址伪装成静态地址提供服务。
3)网址换新域名后,让旧的访问跳转到新的域名上。例如,访问京东的360buy.com会跳转到jd.com。
4)根据特殊变量、目录、客户端的信息进行URL调整等。
2. Rewrite跳转实现
Nginx是通过ngx_http_rewrite_module模块支持url重写、支持if条件判断,但不支持else。另外该模块需要PCRE支持,应在编译Nginx时指定PCRE支持,默认已经安装。根据相关变量重定向和选择不同的配置,从一个location跳转到另一个location,不过这样的循环最多可以执行10次,超过后Nginx将返回500错误。同时,重写模块包含set指令,来创建新的变量并设其值,这在有些情景下非常有用的,如记录条件标识、传递参数到其他location、记录做了什么等等。rewrite功能就是,使用Nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。
3. Rewrite实际场景
在实际工作场景,Nginx跳转需求有三种方式可实现。可以直接用rewrite进行匹配跳转,也可以使用if匹配全局变量后跳转,另外还可以使用location匹配再跳转,所以rewrite只能放在server{},if{},location{}段中,例如location只能对域名后边的除去传递的参数外的字符串起作用,例如http://www.domain.com/index.php?id=1只对/index.php重写。如果想对域名或参数字符串起作用,可以使用if全局变量匹配,也可以使用proxy_pass反向代理。
10.1.3 案例环境
1. 本案例实验环境
表10-1是本章案例的具体环境。
2. 案例需求
通过对正则表达式的学习和理解,根据不同级别的优先级实现常见的跳转需求。
3. 案例实现思路
1)正则表达式的理解。
2)Rewrite语法。
3)Location的分类和优先级介绍。
4)举例说明加深印象和理解程度。
10.2 案例实施
10.2.1 Nginx正则表达式
学习Rewrite之前要对正则表达式要很熟悉,表1-2中列举出一些常用的正则表达式元字符。要深刻理解和学习每个元字符的含义,并且多动手进行实验,在实际生产环境中要结合多个元字符一起使用。
表10-2 常用的正则表达式元字符
10.2.2 Rewrite语法
Rewrite命令的语法如下所示。
rewrite <regex> <replacement> [flag];
· regex:正则
· replacement:跳转后的内容
· flag:rewrite支持的flag标记
flag标记说明:
· last:相当于Apache的[L]标记,表示完成rewrite。
· break:本条规则匹配完成即终止,不再匹配后面的任何规则。
· redirect:返回302临时重定向,浏览器地址会显示跳转后的URL地址,爬虫不会更新url(因为是临时)。
· permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址,爬虫更新url。
如果后面不跟flag标记,那么默认是302临时重定向。在实际工作场景中,还有另一种return指定。因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。 last 和 break 区别有点难以理解:
last一般写在server和if中,而break一般使用在location中。last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配。
10.2.3 Location分类
location大致可以分为三类:
· location = patt {} [精准匹配]
· location patt {} [一般匹配]
· location ~ patt {} [正则匹配]
精准匹配和一般匹配不需要做详细的说明,主要是正则匹配,下面就是正则匹配的一些表达式,需要多加牢记。
· ~:表示执行一个正则匹配,区分大小写。
· ~*:表示执行一个正则匹配,不区分大小写。
· !~:表示执行一个正则匹配,区分大小写不匹配。
· !~*:表示执行一个正则匹配,不区分大小写不匹配。
· ^~:表示普通字符匹配。使用前缀匹配。如果匹配成功,则不再匹配其他location。
· =:进行普通字符精确匹配。也就是完全匹配。
· @:它定义一个命名的 location,使用在内部定向时,例如 error_page, try_files。
10.2.4 Location优先级
在Nginx的location和配置中location的顺序没有太大关系。和location表达式的类型有关,相同类型的表达式,字符串长的会优先匹配。
以下是按优先级排列说明:
1. 等号类型(=)的优先级最高。一旦匹配成功,则不再查找其他匹配项。
2. ^~类型表达式。一旦匹配成功,则不再查找其他匹配项。
3. 正则表达式类型(~和~*)的优先级次之。
4. 常规字符串匹配类型。按前缀匹配。
5. 通用匹配(/),如果没有其它匹配,任何请求都会匹配到。
从功能看rewrite和location似乎有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,还可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:
1. 执行server块里面的rewrite指令。
2. 执行location匹配。
3. 执行选定的location中的rewrite指令。
下面根据不同的Location优先级进行示例说明。
location = / {
# 精确匹配 / ,主机名后面不能带任何字符串。
[ configuration A ]
}
location / {
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求,但是正则和最长字符串会优先匹配。
[ configuration B ]
}
location /documents/ {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索,只有后面的正则表达式没有匹配到时,这一条才会起作用。
[ configuration C ]
}
location ~ /documents/abc {
# 匹配任何以 /documents/abc 开头的地址,匹配符合以后,还要继续往下搜索,只有后面的正则表达式没有匹配到时,这一条才会起作用。
[ configuration D ]
}
location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下匹配。
[ configuration E ]
}
location ~* .(gif|jpg|jpeg)$ {
# 匹配所有以gif,jpg或jpeg结尾的请求,然而,所有请求/images/下的图片会被 [ configuration E ] 处理,因为^~的优先级更高。
[ configuration F ]
}
location /images/abc {
# 最长字符匹配到 /images/abc,优先级最低。
[ configuration G ]
}
location ~ /images/abc {
# 匹配以/images/abc开头的,优先级次之。
[ configuration H ]
}
location /images/abc/1.html {
# 匹配以/images/abc/1.html文件,如果和正则~ /images/abc/1.html相比,正则优先级更高。
[ configuration I ]
}
location优先级总结:
1. 如果是匹配某个具体文件:
(location = 完整路径) > (location ^~ 完整路径) > (location ~* 完整路径) > (location ~ 完整路径) > (location 完整路径) > (location /)
2. 如果是用目录做匹配访问某个文件:
(location = 目录) > (location ^~ 目录/) > (location ~ 目录) > (location ~* 目录) > (location 目录) > (location /)
10.2.5 Rewrite常见示例
1. 安装Nginx源
[root@localhost ~]# rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
2. 安装Nginx软件包
[root@localhost ~]# yum install -y nginx
3. 修改Nginx默认站点配置文件
主要修改/etc/nginx/conf.d/default.conf文件中如下两行红色字体:
server {
listen 80;
server_name www.domain.com;
#charset koi8-r;
access_log /var/log/nginx/www.domain.com-access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
......//省略部分
}
4. 启动Nginx
[root@localhost ~]# systemctl start nginx
注意事项:做每一步场景测试之前请确定这个域名可以正常解析,每做下一个场景之前请删除上一个场景的配置,另外清除浏览器缓存。
1)基于域名的跳转,现在公司旧域名www.domain.com有业务需求有变更,需要使用新域名www.newdomain.com代替,但是旧域名不能废除,需要跳转到新域名上,而且后面的参数保持不变。
在/etc/nginx/conf.d/default.conf文件里的location /下面添加如下内容
if ($host = 'www.domain.com')
{
rewrite ^/(.*)$ http://www.newdomain.com/$1 permanent;
}
[root@localhost ~]# systemctl reload nginx
域名直接跳转,按F12可以清楚的看到从旧域名www.domain.com跳转到了新域名www.newdomain.com上,状态码是301永久重定向。页面效果如图10.1所示。
图10.1
域名后面加参数跳转,因为在代码中很多的请求是带参数的,所以要保证参数都能正常跳转。浏览器输入模拟访问http://www.domain.com/test/1/index.php(虽然这个请求内容是不存在的)跳转到http://www.newdomain.com/test/1/index.php,从headers里面可以看到301实现了永久重定向跳转,而且域名后的参数也正常跳转。页面效果如图10.2所示。
图10.2
2)基于客户端IP访问跳转,例如今天公司业务版本上线,所有IP访问任何内容都显示一个固定维护页面,只有公司IP访问正常。
在/etc/nginx/conf.d/default.conf文件里添加如下内容:
set $rewrite true;
if ($remote_addr = "192.168.9.230") {
set $rewrite false;
}
if ($rewrite = true) {
rewrite (.+) /maintenance.html;
}
location = /maintenance.html {
root /usr/share/nginx/html;
}
[root@localhost ~]# echo "Website is Maintaining, Please visit later!" > /usr/share/nginx/html/maintenance.html
[root@localhost ~]# systemctl reload nginx
只有IP为192.168.9.230能正常访问,其它地址都是维护页面,使用一个局域网的其它IP地址用浏览器访问http://www.domain.com域名和加参数都是请求的maintenance.html页面的内容,而且状态码是200,如图10.3所示。
图10.3
浏览器使用http://www.domain.com/test带参数访问也是显示维护页面内容,如图10.4所示。
图10.4
3)基于旧域名跳转到新域名后面加目录,例如现在访问的是http://bbs.domain.com,现在需要将这个域名下面的发帖都跳转到http://www.domain.com/bbs,注意保持域名跳转后的参数不变。
在/etc/nginx/conf.d/default.conf文件里修改红色字体并添加如下内容:
server_name bbs.domain.com;
location /post {
rewrite (.+) http://www.domain.com/bbs$1 permanent;
}
[root@localhost ~]# systemctl reload nginx
模拟使用浏览器访问http://bbs.domain.com/post/1.php跳转到http://www.domain.com/bbs/post/1.php,如图10.5所示。
图10.5
4)基于参数匹配的跳转,例如现在访问http://www.domain.com/100-(100|200)-100.html跳转到http://www.domain.com页面。
在/etc/nginx/conf.d/default.conf文件里添加如下内容:
if ($request_uri ~ ^/100-(100|200)-(d+).html$) {
rewrite (.*) http://www.domain.com permanent;
}
[root@localhost ~]# systemctl reload nginx
使用浏览器访问http://www.domain.com/100-100-100.html,如图10.6所示。
图10.6
使用浏览器访问http://www.domain.com/100-200-100.html,如图10.7所示。
图10.7
5)基于目录下所有php结尾的文件跳转,访问http://www.domain.com/upload/1.php跳转到首页。
在/etc/nginx/conf.d/default.conf文件里添加如下内容:
location ~* /upload/.*.php$ {
rewrite (.+) http://www.domain.com permanent;
}
浏览器访问http://www.domain.com/upload/1.php,如图10.8
图10.8
6)基于最普通一条url请求的跳转,访问一个具体的页面跳转到首页。
在/etc/nginx/conf.d/default.conf文件里添加如下内容:
location ~* ^/1/test.html {
rewrite (.+) http://www.domain.com permanent;
}
浏览器访问http://www.domain.com/1/test.html跳转到首页,如图10.9所示。
图10.9
关于Nginx rewrite本章的内容就这么多。其实在实际工作中远远不止这些场景,而且会更加复杂繁琐,希望大家能举一反三,多加练习。