• 【转载】Nginx基础:5.rewrite的使用


    5. Nginx rewrite基本语法

     

    Nginx的rewrite语法其实很简单.用到的指令无非是这几个

    • set
    • if
    • return
    • break
    • rewrite

    麻雀虽小,五脏俱全.只是简单的几个指令却可以做出绝对不输apache的简单灵活的配置.

    1.set

    set主要是用来设置变量用的,没什么特别的

    2.if

    if主要用来判断一些在rewrite语句中无法直接匹配的条件,比如检测文件存在与否,http header,cookie等,

    用法: if(条件) {…}

    - 当if表达式中的条件为true,则执行if块中的语句

    - 当表达式只是一个变量时,如果值为空或者任何以0开头的字符串都会当作false

    - 直接比较内容时,使用 = 和 !=

    - 使用正则表达式匹配时,使用

    ~ 大小写敏感匹配 
    ~* 大小写不敏感匹配 
    !~ 大小写敏感不匹配 
    !~* 大小写不敏感不匹配

    这几句话看起来有点绕,总之记住: ~为正则匹配, 后置*为大小写不敏感, 前置!为”非”操作

    随便一提,因为nginx使用花括号{}判断区块,所以当正则中包含花括号时,则必须用双引号将正则包起来.对下面讲到的rewrite语句中的正则亦是如此. 
    比如 “d{4}d{2}.+”

    - 使用-f,-d,-e,-x检测文件和目录

    -f 检测文件存在
    -d 检测目录存在
    -e 检测文件,目录或者符号链接存在
    -x 检测文件可执行

    跟~类似,前置!则为”非”操作

    举例

    if ($http_user_agent ~ MSIE) {
      rewrite  ^(.*)$  /msie/$1  break;
    }

    //如果UA包含”MSIE”,rewrite 请求到/msie目录下

    if ($http_cookie ~* "id=([^;] +)(?:;|$)" ) {
      set  $id  $1;
    }

    //如果cookie匹配正则,设置变量$id等于正则引用部分

    if ($request_method = POST ) {
      return 405;
    }

    //如果提交方法为POST,则返回状态405 (Method not allowed)

    if (!-f $request_filename) {
      break;
      proxy_pass  http://127.0.0.1;
    }

    //如果请求文件名不存在,则反向代理localhost

    if ($args ~ post=140){
      rewrite ^ http://example.com/ permanent;
    }

    //如果query string中包含”post=140″,永久重定向到example.com

    3.return

    return可用来直接设置HTTP返回状态,比如403,404等(301,302不可用return返回,这个下面会在rewrite提到)

    4.break

    立即停止rewrite检测,跟下面讲到的rewrite的break flag功能是一样的,区别在于前者是一个语句,后者是rewrite语句的flag

    5.rewrite

    最核心的功能(废话)

    用法: rewrite 正则 替换 标志位

    其中标志位有四种

    break – 停止rewrite检测,也就是说当含有break flag的rewrite语句被执行时,该语句就是rewrite的最终结果 
    last – 停止rewrite检测,但是跟break有本质的不同,last的语句不一定是最终结果,这点后面会跟nginx的location匹配一起提到 
    redirect – 返回302临时重定向,一般用于重定向到完整的URL(包含http:部分) 
    permanent – 返回301永久重定向,一般用于重定向到完整的URL(包含http:部分)

    因为301和302不能简单的只单纯返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了. 作为替换,rewrite可以更灵活的使用redirect和permanent标志实现301和302. 比如上一篇日志中提到的Blog搬家要做的域名重定向,在nginx中就会这么写

    rewrite ^(.*)$ http://newdomain.com/ permanent;

    举例来说一下rewrite的实际应用

    rewrite  ^(/download/.*)/media/(.*)..*$  $1/mp3/$2.mp3  last;

    如果请求为 /download/eva/media/op1.mp3 则请求被rewrite到 /download/eva/mp3/op1.mp3

    使用起来就是这样,很简单不是么? 不过要注意的是rewrite有很多潜规则需要注意

    - rewrite的生效区块为sever, location, if

    - rewrite只对相对路径进行匹配,不包含hostname 比如说以上面301重定向的例子说明

    rewrite ~* cafeneko.info http://newdomain.com/ permanent;

    这句是永远无法执行的,以这个URL为例

    http://blog.cafeneko.info/2010/10/neokoseseiki_in_new_home/?utm_source=rss&utm_medium=rss&utm_campaign=neokoseseiki_in_new_home

    其中cafeneko.info叫做hostname,再往后到?为止叫做相对路径,?后面的一串叫做query string

    对于rewrite来说,其正则表达式仅对”/2010/10/neokoseseiki_in_new_home”这一部分进行匹配,即不包含hostname,也不包含query string .所以除非相对路径中包含跟域名一样的string,否则是不会匹配的. 如果非要做域名匹配的话就要使用if语句了,比如进行去www跳转

    if ($host ~* ^www.(cafeneko.info)) {
      set $host_without_www $1;
      rewrite ^(.*)$ http://$host_without_www$1 permanent;
    }

    - 使用相对路径rewrite时,会根据HTTP header中的HOST跟nginx的server_name匹配后进行rewrite,如果HOST不匹配或者没有HOST信息的话则rewrite到server_name设置的第一个域名,如果没有设置server_name的话,会使用本机的localhost进行rewrite

    - 前面提到过,rewrite的正则是不匹配query string的,所以默认情况下,query string是自动追加到rewrite后的地址上的,如果不想自动追加query string,则在rewrite地址的末尾添加?

    rewrite  ^/users/(.*)$  /show?user=$1?  last;

    rewrite的基本知识就是这么多..但还没有完..还有最头疼的部分没有说…

     

    /2 Nginx location 和 rewrite retry

     

    nginx的rewrite有个很奇特的特性 — rewrite后的url会再次进行rewrite检查,最多重试10次,10次后还没有终止的话就会返回HTTP 500

    用过nginx的朋友都知道location区块,location区块有点像Apache中的RewriteBase,但对于nginx来说location是控制的级别而已,里面的内容不仅仅是rewrite.

    这里必须稍微先讲一点location的知识.location是nginx用来处理对同一个server不同的请求地址使用独立的配置的方式

    举例:

    location  = / {
      ....配置A
    }
     
    location  / {
      ....配置B
    }
     
    location ^~ /images/ {
      ....配置C
    }
     
    location ~* .(gif|jpg|jpeg)$ {
      ....配置D
    }

    访问 / 会使用配置A 
    访问 /documents/document.html 会使用配置B 
    访问 /images/1.gif 会使用配置C 
    访问 /documents/1.jpg 会使用配置D

    如何判断命中哪个location暂且按下不婊, 我们在实战篇再回头来看这个问题.

    现在我们只需要明白一个情况: nginx可以有多个location并使用不同的配.

    sever区块中如果有包含rewrite规则,则会最先执行,而且只会执行一次, 然后再判断命中哪个location的配置,再去执行该location中的rewrite, 当该location中的rewrite执行完毕时,rewrite并不会停止,而是根据rewrite过的URL再次判断location并执行其中的配置. 那么,这里就存在一个问题,如果rewrite写的不正确的话,是会在location区块间造成无限循环的.所以nginx才会加一个最多重试10次的上限. 比如这个例子

    location /download/ {
      rewrite  ^(/download/.*)/media/(.*)..*$  $1/mp3/$2.mp3  last;
    }

    如果请求为 /download/eva/media/op1.mp3 则请求被rewrite到 /download/eva/mp3/op1.mp3

    结果rewrite的结果重新命中了location /download/ 虽然这次并没有命中rewrite规则的正则表达式,但因为缺少终止rewrite的标志,其仍会不停重试download中rewrite规则直到达到10次上限返回HTTP 500

    认真的朋友这时就会问了,上面的rewrite规则不是有标志位last么? last不是终止rewrite的意思么?

    说到这里我就要抱怨下了,网上能找到关于nginx rewrite的文章中80%对last标志的解释都是

    last – 基本上都用这个Flag

    ……这他妈坑爹呢!!! 什么叫基本上都用? 什么是不基本的情况?  =皿=

    有兴趣的可以放狗”基本上都用这个Flag”…

    我最终还是在stack overflow找到了答案:

    last和break最大的不同在于

    - break是终止当前location的rewrite检测,而且不再进行location匹配 
    – last是终止当前location的rewrite检测,但会继续重试location匹配并处理区块中的rewrite规则

    还是这个该死的例子

    location /download/ {
      rewrite  ^(/download/.*)/media/(.*)..*$  $1/mp3/$2.mp3  ;
      rewrite  ^(/download/.*)/movie/(.*)..*$  $1/avi/$2.mp3  ;
      rewrite  ^(/download/.*)/avvvv/(.*)..*$  $1/rmvb/$2.mp3 ;
    }

    上面没有写标志位,请各位自行脑补…

    如果请求为 /download/acg/moive/UBW.avi

    last的情况是: 在第2行rewrite处终止,并重试location /download..死循环 
    break的情况是: 在第2行rewrite处终止,其结果为最终的rewrite地址.

    也就是说,上面的某位试图下载eva op不但没下到反而被HTTP 500射了一脸的例子正是因为用了last标志所以才会造成死循环,如果用break就没事了.

    location /download/ {
      rewrite  ^(/download/.*)/media/(.*)..*$  $1/mp3/$2.mp3  break;
    }

    对于这个问题,我个人的建议是,如果是全局性质的rewrite,最好放在server区块中并减少不必要的location区块.location区块中的rewrite要想清楚是用last还是break.

    有人可能会问,用break不就万无一失了么?

    不对.有些情况是要用last的. 典型的例子就是wordpress的permalink rewrite

    常见的情况下, wordpress的rewrite是放在location /下面,并将请求rewrite到/index.php

    这时如果这里使用break乃就挂了,不信试试. b( ̄▽ ̄)d…因为nginx返回的是没有解释的index.php的源码…

    这里一定要使用last才可以在结束location / 的rewrite, 并再次命中location ~ .php$,将其交给fastcgi进行解释.其后返回给浏览器的才是解释过的html代码.

    关于nginx rewrite的简介到这里就全部讲完了,水平及其有限,请大家指出错漏…

  • 相关阅读:
    我是菜鸟,开始学习计划
    我是菜鸟,学习计划4月19日笔录
    用HttpSessionListener与HttpSessionBindingListener实现在线人数统计
    mac系统InetAddress.getLocalHost().getHostAddress() 很慢
    获取n位数m进制的随机数 js
    cordova开发环境搭建
    遇到网页中无法选择的文本时或需要登录才可复制时可用
    今天摸的鱼就是以后加的班
    国际化vuei18n 向语言包中传入参数
    vue3 技术浏览 收藏
  • 原文地址:https://www.cnblogs.com/tango-dg/p/3284306.html
Copyright © 2020-2023  润新知