nginx中rewrite模块break和last原理详解

在使用nginx重写(即rewrite)机制时,大家一般会用到last和break,关于这两个指令的作用,网友问的挺多,网上的讨论也挺多,这里做个总结:
网友的给力解释:
last:
重新将rewrite后的地址在server标签中执行
break:
将rewrite后的地址在当前location标签中执行
nginx官方解释: last: stops processing the current set ofngx_http_rewrite_moduledirectives followed by a search for a new location matching the changed URI; break: stops processing the current set of ngx_http_rewrite_moduledirectives;
其实网友的解释更容易懂一些,nginx官方的解释则是从偏重实现的角度来说的,说到这里,许多人可能还是对这两个指令的使用不是太自信,感觉心里没底,说实话我当初也是这么感觉的,那么就让我们打破沙锅问到底,看看代码到底是怎么实现的,毕竟,”代码面前无秘密“。
为了方便我们的讨论我们做出以下的假想配置:
location /download/ { 
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  break;
    return  403;
}
在分析之前,看官们需要熟悉nginx各个phase handler的处理,以及nginx变量的基本原理,不熟悉的同学看起来可能会有点难度,那么这里给出了相关的连接,方面不熟悉的同学学习。前两个是关于handler的,后一个是关于变量的:

现在进入正题:
在函数ngx_http_rewrite中:
if (cf->args->nelts == 4) {
        if (ngx_strcmp(value[3].data, "last") == 0) {
            last = 1;

        } else if (ngx_strcmp(value[3].data, "break") == 0) {
            regex->break_cycle = 1;
            last = 1;

        }  
        ...// 其余处理省略
}
重点在于last = 1的处理,在稍后:
if (last) {
    code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), ®ex);
    if (code == NULL) {
        return NGX_CONF_ERROR;
    }

    *code = NULL;
}
lcf->codes是个数组里面保存了当前各个rewrite执行对应的相关操作(即各种handler)和数据,这里的操作是在这个数组中添加一个null,这个null的意义重大,在rewrite实际执行时,如ngx_http_rewrite_handler的调用,就会对事先放置在这个数组里的handler进行处理:
e->ip = rlcf->codes->elts;
...
// 在这个while循环中,上面的那个null,就会终止rewrite一系列操作的执行
// 可以看到,“last”和“break”在这点上作用是相同的,当前codes数组中有剩余的
// rewrite指令,那么由于这里的null的存在,也就跳过不管了。
while (*(uintptr_t *) e->ip) {
    code = *(ngx_http_script_code_pt *) e->ip;
    code(e);
}
比如在开始的配置里面,我们写成:
location /download/ {
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra;
    return  403;
}
即不写last和break,那么流程就是依次执行这些rewrite,直到最后以403结束这次请求,这种情况下codes数组中的handler都得以执行了,而由于
last和break的出现,处理可能在中间的某个位置终止,后面的rewrite,就不会执行了。

在rewrite阶段的处理结束之后,则会转到find config阶段,这个阶段本来是在rewrite阶段之前的,这样的过程也刻画了rewrite的基本流程,url经过rewrite阶段被改变了,而一个请求处理的关键步骤之一就是要确定对应的server conf和location conf,而find config的作用恰恰就是如此,重写之后url可以看做是一个新的请求,所以这些关键步骤需要走一遍就是理所当然了。

另一个问题,在ngx_http_rewrite函数中break_cycle的设置,也就是在出现break的时候,这个变量会被置1,而这个变量的设置,最终会导致r->uri_changed被置为0,那么它的直接影响可以在下面的地方看到:
// 这个函数之所以名为“post”,意思就是为rewrite处理做一些善后工作
ngx_http_core_post_rewrite_phase
{   
    // 在通常情况下,即r->uri_changed > 0,r->phase_handler会设置为ph->next,
    // 而这个ph->next,在开始初始化phase的时候,已经设置为ph->next = find_config_index,
    // 所以在非break或者last情况下,之后的phase就是所谓find config阶段了,而这里却是
    // r->phase_handler++,意味着将会执行接下来的处理,不会再去走find config的过程了
    if (!r->uri_changed) {
        r->phase_handler++;
        return NGX_AGAIN;
    }
    ... // 此处省略其他处理部分
}
关于r->uri_changed被置为0的操作,可以参考:
ngx_http_script_regex_start_code和ngx_http_script_break_code

所以这里概括下:
last其实就相当于一个新的url,对nginx进行了一次请求,需要走一遍大多数的处理过程,最重要的是会做一次find config,提供了一个可以转到其他location的配置中处理的机会,而break则是在一个请求处理过程中将原来的url(包括uri和args)改写之后,在继续进行后面的处理,这个重写之后的请求始终都是在同一个location中处理。



<think>好的,我现在要详细回答用户关于Nginx rewrite规则的问题。首先,我需要整理用户提供的引用内容,提取关键点,并确保回答结构清晰,符合用户的要求。用户希望了解rewrite的详细用法规则,以及使用示例,所以我要覆盖基本语法、执行顺序、flag标志、正则表达式、与location的配合,以及常见示例。同时,需要引用提供的资料来支持内容,并生成相关问题。 首先,根据引用[5],rewrite的基本语法是rewrite regex replacement [flag];,其中regex是正则,replacement是替换目标,flag可选。需要解释每个部分的作用,并提到支持的flag类型,比如lastbreak、redirect、permanent,这些在引用[5]的示例中有提到。 然后,执行顺序部分,根据引用[3],Nginx的location匹配优先级会影响rewrite的执行顺序,可能需要按优先级列出,比如精确匹配(=) > 前缀匹配(^~) > 正则匹配(~或~*) > 通用匹配。同时,根据引用[5]中的例子,说明rewrite规则在server块location块中的执行顺序,比如server块中的rewrite先执行,然后是location中的。 接下来,flag标志的详细说明。根据引用[4][5],break会停止当前location的后续rewrite,而last会重新发起请求。需要区分两者的不同,比如break在当前位置终止,而last会重新走匹配流程。redirectpermanent分别对应302301重定向,这部分引用[5]的示例中有说明。 正则表达式部分需要解释常用符号,如^表示开始,$结束,()捕获组,$1引用,这些在引用[5]的正则例子中都有用到,比如^/html/(.+?).html$$1的引用。 与location的配合使用,根据引用[2][4],可能需要多个location块来处理不同的rewrite规则,因为一旦匹配到某个location,可能不会继续执行其他location中的rewrite。例如,引用[2]的示例中,/test/yy的location分别处理不同的重写,并且使用lastbreak标志。 常见示例部分需要包括域名重定向、路径转换、动态参数处理等,这些在引用[5]中有多个例子,比如将/search/key重写为/search.html?keyword=key,使用正则捕获组。同时,引用[3]提到的目录匹配优先级也可能影响示例的选择。 注意事项方面,根据引用[1],需要提到避免过度使用if指令,推荐使用rewrite来保持配置简洁。此外,测试配置正则调试的重要性,引用[5]中的例子展示了不同的flag效果,需要注意配置后的重载测试。 最后,生成相关问题,可能需要包括不同flag的区别、正则表达式的编写、与location的优先级问题、性能优化等,确保问题覆盖用户可能关心的其他方面。 现在需要组织这些信息,确保结构清晰,使用中文,正确应用LaTeX格式,引用标记正确,并附上相关问题。需要避免使用Markdown格式,但用户示例中允许代码块,因此可以保留代码示例。同时,确保每个引用来源正确标注,如[^1]、[^2]等。</think>### Nginx rewrite规则详解使用示例 #### 一、rewrite基本语法 Nginxrewrite指令语法为: $$rewrite\ \ \ regex\ \ \ replacement\ \ \ [flag];$$ 其中: - **regex**:正则表达式,用于匹配请求URI - **replacement**:替换后的目标URI - **flag**:标志位(可选),控制重写行为[^5] #### 二、执行顺序与优先级 1. **server块**中的rewrite规则优先执行 2. 根据location匹配优先级执行: $$(location=) \succ (location\verb|^~|) \succ (location~\*) \succ (location/)$$ [^3] 3. 同一作用域内按配置顺序执行 #### 三、flag标志详解 | 标志 | 行为 | HTTP状态码 | |-----------|----------------------------------------------------------------------|------------| | **last** | 停止当前处理,重新发起请求匹配location | - | | **break** | 立即停止后续所有rewrite规则 | - | | redirect | 返回临时重定向 | 302 | | permanent | 返回永久重定向 | 301 | 示例对比: ```nginx # last会重新匹配location,break直接终止 location / { rewrite ^/a.html /b.html last; # 重新发起请求 rewrite ^/b.html /c.html break; # 终止后续处理[^4] } ``` #### 四、正则表达式关键符号 - `^`:匹配字符串开始(如`^/user`) - `$`:匹配字符串结束(如`.html$`) - `()`:捕获分组,通过`$1`-`$9`引用 - `.*`:匹配任意字符(贪婪模式) - `.+?`:非贪婪匹配 #### 五、典型应用场景 1. **域名重定向** ```nginx rewrite ^/(.*) http://newdomain.com/$1 permanent; [^5] ``` 2. **路径转换** ```nginx rewrite ^/product/(\d+)$ /item?id=$1 break; # 将/product/123转为/item?id=123 ``` 3. **动态参数处理** ```nginx rewrite ^/search/([^/]+)/?$ /search.html?keyword=$1? permanent; [^5] ``` 4. **文件扩展名隐藏** ```nginx rewrite ^/(.*)/$ /$1/index.html; # 将/about/重写为/about/index.html ``` #### 六、与location配合规则 ```nginx location /test { rewrite ^/test/(.*) /yy/$1 last; # 重写后重新匹配location[^2] } location /yy { rewrite ^/yy/(.*) http://baidu.com break; # 直接终止处理链[^2] } ``` #### 七、注意事项 1. 避免过度使用`if`判断,优先使用rewrite实现条件逻辑[^1] 2. 测试时建议先用`redirect`标志观察效果 3. 正则表达式需进行边界限定(如`^``$`) 4. 修改配置后需执行`nginx -s reload`重载配置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值