Nginx 配置指令执行顺序
前言
Nginx配置文件使用的语言本质上是声明式的, 而不是过程式的。所以Nginx配置指令的执行和配置文件中书写的先后关系毫无关系,而是看声明的指令所属在Nginx配置解析的哪个阶段。来看例子:
nginx
location / {
set $a hello;
echo $a;
set $a world;
echo $a;
}执行结果:
$ curl http://localhost:8080
world
world通过上边的例子,我们可以看到, 只有最后一条set指令生效,两条echo指令的结果都是最后set的值,这是因为两条指令在Nginx不同的运行阶段。现在我们详细来看一下各个阶段的指令。
Nginx请求处理的11个阶段
1. POST_READ阶段 - 请求头处理之后
功能描述:此阶段在读取完请求头后立即执行,常用于获取真实客户端IP地址。
常用模块与示例:
nginx
# 真实IP模块配置
set_real_ip_from 192.168.1.0/24;
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;适用于Nginx前方有代理服务器的情况,从X-Forwarded-For头中提取真实客户端IP
2. SERVER_REWRITE阶段 - 服务器级别重写
功能描述:在匹配location之前,在server上下文中执行重写规则。
常用模块与示例:
nginx
server {
listen 80;
server_name example.com;
# 强制HTTPS重定向
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
# 域名规范化
rewrite ^ https://www.example.com$request_uri permanent;
# 设置变量供后续使用
set $my_var "value";
}3. FIND_CONFIG阶段 - 查找匹配的location
功能描述:Nginx核心内部阶段,根据请求URI寻找匹配的location块,开发者无法配置。
4. REWRITE阶段 - 位置级别重写
功能描述:在匹配的location上下文中执行重写规则,这是最常用的重写阶段。
常用模块与示例:
nginx
location /api/ {
# 重写URL路径
rewrite ^https://files.metaso.cn/api/v1/(.*)$ /app/api.php?endpoint=$1 last;
rewrite ^https://files.metaso.cn/api/v2/(.*)$ /app/api_v2.php?method=$1 last;
# 条件判断与变量设置
if ($arg_debug = "true") {
set $debug_mode 1;
}
# 直接返回响应
return 200 "API endpoint moved";
}5. POST_REWRITE阶段 - 重写后处理
功能描述:内部阶段,处理重写后的内部跳转和循环检测。
6. PREACCESS阶段 - 访问预处理
功能描述:在访问控制前进行预处理,常用于限流限速。
常用模块与示例:
nginx
# 定义连接数限制区域
limit_conn_zone $binary_remote_addr zone=addr:10m;
# 定义请求频率限制区域
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
location /download/ {
# 限制每个IP最多10个并发连接
limit_conn addr 10;
# 限制请求频率:每秒10个请求,突发不超过20个
limit_req zone=one burst=20 nodelay;
}
}7. ACCESS阶段 - 访问控制
功能描述:实施访问权限控制,包括IP限制、密码认证等。
常用模块与示例:
nginx
location /admin/ {
# IP访问控制
allow 192.168.1.0/24;
allow 10.0.0.1;
deny all;
# 基本认证
auth_basic "Administrator Area";
auth_basic_user_file /etc/nginx/.htpasswd;
# 满足任意条件即可访问
satisfy any;
}
location /private/ {
# 认证请求模块 - 委托到其他服务认证
auth_request /auth;
auth_request_set $user $upstream_http_x_user;
}
location = /auth {
internal;
proxy_pass http://auth-service/validate;
}8. POST_ACCESS阶段 - 访问后处理
功能描述:内部阶段,处理访问控制的结果。
9. PRECONTENT阶段 - 内容预处理
功能描述:在生成内容前进行最后预处理。
常用模块与示例:
nginx
location / {
# try_files指令尝试寻找文件
try_files $uri $uri/ @backend;
# 错误页面处理
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
}
location @backend {
# 内部重定向到后端
proxy_pass http://backend-server;
}10. CONTENT阶段 - 内容生成
功能描述:核心阶段,生成返回给客户端的内容。
常用模块与示例:
nginx
# 静态文件服务
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control public;
}
# 反向代理
location /app/ {
proxy_pass http://app-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# FastCGI处理PHP
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 内容生成
location /hello {
# 使用echo模块直接生成内容
echo "Hello, World!";
echo "Current time: $time_local";
}
# Lua内容处理
location /lua {
content_by_lua_block {
ngx.say("Hello from Lua!")
ngx.say("URI: " .. ngx.var.uri)
}
}11. LOG阶段 - 日志记录
功能描述:请求处理完成后记录日志。
常用模块与示例:
nginx
# 自定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time"';
log_format json_log escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time"'
'}';
# 应用日志配置
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/access.json json_log;
# 条件日志记录
map $status $loggable {
~^[23] 0;
default 1;
}
access_log /var/log/nginx/errors.log main if=$loggable;实战:完整配置示例
nginx
# 在http块中定义共享配置
http {
# 限流限速区域定义
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent"';
# upstream配置
upstream backend {
server 10.0.0.1:8080 weight=3;
server 10.0.0.2:8080 weight=2;
server 10.0.0.3:8080 weight=1;
keepalive 32;
}
server {
listen 80;
server_name example.com;
# POST_READ阶段:获取真实IP
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
# SERVER_REWRITE阶段:全局重写
rewrite ^/old-path$ /new-path permanent;
# 静态文件服务
location /static/ {
alias /var/www/static/;
access_log off;
expires max;
}
# API接口 - 包含多个阶段的配置
location /api/ {
# PREACCESS阶段:限流限速
limit_req zone=api burst=10 nodelay;
limit_conn conn 20;
# ACCESS阶段:访问控制
allow 192.168.1.0/24;
deny all;
auth_basic "API Access";
auth_basic_user_file /etc/nginx/api.htpasswd;
# REWRITE阶段:URL重写
rewrite ^https://files.metaso.cn/api/v1/(.*)$ /v1/index.php?endpoint=$1 last;
# CONTENT阶段:代理到后端
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# LOG阶段:记录日志
access_log /var/log/nginx/api.access.log main;
}
# 错误页面
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
}
}