nginx之旅(第五篇):URL重写介绍URL重写场景URL重写语法

Posted Nicholas--Altshuler

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nginx之旅(第五篇):URL重写介绍URL重写场景URL重写语法相关的知识,希望对你有一定的参考价值。

nginx之旅(第五篇):URL重写

 

一、URL重写介绍

URL重写是指将一个URL请求重新写成网站可以处理的另一个URL的过程。这样说可能不是很好理解,举个例子来说明一下,在开发中可能经常遇到这样的需求,比如通过浏览器请求的http://localhost:8080/getUser?id=1,但是需要通过SEO优化等等原因,需要把请求的地址重写为http://localhost:8080/getUser/1这样的URL,从而符合需求或者更好的被网站阅读。

当遇到这种请求的时候,就需要使用到UrlRewrite重写或者使用一些网关路由,如SpringCloud的Gateway,Zuul,又或者是Nginx来实现这个功能。

和apache等web服务软件一样,rewrite的主要功能是实现URL地址的重定向。Nginx的rewrite功能需要PCRE软件的支持,即通过perl兼容正则表达式语句进行规则匹配的。默认参数编译nginx就会支持rewrite的模块,但是也必须要PCRE的支持。

rewrite和location的功能有点相像,都能实现跳转,主要区别在于rewrite常用于同一域名内更改获取资源的路径,而location是对一类路径做控制访问和反向代理,可以proxy_pass到其他服务器。

Nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。 rewrite只能放在server{},location{},if{}中, 并且只能对域名后边的除去传递的参数外的字符串起作用。

Rewrite主要的功能就是实现URL的重写,Nginx的Rewrite规则采用Pcre,perl兼容正则表达式的语法规则匹配,如果需要Nginx的Rewrite功能,在编译Nginx之前,需要编译安装PCRE库。 通过Rewrite规则,可以实现规范的URL、根据变量来做URL转向及选择配置。

 

二、URL重写应用场景

域名变更 (京东)

用户跳转 (从某个连接跳到另一个连接)

伪静态场景 (便于CDN缓存动态页面数据)

 

 

三、URL重写语法

URL重写语法

rewrite     <regex>     <replacement>     [flag];
​
关键字        正则            替代内容          flag标记
​
​
regex :可以使用正则或者字符串来表示相匹配的地址。
replacement:可以表示重定向的地址。
flag :flag标志的作用是用于控制当匹配到对应的rewrite规则后是否继续检查后续的rewrite规则。
flag值为如下四种,分别是:
​
last:停止处理当前的rewrite指令集,而后通过重写后的规则重新发起请求,浏览器地址栏URL地址不变。
break:和break指令一样,都是停止处理当前上下文中的其他重写模块指令。
redirect:如果替换字符串不以“ http://”,“ https://”或“ $scheme” 开头,返回带有302代码的临时重定向,浏览器地址会显示跳转后的URL地址。
permanent:返回301代码的永久重定向,浏览器地址栏会显示跳转后的URL地址。

  


rewrite参数的标签可使用的位置

应用位置:server、location、if

nginx rewrite指令执行顺序

1.执行server块的rewrite指令(这里的块指的是server关键字后{}包围的区域,其它xx块类似) 2.执行location匹配 3.执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件。

如果循环超过10次,则返回500 Internal Server Error错误。

 

1) set设置变量量

所有的 Nginx变量在 Nginx 配置文件中引用时都须带上 $ 前缀

在 Nginx 配置中,变量只能存放一种类型的值,有且也只存在一种类型,那就是字符串类型

set指令 自定义变量量
Syntax:
set $variable value;
set关键字  $变量名 变量值
Default:
—
Context:
server, location,

  

例:

访问主机ip本来应该进入http://www.cnblogs.com  重写为 http://www.cnblogs.com/Nicholas0707
location / {
                set $name Nicholas0707;
                rewrite ^(.*)$ http://www.cnblogs.com/$name;
        }

  

 

 

变量创建,赋值及作用域问题

 

  • 变量的创建和赋值操作发生在全然不同的时间阶段。Nginx 变量的创建只能发生在 Nginx 配置加载的时候,或者说 Nginx 启动的时候;而赋值操作则只会发生在请求实际处理的时候。这意味着不创建而直接使用变量会导致启动失败,同时也意味着我们无法在请求处理时动态地创建新的 Nginx 变量。

  • Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块

  • Nginx变量名的可见范围虽然是整个配置,但每个请求都有所有变量的独立副本,或者说都有各变量用来存放值的容器的独立副本,彼此互不干扰

例:

server {
       listen 8080;
​
       location /foo {
           echo "foo = [$foo]";
       }
​
       location /bar {
           set $foo 32;
           echo "foo = [$foo]";
       }
   }

  

结果:

$ curl \'http://localhost:8080/foo\'
    foo = []
 
$ curl \'http://localhost:8080/bar\'
foo = [32]
 
$ curl \'http://localhost:8080/foo\'
foo = []

  

分析:从这个例子我们可以看到,set 指令因为是在 location /bar 中使用的,所以赋值操作只会在访问 /bar 的请求中执行。而请求 /foo 接口时,我们总是得到空的 $foo 值,因为用户变量未赋值就输出的话,得到的便是空字符串。

 

在不同层级的标签中声明的变量性的可见性规则如下:

  1. location标签中声明的变量中对这个location块可见

  2. server标签中声明的变量对server块以及server块中的所有子块可见

  3. http标签中声明的变量对http块以及http块中的所有子块可见

NGINX内置预定义变量


内置预定义变量即无需声明就可以使用的变量,通常包括一个http请求或响应中一部分内容的值,以下为一些常用的内置预定义变量

变量名 定义
$arg_PARAMETER  GET请求中变量名PARAMETER参数的值。
$args   这个变量等于GET请求中的参数。例如,foo=123&bar=blahblah;这个变量只可以被修改
$binary_remote_addr 二进制码形式的客户端地址。
$body_bytes_sent    传送页面的字节数
$content_length 请求头中的Content-length字段。
$content_type   请求头中的Content-Type字段。
$cookie_COOKIE  cookie COOKIE的值。
$document_root  当前请求在root指令中指定的值。
$document_uri   与$uri相同。
$host   请求中的主机头(Host)字段,如果请求中的主机头不可用或者空,则为处理请求的server名称(处理请求的server的server_name指令的值)。值为小写,不包含端口。
$hostname   机器名使用 gethostname系统调用的值
$http_HEADER    HTTP请求头中的内容,HEADER为HTTP请求中的内容转为小写,-变为_(破折号变为下划线),例如:$http_user_agent(Uaer-Agent的值);
$http_user_agent : 客户端agent信息;
$http_cookie : 客户端cookie信息;
$sent_http_HEADER   HTTP响应头中的内容,HEADER为HTTP响应中的内容转为小写,-变为_(破折号变为下划线),例如: $sent_http_cache_control, $sent_http_content_type…;
$is_args    如果$args设置,值为"?",否则为""。
$limit_rate 这个变量可以限制连接速率。
$nginx_version  当前运行的nginx版本号。
$query_string   与$args相同。
$remote_addr    客户端的IP地址。
$remote_port    客户端的端口。
$remote_user    已经经过Auth Basic Module验证的用户名。
$request_filename   当前连接请求的文件路径,由root或alias指令与URI请求生成。
$request_body   这个变量(0.7.58+)包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义。
$request_body_file  客户端请求主体信息的临时文件名。
$request_completion 如果请求成功,设为"OK";如果请求未完成或者不是一系列请求中最后一部分则设为空。
$request_method 这个变量是客户端请求的动作,通常为GET或POST。包括0.8.20及之前的版本中,这个变量总为main request中的动作,如果当前请求是一个子请求,并不使用这个当前请求的动作。
$request_uri    这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI,
包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
​
$scheme 所用的协议,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr    服务器地址,在完成一次系统调用后可以确定这个值,如果要绕开系统调用,则必须在listen中指定地址并且使用bind参数。
$server_name    服务器名称。
$server_port    请求到达服务器的端口号。
$server_protocol    请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$uri    请求中的当前URI(不带请求参数,参数位于args),不同于浏览器传递的args),不同于浏览器传递的args),不同于浏览器传递的request_uri的值,它可以通过内部重定向,或者使用index指令进行修改。uri不包含主机名,如”/foo/bar.html”。
​

  

 

2) if 负责语句句中的判断

语法:

Syntax:
​
if (condition) { ... }
​
if (表达式) {
   ... ...
}
​
Default:
​
—
​
Context:
​
server, location
​

 

if语句中conditon规则

当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
直接比较变量和内容时,精确匹配 =或!=
​
匹配符号意义:
~区分大小写正则匹配;
~*不区分大小写正则匹配; 
!~区分大小写正则不匹配; 
!~*不区分大小写正则不匹配;
​
使用“ -f”和“ !-f”运算符检查文件是否存在;
使用“ -d”和“ !-d”运算符检查目录是否存在;
使用“ -e”和“ !-e”运算符检查文件,目录或符号链接是否存在;
使用“ -x”和“ !-x”运算符检查可执行文件。

  

server {
    # 如果文件不存在则返回400
    if (!-f $request_filename) {
        return 400;
    }
    
    #如果浏览器是chrome浏览器则返回403
    if ($http_user_agent ~* \'Chrome\') {
    return 403;
    #return http://www.cnblogs.com;
    }
    
​
    
    # 如果请求类型不是POST则返回405
    if ($request_method = POST) {
        return 405;
    }
    
    # 如果参数中有 a=1 则301到指定域名
    if ($args ~ a=1) {
        rewrite ^ http://example.com/ permanent;
    }
}
 

  

3) return 返回返回值或URL

语法

return 指令 定义返回数据
Syntax:
return code [text];
return code URL;
return URL;
Default:
—
Context:
server, location, if

  


#如果浏览器是chrome浏览器则返回403  其他浏览器返回博客园网址
location / {
                root html;
                index index.html index.htm;
                if ($http_user_agent ~* \'Chrome\') {
                        return 403;
​
                }
                rewrite ^/$ http://www.cnblogs.com permanent ;
                
        }
 

  

 

4) break 终止后续的rewrite规则

用于停止执行rewrite模块的指令,但是其他模块不受影响

语法

Syntax: break;
​
Default:—
​
Context:server, location, if

  


例子

  #如果浏览器是chrome浏览器则返回403,但这里加了break,就不会执行return 403,返回默认的index,
  #也不会返回博客园
       
   location / {
                if ($http_user_agent ~* \'Chrome\') {
                        break;
                        return 403;
                }
                rewrite ^/$ http://www.cnblogs.com permanent ;
                
        }
 

  

5) rewrite 重定向URL

rewrite <regex> <replacement> [flag];
关键字 正则 替代内容 flag标记
​
flag:
last
#本条规则匹配完成后,继续向下匹配新的location URI规则
break
#本条规则匹配完成即终⽌止,不不再匹配后⾯面的任何规则
redirect
#返回302临时重定向,浏览器器地址会显示跳转后的URL地址
permanent #返回301永久重定向,浏览器器地址栏会显示跳转后的URL地址
​
​
重定向就是将网页自动转向重定向
301永久性重定向:新网址完全继承旧网址,旧网址的SEO网络搜索引擎的排名等完全清零
301重定向是网页更改地址后对搜索引擎友好的最好方法,只要不是暂时搬移的情况,都建议使用301来做转址。
302临时性重定向:对旧网址没有影响,但新网址不会有排名
搜索引擎爬虫会抓取新的内容而保留旧的网址

  

 

正则表达式规则

正则表达式匹配,其中:
~       为区分大小写匹配
~*      为不区分大小写匹配
!~和!~*  分别为区分大小写不匹配及不区分大小写不匹配
​
.      匹配除换行符以外的任意字符
\\w     匹配字母或数字或下划线或汉字
\\s     匹配任意的空白符
\\d     匹配数字
\\b     匹配单词的开始或结束
^      匹配字符串的开始
$      匹配字符串的结束
​
*         重复零次或更多次
+         重复一次或更多次
?         重复零次或一次
{n}       重复n次
{n,}      重复n次或更多次
{n,m}     重复n到m次
*?        复任意次,但尽可能少重复
+?        重复1次或更多次,但尽可能少重复
??        重复0次或1次,但尽可能少重复
{n,m}?    重复n到m次,但尽可能少重复
{n,}?     重复n次以上,但尽可能少重复
​
\\W        匹配任意不是字母,数字,下划线,汉字的字符
\\S        匹配任意不是空白符的字符
\\D        匹配任意非数字的字符
\\B        匹配不是单词开头或结束的位置
[^x]      匹配除了x以外的任意字符
[^aeiou]  匹配除了aeiou这几个字母以外的任意字符    
​
(exp)         匹配exp,并捕获文本到自动命名的组里
(?<name>exp)  匹配exp,并捕获文本到名称为name的组里,也可以写成(?\'name\'exp)
(?:exp)       匹配exp,不捕获匹配的文本,也不给此分组分配组号    
(?=exp)       匹配exp前面的位置
(?<=exp)      匹配exp后面的位置
(?!exp)       匹配后面跟的不是exp的位置
(?<!exp)      匹配前面不是exp的位置
(?#comment)   注释分组不对正则表达式的处理产生任何影响

  

常用的rewrite重写规则,用来美化网页的链接。规则里面的$1$2你不知道是怎么来的话,只要记住,第一个()里面的是$1,第二个()里面的是$2.

 

server{
​
    # 如果host不是sogou.com,则301到cnblogs.com中
    if ( $host != "sogou.com" ){
        rewrite ^/(.*)$ https://cnblogs.com/$1 permanent;
    }
  
}

  




permanent标志:永久重定向

域名跳转

域名跳转,测试前修改host文件 192.168.199.228 www.abc.com
​
www.abc.com     重写为  www.cnblog.com
server {
        listen        80;
        server_name www.abc.com;
        location / {
            rewrite ^/$ http://www.cnblog.com permanent;
​
          }
}

  

redirect标志:临时重定向

域名跳转,测试前修改host文件 192.168.199.228 www.abc.com
​
www.abc.com     重写为  www.cnblog.com
server {
        listen        80;
        server_name www.abc.com;
        location / {
            rewrite ^/$ http://www.cnblog.com permanent;
​
          }
}

  

 

last标志:

url重写后,马上发起一个新的请求,再次进入server块,重试location匹配,超过10次匹配不到报500错误,地址栏url不变

last 一般出现在server或if中

location / {
           rewrite ^/test1 /test2;
           rewrite ^/test2 /test3 last;
           rewrite ^/test3 /test4;
        }
location /test2 {
        return 401;
        }
location /test3 {
        return 402;
        }
location /test4 {
        return 403;
        }

  

 

分析:

测试链接:http://192.168.199.328/test1 匹配到 location / {}后,被重写为/test2,顺序执行再次被重写为/test3,因为flag为last,所以不会继续重写为/test4,而是发起一次location匹配,匹配到location /test3{},所以最终返回结果为402;

如果把location /{}中的last改为break,被重写为/test3后,不再重写为/test4,也不会发起location,最终没有可匹配的资源,返回http404。

 

 

 

参考资料

[1]https://www.jianshu.com/p/f62b859bcce7

[2]https://www.imooc.com/article/273653

[3]https://blog.csdn.net/qq_41475058/article/details/89516051

 

以上是关于nginx之旅(第五篇):URL重写介绍URL重写场景URL重写语法的主要内容,如果未能解决你的问题,请参考以下文章

Cento7+Nginx 之 URL重写

Nginx URL重写(rewrite)配置及信息详解

Nginx 的编译安装和URL地址重写

Nginx URL重写规则配置详解

Apache Nginx URL 地址 重写

Nginx URL重写(rewrite)配置及信息详解