基于Nginx的生产环境反向代理典型用法实践

本文实现了一套安全、模块化、生产端的 Nginx 反向代理方案,核心功能包括:

  1. 统一代理支持:通过 Nginx 同时提供 HTTP 和 TCP 反向代理;
  2. 真实客户端信息透传:使用标准代理头(如 X-Real-IP、X-Forwarded-For)确保后端服务获取原始请求信息;
  3. 跨域控制(CORS):允许所有域名跨域请求,但仅限 IP 白名单中的客户端;
  4. 独立 IP 白名单:通过单独的 geo 配置文件实现访问控制,默认拒绝所有非白名单 IP,支持动态更新无需重启;
  5. 全局限制策略:统一设置最大上传文件大小(10MB)和响应空闲超时(60秒);
  6. 按需缓存与维护模式:仅对 /api/ 接口启用缓存,并支持通过文件开关快速切换维护页面。

整套方案采用配置分离、职责清晰、Docker 友好的设计,兼顾安全性、可维护性与高性能,适用于企业级生产环境。

📁 整体目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/etc/nginx/
├── nginx.conf # 全局配置文件
├── conf.d/
│ ├── http.conf # HTTP代理配置
│ ├── db.stream # 数据库代理配置
│ └── maintenance.conf # 维护界面独立配置
├── security/
│ ├── blacklist.conf
│ ├── whitelist.conf
│ ├── rate-limiting.conf
│ ├── security_rules.conf
│ ├── security_checks.conf
│ ├── blacklist.ip
│ └── whitelist.ip
├── proxy_params # 通用代理头配置
├── cache_params # 缓存配置
└── cors_params # 跨域控制独立配置

1. 统一配置

1.1 TCP和HTTP配置(/etc/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 全局配置:TCP 代理和全局参数
stream {
# TCP 代理配置(独立于 HTTP)
include /etc/nginx/conf.d/*.stream;
}

http {
# === 全局控制(所有 HTTP 代理生效) ===
include /etc/nginx/mime.types;
default_type application/octet-stream;

# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain application/json text/css application/javascript;
keepalive_timeout 65;
keepalive_requests 1000;
types_hash_max_size 2048;
server_tokens off;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;

# 超时控制
proxy_connect_timeout 30s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;

# 缓冲区优化
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
proxy_temp_file_write_size 8k;

# 上传文件控制
client_max_body_size 500M;
client_body_buffer_size 1m;
client_body_temp_path /tmp/nginx_body;
client_body_timeout 300s;
proxy_request_buffering on;

set $block_access 0;
set $block_reason "";

# 日志格式
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" '
'uht="$upstream_header_time" urt="$upstream_response_time"';

log_format security 'time=$time_iso8601 client=$realip_remote_addr '
'xff="$http_x_forwarded_for" method=$request_method '
'uri="$request_uri" status=$status '
'user_agent="$http_user_agent" '
'blocked=$block_access block_reason=$block_reason '
'rate_limit=$rate_limit_zone cache_status=$upstream_cache_status';

log_format json_combined escape=json
'{"time":"$time_iso8601",'
'"remote_addr":"$realip_remote_addr",'
'"x_forwarded_for":"$http_x_forwarded_for",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"is_whitelisted":$whitelist_ip,'
'"is_blacklisted":$blacklist_ip}';

# 访问日志
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
access_log /var/log/nginx/security.log security;
access_log /var/log/nginx/audit/audit-$(date +%Y-%m-%d).log json_combined;
error_log /var/log/nginx/error.log warn;

# 真实IP处理(去掉SLB、CDN等内网IP)
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

# 通用代理头(复用)
include proxy_params;

# === 接口缓存配置(全局定义,独立文件生效) ===
# 缓存配置
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m
max_size=1g inactive=60m use_temp_path=off;
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:100m
max_size=10g inactive=365d use_temp_path=off;

# 限制并发连接数
limit_conn_zone $binary_remote_addr zone=per_ip:10m;
limit_conn_zone $server_name zone=per_server:10m;

# 包含安全配置
include /etc/nginx/security/*.conf;

# 包含所有独立配置
include /etc/nginx/conf.d/*.conf;
}

💡 关键配置

  • TCP 代理独立在 stream 块(不能与 HTTP 混合)
  • 全局上传大小/超时设置在 http 块(避免重复配置)
  • 缓存路径全局定义,独立配置文件只指定作用域
  • 缓存路径 /var/cache/nginx 需在 Docker 容器中挂载(-v /host/cache:/var/cache/nginx

1.2 传递客户端的真实信息配置(/etc/nginx/proxy_params

1
2
3
4
5
6
7
8
9
# /etc/nginx/proxy_params
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Server $hostname;

1.3 HTTP接口缓存配置(/etc/nginx/cache_params

1
2
3
4
5
6
7
# /etc/nginx/cache_params
proxy_cache api_cache;
proxy_cache_valid 200 302 1d; # 200/302 缓存 1 天
proxy_cache_valid 404 1m; # 404 缓存 1 分钟
proxy_cache_use_stale error timeout updating;
proxy_cache_background_update on;
proxy_cache_lock on;

💡 缓存机制

  • 200/302 响应缓存 1 天(86400 秒)
  • 404 响应缓存 1 分钟
  • 缓存过期后,若后端返回 200 则更新缓存

1.4 CORS跨域控制配置/etc/nginx/cors_params

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# /etc/nginx/cors_params
# 允许所有域名(CORS 配置)
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-SinceCache-Control,Content-Type,Authorization' always;

# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-WithIf-Modified-Since,Cache-Control,Content-Type,Authorization' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}

💡 关键配置

  • always 参数确保响应头始终添加(避免缓存干扰)

2. 独立代理配置文件(/etc/nginx/conf.d/

✅ 2.1 HTTP 代理(http.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 仅处理特定域名的 HTTP 代理
server {
listen 80;
server_name api.example.com;

location / {
# 限流(白名单豁免)
if ($rate_limit_exempt != 1) {
limit_req zone=$rate_limit_zone burst=20 nodelay;
limit_req_status 429;
}
proxy_pass http://backend:8080; # Docker 服务名
include cache_params;
}
}

✅ 2.2 TCP代理(db.conf

1
2
3
4
5
6
7
8
9
10
11
    upstream mysql_backend {
server db:3306; # Docker 服务名
}

server {
listen 3306;
proxy_pass mysql_backend;
proxy_connect_timeout 1s; # TCP 连接超时
proxy_timeout 10s; # TCP 响应超时
}
}

✅ 2.3 维护界面(maintenance.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 仅对根路径生效(维护模式时重定向)
server {
# 维护模式开关
set $maintenance_mode off;

# 通过环境变量控制维护模式
if ($maintenance_mode = on) {
return 503;
}

# 维护页面配置
error_page 503 @maintenance;
location @maintenance {
# 静态维护页面
root /usr/share/nginx/html;
try_files /maintenance.html =503;

# 或者重定向到外部维护页面
# return 307 https://maintenance.example.com;
}

# API特殊处理(返回JSON)
location /api/ {
if ($maintenance_mode = on) {
add_header Content-Type application/json;
return 503 '{"status":503,"message":"系统维护中,请稍后再试"}';
}

proxy_pass http://backend-service:8080;
}
}

3. 安全配置文件(/etc/nginx/security/

✅ 3.1 限流控制(rate-limiting.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# /etc/nginx/security/rate-limiting.conf

# 限流区域定义
limit_req_zone $binary_remote_addr zone=global_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=3r/m;
limit_req_zone $binary_remote_addr zone=search_limit:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=static_limit:10m rate=500r/s;

# 基于请求URI的限流区域映射
map $request_uri $rate_limit_zone {
default global_limit;

# API接口
~*/api/v1/ api_limit;
~*/api/v2/ api_limit;

# 认证接口
~*/auth/login login_limit;
~*/oauth/token login_limit;
~*/user/login login_limit;

# 搜索接口
~*/search search_limit;
~*/query search_limit;
~*/find search_limit;

# 静态资源
~*\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ static_limit;

# 管理接口
~*/admin/ global_limit;
~*/system/ global_limit;
}

# 白名单不限流
map $whitelist_ip $rate_limit_exempt {
0 0;
1 1;
}

# 连接数限制
limit_conn per_ip 10;
limit_conn per_server 1000;

✅ 3.2 黑白名单控制(blacklist.confwhitelist.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# /etc/nginx/security/blacklist.conf

# 全局黑名单设置
geo $blacklist_ip {
default 0;

# 动态黑名单(从文件读取)
include /etc/nginx/security/blacklist.ip;

# 静态黑名单
10.0.0.0/8 1;
192.168.1.100 1;
172.16.0.0/12 1;
}

# /etc/nginx/security/whitelist.conf
# 全局白名单设置
geo $whitelist_ip {
default 0;

# 动态白名单(从文件读取)
include /etc/nginx/security/whitelist.ip;

# 静态白名单
10.0.0.0/8 1;
192.168.1.100 1;
172.16.0.0/12 1;
}

# API白名单路径
map $request_uri $whitelist_path {
default 0;

# 健康检查
/health 1;
/status 1;
/ping 1;

# 监控
/metrics 1;
/nginx-status 1;

# 静态资源
~*\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|mp4|webm|mp3)$ 1;

# 公开API
/api/v1/public/ 1;
/api/v1/docs 1;
/swagger-ui 1;

# 机器人协议
/robots.txt 1;
/sitemap.xml 1;
/favicon.ico 1;
}
1
2
3
4
5
6
7
8
9
10
# /etc/nginx/security/blacklist.ip(whitelist.ip)

# 格式:IP/CIDR 标志值;
# 可以通过脚本动态更新

# 恶意IP示例
203.0.113.1 1;
198.51.100.0/24 1;
10.20.30.40 1;

✅ 3.3 综合安全控制(blacklist.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# security/security-rules.conf


# 恶意User-Agent检测
map $http_user_agent $bad_bot {
default 0;

# 已知恶意爬虫
~*(ahrefs|mj12bot|semrushbot|masscan) 1;
~*(sqlmap|nmap|nikto|dirbuster) 1;
~*(nessus|openvas|acunetix|nessus) 1;

# 垃圾机器人
~*(spammer|scraper|harvest|collector) 1;

# 可疑模式
~*[0-9]{10}bot 1;
~*bot[0-9]{10} 1;
"" 1;

# 允许的友好爬虫
~*(baiduspider|googlebot|bingbot|yandexbot|duckduckbot) 0;
~*(slurp|twitterbot|facebookexternalhit|linkedinbot) 0;

# 正常浏览器和工具
~*(Mozilla|Chrome|Safari|Firefox|Edge|Opera) 0;
~*(curl|wget|python-requests|PostmanRuntime) 0;
}

# 恶意请求检测
map $request_uri $bad_request {
default 0;

# 路径遍历
~*(\.\./|\.\.\\|/%2e%2e|/%252e%252e) 1;

# 敏感文件
~*(/etc/passwd|/etc/shadow|/proc/self|/etc/hosts) 1;
~*(\.git/|\.svn/|\.hg/|\.bzr/|\.DS_Store) 1;
~*(\.env|config\.php|settings\.py|web\.config) 1;

# 管理后台
~*(phpmyadmin|adminer|wp-admin|/admin/login|/manager/html) 1;

# SQL注入模式
~*(union\s+select.*from) 1;
~*(sleep\(|benchmark\(|waitfor\s+delay) 1;
~*(load_file\(|into\s+outfile|into\s+dumpfile) 1;
~*(select\s+.*from.*where.*=.*\() 1;

# XSS攻击
~*(<script|javascript:|onload=|onerror=|alert\(|confirm\(|prompt\() 1;

# 命令注入
~*(\|\s*sh|\|\s*bash|\|\s*cmd|\;\s*rm\s+|\;\s*wget\s+) 1;

# 可疑文件
~*\.(asp|aspx|jsp|php|pl|py|sh|cgi|exe|dll|bat|cmd)$ 1;

# 扫描特征
~*(/w00tw00t|test.cgi|xmlrpc.php|/vendor/phpunit) 1;
}

# 错误页面映射
map $block_reason $block_error_page {
default /errors/403.html;
"blacklist_ip" /errors/403.html;
"bad_bot" /errors/444.html;
"bad_request" /errors/403.html;
"rate_limit" /errors/429.html;
"whitelist_only" /errors/403.html;
}

# 访问控制决策
map "$whitelist_ip:$maintenance_mode" $access_allowed {
# 规则优先级:白名单 > 维护模式

# 1. 白名单IP直接允许(即使维护模式)
"~^1:.+" 1;
# 2. 其他情况拒绝
default 0;
}

# 维护模式检测
map $maintenance_mode $is_maintenance {
default 0;
"on" 1;
"yes" 1;
"true" 1;
"1" 1;
}

✅ 3.4 安全检查(security-checks.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# security/security-checks.conf

# 初始化阻塞原因
set $block_reason "";
set $block_access 0;

# 1. 白名单路径检查(直接通过)
if ($whitelist_path = 1) {
set $block_access 0;
return 200; # 继续处理请求,但标记为不阻止
}

# 2. 访问控制决策
if ($access_allowed = 0) {
set $block_access 1;

if ($whitelist_ip = 0) {
set $block_reason "whitelist_only";
}

if ($maintenance_mode = "on" && $whitelist_ip = 0) {
set $block_reason "maintenance_mode";
return 503;
}
}

# 3. 黑名单检查(白名单可以覆盖)
if ($whitelist_ip != 1) {
if ($blacklist_ip = 1) {
set $block_access 1;
set $block_reason "blacklist_ip";
access_log /var/log/nginx/blocked.log security;
return 403;
}
}

# 4. 恶意User-Agent检查
if ($bad_bot = 1) {
set $block_access 1;
set $block_reason "bad_bot";
access_log /var/log/nginx/bad_bots.log combined;
return 444;
}

# 5. 恶意请求检查
if ($bad_request = 1) {
set $block_access 1;
set $block_reason "bad_request";
access_log /var/log/nginx/attack.log combined;
return 403;
}

🐳 Docker 部署关键配置

1. docker-compose.yml 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# docker-compose.yml
version: '3.8'

services:
nginx:
build: .
container_name: nginx-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- NGINX_ENV=production
- BACKEND_SERVICE=backend:8080
- MAINTENANCE_MODE=${MAINTENANCE_MODE:-off}
- API_KEY_WHITELIST=${API_KEY_WHITELIST:-}
- INTERNAL_NETWORK=172.16.0.0/12
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./conf.d:/etc/nginx/conf.d:ro
- ./security:/etc/nginx/security:ro
- ./templates:/etc/nginx/templates:ro
- ./ssl:/etc/nginx/ssl:ro
- ./html:/usr/share/nginx/html:ro
- ./logs:/var/log/nginx
- ./scripts:/scripts:ro
- dynamic_data:/etc/nginx/dynamic
networks:
- proxy-network
healthcheck:
test: ["CMD", "/scripts/health-check.sh"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

backend:
image: ${BACKEND_IMAGE:-your-backend:latest}
container_name: backend-app
restart: unless-stopped
environment:
- VIRTUAL_HOST=app.example.com
- VIRTUAL_PORT=8080
networks:
- proxy-network
expose:
- "8080"

networks:
proxy-network:
driver: bridge
ipam:
config:
- subnet: 172.16.0.0/12

volumes:
dynamic_data:

2. nginx容器Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# Dockerfile
FROM nginx:1.24-alpine

# 安装必要工具
RUN apk add --no-cache \
curl \
jq \
certbot \
certbot-nginx \
bash \
gettext \
openssl \
geoip \
geoip-dev \
libmaxminddb \
libmaxminddb-dev \
&& rm -rf /var/cache/apk/*

# 安装nginx模块
RUN apk add --no-cache nginx-mod-http-geoip2

# 创建目录结构
RUN mkdir -p \
/etc/nginx/security \
/etc/nginx/dynamic \
/etc/nginx/templates \
/usr/share/nginx/html/errors \
/scripts \
/var/log/nginx/audit

# 复制配置文件
COPY nginx.conf /etc/nginx/
COPY conf.d/ /etc/nginx/conf.d/
COPY security/ /etc/nginx/security/
COPY templates/ /etc/nginx/templates/
COPY ssl/ /etc/nginx/ssl/
COPY html/ /usr/share/nginx/html/
COPY scripts/ /scripts/

# 设置权限
RUN chmod +x /scripts/*.sh \
&& chown -R nginx:nginx /var/log/nginx \
&& chmod 600 /etc/nginx/security/.htpasswd \
&& chmod 644 /etc/nginx/security/*.map \
&& chmod 755 /etc/nginx/dynamic

# 生成SSL证书(如果不存在)
RUN if [ ! -f /etc/nginx/ssl/cert.pem ]; then \
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/key.pem \
-out /etc/nginx/ssl/cert.pem \
-subj "/C=CN/ST=State/L=City/O=Organization/CN=localhost"; \
fi

# 生成强DH参数
RUN if [ ! -f /etc/nginx/ssl/dhparam.pem ]; then \
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048; \
fi

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD /scripts/health-check.sh

# 启动脚本
ENTRYPOINT ["/scripts/entrypoint.sh"]

🛠️ 生产环境验证建议

  1. 测试跨域

    1
    curl -H "Origin: https://frontend.example.com" -X OPTIONS http://api.example.com/api/test

    应返回 204 No Content 且包含 CORS 头

  2. 测试维护模式

    1
    2
    3
    4
    5
    6
    # 启用维护
    export MAINTENANCE_MODE=on


    # 访问应返回 503
    curl -I http://example.com
  3. 测试缓存

    1
    2
    3
    4
    5
    # 第一次请求(应返回 200)
    curl -I http://api.example.com/api/data

    # 第二次请求(应返回 200 from cache)
    curl -I http://api.example.com/api/data

✅ 总结

功能 位置 作用范围 Docker 适配关键点
TCP 代理 nginx.conf (stream) 全局 独立于 HTTP,端口映射 3306:3306
上传大小/超时 nginx.conf (http) 全局 全局生效,避免重复配置
HTTP 代理 http_proxy.conf 按域名生效 通过 Docker 网络解析服务名
跨域控制 cors.conf /api/ always 参数确保头始终添加
接口缓存 cache.conf /api/ 缓存路径挂载到主机,避免容器重启丢失
维护界面 maintenance.conf 仅根路径 通过文件开关,无需重启 Nginx

💡 为什么这样设计?

  1. 全局配置:TCP 和基础参数(上传/超时)放在全局,避免重复且易于全局调整
  2. 独立文件:每个功能模块独立配置,符合“单一职责原则”
  3. Docker 适配
    • 所有挂载路径(/var/cache/nginx, /var/www/maintenance)在主机定义
    • 服务名通过 Docker 网络自动解析(backend:8080
    • 维护模式通过文件开关,无需修改 Nginx 配置