前言
现在 Nginx 已经成为很多公司作为前端反向代理 (proxy pass) 服务器的首选,在实际工作中往往会遇到很多跳转(重写URL)的需求。比如,更换域名后需要保持旧的域名能跳转到新的域名上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。如果在后端使用的Apache服务器,虽然也能做跳转,规则库也很强大,但是用 Nginx 跳转效率会更高
一,Nginx-Rewrite叙述
1.Rewrite 场景
- 调整用户浏览的 URL,看起来更规范,合乎开发及产品人员的需求
- 为了让搜索引擎搜录网站内容及用户体验更好,企业会将动态URL地址伪装成静态地址提供服务
- 网址换新域名后,让旧的访问跳转到新的域名上。例如,访问京东的 360buy.com 会跳转到 jd.com
- 服务端某些业务调整,比如根据特殊变量、目录、客户端的信息进行 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 实际场景
3.1 Nginx 跳转需求的实现方式
- 使用 rewrite 进行匹配跳转
- 使用 if 匹配全局变量后跳转(全局变量指定的是nginx服务本身)
- 使用 location 匹配再跳转
3.2 rewrite 放在 server{},if{},location{} 段中
- location 只对域名后边的除去传递参数外的字符串起作用——页面文件的路径
3.3 对域名或参数字符串
- 使用 if 全局变量匹配
- 使用 proxy_pass 反向代理
3.4 rewrite 执行顺序
(1) 执行 server 块里面的 rewrite 指令
(2) 执行 location 匹配
(3) 执行选定的 location 中的 rewrite 指令
二,Nginx 正则表达式
1. 常见的正则表达式元字符
字符 | 说明 |
---|---|
^ | 匹配输入字符串的起始位置 |
$ | 匹配输入字符串的结束位置 |
* | 匹配前面的字符零次或多次 |
+ | 匹配前面的字符一次或多次 |
? | 匹配前面的字符零次或一次 |
. | 匹配除“\n”之外的任何单个字符,若要匹配包括“\n”在内的任意字符,请使用诸如“[.\n]”之类的模式 |
\ | 将后面接着的字符标记为一个特殊字符或一个原义字符或一个向后引用,如“\n”匹配一个换行符,而“$”则匹配“$” |
\d | 匹配纯数字 |
{n} | 重复 n 次 |
{n,} | 重复 n 次或更多次 |
{n,m} | 重复 n 到 m 次 |
[] | 定义匹配的字符范围 |
[c] | 匹配单个字符 c |
[a-z] | 匹配 a-z 小写字母的任意一个 |
[a-zA-Z0-9] | 匹配所有大小写字母或数字 |
() | 表达式的开始和结束位置 |
| | 或运算符 |
2. nginx 与 apache 区别
- 正则表达式是二者的区别之一
正则表达式:可以更加精确的匹配需求的字符串/参数/位置等 - 从功能看 rewrite 和 location 似乎有点像,都能实现跳转,主要区别在于 rewrite 是在同一域名内更改获取资源的路径,而 location 是对一类路径做控制访问或反向代理,还可以proxy_pass 到其他机器
三、Rewrite 命令介绍
1. 语法格式
rewrite <regex> <replacement> [flag];
#<regex>:正则表达式
#<replacement>:跳转后的内容
#[flag]rewrite支持的flag标记
2. flag 标记
标记 | 说明 |
---|---|
last | 相当于Apache的[L]标记,表示完成rewrite,本条规则匹配完成后,继续向下匹配新的location URL规则,一般用在 server 和 if 中 |
break | 本条规则匹配完成即终止,不再匹配后面的任何规则,一般使用在 location 中 |
redirect | 返回302临时重定向,浏览器地址会显示跳转后的URL地址,爬虫不会更新url,浏览器地址会显示跳转后的URL地址 |
permanent | 返回301永久重定向,浏览器地址会显示跳转后的URL地址,爬虫更新url |
- last 和 break 比较
last | break | |
---|---|---|
使用场景 | 一般写在 server 和 if 中 | 一般使用在 location 中 |
URL匹配 | 不终止重写后的 urI 匹配 | 终止重写后的 url 匹配 |
- last:url 重写后,马上发起一个新请求,再次进入 server 块,重试 location 匹配,超过10次匹配不到报 500 错误,地址栏不变
- break:url重写后,直接使用当前资源,不再使用location余下的语句,完成本次请求,地址栏不变
3.小总结
last 和 break 再重定向后,地址栏都不会发生变化,这是他们的相同点。不同点在于 last 会写在 server 和 if中,break 是写在location 中,last 不会终止重写后的 url 匹配,break 会终止重写后的 url 匹配
四、location 解析
1. location 分类
location = / {..} [精准匹配]
location / {..} [一般匹配]
location ~ / {..} [正则匹配]
2. 正则匹配的常用表达式
标记 | 说明 |
---|---|
~ | 执行一个正则匹配,区分大小写 |
~* | 执行一个正则匹配,不区分大小写 |
!~ | 执行一个正则匹配,区分大小写不匹配 |
!~* | 执行一个正则匹配,不区分大小写不匹配 |
^~ | 普通字符匹配,使用前缀匹配,如果匹配成功,则不再匹配其他location |
= | 普通字符精确匹配,也就是完全匹配 |
@ | 定义一个命名的location,使用在内部定向时 |
3. location 优先级
-
首先精确匹配 =
-
其次前缀匹配 ^~
-
其次是按文件中顺序的正则匹配 ~ 或~*
-
然后匹配不带任何修饰的前缀匹配
扫描二维码关注公众号,回复: 14398685 查看本文章 -
最后是交给 / 通用匹配
-
相同类型的表达式,字符串长的会优先匹配
按优先级匹配
= 类型
^~ 类型表达式
正则表达式(~和~*)类型
常规字符串匹配类型,按前缀匹配
通用匹配(/),如果没有其他匹配,任何请求都会匹配到
- 总结:
1.首先看 优先级:精确>前缀>正则>一般>通用
2.优先级相同:正则看上下顺序,上面的优先;
3.一般匹配看长度,最长匹配的优先
4.精确、前缀、正则、一般 都没有匹配到,最后再看通用匹配
4. 优先级示例
location = / {
[ configuration A ]
}
#精确匹配,主机名后面不能带任何字符串
location / {
[configuration B ]
}
#一般匹配,所有的地址都以/开头,这条规则将匹配到所有请求,但正则表达式和最长字符会优先匹配
location /documents/ {
[ configuration C]
}
#匹配任何以/documents/开头的地址,当后面的正则表达式没有匹配到时,才起作用
location ~ /documents/abc {
[ configuration D ]
}
#匹配任何以/documents/abc开头的地址,当后面正则表达式没有匹配到时,才起作用
5. rewrite 和 location 比较
-
相同点:都能实现跳转
-
不同点:
① rewrite 是在同一域名内更改获取资源的路径
② location是对一类路径控制访问或反向代理,还可以 proxy_pass 到其他机器 -
rewrite会写在location里,执行顺序
①执行server块里面的rewrite指令
②执行location匹配
③执行选定的location中的rewrite指令 -
rewrite会写在location里,执行顺序
①执行server块里面的rewrite指令
②执行location匹配
③执行选定的location中的rewrite指令
6. location 优先级规则
① 那么 location 优先级到底是怎么排列的
- 匹配某个具体文件
(location = 完整路径)>(location ^~ 完整路径)>(location ~* 完整路径)>(location ~ 完整路径)>(location 完整路径)>(location /)
- 用目录做匹配访问某个文件
(location = 目录)>(location ^~ 目录/)>(location ~ 目录)>(location ~* 目录)>(location 目录)(location /)
② 文件和目录为什么只会在区不区分大小写上会有变动
- 正则表达式的目的是为了尽量精确的去匹配
- 文件,尽量精确的匹配,不区分的话更容易找到
- 目录,尽量精确匹配,区分大小写优先级更高,更容易找到
③ 在实际网站使用中,至少要有三个匹配规则定义
#第一个必选规则: 直接匹配网站根,通过域名访问网站首页比较频繁(www.baidu.com/),使用这个会加速处理,比如说官网;
可以是一个静态首页,也可以直接转发给后端应用服务器
location = / {
root html ;
index index.html index.htm;
}
#第二个必选规则:处理静态文件请求,这是nginx作为http服务器的强项;
有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
root /webroot/static/ ;
}
location ~* \. (html|gif|jpg|jpeg|png)$ {
root /webroot/res/ ;
}
#第三个必选规则就是通用规则,比如用来转发带.php、.jsp后缀的动态请求到后端应用服务器,
非静态文件请求就默认是动态请求,即跳转或反向代理
upstream tomcat_ server {
192.168.113.128:80
192.168.113.129:80
location / {
proxy_ pass http://tomcat_server;
}
五,实例
1. 基于域名的跳转
公司旧域名 www.xjz.com 因业务需求的变更,需要使用新域名 www.xtzhc.com 代替,但是旧域名不能废除,并且旧域名要可跳转到新的域名上,而且后面的参数保持不变
- ① 添加映射,开启 nginx 服务
[root@server2 conf]# systemctl status nginx
● nginx.service - SYSV: Nginx Service Control Script
Loaded: loaded (/etc/rc.d/init.d/nginx; bad; vendor preset: disabled)
Active: active (running) since 一 2022-07-25 09:46:59 CST; 4h 4min ago
[root@server2 conf]# vim /etc/hosts
[root@server2 conf]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.113.128 www.xtj.com www.xtzhc.com
- ② 创建日志目录,修改主配置文件
[root@server2 conf]# mkdir -p /var/log/nginx ##递归创建
[root@server2 conf]# pwd
/usr/local/nginx/conf
[root@server2 conf]# vim nginx.conf
.....
server {
listen 80;
server_name www.xtj.com; ##修改域名
charset utf-8; ##字符集可选择性修改
access_log /var/log/nginx/www.xtj.com.log; ##开启并对日志保存路径进行修改
location / {
##在原有的location位置进行插入下面内容
if ($host = 'www.xtj.com'){
##$host为rewrite全局变量,表示请求主机头字段或主机名
rewrite ^/(.*)$ http://www.xtzhc.com/$1 permanent; ##$1为匹配的位置变量,即域名后面的字符串,同时永久跳转
}
root html;
index index.html index.htm;
}
......
[root@server2 conf]# nginx -t ##验证添加抒写有没有错误
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx ##重启刷新服务
- ③浏览器访问验证
2. 基于 IP 地址访问跳转
例:由于公司的业务新版本上线,要求所有 IP 访问任何内容都显示一个固定的维护页面,只有公司自己的IP地址 192.168.113.128 才能正常访问
- ① 创建维护页面的目录,并添加内容
[root@server2 conf]# mkdir -p /var/www/html
[root@server2 conf]# echo "xtj维护中">/var/www/html/weihu.html
[root@server2 conf]# cat /var/www/html/weihu.html
xtj
- ②主配置文件配置
[root@server2 conf]# pwd
/usr/local/nginx/conf
[root@server2 conf]# vim nginx.conf
.......
server {
listen 80;
server_name www.xtj.com;
charset utf-8;
access_log /var/log/nginx/www.xtj.com.log; ##开启并对日志保存路径进行修改
set $rewrite true; ##设置变量$rewrite,变量值为布尔值为true
if ($remote_addr = "192.168.113.128"){
##当客户端ip为192.168.113.128时,将变量值设为flase,不进行重写
set $rewrite false;
}
if ($rewrite = true){
##除了合法IP,其他都是非法IP,进行重写跳转到维护页面
rewrite (.+) /weihu.html; ##布尔值表达式在不满足false情况下,会匹配满足true的location
} ##重写在访问IP后边入/weihu.html192.168.113.128/weihu.html
}
location = /weihu.html {
root /var/www/html; ##页面返回/var/www/html/weihu.html里面的内容
}
location / {
root html;
index index.html index.htm;
}
......
[root@server2 conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx
- ③浏览器访问验证
用另一台机器进行访问192.168.113.129这个地址,会发现页面进行了跳转,跳转到维护的页面,只有IP地址为192.168.113.128的本机才可正常访问
3. 基于旧域名跳转到新域名后面加目录
当访问的域名为 http://bbs.xtj.com/post/1.html 时自动跳转到 http: //www.xhzhc.com/bbs/post/1.html
- ① 创建一个bbs目录下post的网页文件,并添加内容,添加新的映射关系
[root@server2 conf]# mkdir -p /usr/local/nginx/html/bbs/post
[root@server2 conf]# vim /usr/local/nginx/html/bbs/post/1.html
[root@server2 conf]# cat /usr/local/nginx/html/bbs/post/1.html
<h1>跳转了</h1>
[root@server2 conf]# vim /etc/hosts
[root@server2 conf]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.113.128 bbs.xtj.com
- ② 主配置文件配置
[root@server2 conf]# pwd
/usr/local/nginx/conf
[root@server2 conf]# vim nginx.conf
......
server {
listen 80;
server_name bbs.xtj.com; ##更改域名
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log; ##开启并对日志保存路径进行修改
location /post {
##添加location;以post开头匹配
rewrite (.+) http://www.xtj.com/bbs$1 permanent; ##$1为位置变量,代表/post
}
location / {
root html;
index index.html index.htm;
}
......
[root@server2 conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx
- ③浏览器访问验证
4. 基于参数匹配(多余)的跳转
访问 http://www.xtj.com/100-(100|200)-100.html 会跳转到 http://www.xtj.com的页面
- ① 主配置文件配置
[root@server2 conf]# pwd
/usr/local/nginx/conf
[root@server2 conf]# vim nginx.conf
......
server {
listen 80;
server_name www.xtj.com; ##更改域名
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log; ##开启并对日志保存路径进行修改
if ($request_uri ~ ^/100-(100|200)-(\d+)\.html$){
##$repuest_uri内置变量,表示uri,\d 纯数字
rewrite (.*) http://www.xtj.com permanent; ##设置重写
}
location / {
root html;
index index.html index.htm;
}
......
[root@server2 conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx
$request uri:包含请求参数的原始URI,不包含主机名,如:
http://www.kgc.com/abc/bbs/index.html?a=16b=2/abc/bbs/index.php?a-18b-2
$uri:这个变量指当前的请求URI,不包括任何参数,如:/abc/bbs/index.html
$document_uri:与$uri相同,这个变量指当前的请求URI,不包括任何传递参数,
如:/abc/bbs/index.html
- 方法二:不使用if,用location来配置效果是一样的
server {
listen 80;
server_name www.xtj.com;
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log;
location ~ /100-(100|200)-(\d+)\.html$ {
rewrite (.*) http://www.xtj.com permanent;
}
location / {
root html;
index index.html index.htm;
}
- ③浏览器访问验证
访问域名 http://www.xtj.com/100-100-100.html 时直接跳转到了 http://www.xtj.com 的页面
访问 http://www.xtj.com/100-200-100.html 也可跳转前面http://www.xtj.com/100-200和http://www.xtj.com/100-100是固定格式不可以变,配置文件里可以设置更改,但后面的数字可随以更改因为使用了正则的 “+” 匹配前面字符一或多个
5. 基于目录下所有 php 结尾的文件跳转
访问 http://www.xtj.com/upload/666.php 跳转到首页(常用场景:网站新用户提示需要注册)
- ① 主配置文件配置
[root@server2 conf]# pwd
/usr/local/nginx/conf
[root@server2 conf]# vim nginx.conf
......
server {
listen 80;
server_name www.xtj.com;
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log;
location ~* /upload/.*\.php$ {
rewrite (.*) http://www.xtj.com permanent;
}
## ~* 表示正则匹配,不区分大小写,匹配/upload目录下所有以.php结尾的文件
location / {
root html;
index index.html index.htm;
}
......
[root@server2 conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx
- ②浏览器访问验证
6. 基于最普通一条 url 请求的跳转
访问一个具体的页面,如: http://www.xtj.com/qqq/888.html,能跳转到首页
- ① 主配置文件配置
[root@server2 conf]# pwd
/usr/local/nginx/conf
[root@server2 conf]# vim nginx.conf
......
server {
listen 80;
server_name www.xtj.com;
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log;
location ~* /qqq/888.html {
##修改自定义路径
rewrite (.+) http://www.xtj.com permanent;
}
location / {
root html;
index index.html index.htm;
}
......
[root@server2 conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx
- 方法二:不使用location,用if来配置效果是一样的
server {
listen 80;
server_name www.xtj.com;
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log;
if ($request_uri ~^/qqq/888.html) {
rewrite (.+) http://wwww.xtj.com permanent;
}
location / {
root html;
index index.html index.htm;
}
- ②浏览器访问验证
nfiguration file /usr/local/nginx/conf/nginx.conf test is successful
[root@server2 conf]# systemctl restart nginx
- 方法二:不使用location,用if来配置效果是一样的
```bash
server {
listen 80;
server_name www.xtj.com;
charset utf-8;
access_log /var/log/nginx/www.xtj.com-access.log;
if ($request_uri ~^/qqq/888.html) {
rewrite (.+) http://wwww.xtj.com permanent;
}
location / {
root html;
index index.html index.htm;
}
- ②浏览器访问验证
[外链图片转存中…(img-VQvRfxQs-1658741392782)]