Nginx 重定向 rewrite

1,695 阅读9分钟

一、什么是Rewrite

因为目前很多工作前端开发都会选择使用Nginx作为反向代理服务器,但是平时业务需要难免碰到重写URL,Nginx的Rewrite跳转有什么使用场景呢?

  • 公司更换域名需要访问旧域名时跳转到新域名
  • 请求静态文件跳转到CDN
  • 根据用户设备不同跳转到不同站点(pc端,移动端)

不得不说的是Apache服务器规则库很强大,做跳转也很简单,但是Nginx使用Rewrite实现跳转效率更高,所以这也是我们需要学习Nginx的Rewrite模块的目的所在。Rewrite是Nginx的静态重写模块,跳转的使用场景有以下几种情形:

  • 可以改变用户访问的URL
  • 可以将动态URL伪装成静态URL提供服务
  • 可以访问旧域名时跳转到新域名
  • 可以根据变量,目录,客户端信息等跳转不同的URL

1、Rewrite如何实现跳转?

Nginx利用ngxhttprewrite_module模块解析和处理rewrite请求。Rewrite用于实现URL重写,其实有点类似于重定向功能,可以将用户的请求重写至别的目录,在一定程度上提高了网站安全性。Rewrite支持if条件判断,但不支持else判断。而且Rewrite需要PCRE支持,一次重定向最多可以跳转10次,超过10次将返回500错误。Rewrite模块包含set命令,可以创建变量用来记录条件标识或者传递变量到其他的Location中。Rewrite实际上就是使用Nginx已有的全局变量或者通过set命令设置的变量结合正则表达式实现URL重写。

2、Rewrite使用场景

在Nginx中使用Rewrite实现跳转有以下三种场景:

  • 直接用Rewrite进行匹配跳转
  • 使用if匹配全局变量进行跳转
  • 使用location匹配再进行跳转

所以说rewrite语句只允许放在server{ },if{ },location{ }中。接下来我们先看看if指令。

if指令

if指令用于条件匹配判断,根据判断结果选择不同的Nginx配置,在server或location中配置。Nginx中的if指令只支持单重判断,不支持多重判断。我们简单看个例子:

location /test{

index index.html;

        if ( $scheme = http ){    
            rewrite / https://www.niyueling.cn permanent; 
        }  复制代码
        if  ($scheme = https ){    
            echo "if ---> $scheme";  
        }
>        }

复制代码

location上篇文章讲过了,这里不再细讲,接下来我们看下rewrite语法.

rewrite语法

  • rewrite regex replacement [flag]

rewrite将用户请求的URL基于正则表达式regex进行检查,匹配到时将其替换为正则表达式对应的#新的URL。若在同一级配置模块中存在多个rewrite规则,则会自顶向下检查。replacement则为跳转后的内容。[flag]作为标识符用于控制循环机制,如果替换后的URL是以http或者https开头,则会直接301永久重定向。

flag参数介绍

rewrite语句有四种flag状态:redirect、permanent、break、last。前两种属于客户浏览器重新发起对新地址的请求,后两种是在WEB服务器内部实现跳转。

  • redirect #临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新URL给客户端,有客户端重新发起请求,使用相对路径,http://或https://开头,状态码:302
  • permanent #永久重定向,以永久重定向的方式直接返回重写后生成的新URL给客户端,由客户端重新发起新的请求,状态码:301
  • last #重写完成后停止对当前location中后续的其他重写操作,而后对新的URL启动新一轮重写检查,不建议在location中使用
  • break #重写完成后停止对当前URL在当前location中后续的其他写操作,而后直接跳转至重写规则匹配块之后的其他配置;结束循环,建议在location中使用

image.png

二、常用的Nginx正则表达式

字符涵义以及示例
^匹配输入字符串的起始位置
$匹配输入字符串的结束位置
*匹配前面的字符零次或多次;如“ol*”能匹配“o”及“ol”、“oll”
+匹配前面的字符一次或多次;如“ol+”能匹配“ol”及“oll”、“olll”,但不能匹配“o”
?匹配前面的字符零次或一次;例如“do(es)?”能匹配“do”或者“does”,”?”等效于”{0,1}”
.匹配除“\n”之外的任何单个字符,若要匹配包括“\n”在内的任意字符,请使用诸如“[.\n]”之类的模式
\将后面接着的字符标记为一个特殊字符或一个原义字符或一个向后引用。如“\n”匹配一个换行符,而“”则匹配“”则匹配“
\d匹配纯数字
\D匹配非数字字符,等价于[^0-9]
\s匹配空白字符(空格或者制表符)
\S匹配非空字符
\w匹配包括下划线的任何字符,等价于[a-zA-Z0-9_]
\W匹配任何非单词字符。 等价于 [^a-zA-Z0-9_]
{n}重复 n 次
{n,}重复 n 次或更多次
{n,m}重复 n 到 m 次
[ ]定义匹配的字符范围
[c]匹配单个字符 c
[a-z]匹配 a-z 小写字母的任意一个
[a-zA-Z0-9]匹配所有大小写字母或数字
()表达式的开始和结束位置
或运算符

三、nginx 配置文件里 location 项

1、localtion 作用

location 用于匹配用户访问的URL,来适用哪一个目录中的资源

2、location 可以分为三类

  • 精准匹配 : location = / {......}
  • 一般匹配 : location / {......}
  • 正则匹配 : location ~ / {.....}

3、location 的常用匹配规则

字符涵义
=进行普通字符精确匹配,也就是完全匹配
^~表示普通字符匹配。使用前缀匹配。如果匹配成功,则不再匹配其它 location
~区分大小写的匹配
~*不区分大小写的匹配
!~区分大小写的匹配取非
!~*不区分大小写的匹配取非

4、location 的 优先级与匹配规则

  1. 首先是 精确匹配 =

    eg: location = /test/ { }

  2. 其次是 前缀匹配 ^~\

    eg: location ^~ /test/ { }

  3. 其次是按文件中顺序的正则匹配 ~ 或 ~*

    eg: location ~ /test/ { }

  4. 然后是一般匹配 ,后面不带 / 的匹配

    eg: locaton /test/ { }

  5. 最后是 交给 / 通用匹配

    eg: location / { }

首先看匹配的优先级 精确匹配 > 前缀匹配 > 正则匹配 > 一般匹配 > 通用匹配

当 没有精确匹配,只有前缀匹配,或者 正则匹配 时,同等级的优先级遵循匹配即停止原则。即当都是正则匹配时,哪个localtion 写在配置文件的上面,就匹配哪个

一般匹配,则遵循最长匹配原则,当只有一般匹配符合时,哪个location 匹配的url 最长,则使用哪个。但是,如果有符合规则的精确匹配,前缀匹配,正则匹配,则使用优先级高的。

5、location 示例说明

(1)location = / {}

=为精确匹配 / ,主机名后面不能带任何字符串,比如访问 / 和 /data,则 / 匹配,/data 不匹配
再比如 location = /abc,则只匹配/abc ,/abc/或 /abcd不匹配。

(2)location / {}

因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求 比如访问 / 和 /data, 则 / 匹配, /data 也匹配,
但若后面是正则表达式会和最长字符串优先匹配(最长匹配原则)

(3)location /test/ {}

匹配任何以 /test/ 开头的地址,匹配符合以后,还要继续往下搜索其它 location
只有其它 location后面的正则表达式没有匹配到时,才会采用这一条

(4)location /test/abc {}

匹配任何以 /test/abc 开头的地址,匹配符合以后,还要继续往下搜索其它 location
只有其它 location后面的正则表达式没有匹配到时,才会采用这一条

(5)location ^~ /images/ {}

匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条(匹配即停止原则)

(6)location ~* .(gif|jpg|jpeg)$ {}

匹配所有以 gif、jpg或jpeg 结尾的请求
然而,所有请求 /images/ 下的图片会被 location ^~ /images/ 处理,因为 ^~ 的优先级更高,所以到达不了这一条正则

(7)location /images/abc {}

最长字符匹配到 /images/abc ,如果只有一般匹配,则会使用它,但时由于有 正则匹配和前缀匹配,而一般匹配优先级最低,继续往下搜索其它 location,会发现 ^~ 和 ~ 存在

(8)location ~ /images/abc {}

匹配以/images/abc 开头的,优先级次之,只有去掉 location ^~ /images/ 才会采用这一条

(9)location /images/abc/1.html {}

匹配/images/abc/1.html 文件,如果和正则 ~ /images/abc/1.html 相比,正则优先级更高

6、网站使用中的三个匹配规则

1 第一个必选规则

直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,比如说官网。
这里是直接转发给后端应用服务器了,也可以是一个静态首页

location = / { 
    proxy_pass http://tomcat_server/; 
    }

2、第二个必选规则是处理静态文件请求

这是nginx作为http服务器的强项!
有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用

location ^~ /static/ { 
   root /webroot/static/; 
   } 
   location ~* \.(html|gif|jpg|jpeg|png|css|js|ico)$ {
   root /webroot/res/; 
   }

3、第三个规则就是通用规则

比如用来转发带.php、.jsp后缀的动态请求到后端应用服务器
非静态文件请求就默认是动态请求

location / { 
    proxy_pass http://tomcat_server; 
}

四、访问重新rewrite

1 rewrite 概述

rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向

rewrite只能放在server{},location{},if{}中,并且默认只能对域名后边的除去传递的参数外的字符串起作用

例如:

www.nmixx.com/a/we/index.… 只对/a/we/index.php 部分重写

2 rewrite 跳转实现

Nginx: 通过 ngx_http_rewrite_module 模块支持URL重写,支持 if 判断,但是不支持else

跳转: 从一个 location 跳转到 另一个location ,循环最多可以执行10次。超出后,返回nginx 500 错误

PCRE 支持: perl 兼容正则表达式的语法规则匹配

重写模块 set 指令: 创建新的变量,并设其值

3 rewrite 执行顺序如下:

(1) 执行 server 块里面的 rewrite 指令

(2) 执行 location 匹配

(3) 执行选定的 location 中的 rewrite 指令

语法: rewrite [flag];

regex :表示正则匹配规则

replacement :表示跳转后的内容

flag :表示 rewrite 支持的 flag 标记

###flag标记说明###

last :本条规则匹配完成后,继续向下匹配新的location URI规则,一般用在 server 和 if 中

break :本条规则匹配完成即终止,不再匹配后面的任何规则,一般使用在 location 中

redirect:返回302临时重定向,浏览器地址会显示跳转后的URL地址

permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址。

五、rewrite 示例

1 基于域名的跳转

应用场景:现在公司旧域名www.benet.com 有业务需求变更,需要使用新域名www.mynet.com 代替,但是旧域名不能废除,需要跳转到新域名上,而且后面的参数保持不变。

1)配置域名跳转

vim /usr/local/nginx/conf/nginx.conf
    
    server {
        listen       80;
        server_name  www.benet.com;  #设置域名

        charset utf-8;

        access_log  logs/benet.access.log ;   #设置日志路径


        location  / {
            if ($host = 'www.benet.com') {   #变量host 为rewrite 的全局变量,代表请求头字段或者主机名
                rewrite ^/(.*)$   http://www.mynet.com/$1  permanent;  #$1 为正则匹配之后的内容,即 “www.benet.com/" 后面的字符串
            }
            root   html;
            index  index.html index.htm;
        }
        .......
    }
    
    

例图:
image.png

2)访问测试

  echo '192.168.37.133  www.benet.com   www.mynet.com'  >> /etc/hosts
 
 #注意,网页文件应该放在 /usr/local/nginx/html/目录中
 echo '<h1> hello </h1>'  > /usr/local/nginx/html/index.html
 
 
 /usr/local/nginx  -s reload 

 firefox  http://www.benet.com
   

2、基于客户端IP 访问跳转

应用场景

要求:今天公司业务新版本上线,要求所有 IP 访问任何内容都显示一个固定维护页面,只有公司 IP :192.168.37.133 访问正常

1)修改配置文件,实现 ip 访问跳转

    vim /usr/local/nginx/conf/nginx.conf
 
 server {
        listen       80;
        server_name  www.benet.com;

        charset utf-8;

        access_log  logs/benet.access.log ;
      
       #设置是否合法的IP 标记
        set $rewrite true;  #设置变量rewrite,变量值为boole 值,true
        
        #判断是否合法的IP 标记
        if  ($remote_addr = "192.168.37.133" ) {
            set $rewrite false;        #当客户端的ip 为 192.168.23.104,将变量值设置为false,不进行重写
        }


       #除了  192.168.23.104,其他的iP 都是非法ip,进行重写跳转到 维护界面
       if  ($rewrite = true) {   #变量值为true 时,进行重写
            rewrite (.+) /weihu.html;   #将域名后面的路径重写为 /weihu.html。这里不可以加上 permanent,否则会陷入死循环
        }

        # 设置网页www.benet.com/weihu.html 的内容
        location = /weihu.html {   
             root /var/www/html;    
        }
        
        
        location  / {
            root   html;
            index  index.html index.htm;
        }
     .......
}
   
   
   echo  "<h1> hello </h1>" > /usr/local/nginx/html/index.html
   mkdir -p /var/www/html/
   echo "<h1> weihu </h1>"   >  /var/www/html/index.html
   
   nginx -s reload  #重载 nginx 配置文件

 

例图:
image.png

2)访问测试

192.168.37.133 主机访问

 echo "192.168.37.133 www.benet.com " >> /etc/hosts
firefox  http://www.benet.com    
   

2 、基于旧域名跳转到新域名后面加目录(把原本域名的主机名变为现在的目录名

现在访问的是 bbs.benet.com/post ,现在需要将这个域名下面的访问都跳转到www.benet.com/bbs/post

1)修改配置文件

 vim /usr/local/nginx/conf/ngixn.conf    
server {        
	listen       80;        
	server_name  bbs.benet.com;        
	charset utf-8;        
	access_log  
	logs/bbs.benet.access.log  main;        
	location  /test {            
		rewrite (.+)   http://www.betnet.com/bbs/$1  permanent;        
		}  #$1 表示位置变量,值是 /test        
	location / {            
		root   html;            
		index  index.html index.htm;        
		}       
	...... 
}
   

例图:

image.png

2)访问测试

  mkdir -p /usr/local/nginx/html/bbs/test
echo "this is 1.html" > /usr/local/nginx/html/bbs/test/1.html
echo "192.168.37.133 bbs.benet.com" >> /etc/hosts
nginx  -s reload
firefox http://bbs.benet.com/test/1.html
  

4、基于参数匹配跳转

访问www.benet.com/100-(100|20… 跳转到www.benet.com页面

1、修改配置文件

    vim /usr/localnginx/conf/nginx.conf
 server {
        listen       80;
        server_name  www.benet.com;

        charset utf-8;

        listen       80;
        server_name  www.benet.com;

        charset utf-8;

        access_log  logs/www.benet.access.log ;

        if ($request_uri ~ ^/100-(100|200)-(\d+).html$) {  #uri为 完整的url除去$host部分. \d 表示匹配数字。
                rewrite (.+) http://www.benet.com permanent;
        }



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


#$request_uri: 包含请求参数的原始URI,不包含主机名,如: http://www.nmixx.com/abc/bbs/index.html?a=1&b=2中的/abc/bbs/index.php?a=1&b=2

#$$uri:这个变量指当前的请求URI,不包括任何参数,如: /abc/bbs/index.html

#$document_uri: 与$uri相同, 这个变量指当前的请求URI,不包括任何传递参数,如:/abc/bbs/index.html
 

例图:

image.png

5 基于目录下所有的php 结尾的文件跳转

要求访问 www.benet.com/upload/123.… 跳转到首页。

1 修改配置文件

 vim /usr/local/nginx/conf/nginx.conf
 server {
        listen       80;
        server_name  www.benet.com;

        charset utf-8;

        access_log  logs/www.benet.access.log ;

		#将/upload/路径后面,所有.php 结尾的转发
        location ~* /upload/.*\.php$ {   
             rewrite (.+)  http://www.benet.com permanent;
        }

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

例图: image.png

6、基于最普通一条 URL 请求的跳转

要求访问一个具体的页面如 www.benet.com/abc/888.htm… 跳转到首页

1 修改配置文件

vim /usr/local/nginx/conf/nginx.conf

    server {
        listen       80;
        server_name  www.benet.com;

        charset utf-8;

        access_log  logs/www.benet.access.log ;

        location ~*  ^/abc/123.html {   
             rewrite (.+)  http://www.benet.com permanent;
        }

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

测试

nginx -s reload
firefox http://www.benet.com/abc/123.html

六、总结

location 用于匹配 用户访问请求的URL ,来使用哪一个目录中的网页资源,

rewrite ....... permanent 修改url 后再次请求访问

rewrite /weihu.html 按照修改域名后的 url 直接跳转到对应的 location

location ~ .... if ($request_uri) {

​ rewrite 如果对全URL 重写,则重写部分就以http:// 开头,后面跟上重写后的URL。 http://.....

​ 如果不以http://.... 开始重写, 默认只对于域名后的路径重写,对 ?后传递的参数无效如www.benet.com/abc/?..... ,则只 对 " abc" 进行重写