在工业边缘场景中,Nginx 常被放在网关入口,承担 API 反向代理、TLS 终止、WebSocket 转发、限流、缓存、日志留痕和后端隔离等职责。它的价值不只是"把请求转发过去",而是把入口层变成一个可控、可观测、可回滚的安全边界。
本文从工程实战角度梳理 Linux 下 Nginx 反向代理的配置方法,覆盖基础配置、upstream、HTTPS、WebSocket、限流、缓存、JSON 日志、健康检查、性能与上线验收。
一、入口层要解决什么问题
一个典型的工业边缘部署是:
text
云端 / 浏览器 / 运维平台
|
| HTTPS / WSS
v
Linux Nginx 入口层
|
+--> 协议运行时 API
+--> 设备管理服务
+--> Web 控制台
+--> WebSocket 通道
|
v
现场设备 / 本地数据库 / 北向平台
入口层通常要回答这些问题:
| 目标 | Nginx 能做什么 |
|---|---|
| 统一入口 | 多个后端服务共用域名和端口 |
| 安全边界 | TLS 终止、请求大小限制、限流、隐藏后端 |
| 流量调度 | 负载均衡、失败重试、被动健康检查 |
| 长连接 | WebSocket / Server-Sent Events 转发 |
| 性能优化 | keepalive、gzip、静态资源、代理缓存 |
| 可观测性 | JSON 访问日志、上游状态、耗时记录 |
| 运维安全 | 配置检查、平滑 reload、证书自动续签 |
如果只是本地内网临时调试,可以先用最简反代;一旦涉及生产设备、远程运维或北向 API,就必须补齐 TLS、超时、日志、限流和发布流程。
二、基础配置:先建立稳定骨架
下面是 Linux 环境的示例骨架,实际参数应结合 CPU、内存、连接数和磁盘容量调整:
nginx
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;
events {
worker_connections 10240;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format json_nginx escape=json '{'
'"time":"$time_iso8601",'
'"request_id":"$request_id",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$uri",'
'"status":$status,'
'"request_time":$request_time,'
'"upstream_addr":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"upstream_response_time":"$upstream_response_time"'
'}';
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65s;
server_tokens off;
client_max_body_size 10m;
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript;
include /etc/nginx/conf.d/*.conf;
}
几点说明:
worker_processes auto适合多数物理机和虚拟机;worker_rlimit_nofile需要与 systemd、内核文件句柄限制一起调整;worker_connections × worker_processes是理论连接容量,真实容量还受带宽、代理耗时、后端能力和内存影响;multi_accept on不一定在所有负载模型下都更优,压测后再决定;server_tokens off可以减少版本信息暴露;client_max_body_size应按最小需要设置,避免异常大请求占满资源。
三、反向代理与 upstream
1. 定义后端池
nginx
# /etc/nginx/conf.d/gateway.conf
upstream edge_backend {
least_conn;
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
常用调度策略:
| 策略 | 行为 | 适用情况 |
|---|---|---|
| 默认轮询 | 依次分发 | 后端能力相近 |
least_conn |
优先发给当前连接最少的后端 | 请求耗时差异较大 |
ip_hash |
按客户端 IP 做会话粘性 | 有会话状态但要谨慎使用 |
hash ... consistent |
一致性哈希 | 缓存或对象分布场景 |
不要用 ip_hash 替代应用层的认证和会话管理。它可能受 NAT、代理链和 IPv6 地址变化影响。
2. 转发 API 请求
nginx
server {
listen 80;
server_name api.local;
location /api/ {
proxy_pass http://edge_backend/;
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 Connection "";
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
}
这里有一个非常容易踩的路径差异:
| 写法 | /api/devices 转发到后端 |
适用情况 |
|---|---|---|
proxy_pass http://edge_backend/; |
/devices |
后端服务没有 /api 前缀 |
proxy_pass http://edge_backend; |
/api/devices |
后端服务自己也有 /api 路由 |
proxy_pass http://edge_backend/v2/; |
/v2/devices |
网关侧做路径重写 |
不要凭感觉加斜杠。上线前必须用真实路径验证一遍,尤其是 OpenAPI、Web 控制台、重定向和静态资源路径。
3. 传递客户端真实 IP
Nginx 直接面向客户端时,后端可以从 X-Real-IP 或 X-Forwarded-For 取真实来源。如果 Nginx 前面还有云 LB、运营商网关或其他代理,则不要盲目信任这些头,应使用 ngx_http_realip_module 配置可信代理:
nginx
set_real_ip_from 10.10.0.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
只信任明确的外层代理地址,避免客户端伪造来源 IP。
四、HTTPS:证书、协议与跳转
生产环境建议默认使用 HTTPS,并明确证书续签流程。示例:
nginx
server {
listen 443 ssl;
http2 on;
server_name api.local;
ssl_certificate /etc/letsencrypt/live/api.local/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.local/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
proxy_pass http://edge_backend;
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 https;
proxy_set_header Connection "";
}
}
server {
listen 80;
server_name api.local;
return 301 https://$host$request_uri;
}
证书自动续签建议使用 Certbot:
bash
sudo certbot renew --dry-run
sudo certbot renew --deploy-hook 'systemctl reload nginx'
注意事项:
listen 443 ssl http2;是旧写法,较新版本建议拆成listen 443 ssl;和http2 on;;- HSTS 会让浏览器强制使用 HTTPS,确认所有子域名和资源都支持 HTTPS 后再增加
includeSubDomains; - 自签名证书用于内网时,必须明确分发和轮换机制;
- 证书私钥权限应最小化,并纳入巡检;
- 证书过期前要有监控告警,而不是只依赖续签任务成功。
五、WebSocket 转发
WebSocket 的 Upgrade 头是动态的,推荐在 http 层定义映射:
nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
在对应 location 中使用:
nginx
server {
listen 443 ssl;
http2 on;
server_name api.local;
location /ws/ {
proxy_pass http://edge_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
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 https;
proxy_connect_timeout 3s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
}
}
不要把普通 API 的 Connection "" 直接复制给 WebSocket。普通代理用空连接头配合 keepalive;WebSocket 需要按 Upgrade 协议设置。
边缘设备链路可能长时间空闲,超时不宜照抄 86400s。更稳妥的做法是:
- 应用层实现心跳;
- Nginx 超时略大于心跳间隔;
- 断线后客户端可重连;
- 服务端能清理失效会话。
六、限流与连接控制
限流配置通常放在 http 层:
nginx
limit_req_zone $binary_remote_addr zone=api_per_ip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=api_conn_per_ip:10m;
在 server 或 location 中应用:
nginx
server {
listen 443 ssl;
http2 on;
server_name api.local;
location /api/ {
limit_req zone=api_per_ip burst=20 nodelay;
limit_req_status 429;
limit_conn api_conn_per_ip 10;
limit_conn_status 429;
proxy_pass http://edge_backend;
}
}
10m 状态区大约可以记录 16 万个 IPv4 地址,实际容量与键长度有关。限流键也要考虑现场网络:
| 场景 | 只按 IP 限流的问题 | 建议 |
|---|---|---|
| 多设备共享出口 NAT | 正常设备一起被限 | 结合认证主体、API Key 或设备 ID |
| 公网扫描器 | 单 IP 限流有效 | 保留 IP 维度并叠加黑名单 |
| 内网平台调用 | 单 IP 高频但合法 | 按服务身份单独配置 |
| 控制指令接口 | 误限可能影响运维 | 分接口配置阈值并区分读写 |
对于写操作、控制指令和批量导入,不要只靠 Nginx 限流,应用层仍需认证、授权、幂等和审计。
七、代理缓存:只缓存明确允许的数据
缓存必须先确认后端语义。设备实时值、控制结果、鉴权响应、带用户身份的请求通常不能随意缓存。
nginx
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
listen 443 ssl;
http2 on;
server_name api.local;
location /api/devices/ {
proxy_cache api_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_valid 200 30s;
proxy_cache_valid 404 10s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_background_update on;
add_header X-Cache-Status $upstream_cache_status always;
proxy_pass http://edge_backend;
}
}
两个关键细节:
- 缓存 key 使用
$request_uri时会包含查询参数,适合同一查询参数代表同一数据的接口; proxy_cache_valid只是本地规则,后端仍应返回明确的Cache-Control,Nginx 默认会尊重后端响应头。
如果接口返回实时电压、告警状态或用户私有数据,应显式设置 proxy_cache off 或由后端返回 Cache-Control: no-store。
八、JSON 日志与排障字段
建议把访问日志输出为 JSON,便于 Loki、Vector、Filebeat 等采集:
nginx
log_format json_nginx escape=json '{'
'"time":"$time_iso8601",'
'"request_id":"$request_id",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$uri",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_addr":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"upstream_response_time":"$upstream_response_time"'
'}';
server {
access_log /var/log/nginx/api-access.log json_nginx;
error_log /var/log/nginx/api-error.log warn;
}
排障时重点看:
| 字段 | 用途 |
|---|---|
$status |
客户端看到的状态码 |
$upstream_status |
后端真实状态,可能是多个值 |
$upstream_addr |
请求被转发到哪个后端 |
$request_time |
Nginx 处理请求总耗时 |
$upstream_response_time |
后端响应耗时 |
$request_id |
入口请求 ID,可传给应用日志 |
如果需要跨层关联,可以把 $request_id 传给后端:
nginx
proxy_set_header X-Request-ID $request_id;
应用侧接收或生成自己的 Trace ID,并写入日志,才能真正从入口定位到具体服务。
九、后端健康与失败重试
Nginx 开源版的 max_fails / fail_timeout 是被动健康检查,不能主动探测后端业务是否"半死"。更完整的策略包括:
nginx
upstream edge_backend {
least_conn;
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
配合:
nginx
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
需要注意的是,重试不是总是安全的。POST 写入、控制指令或支付类请求一旦被后端处理完成但响应超时,盲目重试可能造成重复执行。对非幂等请求应谨慎控制 proxy_next_upstream,并由应用层保证幂等。
建议后端提供轻量健康接口:
| 检查类型 | 示例 | 判断 |
|---|---|---|
| 存活检查 | /healthz |
进程能响应 |
| 就绪检查 | /readyz |
依赖可用,可接流量 |
| 版本信息 | /version |
发布和回滚验证 |
开源 Nginx 可结合外部探测、编排系统健康检查或监控告警摘除异常节点。
十、性能与容量调整
常见参数如下:
nginx
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 10240;
multi_accept on;
}
如果增大连接数,还需要同步检查:
bash
ulimit -n
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
压测时不要只看 QPS,还要记录:
- P95 / P99 延迟;
- 错误率和超时率;
- Nginx CPU、内存、连接数;
- 后端 CPU、内存、数据库和队列;
- 带宽和磁盘日志写入;
- reload 前后的连接行为。
生产容量应保留余量,避免把实验室峰值直接当成线上上限。
十一、配置发布与回滚
每次修改配置后应执行:
bash
sudo nginx -t
sudo nginx -s reload
更稳妥的发布流程:
- 配置变更走代码仓库评审;
- 在测试环境验证路由、证书、头部和超时;
- 使用
nginx -T导出完整配置留档; - 灰度发布或逐台 reload;
- 观察错误率、延迟和连接数;
- 出现异常立即回滚上一版配置;
- 记录变更原因、影响范围和回滚入口。
示例:
bash
sudo cp /etc/nginx/conf.d/gateway.conf /etc/nginx/conf.d/gateway.conf.bak
sudo nginx -t && sudo nginx -s reload
注意备份只是临时手段,长期应以版本仓库和自动化发布为准。
十二、上线验收清单
1. 路由与代理
-
proxy_pass是否带斜杠与后端路由匹配; - API、Web 控制台、静态资源和重定向全部可访问;
-
Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto设置正确; - WebSocket 能完成 Upgrade 并稳定运行;
- 普通请求和长连接使用正确的
Connection策略; - 后端看到的客户端 IP 符合预期。
2. 安全与可用性
- HTTP 自动跳转 HTTPS;
- TLS 1.2 / 1.3 可用,弱协议已禁用;
- 证书自动续签和过期告警可用;
- HSTS 策略确认后再启用;
- 请求体大小和方法限制明确;
- 限流阈值经过压测;
- 控制类接口具备认证、授权和幂等保障。
3. 可观测性与运维
- JSON 日志字段完整;
- 日志轮转和磁盘保留策略明确;
- 能区分客户端状态码和后端状态码;
- 能定位具体 upstream 节点;
- 请求或 Trace ID 能贯穿后端;
-
nginx -t进入发布流程; - reload 和回滚步骤有文档;
- 配置变更纳入版本管理。
十三、常见坑与应对
| 坑 | 现场表现 | 应对 |
|---|---|---|
proxy_pass 斜杠错误 |
后端 404 或 API 路径重复 | 用真实路径验证路径映射 |
| WebSocket 头写死 | 连接立即断开或无法升级 | 使用 map $http_upgrade |
| 超时过短 | 设备慢响应被切断 | 分接口设置并压测 |
| 证书续签未 reload | 到期后仍在使用旧证书 | deploy-hook + 证书监控 |
| HSTS 过早上线 | HTTP 环境无法回退 | 确认全站 HTTPS 后启用 |
| 缓存了私有或实时数据 | 用户看到错误数据 | 明确 no-store 或关闭代理缓存 |
| 缓存 key 忽略查询参数 | 不同查询返回同一结果 | 使用 $request_uri 或明确缓存键 |
| 只按 IP 限流 | NAT 后正常用户被误伤 | 结合服务身份和应用层策略 |
| 非幂等请求自动重试 | 控制或写入重复执行 | 收敛重试条件并做幂等 |
| 日志字段不可解析 | JSON 缺引号或包含异常值 | 用 escape=json 并测试边界值 |
| 修改后未检查 | reload 失败或整站中断 | nginx -t 和灰度 reload |
TL;DR
Linux Nginx 反向代理的核心是明确边界和路径语义:基础层配置 worker、连接数、日志和请求大小;代理层定义 upstream、keepalive、失败重试和真实 IP 头;安全层默认 HTTPS、自动续签证书、启用必要安全头和限流;长连接层用 map $http_upgrade 正确处理 WebSocket;性能层按需启用 gzip 和代理缓存,但不能缓存私有或实时数据;观测层输出 JSON 日志并记录 upstream 状态与耗时;发布层坚持 nginx -t、灰度 reload、版本管理和回滚。最容易被忽略的是 proxy_pass 斜杠、非幂等重试、HSTS 影响和缓存 key 设计。
如果在工业边缘协议运行时中评估接入能力,可以参考 Zenova EdgeOS 的 Nginx 反向代理、TLS 接入、WebSocket 转发和远程运维实践。