nginx之旅(第五篇):URL重写介绍、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 值,因为用户变量未赋值就输出的话,得到的便是空字符串。
在不同层级的标签中声明的变量性的可见性规则如下:
-
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 <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]
posted on 2020-01-18 22:32 Nicholas-- 阅读(12756) 评论(0) 编辑 收藏 举报