Nginx/OpenResty 反代配置最佳实践

引言

大家好,今天来聊聊 Nginx 和 OpenResty 反向代理配置的那些事儿。说起来,反向代理可以说是咱们日常开发中最常用的技术手段之一了,不管是前后端分离架构、负载均衡,还是 API 网关,都离不开它。我自己在项目里也踩过不少坑,今天就把这些经验整理一下,分享给各位。

咱们今天不聊太基础的东西,假设你已经知道什么是反向代理、怎么安装 Nginx。主要来看看,在生产环境中,哪些配置能让你的反代更稳、更快、更安全。

一、基础反代配置:这些坑别再踩了

先说最基础的配置。很多人写反代就是简单写个 proxy_pass,完事儿。但其实这里有不少细节要注意。

location /api/ {

proxy_pass http://backend.example.com;

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;

}

上面这段代码,有几个关键点得强调一下:

1. Header 传递X-Real-IPX-Forwarded-For 必须设置,不然后端日志里全是 Nginx 的 IP,你还想不想排查问题了?X-Forwarded-For 要用 $proxy_add_x_forwarded_for,这样才能保留完整的代理链。

2. 连接超时:生产环境一定要设置超时时间,别用默认的。你永远不知道后端会不会卡死。

proxy_connect_timeout 5s;

proxy_send_timeout 60s;

proxy_read_timeout 60s;

3. Buffer 机制:Nginx 默认会缓冲后端响应,这对大多数场景是友好的,但如果你做的是实时推送或者大文件下载,得考虑关闭 buffer。

# 关闭 buffer,适合实时流

proxy_buffering off;

chunked_transfer_encoding off;

二、负载均衡:别把所有鸡蛋放一个篮子

单节点反代始终是个单点故障,生产环境肯定得上负载均衡。Nginx 自带的 upstream 模块就能搞定这个,而且配置相当简单。

upstream backend {

least_conn; # 最少连接数算法

server backend1.example.com:8080 weight=3;

server backend2.example.com:8080 weight=2;

server backend3.example.com:8080 backup; # 备用节点

}

server {

location / {

proxy_pass http://backend;

# 健康检查相关

proxy_connect_timeout 3s;

proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

proxy_next_upstream_tries 2;

}

}

这里有几个小技巧:

- 权重分配:根据机器性能合理设置权重,性能强的多分配点请求,很正常的事儿。

- backup 节点:关键时刻能救命,当主要节点都挂了,backup 会自动上场。

- 故障转移proxy_next_upstream 这个指令非常实用,遇到 500 错误或者超时,自动切换到下一个节点,用户基本无感知。

不过说实话,Nginx 自带的上游健康检测比较基础。如果你的业务对高可用要求极高,可以考虑用 nginx_upstream_check_module 这个第三方模块,或者在 OpenResty 里用 Lua 实现更精细的健康检查逻辑。

三、缓存配置:让响应飞起来

反向代理另外一个重要作用就是缓存。配置好了,能大幅减轻后端压力,用户访问速度也能快不少。

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=api_cache:10m 

max_size=1g inactive=60m use_temp_path=off;

server {

location /api/ {

proxy_cache api_cache;

proxy_cache_valid 200 302 10m;

proxy_cache_valid 404 1m;

proxy_cache_key "$scheme$request_method$host$request_uri";

# 条件化缓存

proxy_cache_bypass $http_cache_bypass;

proxy_cache_use_stale error timeout http_500 http_502 http_503;

add_header X-Cache-Status $upstream_cache_status;

}

}

几点经验之谈:

1. 缓存 key 设计:一定要根据业务特点设计好缓存 key。Query 参数要不要包含?POST 请求的 body 怎么处理?这些都得想清楚。

2. 缓存失效策略inactive 参数设置的是多长时间没人访问就删除,而 proxy_cache_valid 是针对不同状态码的缓存时间。两者配合使用效果更好。

3. 调试技巧:加上 X-Cache-Status 这个响应头,HIT/MISS/EXPIRED 一目了然,排查问题特别方便。

4. 条件缓存:用 proxy_cache_bypass 可以实现按需绕过缓存,比如登录用户不走缓存,这个在项目中很常见。

四、安全加固:别让人有机可乘

反代作为入口,安全方面可得重视起来。下面这些配置,建议都加上:

server {

# 隐藏 Nginx 版本号

server_tokens off;

# 防止常见攻击

proxy_hide_header X-Powered-By;

proxy_hide_header X-AspNet-Version;

# 限流防爬

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

location /api/ {

limit_req zone=api_limit burst=20 nodelay;

# 禁止敏感目录

if ($request_uri ~* "\.(git|env|conf)$") {

return 403;

}

}

}

}

防止 DDoS

limit_conn_zone $binary_remote_addr zone=addr:10m;

server {

location / {

limit_conn addr 10;

}

}

这里有个小提醒:限流配置要根据实际业务流量来调,别一上来就限得太死,容易误伤正常用户。建议先用 ngx_http_stub_status_module 监控一段时间,摸清流量规律再调整。

五、OpenResty 高级玩法:Lua 脚本赋能

如果说普通 Nginx 是把瑞士军刀,那 OpenResty 就是加装了各种插件的超级武器库。借助 Lua 脚本,我们可以实现很多原生 Nginx 做不到的功能。

动态路由

-- init_by_lua_block 中加载配置

local route_rules = {

["/api/v1/user"] = "user-service",

["/api/v1/order"] = "order-service",

["/api/v1/pay"] = "pay-service"

}

server {

location ~ ^/api/v1/(\w+) {

set $service_name "";

rewrite_by_lua_block {

local path = ngx.var.uri

local service = route_rules[path]

if service then

ngx.var.service_name = service

else

return ngx.exit(404)

end

}

proxy_pass http://$service_name;

}

}

请求限流与熔断

local function check_rate_limit(key, rate, burst)

local limit = ngx.shared.rate_limit

local current = limit:get(key) or 0

if current > rate then

return false

end

limit:incr(key, 1, 0, 1) -- 1秒后重置

return true

end

access_by_lua_block {

if not check_rate_limit(ngx.var.binary_remote_addr, 100, 50) then

ngx.exit(429) -- Too Many Requests

end

}

请求改写与参数处理

有时候后端接口规范和前端不一样,需要在网关层做转换:

rewrite_by_lua_block {

local args = ngx.req.get_uri_args()

-- 旧版 API 参数兼容

if args.old_param then

args.new_param = args.old_param

args.old_param = nil

ngx.req.set_uri_args(args)

end

}

OpenResty 的玩法真的很多,动态限流、灰度发布、请求日志、熔断降级……只要你敢想,基本都能实现。唯一需要注意的是 Lua 代码的性能,别写个死循环把自己坑了。

总结

好,今天聊了不少,总结一下重点:

1. 基础配置:Header 传递、超时设置、Buffer 调优,这些看似简单,但出问题的时候都是因为没配置好。

2. 负载均衡:合理设置权重,配置好故障转移,关键时刻能救命。

3. 缓存策略:根据业务特点设计缓存 key,调试时加上状态头方便排查。

4. 安全加固:隐藏版本号、限流防爬、敏感文件防护,一个都不能少。

5. OpenResty:用 Lua 脚本可以实现很强大的自定义逻辑,是进阶玩家的必备技能。

反向代理配置看起来简单,但要做到生产级别稳定可靠,里面的门道还是很多的。希望这篇文章能帮到各位,如果有问题,欢迎在评论区交流讨论。咱们下期再见!