概述
本文主要针对nginx rewrite指令困惑的地方进行讲解,中间会涉及部分原理部分,我尽量用通俗易懂的语言来形容
功能讲解
大致流程
The ngx_http_rewrite_module
module directives are processed in the following order:
the directives of this module specified on the server level are executed sequentially;
repeatedly:
-
-
a location is searched based on a request URI;
-
the directives of this module specified inside the found location are executed sequentially;
-
the loop is repeated if a request URI was rewritten, but not more than 10 times.
-
涉及phase
-
NGX_HTTP_SERVER_REWRITE_PHASE,
-
NGX_HTTP_FIND_CONFIG_PHASE,
-
NGX_HTTP_REWRITE_PHASE,
-
NGX_HTTP_POST_REWRITE_PHASE,
环境:
-
/aaa内容aaa
-
/bbb内容bbb
Example 1:
server {
listen
80
;
location /aaa {
if
($http_user_agent ~ Mozilla) {
rewrite /aaa /bbb;
}
return
403
;
}
location /bbb {
return
402
;
}
}
|
在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb),
然后继续执行rewrite模块其他指令,执行到return 403后,该request在nginx中的处理完毕,返回403;
因此浏览器收到应答码为403
Example 2:
server {
listen
80
;
location /aaa {
if
($http_user_agent ~ Mozilla) {
rewrite /aaa /bbb;
}
#
return
403
;
}
location /bbb {
return
402
;
}
}
|
在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb)
rewrite模块执行完毕。因为所有rewrite模块的指令都执行完毕,进入POST_REWRITE_PHASE所在的checker函数ngx_http_core_post_rewrite_phase
中,由于执行了rewrite指令,在函数ngx_http_script_regex_start_code中
if
(code->uri) {
r->internal =
1
;
r->valid_unparsed_uri =
0
;
if
(code->break_cycle) {
//rewrite ....break 指令分支
r->valid_location =
0
;
r->uri_changed =
0
;
}
else
{
r->uri_changed =
1
;
}
}
|
可以得知r->uri_changed=1,于是在函数ngx_http_core_post_rewrite_phase中执行如下流程
r->uri_changes--;
if
(r->uri_changes ==
0
) {
ngx_log_error(NGX_LOG_ERR, r->connection->log,
0
,
"rewrite or internal redirection cycle "
"while processing \"%V\""
, &r->uri);
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return
NGX_OK;
}
r->phase_handler = ph->next;
//ph->next为find_config_index,在ngx_http_init_phase_handlers中可见
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
r->loc_conf = cscf->ctx->loc_conf;
|
由于重新进行server和location匹配,因此在上述例子中,浏览器收到的应答状态吗为402
Example 3:
server {
listen
80
;
location /aaa {
if
($http_user_agent ~ Mozilla) {
rewrite /aaa /bbb
break
;
}
return
403
;
}
location /bbb {
return
402
;
}
}
|
在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb),
由于rewrite break标记符存在,从rewrite指令的配置解析函数ngx_http_rewrite和rewrite模块的handler函数ngx_http_rewrite_handler中
如下部分可以得知,将停止执行rewrite模块的其他一切指令;
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
;
}
else
if
(ngx_strcmp(value[
3
].data,
"redirect"
) ==
0
) {
regex->status = NGX_HTTP_MOVED_TEMPORARILY;
regex->redirect =
1
;
last =
1
;
}
else
if
(ngx_strcmp(value[
3
].data,
"permanent"
) ==
0
) {
regex->status = NGX_HTTP_MOVED_PERMANENTLY;
regex->redirect =
1
;
last =
1
;
}
else
{
ngx_conf_log_error(NGX_LOG_EMERG, cf,
0
,
"invalid parameter \"%V\""
, &value[
3
]);
return
NGX_CONF_ERROR;
}
.....
.....
.....
if
(last) {
code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), ®ex);
if
(code == NULL) {
return
NGX_CONF_ERROR;
}
*code = NULL;
}
}
ngx_http_rewrite_handler
{
.....
.....
while
(*(uintptr_t *) e->ip) {
code = *(ngx_http_script_code_pt *) e->ip;
code(e);
}
if
(e->status < NGX_HTTP_BAD_REQUEST) {
//如果rewrite指令后不是break和last标记,而是其他两个重定向标记,则从此处直接结束该request处理流程
return
e->status;
}
if
(r->err_status ==
0
) {
return
e->status;
}
return
r->err_status;
.....
.....
}
|
进入到POST_REWRITE_PHASE所在的checker函数ngx_http_core_post_rewrite_phase中,由example 2 可知r->uri_changed = 0;
在ngx_http_core_post_rewrite_phase中会执行如下流程,因此会继续执行当前server 和 location 上下文中后续的handler,因此浏览器收到内容为bbb
if
(!r->uri_changed) {
r->phase_handler++;
return
NGX_AGAIN;
}
|
Example 4:
server {
listen
80
;
location /aaa {
if
($http_user_agent ~ Mozilla) {
rewrite /aaa /bbb last;
}
return
403
;
}
location /bbb {
return
402
;
}
}
|
在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb),
由于rewrite last标记符存在,停止执行rewrite模块的其他一切指令,参考example 3。
进入到rewrite模块所在的checker函数ngx_http_core_post_rewrite_phase中,由example 2可知r->uri_changed = 1;
于是重新进行server和location匹配,因此在上述例子中,浏览器收到的内容的状态码为402