前言
现在 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 反向代理
二,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 区别
-
正则表达式是二者的区别之一
正则表达式:可以更加精确的匹配需求的字符串/参数/位置等3.从功能看 rewrite 和 location 似乎有点像,
都能实现跳转,主要区别在于 rewrite 是在同一域名内更改获取资源的路径,而 location 是对一类路径做控制访问或反向代理,还可以proxy_pass 到其他机器
三、Rewrite 命令介绍
1. 语法格式
rewrite <regex> <replacement> [flag];
#<regex>:正则表达式
#<replacement>:跳转后的内容
#[flag]rewrite支持的flag标记
2. flag 标记
标记 | 说明 |
---|---|
last | 本条规则匹配完成后,继续向下匹配新的location URL规则,一般用在serve和if中 |
break | 本条规则匹配完成即终止,不再匹配后面的任何规则 |
redirect | 返回302临时重定向,浏览器地址会显示跳转后的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 优先级
-
首先精确匹配 =
-
其次前缀匹配 ^~
-
其次是按文件中顺序的正则匹配 ~ 或~*
-
然后匹配不带任何修饰的前缀匹配
-
最后是交给 / 通用匹配
-
相同类型的表达式,字符串长的会优先匹配
按优先级匹配
= 类型
^~ 类型表达式
正则表达式(~和~*)类型
常规字符串匹配类型,按前缀匹配
通用匹配(/),如果没有其他匹配,任何请求都会匹配到
- 总结:
1.首先看 优先级:精确>前缀>正则>一般>通用
2.优先级相同:正则看上下顺序,上面的优先;
3.一般匹配看长度,最长匹配的优先
4.精确、前缀、正则、一般 都没有匹配到,最后再看通用匹配
4. 优先级示例
location = / {
[ configuration A ]
}
#精确匹配,主机名后面不能带任何字符串
location / {
[configuration B ]
}
#一般匹配,所有的地址都以/开头,这条规则将匹配到所有请求,但正则表达式和最长字符会优先匹配
location /documents/ {
[ configuration C]
}
#匹配任何以/documents/开头的地址,当后面location的正则表达式没有匹配到时,才起作用
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;
}
rewrite示例:
(1)基于域名的跳转
现在公司I旧域名www.kgc.com有业务需求变更,需要使用新域名www.benet.com代替,但是旧域名不能废除,需要跳转到新域名上,而且后面的参数保持不变。
vim /usr/local/nginx/conf/nginx.conf
server
listen 80:
server name www.kgc.com; 域名修改
charset utf-8;
access log /var/log/nginx/www.kgc.com-access.log; 日志修改
location / {
添加域名重定向
if ($host = 'www.kgc.com'){
$host为rewrite全局变量,代表请求主机头字段或主机名
rewrite /(.*)http://www.benet.com/$1 permanent;
$1为正则匹配的内容,即"域名/"之后的字符串
}
root html;
index index.html index.htm;
}
}
(2)基于客户端IP访问跳转
今天公司业务新版本上线,要求所有IP访问任何内容都显示一个固定维护页面,只有公司IP
:192.168.10.19访问正常。也就是本机IP
vim /usr/local/nginx/conf/nginx.conf
server {
listen 80;
server name www.kgc.com; 域名修改
charset utf-8;
access log /var/log/nginx/www.kgc.com-access.log; 日志修改
设置是否合法的IP标记
set $rewrite true; 设置变量Srewrite,变量值为boole值true
判断是否为合法IP
if ($remote addr = "192.168.10.19"){
当客户端IP为192.168.10.19时,将变量值设为false,不进行重写
set Srewrite false;
}
除了合法IP,其它都是非法IP,进行重写跳转维护页面
if ($rewrite = true){ 当变量值为true时,进行重写
rewrite (.+)/weihu.html;
将域名后边的路径重写成/weihu.html,例如www.kgc.com/weihu.html
}
location = /weihu.html {
root /var/www/html; #网页返回/var/www/html/weihu.html的内容
}
location / {
root html;
index index.html index.htm;
}
}
mkdir -p /var/www/html/
echo "<hl>We are maintaining now!</hl>">/var/www/html/weihu.html
systemctl restart nginx
只有1P为192.168.10.19能正常访问,其它地址都是维护页面
(3)基于旧域名跳转到新域名后面加目录
现在访问的是http://bbs,kgc.com/post,现在需要将这个域名下面的访问都跳转到
nttp://www.kac.com/bbs/post
vim /usr/local/nginx/conf/nginx.conf
server {
listen 80;
server name bbs.kgc.com; 域名修改
charset utf-8;
access log /var/log/nginx/www.kgc.com-access.log;
添加
location /post {
rewrite (.+) http://www.kgc.com/bbs$1 permanent;
这里的$1为位置变量,代表/post
location / {
root html;
index index.html index.htm;
}
}
mkdir -p /usr/local/nginx/html/bbs/post
echo "this is 1.html">/usr/local/nginx/html/bbs/post/1.html
echo "192.168.10.19 bbs.kgc.com">>/etc/hosts
systemctl restart nginx
使用浏览器访问http:Lbbs.kgc.com/post/1.html跳转到http:Lm.kgc,com/bbs/post1.html
(4)基于参数匹配的跳转
现在访问http://www.kgc.com/100-(100|200)-100.html跳转到http:www.kgc.com页面。
vim /usr/local/nginx/conf/nginx.conf
server
listen 80;
server_name www.kgc.com; 域名修改
charset utf-8;
access log /var/log/nginx/www.kgc.com-access.log;
if ($request uri ~/100-(100|200)-(\d+).html$){
rewrite (.+) http:/www.kgc.com permanent;
}
location / {
root html;
index index.html index.htm;
}
}
使用浏览器访问http://www.kgc.com/100-200-100,html或http://www.kgc.com/100-100-100.html
跳转到http://www.kgc.com页面。
(5)基于目录下所有php结尾的文件跳转
要求访问http://www.kgc.com/upload/123.php跳转到首页。
vim /usr/local/nginx/conf/nginx.conf
server {
listen 80;
server_name www.kgc.com; 域名修改
charset utf-8;
access_log /var/log/nginx/www.kgc.com-access.log;
location ~* /upload/.*\.php$ {
rewrite (.+) http://www.kgc.com permanent;
location / {
root html;
index index.html index.htm;
}
}
systemctl restart nginx
浏览器访问http://www.kgc.com/upload/123.php跳转到http://www.kgc.com页面。
(6)基于最普通一条url请求的跳转
要求访问一个具体的页面如http://www.kgc.com/abc123.html跳转到首页
vim /usr/local/nginx/conf/nginx.conf
server
listen 80;
server_name www.kgc.com; 域名修改
charset utf-8;
access_log /var/log/nginx/www.kgc.com-access.log;
location ~*/abc/123.html
rewrite (.+) http://www.kgc.com permanent;
}
location / {
root html;
index index.html index.htm;
}
}
systemctl restart nginx
3.html跳转到首页
vim /usr/local/nginx/conf/nginx.conf
server
listen 80;
server_name www.kgc.com; 域名修改
charset utf-8;
access_log /var/log/nginx/www.kgc.com-access.log;
location ~*/abc/123.html
rewrite (.+) http://www.kgc.com permanent;
}
location / {
root html;
index index.html index.htm;
}
}
systemctl restart nginx
浏览器访问http://www.kgc.com/abc/123.html跳转到http://www.kgc.com页面。