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

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 [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 值,因为用户变量未赋值就输出的话,得到的便是空字符串。

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

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

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

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 [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,并捕获文本到自动命名的组里

(?exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'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