搞Web开发的人,迟早都要跟Nginx反向代理配置打交道。我自己接手过一个老项目的维护工作,光梳理Nginx配置就花了一整天——里面堆了四五个server块,location嵌套了好几层,有的转发到内网Tomcat,有的代理到外部地图服务,还有一个负责前端静态资源。边看边改的过程中踩了不少坑,也把Nginx反向代理这个知识盲区彻底补上了。
这篇文章我就把Nginx反向代理配置这件事从头到尾捋一遍:它到底解决什么问题、Nginx怎么装、核心配置每行什么意思、几个实战场景怎么落地、以及最常见的报错和排查思路。适合刚接触Nginx的后端、前端同学,也适合部署踩坑之后回来查资料的运维新手。我尽量少讲抽象理论,多给可以直接抄作业的配置和实测经验。
1. 反向代理到底在解决什么问题
1.1 反向代理的本质:前台帮你转接
你可以把反向代理理解为公司的前台。外部来访者不需要知道要找的人具体坐在哪个工位,只要把需求递给前台,前台根据情况分给相应的部门。对应到技术上,客户端发请求到Nginx,Nginx按照规则转发给后端的某个服务,再把后端返回的结果带回给客户端。整个过程对客户端是透明的,客户端始终只认识Nginx这一个入口。
在没有反向代理之前,服务端架构是“一对多直接暴露”:每个后端服务都得有独立的域名或端口,客户端需要知道具体的host、端口、路径。一旦后端服务迁移、扩容、加鉴权,客户端也得跟着改,维护成本非常高。有了反向代理之后,客户端只管访问一个统一入口,后端怎么变都由Nginx在内部消化。
这一点在微服务和小型集群里特别重要。比如我原来维护的项目,后端同时有Java接口服务、Python定时任务管理面板、Node.js的推送服务,如果不做反代,用户得记住三个端口,而且跨域问题也会头疼。用Nginx统一代理之后,外部只看到80端口,内部的三个服务各自躲在后面,安全性和可维护性都好了不少。
1.2 正向代理与反向代理,别搞混
面试和日常讨论里最容易混淆的就是“正向代理”和“反向代理”。一句话区分:正向代理是代理客户端,反向代理是代理服务端。
正向代理的场景,最典型的是公司内网统一上网出口。用户不能直接访问外网,先请求公司代理服务器,由代理服务器去访问目标网站再把内容传回来。这个代理服务器代表的是用户,目标服务器看到的是代理的IP,不知道真实用户是谁。反向代理则反过来,用户访问的是Nginx,Nginx代表的是后端服务。用户根本不知道后端真实服务器在哪,只知道Nginx的地址。
我用一个表格把两者的关键差异列清楚:
| 对比项 | 正向代理 | 反向代理 |
|---|---|---|
| 代理对象 | 客户端 | 服务端 |
| 使用者 | 客户端配置,知道代理的存在 | 客户端无感,不知道反向代理存在 |
| 典型场景 | 内网上网出口、访问受限资源 | 负载均衡、统一入口、动静分离、SSL终止 |
| 代表技术 | Squid、HTTP Proxy | Nginx、HAProxy、Apache mod_proxy |
| 客户端感知 | 客户端需要手动配置代理 | 客户端只访问目标域名/端口 |
明白了这个区别,你在配置Nginx时就不会把它理解成“给用户开代理”,而是“替后端服务接客”。
1.3 为什么挑Nginx而不是Apache、Caddy
市面上能承担反向代理的软件不只Nginx一个,Apache有mod_proxy,Caddy配置更简单,HAProxy在负载均衡领域也很强。我之所以长期用Nginx,是因为它在性能、配置灵活度和生态成熟度之间取了一个很好的平衡。
Nginx采用事件驱动的异步架构,一个worker进程可以同时处理成千上万个连接,内存占用比Apache的进程/线程模型低很多。同样是扛几万并发,Nginx对服务器资源的要求明显更低。配置方面,Nginx的配置文件虽然语法稍显啰嗦,但逻辑清晰,server、location、upstream这些块级指令分层明确,改起来不容易出错。而且Nginx模块生态非常成熟:负载均衡、SSL终止、反向代理、缓存、限流、gzip压缩都能在一个配置文件里搞定,不需要额外装一堆插件。
Caddy的自动HTTPS确实方便,但遇到复杂的URI改写、多条件判断、灰度发布这种场景,表达能力反而不如Nginx直接。HAProxy在四层和七层负载均衡上性能极佳,但静态资源服务、URL重写、和Web应用层的深度联动就不是它的强项了。所以我的结论是,把Nginx作为默认的反向代理方案,配合上负载均衡和静态资源托管,绝大多数项目都够用了。
2. 先把Nginx装好:Windows与Linux实操
2.1 Windows下跑起来:解压就能用
很多本地开发和测试场景需要在Windows上先跑一个Nginx。Nginx在Windows上不需要安装,下载zip包解压即用。去Nginx官网下载Windows版本,解压到一个比较干净的路径下,比如D:\nginx-1.26.2。注意路径里不要有中文和空格,虽然现在不少情况也能跑,但遇到奇怪的相对路径报错你会后悔的。
启动方式是在解压目录下执行start nginx,或者直接双击nginx.exe。启动后打开任务管理器,正常应该看到两个nginx进程:一个master进程(管理进程)和一个worker进程(实际处理请求)。如果只有一个进程,说明启动可能失败了,可以去logs/error.log里看原因。验证是否启动成功,在浏览器访问http://localhost,看到“Welcome to nginx!”页面就说明没问题。
我踩过的坑是Windows的80端口被其他程序占用。常见嫌疑犯包括IIS、SQL Server Reporting Services、VMware的HTTP服务、以及各种开发工具的本地调试服务器。查看端口占用用netstat -ano | findstr :80,找到占用进程后在任务管理器里结束,或者直接改Nginx配置里的监听端口。Windows下修改完配置文件,用nginx -s reload就能生效,不需要重启整个服务,这点和Linux是一致的。
2.2 Linux下安装:包管理器与编译安装
Linux服务器上安装Nginx通常有两种思路:用系统包管理器安装,或者源码编译安装。前者省事,后者灵活。如果你用的是AlmaLinux、CentOS、Rocky Linux这类RHEL系发行版,系统自带的基础源里可能没有Nginx,需要先启用EPEL源。配置好EPEL源之后执行dnf install nginx,安装完用systemctl start nginx启动,设为开机自启用systemctl enable nginx。
Ubuntu/Debian系更简单,直接apt install nginx,安装完成后服务一般会自动启动。包管理器安装的Nginx,配置文件结构很规整:/etc/nginx/nginx.conf是主配置,/etc/nginx/conf.d/下放自定义的站点配置,/etc/nginx/sites-available/和sites-enabled/在Ubuntu上用来管理站点启用和禁用。我习惯把每个项目单独建一个conf文件放在conf.d/目录下,文件名用项目名命名,例如myproject.conf,方便日后维护。
包管理器安装的优点是升级方便、配置目录规范、可以和systemd无缝集成。缺点是默认编译的模块不一定满足你的需求,比如你需要在Nginx里做sub_filter内容替换、想用--with-http_v2_module关闭HTTP/2,或者打算集成第三方模块(如Lua、Brotli压缩),这些都是系统包默认不带的。这时候就得考虑源码编译。
2.3 编译安装与平滑升级
源码编译安装Nginx,核心步骤是先准备好依赖库,再./configure生成Makefile,然后make && make install。以RHEL系发行版为例,需要提前安装gcc、pcre-devel、zlib-devel、openssl-devel这几样东西,分别对应编译工具、正则表达式库、压缩库、TLS加密库。
下载Nginx源码包并解压后,执行编译配置,下面是常用参数:
./configure \ --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_sub_module \ --with-http_gzip_static_module \ --with-stream make && make install编译安装的二进制文件在/usr/local/nginx/sbin/nginx,配置文件在/usr/local/nginx/conf/nginx.conf,日志在/usr/local/nginx/logs/。和包管理器版本相比,需要自己写systemd服务文件才能用systemctl管理,不过网上有现成模板,复制改改就行。
再提一个王炸功能:平滑升级。Nginx支持在不停机的状态下替换二进制文件,实现版本升级。具体流程是:下载新版本源码,编译出新的nginx二进制,备份旧二进制,执行make upgrade或者手动方式——发送USR2信号让旧master启动新master,再发WINCH让旧worker逐渐退出。我实测下来还是用make upgrade最省心,两条命令就完成升级,连接不中断。这个技能在处理Nginx安全漏洞需要紧急升级时非常有用。
2.4 基本管理与验证命令
不管哪种安装方式,以下命令都是日常高频使用的,我直接汇总成一张速查表。
| 命令 | 作用 | 使用频率 |
|---|---|---|
nginx -t | 校验配置文件语法,输出ok和successful即通过 | 每次改配置后必用 |
nginx -s reload | 重新加载配置,不停机 | 日常改配置后 |
nginx -s stop | 快速停止服务 | 较少 |
nginx -s quit | 优雅停止,处理完当前请求再停 | 较少 |
systemctl start/stop/restart nginx | systemd管理服务 | 配systemd后推荐 |
systemctl status nginx | 查看服务运行状态 | 排查时常用 |
nginx -T | 输出最终生效的完整配置 | 排查配置问题时推荐 |
我个人的习惯是:每次修改配置文件后,先执行nginx -t校验语法,再执行nginx -s reload生效。这个习惯帮我避免过好几次“改完配置忘了reload,线上还在跑旧配置”的尴尬情况。
3. 核心配置逐段拆解
3.1 nginx.conf的整体目录结构
Nginx的配置文件通过指令块分层组织。最外层是main块,设置全局参数;events块配置事件驱动模型;http块封装所有HTTP相关的配置;http块里可以定义多个server(虚拟主机);server里可以定义多个location(URI匹配规则)。剥掉注释之后,一个最简配置长这样:
user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; events { worker_connections 10240; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; server { listen 80; server_name example.com; location / { root /usr/share/nginx/html; index index.html; } } }worker_processes auto值得说一句:它让Nginx根据服务器CPU核心数自动启动对应数量的worker进程。以前老配置喜欢手动填4、8之类的数字,其实auto在大多数场景下最合理。worker_connections表示每个worker能同时打开的连接数上限,默认1024太小,压测时容易突破上限,调到10240是常见做法。
3.2 location匹配规则解析
反向代理配置的重头戏基本都在location块里。location的作用是告诉Nginx:当请求的URI满足某种匹配规则时,执行块内的处理逻辑。理解匹配规则,你才能准确控制哪些请求转给后端,哪些请求直接返回静态文件。
Nginx的location匹配优先级从高到低是:
location = /path:精确匹配,完全相等才命中,优先级最高。location ^~ /prefix:前缀匹配,一旦命中就不再检查正则。location ~ /正则/、location ~* /正则/i:正则匹配,按配置文件中的顺序执行,第一个匹配到的生效。location /prefix:普通前缀匹配,匹配到后继续检查正则,正则优先。
举个例子,用户请求/api/user/info,配置里有location /api/和location ~ \.php$,两者都能匹配时,正则的优先级更高,会走php处理逻辑。想强制某个前缀优先,就用^~。
我建议刚上手的人不要一上来就堆一堆正则。先写普通前缀匹配,比如location /api/代理后端、location /static/走本地目录,把基础路径理清楚之后,再逐步引入正则。正则写多了,匹配优先级一旦搞混,排查一个404能折腾半天。
3.3 proxy_pass带不带斜杠的区别
proxy_pass是反向代理的核心指令,语法很简单:proxy_pass http://目标地址;。但有一个细节特别容易坑人:目标地址结尾带不带斜杠,直接决定了转发给后端的URI长什么样。
不带斜杠时,Nginx会将客户端请求的原始URI完整传给后端。举例:
location /api/ { proxy_pass http://127.0.0.1:8080; }客户端请求/api/user/list,后端Spring Boot收到的URI是/api/user/list。这要求后端接口路径本身就包含/api前缀,或者说后端能容忍这个前缀。
带斜杠时,Nginx会进行路径替换,将location匹配到的那部分前缀替换为/。仍以上面为例:
location /api/ { proxy_pass http://127.0.0.1:8080/; }客户端请求/api/user/list,后端收到的URI是/user/list。感觉像是把/api这个前缀“吃掉了”。类似的,如果proxy_pass写成http://127.0.0.1:8080/v2/,那么/api/user/list会被替换为/v2/user/list传给后端。
我踩过最典型的一个坑是:前端和后端联调,前端请求/api/login,后端接口实际是/login。前端项目里所有的请求都带/api前缀,但Java接口类的@RequestMapping里没写/api,于是配置里漏了结尾的斜杠,导致后端口口声声说找不到路由。这问题在联合调试时隐蔽性极强,因为Nginx本身没有报错,后端也没报错,就是404。排查方法就是看后端access log里实际收到的URI,一眼就能确认是不是路径被错误地保留了。
3.4 转发请求头:别让你的后端“失忆”
反向代理隐藏了客户端的真实IP,如果Nginx不把客户端的相关信息传递下去,后端服务获取不到访客IP、无法判断请求来源是http还是https,会出现“失忆”症状。所以配置里几乎必带下面这几行:
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;Host $host保持请求的原始域名,这样后端的虚拟主机、权限校验、模板渲染都能拿到正确的Host。X-Real-IP是客户端真实IP,X-Forwarded-For会追加每一层代理的IP,X-Forwarded-Proto则告诉后端原始请求是http还是https。像Spring Boot内置的Tomcat、Nginx后端的日志分析工具,很多都依赖这几个头部来展示真实访客IP。不配的话,后端日志里看到的一律是127.0.0.1,这会让你在排查安全和风控问题时无从下手。
除了请求头,还有几个proxy超时参数也需要关注。proxy_connect_timeout是Nginx与后端建立连接的等待时间,默认60秒;proxy_read_timeout是两次读取操作之间的间隔超时,默认60秒。如果你的后端接口偶尔需要执行超过一分钟的长任务(比如导出报表、批量审批),默认超时会导致504。我一般会把proxy_read_timeout调到120秒甚至更长,具体看业务。此外,大文件上传要设置client_max_body_size,默认1MB会直接憋死上传功能,改为50m或更大是常见操作。
4. 三个实战配置,拿来就能抄
4.1 前后端分离项目:/api代理到后端服务
现在主流的前后端分离开发模式,前端用Vue、React,后端用Spring Boot、Go、Node.js。部署的时候,前端打包成静态文件放在Nginx上,后端接口跑在某个本地端口。最直接的做法是把前端页面和服务端接口都放在同一个Nginx下,通过location区分。
先看完整配置:
server { listen 80; server_name www.example.com; # 前端静态资源 location / { root /opt/frontend/dist; index index.html; try_files $uri $uri/ /index.html; } # 后端接口代理 location /api/ { proxy_pass http://127.0.0.1:8080/api/; 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_read_timeout 120s; client_max_body_size 50m; } # 静态资源单独走本地目录 location /static/ { alias /opt/frontend/dist/static/; expires 7d; } }这里几个关键点:try_files $uri $uri/ /index.html;是给Vue/React的history路由模式用的。前端路由是/user/center这种地址,但服务器上没有这个物理文件,如果没有try_files兜底,刷新页面就会出现404。有了这一行,所有匹配不到文件的请求都会回到index.html,前端路由接管后再渲染对应页面。/static/目录用alias指向本地目录,而且加上expires 7d可以缓存静态资源,减轻后端压力。
后端接口的代理,proxy_pass结尾带不带斜杠取决于后端接口是否包含/api前缀。如果后端Controller的@RequestMapping里面没有加/api,那proxy_pass http://127.0.0.1:8080/;可以帮你把前缀剥掉。这一点我在上一节已经详细讲过,实际配置时先和后端确认清楚接口路由,再决定写法。
4.2 内网地图代理:以百度地图为例
有读者问过“内网要反向代理地图供内网使用应该怎么做,以百度地图为例”,这是个很典型的场景。企业内网为了安全通常不能直接访问外网,但内部业务系统又需要展示地图。解决办法是让一台能够访问外网的服务器作为中转,由Nginx代收地图请求再转发给百度地图服务器。
这个需求的麻烦点在于:地图服务并不是简单的一个API地址,它包含页面、JavaScript脚本、CSS样式、瓦片数据等多个请求路径,而且这些资源里硬编码了map.baidu.com这样的域名。代码层面如果直接请求我们自己的域名,JS脚本里的域名不换,浏览器还是会直接去请求外网地址,照样被墙在门内。
我在实际项目中采用过方案是:Nginx代理百度的多个二级域名,并用sub_filter模块替换响应内容里出现的域名。
server { listen 80; server_name maps.internal.example.com; # 页面和API请求 location /baidu-map/ { proxy_pass https://map.baidu.com/; proxy_set_header Host map.baidu.com; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_ssl_server_name on; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_redirect https://map.baidu.com/ /baidu-map/; sub_filter_once off; sub_filter '//map.baidu.com' '//maps.internal.example.com/baidu-map'; sub_filter_types text/html application/javascript text/css; } }sub_filter是Nginx的内容替换模块,它会把后端返回的HTML、JS、CSS字符串里的//map.baidu.com替换成我们内网地址。sub_filter_once off表示替换所有匹配而不只是第一处。proxy_redirect把后端返回的Location响应头里的外网地址改写为内网地址。
实际执行时还要注意几个细节。第一,百度地图API会在Referer里做域名白名单校验,你直接代理过去,Referer是内网域名,API可能会拒绝服务,需要在百度地图开放平台把内网域名加进白名单。第二,JS脚本里如果通过变量拼接地址而不是写死域名,sub_filter就替换不了,得配合更精细的改写规则或者直接在代码里配置地图服务的host指向。第三,HTTPS证书问题,内网域名需要配置SSL证书,否则浏览器会拦截。
这里我不建议把地图代理方案做成通用标准,因为地图服务商的防盗链、跨域、证书策略各不相同,更稳妥的做法是使用地图服务商提供的离线地图SDK,或者自己做一个轻量TCP/HTTP转发中间层。但通过这个例子,你可以掌握Nginx反向代理外网服务进内网的核心思路:域名替换、响应体改写、Referer和Host处理。这套方法论在很多内网服务穿透场景里都通用。
4.3 负载均衡配置:让后端扛住高并发
Nginx反向代理最经典的应用之一就是负载均衡。当单个后端服务扛不住并发压力,把同样的服务部署到多台机器上,Nginx作为流量入口,把请求分发给各个后端实例。配置方式是在http块中定义一个upstream服务器组:
upstream backend_servers { # 默认按权重轮询 server 192.168.1.11:8080 weight=3; server 192.168.1.12:8080 weight=2; server 192.168.1.13:8080 weight=1 max_fails=3 fail_timeout=30s; # keepalive 保持后端连接 keepalive 64; } server { listen 80; server_name www.example.com; location /api/ { proxy_pass http://backend_servers; 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_http_version 1.1; proxy_set_header Connection ""; } }负载均衡的策略默认是轮询,每个请求依次分发给后端,后者挂了会自动摘除。weight参数调整分配比重,性能好的机器权重给大一点。如果业务要求同一个用户的请求始终打到同一台后端(比如Session存储在本地内存),可以改用ip_hash策略:
upstream backend_servers { ip_hash; server 192.168.1.11:8080; server 192.168.1.12:8080; }ip_hash根据客户端IP计算哈希值,相同IP的请求会被固定分配到同一台服务器。缺点是如果某个客户端的流量特别大,可能造成单台机器过载。另外一个容易忽略的点是proxy_http_version 1.1;和proxy_set_header Connection "";,其目的是启用Nginx到后端的HTTP长连接,避免每个请求都重新建连。在高并发场景下,这两行配置对后端性能的提升非常明显,我实测过短连接和长连接模式下后端QPS可以有接近一倍的差距。
高并发调优不仅靠负载均衡,Nginx本身的参数也要配合。worker_processes auto;让CPU核心都用起来,worker_connections 10240;提升单进程连接数上限,keepalive_timeout 65;保持客户端连接,gzip on;压缩传输数据减少带宽占用。把这些基础项调好,一个普通的四核八G服务器扛住几千并发是没有问题的。
5. 常见问题排查与避坑记录
5.1 502和504:最常见的两个“坏网关”
502 Bad Gateway是Nginx反向代理配置里出现频率最高的报错。排查思路非常明确:Nginx本身没事,问题是Nginx转发请求之后,后端没有给出正常响应。可能的原因有:后端服务根本没启动、后端监听的IP端口写错、后端在处理请求时崩溃、防火墙或SELinux拦截了连接。
我的排查顺序是:先确认后端服务本身能不能直接访问,在Nginx服务器上执行curl http://127.0.0.1:8080/health,如果直接curl都失败,问题在后端服务本身。如果curl正常但Nginx代理后502,重点检查proxy_pass里的地址和端口是否错误、DNS解析是否正常、Nginx日志里有没有connect() failed (111: Connection refused)这种记录。
还有一个容易被忽略的是SELinux。在RHEL系发行版上,即使防火墙放行了端口,SELinux也可能拦截Nginx发起对外连接。遇到这种情况执行curl时可能正常,但通过Nginx访问就502。临时验证的方法是把SELinux设为许可模式setenforce 0,如果问题消失,那就是SELinux策略问题。永久解决方案是setsebool -P httpd_can_network_connect 1,网上有不少人因为这一步卡了两三个小时。
504 Gateway Time-out的含义是Nginx等待后端处理超时。后端接口执行时间过长,超过了proxy_read_timeout或proxy_send_timeout的设定值。解决办法是调大超时时间,例如proxy_read_timeout 300s;,或者优化后端接口的响应速度。
5.2 404与路径丢失:location与proxy_pass的坑
404问题在反向代理里经常不是后端路由写错,而是URI在转发过程中变了形或根本没匹配到正确的location。典型场景一:客户端请求/api/login,配置里的location是/api(不带尾部斜杠),访问/api时正常,但访问/api/login就是404。原因在于location /api匹配的是以/api开头的URI,但proxy_pass http://backend;会把原始URI传过去,后端如果预期路径是/api/login,这不会404;但如果后端预期是/login,这里就出问题了,需要按上文的斜杠规则调整。
典型场景二是root和alias用混。在配置location时,root和alias都能指定本地目录,但逻辑不同。root /opt/html;会在访问/static/app.js时查找/opt/html/static/app.js,而alias /opt/html/;会把location前缀替换为alias路径,访问/static/app.js时查找/opt/html/app.js。如果你把alias写成root的样子,资源路径会多出一层目录,静态资源全部404。我建议静态资源目录统一用alias,搭配location /static/前缀,可读性更好也少犯错。
要快速排查路径问题,最直接的方法是看Nginx访问日志和后端应用日志里的实际URI,对比前后端收到的地址差异,一两分钟就能定位。
5.3 重定向与WebSocket:容易被忽略的细节
有些后端服务在做用户认证之后会返回302重定向,例如跳转到登录页。如果后端返回的Location是http://127.0.0.1:8080/login,那么客户端会跟着Location直接访问这个内网地址,结果当然是失败或者暴露了内网信息。Nginx提供了proxy_redirect来解决这个问题。默认值proxy_redirect default;会自动把后端返回的Location响应头里的http://127.0.0.1:8080替换为Nginx自己接收请求的地址。如果你后端返回的Location路径格式特殊,可以用显式配置:
proxy_redirect http://127.0.0.1:8080/ /;WebSocket是现代Web应用很常用的协议,做实时推送、聊天、在线协作都离不开。Nginx反向代理默认是按HTTP协议处理的,WebSocket升级请求需要的Upgrade和Connection头不会自动保留,必须显式配置:
location /ws/ { proxy_pass http://127.0.0.1:8080/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; }proxy_read_timeout这里要特别注意,WebSocket连接是长连接,默认60秒没有数据就会断开。做在线聊天这类功能,建议把超时时间设到数小时甚至不设超时,否则用户会莫名其妙地频繁掉线。
5.4 配置安全与性能小调优
反向代理把后端服务隐藏得很好,但Nginx自身也会暴露版本信息,给攻击者可乘之机。隐藏版本号只需要一行:server_tokens off;。如果你代理的是敏感内部系统,再加上基于IP的访问控制,没在名单里的IP直接拒绝:
location /admin/ { allow 192.168.1.0/24; deny all; proxy_pass http://127.0.0.1:8080/admin/; }日志方面,access_log off;可以关闭静态资源目录的访问日志,因为大量图片、JS、CSS的日志除了撑爆磁盘没有太多分析价值。动态接口的日志则建议保留并开启log_format,后面排查问题时非常有用。
最后分享一个压箱底的习惯:每次要改动Nginx配置之前,先备份当前配置文件,比如cp nginx.conf nginx.conf.bak.20250101。不要问我为什么,我只知道有一次我改完配置reload之后忘了具体改了哪些行,找了两小时才靠备份对比找回旧配置。配置文件应该纳入版本管理,Git仓库里留一份,服务器上再留一份,线上出问题恢复起来会快得多。
在我实际配置Nginx的经验里,最难的不是记住指令,而是理解每个指令背后的数据流。客户端请求到了Nginx之后怎么匹配location、URI如何拼接、请求头怎么变换、后端返回之后又做了什么改写——脑子里有一条清晰的链路,配置起来就很少出错。遇到问题也别慌,先看/var/log/nginx/error.log,再确认location匹配顺序,最后检查后端地址和超时设置,八成的问题都能在这三步里解决。希望这篇文章能让你在配置Nginx反向代理时少走点弯路。