执行过程图
Nginx 的11个执行阶段以及对应的http模块:
Nginx 的11个执行阶段的枚举类可以参考nginx源码中的ngx_http_core_module.h 中ngx_http_phases枚举:
11个执行阶段的各个模块的执行流程图:
post-read
Nginx的第一个阶段post_read 阶段是在正式处理请求之前工作的。在这个阶段刚刚获取了请求头的信息,还没有进行任何处理。我们可以拿到当前请求的原始数据,比如当前请求的真实IP。
在http协议中有两种方式获取用户IP:
- X-Forwardex-For:用来传递IP,这个头部会把经过的节点IP都记录下来。
- X-Rral-IP:可以记录用户真实的IP地址,只能有一个。
ngx_http_realip_module模块
post_read涉及到的模块 ngx_http_realip_module 当前模块不会自动编译进Nginx中所以需要手动编译进入nginx源码文件夹中执行./configure --with-http_realip_module
内嵌变量
- $realip_remote_addr #保留原始客户地址
- $realip_remote_port #保留原始客户端端口
模块指令
set_real_ip_from:指定可信的地址,只有从该地址建立的连接,获取的realip才是可信的
句法:set_real_ip_from address | CIDR | unix:;
默认:-
内容:http,server,location
real_ip_header:指定从那个头部获取真实的IP地址,默认从X-Real-IP中取,如果设置从X-Forwarded-For中取,会先从最后一个IP开始取。
句法:real_ip_header field | X-Real-IP | X-Forwarded-For | proxy_protocol;
默认:real_ip_header X-Real-IP;
内容:http,server,location
real_ip_recursive:环回地址,默认关闭,打开的时候,如果X-Forwarded-For最后一个地址与客户端地址相同,会过滤调该地址。
句法:real_ip_recursive on | off;
默认:real_ip_recursive off;
内容:http,server,location
配置示例
set_real_ip_from 192.168.1.0/24;
set_real_ip_from 192.168.2.1;
set_real_ip_from 2001:0db8 :: / 32;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
修改nginx.conf配置文件内容如下:
server {
listen 80;
server_name abbila.com;
set_real_ip_from 10.211.55.2;
real_ip_recursive off;
#real_ip_recursive on;
real_ip_header X-Forwarded-For;
location / {
return 200 "client real Ip is:$remote_addr \n";
}
}
上面的配置中设置了可信IP为10.211.55.2,real_ip_recursive是关闭状态的,real_ip_header从X-Forwarded-For获取。测试结果如下:
curl -H "X-Forwarded-For: 1.1.1.1,22.22.22.22,10.211.55.2" abbila.com
client real Ip is:10.211.55.2
然后把real_ip_recursive打开,内容如下:
server {
listen 80;
server_name abbila.com;
set_real_ip_from 10.211.55.2;
#real_ip_recursive off;
real_ip_recursive on;
real_ip_header X-Forwarded-For;
location / {
return 200 "client real Ip is:$remote_addr \n";
}
}
测试结果如下:
curl -H "X-Forwarded-For: 1.1.1.1,22.22.22.22,10.211.55.2" abbila.com
client real Ip is:22.22.22.22
可以通过上面的测试看出,如果real_ip_recursive关闭的话,获取的realIp为X-Forwarded-For的最后一个IP如果是打开状态的话那就把X-Forwarded-For中与可信IP重复的IP过滤掉然后取剩下的最后一个IP为realip。如果使用 X-Forwarded-For 获取 realip 的话,real_ip_recursive 需要打开。并且,realip 依赖于 set_real_ip_from 设置的可信地址。那么有人可能就会问了,那直接用 X-Real-IP 来选取真实的 IP 地址不就好了。这是可以的,但是 X-Real-IP 是 Nginx 独有的,如果客户端与服务器之间还有其他非 Nginx 软件实现的代理,就会造成取不到 X-Real-IP 头部,所以这个要根据实际情况来定。
server-rewrite
阶段标准函数:
- ngx_rewrite
- set
- openresty函数set_by_lua、rewrite_by_lua
post-read阶段之后便是server-rewrite阶段。当ngx_rewrite模块的配置指令直接书写在server配置块中时,基本上都是运行在server-rewrite阶段。
server {
listen 8080;
location /test {
set $b "$a, world";
echo $b;
}
set $a hello;
}
这里,配置语句set a hello 直接卸载了server配置块中,因此它就运行在server-rewrite阶段。而server-rewrite阶段要早于rewrite阶段运行,因此卸载location配置块中的语句 set b "a,world"便晚于外面的set a hello 语句运行。
$ curl localhost:8080/test
hello, world
find-config
这个阶段并不支持Nginx模块注册处理程序,而是由Nginx核心来完成当前请求与location配置块之间的配对工作。
location /hello {
echo "hello world";
}
rewrite
阶段标准函数:
- set_unescape_uri
- rewrite
- openresty函数set_by_lua、rewrite_by_lua
post-rewrite
post-rewrite阶段,不接受Nginx模块注册处理程序,而是由Nginx核心完成rewrite阶段所要求的内部跳转操作
内部跳转工作原理:本质上其实就是把当前的请求处理阶段强行倒退到find-config阶段,以便重新进行请求URI与location配置块的配对。比如下例中,运行在rewrite阶段的rewrite指令就让当前请求的处理阶段倒退回了find-config阶段。由于此时当前请求的URI已经被rewrite指令修改为了/bar,所以这一次换成了location /bar 与当前请求相关联,然后再接着从rewrite阶段往下执行。为什么不直接在rewrite指令执行时立即进行跳转呢?是为了在最初匹配的location块中支持多次反复地改写URI
server {
listen 8080;
location /foo {
set $a hello;
rewrite ^ /bar;
}
location /bar {
echo "a = [$a]";
}
}
location /foo {
rewrite ^ /bar;
rewrite ^ /baz;
echo foo;
}
location /bar {
echo bar;
}
location /baz {
echo baz;
}
注意:如果在server配置块中直接使用rewrite配置指令对请求URI进行改写,则不会涉及”内部跳转“
server {
listen 8080;
rewrite ^/foo /bar;
location /foo {
echo foo;
}
location /bar {
echo bar;
}
}
preaccess
阶段标准函数:
- ngx_access_allow
- ngx_access_deny
- ngx_limit_zone
- ngx_auth_request
server {
listen 8080;
location /test {
set_real_ip_from 127.0.0.1;
real_ip_header X-Real-IP;
echo "from: $remote_addr";
}
}
与之前的例子相比,此例最重要的区别在于把ngx_realip的配置指令放在了location配置块中。前面我们介绍过,Nginx匹配location的动作发生在find-config阶段,而find-config阶段远远晚于post-read阶段执行,所以在post-read阶段,当前请求还没有和任何location相关联。
建议尽量在server配置块中配置ngx_realip这样的模块。
access
模块:
- ngx_http_access_moudule
- ngx_heep_auth_basic_module
post-access
该阶段不支持Nginx模块注册处理程序,而是由Nginx核心自己完成一些处理工作。
try-files
实现标准配置指令tra_files的功能,并不支持Nginx模块注册处理程序。
try_files指令接受两个以上任意数量的参数,每个参数都指定了一个URI,这里假设配置了N个参数,则Nginx会在try-files阶段,依次把前N-1个参数映射为文件系统上的对象(文件或目录),然后检查这些对象是否存在。一旦Nginx发现某个文件系统对象存在,就会在try-files阶段把当前请求的URI改写为该对象所对应的参数URI(但不会包含末尾的斜杠支付,也不会发生”内部跳转“)。如果前N-1个参数所对应的文件系统对象都不存在,try-files阶段就会立即发起”内部跳转“到最后一个参数(即第N个参数)所指定的URI。
location /test {
try_files /foo /bar/ /baz;
echo "uri: $uri";
}
location /foo {
echo foo;
}
location /bar/ {
echo bar;
}
location /baz {
echo baz;
}
在location /test 中使用了try_files指令,提供了3个参数,/foo、/bar/和/baz。它会在try-files阶段依次检查前面两个参数/foo和/bar/所对应的文件系统对象是否存在。
假设现在/var/www/路径下是空的,则第一个参数/foo映射成的文件/var/www/foo是不存在的,同样,对于第二个参数/bar/所映射成的目录/var/www/bar/也是不存在的。于是此时Nginx会在try-files阶段发起最后一个参数所指定的URI(即/baz)的“内部跳转”。请求及返回如下:
$ curl localhost:8080/test
baz
在/var/www/下创建一个名为foo的文件,其内容为hello world,然后请求/test接口:
$ curl localhost:8080/test
uri: /foo
try_files指令的第一个参数/foo可以映射为文件/var/www/foo,而Nginx在try-files阶段发现此文件确实存在,于是立即把当前请求的URI改写为这个参数的值,及/foo,并且不再继续检查后面的参数,而是直接运行后面的请求处理阶段。
通过上面的示例可以看出,try_files指令本质上只是有条件的改写当前请求的URI,而这里说的“条件”其实就是文件系统上的对象是否存在。当”条件“都不满足时,他就会无条件的发起一个指定的”内部跳转“。除了发起”内部跳转“外,try_files指令还支持直接返回指定状态码的HTTP错误页,例如:
try_files /foo /bar/ =404;
当/foo和/bar/参数对应的文件系统对象都不存在时,就直接返回404 Not Found错误页。
content
阶段标准函数:
- echo
- proxy_pass
所有请求的标准输出都在该阶段。几乎所有逻辑代码也在该阶段执行。这个阶段比较常见
log
阶段标准函数:
- access_log
- error_log