前言
现在 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 匹配全局变量后跳转 -
使用 location 匹配再跳转
3.2 rewrite 放在 server{},if{},location{} 段中
- location 只对域名后边的除去传递参数外的字符串起作用——页面文件的路径
3.3 对域名或参数字符串
- 使用 if 全局变量匹配
- 使用 proxy_pass 反向代理
二、Nginx 正则表达式
1. 常见的正则表达式元字符
字符 | 说明 |
---|
^ | 匹配输入字符串的起始位置 | $ | 匹配输入字符串的结束位置 | * | 匹配前面的字符零次或多次 | + | 匹配前面的字符一次或多次 | ? | 匹配前面的字符零次或一次 | . | 匹配除”\n”之外的任何单个字符 | \ | 将后面接着的字符标记为一个特殊字符或一个原义字符或一个向后引用 | \ d | 匹配纯数字 | {n} | 重复n次 | {n,} | 重复n次或更多次 | [c] | 匹配单个字符c | [a-z] | 匹配a-z小写字母的任意一个 | [a-zA-Z] | 匹配a-z小写字母或A-Z大写字母的任意一个 |
2. nginx 与 apache 区别
- 正则表达式是二者的区别之一
正则表达式:可以更加精确的匹配需求的字符串/参数/位置等
三、Rewrite 命令详解
1. 语法格式
rewrite <regex> <replacement> [flag];
#<regex>:正则表达式
#<replacement>:跳转后的内容
#[flag]rewrite支持的flag标记
2. flag 标记
标记 | 说明 |
---|
last | 相当于Apache的[L]标记,表示完成rewrite | break | 本条规则匹配完成即终止,不再匹配后面的任何规则 | redirect | 返回302临时重定向,浏览器地址会显示跳转后的URL地址,爬虫不会更新url | permanent | 返回301永久重定向,浏览器地址会显示跳转后的URL地址,爬虫更新url |
| last | break |
---|
使用场景 | 一般写在 server 和 if 中 | 一般使用在 location 中 | URL匹配 | 不终止重写后的 urI 匹配 | 终止重写后的 url 匹配 |
- last:url 重写后,马上发起一个新请求,再次进入 server 块,重试 location 匹配,超过10次匹配不到报 500 错误,地址栏不变;
- break:url重写后,直接使用当前资源,不再使用location余下的语句,完成本次请求,地址栏不变
总的来说:last 和 break 再重定向后,地址栏都不会发生变化,这是他们的相同点。不同点在于 last 会写在 server 和 if中,break 是写在location 中,last 不会终止重写后的 url 匹配,break 会终止重写后的 url 匹配。
四、location 解析
1. location 分类
location = patt {} [精准匹配]
location patt {} [一般匹配]
location ~ patt {} [正则匹配]
2. 正则匹配的常用表达式
标记 | 说明 |
---|
~ | 执行一个正则匹配,区分大小写 | ~* | 执行一个正则匹配,不区分大小写 | !~ | 执行一个正则匹配,区分大小写不匹配 | !~* | 执行一个正则匹配,不区分大小写不匹配 | ^~ | 普通字符匹配,使用前缀匹配,如果匹配成功,则不再匹配其他location | = | 普通字符精确匹配,也就是完全匹配 | @ | 定义一个命名的location,使用在内部定向时 |
3. location 优先级
-
相同类型的表达式,字符串长的会优先匹配 -
按优先级匹配
= 类型
^~ 类型表达式
正则表达式(~和~*)类型
常规字符串匹配类型,按前缀匹配
通用匹配(/),如果没有其他匹配,任何请求都会匹配到
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指令
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.8.135:80
192.168.8.132:80
location / {
proxy_ pass http:
}
------------------------------------------------------------------------------------------------
五、案例解析
1. 基于域名的跳转
公司旧域名 www.gkd.com 因业务需求的变更,需要使用新域名 www.haha.com 代替,但是旧域名不能废除,并且旧域名要可跳转到新的域名上,而且后面的参数保持不变。
[root@localhost ~]#systemctl status nginx.service
● nginx.service - nginx
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: active (running) since 六 2021-10-09 00:30:45 CST; 3 days ago
[root@localhost ~]#vim /etc/hosts
......
192.168.8.135 www.gkd.com www.haha.com
[root@localhost ~]#mkdir -p /var/log/nginx
[root@localhost ~]#vim /usr/local/nginx/conf/nginx.conf
......
server {
listen 80;
server_name www.gkd.com; #修改域名
charset utf-8; #字符集可选择性修改
access_log /var/log/nginx/www.gkd.com.log; #开启并对日志保存路径进行修改
location / { #在原有的location位置进行插入下面内容
if ($host = 'www.gkd.com'){ #$host为rewrite全局变量,表示请求主机头字段或主机名
rewrite ^/(.*)$ http:
} #$1为匹配的位置变量,即域名后面的字符串,同时永久跳转
root html;
index index.html index.htm;
}
......
[root@localhost ~]#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@localhost ~]#systemctl restart nginx.service
- ③ 使用浏览器进行验证
- 我们可以看到此时访问 www.gkd.com 时会自动跳转到 www.haha.com 上进行访问,并且在访问www.gkd.com/1.txt 时在错误页面显示时可以看到域名也发生了变化,变成了www.haha.com/1.txt。
2. 基于 IP 地址访问跳转
由于公司的业务新版本上线,要求所有 IP 访问任何内容都显示一个固定的维护页面,只有公司自己的IP地址 192.168.8.135 才能正常访问。
[root@localhost /usr/local/nginx/html]#mkdir -p /var/www/html
[root@localhost /var/www/html]#vim weihu.html
网站维护中~~~~~~~~~~
......
server {
listen 80;
server_name www.gkd.com;
charset utf-8;
access_log /var/log/nginx/www.gkd.com.log; #开启并对日志保存路径进行修改
set $rewrite true; #设置变量$rewrite,变量值为布尔值为true
if ($remote_addr = "192.168.8.135"){ #当客户端ip为192.168.8.135时,将变量值设为flase,不进行重写
set $rewrite false;
} #除了合法IP,其他都是非法IP,进行重写跳转到维护页面
if ($rewrite = true){ #布尔值表达式在不满足false情况下,会匹配满足true的location
rewrite (.+) /weihu.html; #重写在访问IP后边加入/weihu.html,如192.168.8.135/weihu.html
}
location = /weihu.html {
root /var/www/html; #页面返回/var/www/html/weihu.html里面的内容
}
location / {
root html;
index index.html index.htm;
}
......
[root@localhost /usr/local/nginx/html]#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@localhost /usr/local/nginx/html]#systemctl restart nginx.service
此时用另一台机器进行访问192.168.8.135这个地址,会发现页面进行了跳转,跳转到维护的页面,只有IP地址为192.168.8.135的本机才可正常访问
3. 基于旧域名跳转到新域名后面加目录
要求:当访问的域名为 http://bbs.gkd.com/post/1.html 时自动跳转到 http: //www.haha.com/bbs/post/1.html
- ① 创建一个bbs目录下post的网页文件,并添加内容
[root@localhost /var/www/html]#mkdir -p /usr/local/nginx/html/bbs/post
[root@localhost /var/www/html]#vim /usr/local/nginx/html/bbs/post/1.html
[root@localhost /var/www/html]#cat /usr/local/nginx/html/bbs/post/1.html
<h1>文档有毒~~~~~~</h1>
[root@localhost /var/www/html]#vim /etc/hosts
192.168.8.135 bbs.gkd.com www.haha.com
.......
server {
listen 80;
server_name bbs.gkd.com; #更改域名
charset utf-8;
access_log /var/log/nginx/www.gkd.com-access.log; #开启并对日志保存路径进行修改
location /post { #添加location;以post开头匹配
rewrite (.+) http:
}
location / {
root html;
index index.html index.htm;
}
......
[root@localhost /var/www/html]#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@localhost /var/www/html]#systemctl restart nginx.service
可以看到此时域名已经自动跳转到了 http: //www.haha.com/bbs/post/1.html
4. 基于参数匹配(多余)的跳转
要求:访问 http://www.gkd.com/100-(100|200)-100.html 会跳转到 http://www.gkd.com的页面
[root@localhost /var/www/html]#vim /usr/local/nginx/conf/nginx.conf
......
server {
listen 80;
server_name www.gkd.com; #更改域名
charset utf-8;
access_log /var/log/nginx/www.gkd.com-access.log; #开启并对日志保存路径进行修改
#设置正则匹配,例如:http:
if ($request_uri ~ ^/100-(100|200)-(\d+)\.html$){ #$repuest_uri内置变量,表示uri,\d 纯数字
rewrite (.*) http:
}
location / {
root html;
index index.html index.htm;
}
......
[root@localhost /var/www/html]#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@localhost /var/www/html]#systemctl restart nginx.service
[root@localhost /var/www/html]#vim /etc/hosts
192.168.8.135 www.gkd.com
- ③ 使用浏览器进行验证
可以看到访问域名 http://www.gkd.com/100-100-100.html 时直接跳转到了 http://www.gkd.com 的页面 访问 http://www.gkd.com/100-200-100.html 也可跳转
5. 基于目录下所有 php 结尾的文件跳转
要求:访问 http://www.gkd.com/upload/123.php 跳转到首页(常用场景:网站新用户提示需要注册)
[root@localhost /var/www/html]#vim /usr/local/nginx/conf/nginx.conf
......
server {
listen 80;
server_name www.gkd.com;
charset utf-8;
access_log /var/log/nginx/www.gkd.com-access.log;
location ~* /upload/.*\.php$ {
# ~* 表示正则匹配,不区分大小写,匹配/upload目录下所有以.php结尾的文件
rewrite (.*) http:
}
location / {
root html;
index index.html index.htm;
}
......
[root@localhost /var/www/html]#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@localhost /var/www/html]#systemctl restart nginx.service
可以看到访问 http://www.gkd.com/upload/123.php 会跳转到 http://www.gkd.com 首页
6. 基于最普通一条 url 请求的跳转
要求:访问一个具体的页面,如: http://www.gkd.com/abc/123.html,能跳转到首页
[root@localhost /var/www/html]#vim /usr/local/nginx/conf/nginx.conf
......
server {
listen 80;
server_name www.gkd.com;
charset utf-8;
access_log /var/log/nginx/www.gkd.com-access.log;
location ~* /abc/123.html {
rewrite (.+) http:
}
location / {
root html;
index index.html index.htm;
}
......
[root@localhost /var/www/html]#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@localhost /var/www/html]#systemctl restart nginx.service
可以看到访问 http://www.gkd.com/abc/123.html 的时候进行了跳转,跳转到了主页面!
总结
-
配置跳转的原因:为了让URL看起来更规范、合理;企业会将动态URL地址伪装成静态地址提供服务;网址换新域名后,期望用户可以通过旧的访问跳转到新的域名上;服务端某些业务调整 -
Nginx跳转需求的实现方式:①使用rewrite进行匹配跳转(主要匹配的是具体的路径);②使用if匹配全局变量后跳转,结合布尔值时间true和fales的跳转③使用location匹配再跳转(可以匹配后执行proxy_pass,将请求跳转到其他服务上) -
nginx 服务基于 http 协议 rewrite 重写 mudule 模块,主从URL重写,其中有一个布尔值:返回值的结果只有true或者false;只支持if单分支语句 -
location 分类:① location = patt{} [精准匹配];② location patt {} [一般匹配];③ location ~ patt {} [正则匹配] -
location 的匹配优先级为:首先精确匹配,其次是前缀匹配,再其次是按文件中顺序的正则匹配,然后匹配不带任何修饰的前缀匹配 ,最后交给 / 进行通用匹配。
|