Rewrite简介

Rewrite是Nginx服务器提供的一个重要基本功能,是Web服务器产品中几乎必备的功能。主要的作用是用来实现URL的重写
注意:Nginx服务器的Rewrite功能的实现依赖于PCRE的支持,因此在编译安装Nginx服务器之前,需要安装PCRE库。Nginx使用的是ngx_http_rewrite_module模块来解析和处理Rewrite功能的相关配置。

  • “地址重写”与”地址转发”

重写和转发的区别:

  1. 地址重写浏览器地址会发生变化而地址转发则不变
  2. 一次地址重写会产生两次请求而一次地址转发只会产生一次请求
  3. 地址重写到的页面必须是一个完整的路径而地址转发则不需要
  4. 地址重写因为是两次请求所以request范围内属性不能传递给新页面,地址转发因为是一次请求所以可以传递值
  5. 地址转发速度快于地址重写

    Rewrite规则

Rewrite常用全局变量

变量 说明 案例
$args 变量中存放了请求URL中的请求指令。
功能和$query_string一样
http://172.41.100.15:8088/args?a=1&b=3
$args=a=1&b=3
$http_user_agent 变量存储的是用户访问服务的代理信息
(如果通过浏览器访问,记录的是浏览器的相关版本信息)
http_user_agent = Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
$host 变量存储的是访问服务器的server_name值 host =172.41.100.15
$document_uri 变量存储的是当前访问地址的URI。 document_uri =/args
$document_root 变量存储的是当前请求对应location的root值,如果未设置,默认指向Nginx自带html目录所在位置 document_root=/home/nginx/html
$content_length 变量存储的是请求头中的Content-Length的值
$content_type 变量存储的是请求头中的Content-Type的值
$http_cookie 变量存储的是客户端的cookie信息,可以通过add_header Set-Cookie ‘cookieName=cookieValue’来添加cookie数据
$limit_rate 变量中存储的是Nginx服务器对网络连接速率的限制,也就是Nginx配置中对limit_rate指令设置的值,默认是0,不限制。
$remote_addr 变量中存储的是客户端的IP地址 remote_addr=172.41.100.13
$remote_port 变量中存储了客户端与服务端建立连接的端口号 remote_port=50360
$remote_user 变量中存储了客户端的用户名,需要有认证模块才能获取
$scheme 变量中存储了访问协议 scheme =http
$server_addr 变量中存储了服务端的地址 server_addr=172.41.100.15
$server_name 变量中存储了客户端请求到达的服务器的名称 server_name=localhost
$server_port 变量中存储了客户端请求到达服务器的端口号 server_port=8088
$server_protocol 变量中存储了客户端请求协议的版本,比如”HTTP/1.1” server_protocol=HTTP/1.1
$request_body_file 变量中存储了发给后端服务器的本地文件资源的名称
$request_method 变量中存储了客户端的请求方式,比如”GET”,”POST”等 request_method =GET
$request_filename 变量中存储了当前请求的资源文件的路径名 request_filename =/home/nginx/html/args
$request_uri 变量中存储了当前请求的URI,并且携带请求参数 request_uri=/args?a=1&b=3

set指令

该指令用来设置一个新的变量。

语法 set $variable value;
默认值
位置 server、location、if

variable:变量的名称,变量名称要用”$”作为变量的第一个字符,且不能与Nginx服务器预设的全局变量同名。
value:变量的值,可以是字符串、其他变量或者变量的组合等。

if指令

该指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置。

语法 if (condition){…}
默认值
位置 server、location

condition为判定条件,可以支持以下写法:

  1. 变量名。如果变量名对应的值为空或者是0,if都判断为false,其他条件为true。 ```nginx if ($param){

}

  1. 2. 使用"=""!="比较变量和字符串是否相等,满足条件为true,不满足为false
  2. ```nginx
  3. if ($request_method = POST){
  4. return 405;
  5. }

注意:此处和Java不太一样的地方是字符串不需要添加引号

  1. 使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false。变量与正则表达式之间使用”~”、”~“、”!~”、”!~“来连接

    1. “~”代表匹配正则表达式过程中区分大小写
    2. “~*”代表匹配正则表达式过程中不区分大小写
    3. “!~”和”!~*”刚好和上面取相反值,如果匹配上返回false,匹配不上返回true
      1. if ($http_user_agent ~ MSIE){
      2. #$http_user_agent的值中是否包含MSIE字符串,如果包含返回true
      3. }
      注意:正则表达式字符串一般不需要加引号,但是如果字符串中包含”}”或者是”;”等字符时,就需要把引号加上。
  2. 判断请求的文件是否存在使用”-f”和”!-f”,
    当使用”-f”时,如果请求的文件存在返回true,不存在返回false。
    当使用”!-f”时,如果请求文件不存在,但该文件所在目录存在返回true,文件和目录都不存在返回false,如果文件存在返回false

    1. if (-f $request_filename){
    2. #判断请求的文件是否存在
    3. }
    4. if (!-f $request_filename){
    5. #判断请求的文件是否不存在
    6. }
  3. 判断请求的目录是否存在使用”-d”和”!-d”,
    当使用”-d”时,如果请求的目录存在,if返回true,如果目录不存在则返回false
    当使用”!-d”时,如果请求的目录不存在但该目录的上级目录存在则返回true,该目录和它上级目录都不存在则返回false,如果请求目录存在也返回false.

  4. 判断请求的目录或者文件是否存在使用”-e”和”!-e”
    当使用”-e”,如果请求的目录或者文件存在时,if返回true,否则返回false.
    当使用”!-e”,如果请求的文件和文件所在路径上的目录都不存在返回true,否则返回false
  5. 判断请求的文件是否可执行使用”-x”和”!-x”
    当使用”-x”,如果请求的文件可执行,if返回true,否则返回false
    当使用”!-x”,如果请求文件不可执行,返回true,否则返回false

    break指令

    用于中断当前相同作用域中的其他Nginx配置。与该指令处于同一作用域的Nginx配置中,位于它前面的指令配置生效,位于后面的指令配置无效。
语法 break;
默认值
位置 server、location、if
  1. location /{
  2. if ($param){
  3. set $id $1;
  4. break;
  5. #不生效,限制nginx向客户端每秒传输速率
  6. limit_rate 10k;
  7. }
  8. }

return指令

用于完成对请求的处理,直接向客户端返回响应状态代码。在return后的所有Nginx配置都是无效的。

语法 return code [text];
return code URL;
return URL;
默认值
位置 server、location、if
  • code:为返回给客户端的HTTP状态代理。可以返回的状态代码为0~999的任意HTTP状态代理
  • text:为返回给客户端的响应体内容,支持变量的使用
  • URL:为返回给客户端的URL地址

    rewrite指令

    该指令通过正则表达式的使用来改变URI。可以同时存在一个或者多个指令,按照顺序依次对URL进行匹配和处理。
语法 rewrite regex replacement [flag];
默认值
位置 server、location、if

regex:用来匹配URI的正则表达式
replacement:匹配成功后,用于替换URI中被截取内容的字符串。
如果该字符串是以”http://"或者"https://"开头的,则不会继续向下对URI进行其他处理,而是**直接返回重写后的URI**给客户端。
flag:用来设置rewrite对URI的处理行为,可选值有如下:

  • last: 终止执行rewrite模块指令集,并开始搜寻重写url后匹配的location
  • break:终止执行rewrite模块指令集
  • redirect:临时重定向
  • permanent:永久重定向
    • 案例
      1. server{
      2. listen 8077;
      3. server_name localhost;
      4. location / {
      5. rewrite ^/test1 /test3 last;
      6. rewrite ^/test2 /test4 break;
      7. rewrite ^/test5 /test6 redirect;
      8. rewrite ^/test7 /test6 permanent;
      9. }
      10. location /break/ {
      11. rewrite ^/break/(.*) /test$1 break;
      12. # echo需要安装第三方模块 echo-nginx-module
      13. # git clone https://codechina.csdn.net/mirrors/agentzh/echo-nginx-module.git
      14. echo "break page";
      15. }
      16. location /test3 {
      17. return 200 "/test3";
      18. }
      19. location /test4 {
      20. return 200 "/test4";
      21. }
      22. location /test6 {
      23. return 200 "/test6";
      24. }
      25. }
  • 访问请求
  1. 访问[http://172.41.100.15:8077/test1](http://172.41.100.15:8077/test1) 返回 /test3

访问/test1被重写为/test3,因为flag为last,所以发起一次location匹配,匹配到location /test3 {},所以最终返回结果为http200及/test3;

  1. 访问[http://172.41.100.15:8077/test2](http://172.41.100.15:8077/test2) 返回 404

访问/test2,虽然/test2被重定向到/test4,但是break指令不会重新开启一个新的请求继续匹配,所以nginx是不会匹配到下面的/test/这个location,nginx尝试找/test4这个html页面并输出起内容,事实上,这个页面不存在,所以会报404的错误。可以看error.log

  1. 2021/08/18 10:15:19 [error] 29936#0: *7 open() "/home/nginx/html/test4" failed (2: No such file or directory), client: 172.41.100.13, server: localhost, request: "GET /test2 HTTP/1.1", host: "172.41.100.15:8077"
  1. 访问http://172.41.100.15:8077/test5 返回 /test6

访问/test1被重写为/test6,因为flag为redirect,地址栏被重写为http://172.41.100.15:8077/test6 http状态码为302 临时重定向,浏览器发起两次请求,一次/test5,一次/test6
image.png

  1. 访问[http://172.41.100.15:8077/test7](http://172.41.100.15:8077/test5) 返回 /test6

访问/test1被重写为/test7,因为flag为permanent,地址栏被重写为[http://172.41.100.15:8077/test6](http://172.41.100.15:8077/test6) http状态码为301 永久重定向,浏览器发起两次请求,一次/test7,一次/test6
image.png

  1. 访问http://172.41.100.15:8077/break/6返回 break page

break是跳过当前请求的rewrite阶段,并继续执行本请求的其他阶段,对于/break/对应的content阶段的输出为 echo “break page”;
content阶段,可以简单理解为产生数据输出的阶段,如返回静态页面内容也是在content阶段;echo指令也是运行在content阶段,一般情况下content阶段只能对应一个输出指令,如同一个location配置两个echo,最终只会有一个echo指令被执行;
如果把/break/里的echo 指令注释,然后再次访问/break/xx会报404
虽然/break/xx被重定向到/test/xx,但是break指令不会重新开启一个新的请求继续匹配,所以nginx是不会匹配到下面的/test/这个location;在echo指令被注释的情况下,/break/ 这location里只能执行nginx默认的content指令,即尝试找/test/xx这个html页面并输出起内容,事实上,这个页面不存在,所以会报404的错误。

  • URL和URI的区别:

URI:统一资源标识符
URL:统一资源定位符

rewrite_log指令

配置是否开启URL重写日志的输出功能。

语法 rewrite_log on|off;
默认值 rewrite_log off;
位置 http、server、location、if

开启后,URL重写的相关日志将以notice级别输出到error_log指令配置的日志文件汇总。

  • 案例
    1. server{
    2. rewrite_log on;
    3. # 重定向error_log地址
    4. error_log /home/rewrite.log info;
    5. listen 8077;
    6. server_name localhost;
    7. }
    日志输出,notice都是重定向日志
    image.png

    配合防盗链使用

    使用rewrite将盗链请求转发到自定义的一张图片和页面,给用户比较好的提示信息。
  1. 添加一张图片或者页面做提示展示

在172.41.100.14的nginx目录中添加403图片
image.png

  1. 根据防盗链条件重定向盗链请求 ```nginx server{ listen 9088; server_name localhost; location /images { valid_referers none blocked server_names 127.0.0.1; if ($invalid_referer){

    重定向到指定图片

    rewrite ^/ http://172.41.100.14:9088/403/403.jpg; } root /usr/local/nginx/html; } }
  1. 3. 效果展示
  2. 原请求被重定向到403页面了。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22353188/1629255835610-02a79a0c-03df-40ca-b6c3-720767d1b28c.png#height=918&id=tchg2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=918&originWidth=1571&originalType=binary&ratio=1&size=161941&status=done&style=none&width=1571)
  3. <a name="nVi3H"></a>
  4. # 目录合并
  5. 网站中有一个资源文件的访问路径时 /server/11/22/33/44/20.html,也就是说20.html存在于第5级目录下,如果想要访问该资源文件,客户端的URL地址就要写成 `http://172.41.100.15/server/11/22/33/44/20.html`,这个是非常不利于SEO搜索引擎优化的,同时客户端也不好记,使用rewrite可以进行如下配置
  6. ```nginx
  7. server {
  8. listen 80;
  9. server_name localhost;
  10. location /server{
  11. rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
  12. }
  13. }

客户端只需要输入http://172.41.100.15/server-11-22-33-44-20.html就可以访问到20.html页面了。这里也充分利用了rewrite指令支持正则表达式的特性。