Nginx as a Reverse Proxy & Web Server
Location matching, proxy_pass, HTTPS, gzip, rate limiting and buffering — the Nginx configs behind most production services.
Config file anatomy
Nginx config is a nested block tree: http wraps server (virtual hosts) wraps location (URI matching). Directives inherit downward, and location matching has a surprising precedence order.
main 上下文(nginx.conf)
└─ http {
├─ upstream api { server 10.0.0.5:8080; server 10.0.0.6:8080; } ← 负载均衡组
├─ server { ← 一个虚拟主机(一个域名/端口)
│ listen 443 ssl http2;
│ server_name api.example.com;
│ ├─ location / { proxy_pass http://api; }
│ └─ location /static/ { root /var/www; expires 30d; }
│ }
}
| Location modifier | Priority | Meaning |
|---|---|---|
| = /uri | 1 最高 | 精确匹配,匹配到立即停止 |
| ^~ /uri | 2 | 前缀匹配命中后不再走正则 |
| ~ / ~* regex | 3 | 正则(区分/不区分大小写),按在文件里的顺序 |
| 无修饰符 /uri | 4 最低 | 最长前缀匹配 |
Regex locations are evaluated in the order they appear in the file, and the first win — this is why a careless ~ .*\.php location can hijack traffic you intended for a longer prefix match. Prefer ^~ for static paths.
Reverse proxy: the config you will reuse forever
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 限流:按客户端 IP,每秒 10 个请求,突发 20 个排队
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location / {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://api; # upstream 组
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# 关闭缓冲,必须立刻收到的 SSE / 流式接口才需要
# proxy_buffering off;
}
}
For SSE / WebSocket / chunked streams you MUST set proxy_buffering off and Upgrade/Connection headers, otherwise Nginx buffers the whole response and the client hangs until timeout.
# WebSocket 升级
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# location 内:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
Load balancing strategies
| Strategy | Directive | Pick when |
|---|---|---|
| Round robin | 默认 | 无状态服务,机器配置相近 |
| Least connections | least_conn | 请求耗时差异大 |
| IP hash | ip_hash | 需要会话粘性(但应优先用无状态方案) |
| Consistent hash by key | hash $uri consistent | 缓存命中优先 |
| Weighted | server x weight=3 | 机器配置不同 |
upstream api {
least_conn;
server 10.0.0.5:8080 weight=3 max_fails=3 fail_timeout=10s;
server 10.0.0.6:8080;
server 10.0.0.7:8080 backup; # 全挂了才启用
keepalive 32; # 到上游的长连接池
}
Passive health checks (max_fails/fail_timeout) are default; the free nginx passive check can strand traffic to a slow node. If you need active checks or finer policy, look at HAProxy or nginx Plus.
Performance, compression and caching
http {
gzip on;
gzip_min_length 1k;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml image/svg+xml;
gzip_vary on;
# 客户端缓存 + 代理缓存(需要共享存储才能多副本一致)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=apic:10m
max_size=1g inactive=30m use_temp_path=off;
open_file_cache max=2000 inactive=20s;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
}
- gzip_comp_level above 5 wastes CPU for ~1% size gain; 4-5 is the sweet spot
- Do NOT gzip images/video (already compressed) — CPU cost with negative gain
- For static assets: cache-bust by versioned filenames (style.20261101a.css) + far-future Cache-Control
- Verify config BEFORE reload: nginx -t, then nginx -s reload for zero-downtime
nginx -t # 语法检查(reload 前必做)
nginx -s reload # 平滑重载(SIGUSR1 式热生效)
nginx -s reopen # 日志切割:mv 日志后重新打开文件句柄配置文件结构
Nginx 配置是嵌套块树:http 包 server(虚拟主机)包 location(URI 匹配)。指令向下继承,而 location 的匹配优先级经常让人意外。
main 上下文(nginx.conf)
└─ http {
├─ upstream api { server 10.0.0.5:8080; server 10.0.0.6:8080; } ← 负载均衡组
├─ server { ← 一个虚拟主机(一个域名/端口)
│ listen 443 ssl http2;
│ server_name api.example.com;
│ ├─ location / { proxy_pass http://api; }
│ └─ location /static/ { root /var/www; expires 30d; }
│ }
}
| location 修饰符 | 优先级 | 含义 |
|---|---|---|
| = /uri | 1 最高 | 精确匹配,匹配到立即停止 |
| ^~ /uri | 2 | 前缀匹配命中后不再走正则 |
| ~ / ~* regex | 3 | 正则(区分/不区分大小写),按在文件里的顺序 |
| 无修饰符 /uri | 4 最低 | 最长前缀匹配 |
正则 location 按文件中出现顺序依次匹配,第一个命中即生效——这就是为什么随手写个 ~ .*\.php 会把本该走长前缀的流量抢走。静态路径用 ^~ 更稳。
反向代理:一套能用到退休的配置
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 限流:按客户端 IP,每秒 10 个请求,突发 20 个排队
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location / {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://api; # upstream 组
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# 关闭缓冲,必须立刻收到的 SSE / 流式接口才需要
# proxy_buffering off;
}
}
SSE / WebSocket / chunked 流式接口必须 proxy_buffering off 并设置 Upgrade/Connection 头,否则 Nginx 会把整个响应缓存起来,客户端一直卡到超时。
# WebSocket 升级
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# location 内:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
负载均衡策略
| 策略 | 写法 | 适用场景 |
|---|---|---|
| Round robin | 默认 | 无状态服务,机器配置相近 |
| Least connections | least_conn | 请求耗时差异大 |
| IP hash | ip_hash | 需要会话粘性(但应优先用无状态方案) |
| Consistent hash by key | hash $uri consistent | 缓存命中优先 |
| Weighted | server x weight=3 | 机器配置不同 |
upstream api {
least_conn;
server 10.0.0.5:8080 weight=3 max_fails=3 fail_timeout=10s;
server 10.0.0.6:8080;
server 10.0.0.7:8080 backup; # 全挂了才启用
keepalive 32; # 到上游的长连接池
}
被动健康检查(max_fails/fail_timeout)是默认行为;免费版 nginx 的被动检查可能让流量继续流向慢节点。需要主动健康检查或更细策略,看 HAProxy 或 nginx Plus。
性能、压缩与缓存
http {
gzip on;
gzip_min_length 1k;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml image/svg+xml;
gzip_vary on;
# 客户端缓存 + 代理缓存(需要共享存储才能多副本一致)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=apic:10m
max_size=1g inactive=30m use_temp_path=off;
open_file_cache max=2000 inactive=20s;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
}
- gzip_comp_level 超过 5 纯费 CPU,体积只小约 1%,4-5 是甜点
- 图片/视频千万别压缩(本身已压缩)——倒贴 CPU
- 静态资源用版本化文件名(style.20261101a.css)+ 长缓存 Cache-Control 破缓存
- 重载前先校验:nginx -t,再 nginx -s reload 零停机生效
nginx -t # 语法检查(reload 前必做)
nginx -s reload # 平滑重载(SIGUSR1 式热生效)
nginx -s reopen # 日志切割:mv 日志后重新打开文件句柄