• 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     匹配数字
         匹配单词的开始或结束
    ^      匹配字符串的开始
    $      匹配字符串的结束
    ​
    *         重复零次或更多次
    +         重复一次或更多次
    ?         重复零次或一次
    {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

     

  • 相关阅读:
    计算机组成原理 04 认识各个硬件部件
    计算机组成原理 02 计算机的发展
    计算机组成原理 01 你好,计组
    蓝桥杯-2020-B组 &#183; 题解/解析(4/17)
    「HTML 5」1.HTML简介
    「易语言」主题颜色配置方案
    「易语言」那些年,我们经历的故事
    彻底解决Ubuntu中 “检测到系统程序错误”
    FFMPEG 的简单使用介绍 —— 合并音视频
    oh-my-zsh 中 agnoster主题 之 隐藏用户名信息
  • 原文地址:https://www.cnblogs.com/Nicholas0707/p/12210551.html
Copyright © 2020-2023  润新知