1. 反向代理到底是什么为什么你的服务离不开它先说结论Nginx的反向代理简单来说就是让Nginx站在后端服务的前台帮后端服务接收所有外部请求再按规则把请求转交给内部的真实服务器拿到响应后再原样返回给客户端。客户端全程只知道Nginx的地址根本接触不到后端服务本身。很多刚入门的朋友会把反向代理和正向代理搞混。打个比方正向代理是替客户端出门办事比如你在内网通过代理访问外网资源代理代表的是你的身份反向代理则是替服务端迎客进门所有访客都先到Nginx这里报到Nginx再根据请求内容决定把这些访客带去后台哪个具体的服务窗口。我们平时说的Nginx反向代理做的就是迎客这件事这在Web架构里几乎成了标配。为什么需要它我能列出的理由至少有这几个后端服务不能直接暴露公网通过Nginx统一入口可以隐藏真实IP降低被直接攻击的风险。一个Nginx可以把请求分发给多个后端实例天然解决单点压力问题配合负载均衡策略让后端集群分摊流量。多个不同域名的项目可以共用一台服务器和80/443端口靠Nginx的路由规则互不干扰。HTTP和HTTPS的切换、超时控制、请求头改写、响应体处理等杂活都可以在Nginx这一层统一完成后端代码不用跟着折腾。这篇文章我打算从部署开始把配置逐块拆开讲清楚然后落到几个最常见的实战场景比如前后端分离项目部署、微服务路由转发、负载均衡、字符替换最后把日志、排错和安全加固这些运维中最常踩的坑也一并整理出来。内容适合正在学习Nginx的初学者也适合已经用了一阵子但想系统理清配置逻辑的开发者。2. 从零部署编译安装、二进制包与Docker三条路线怎么选2.1 二进制包安装最快上手适合大多数Linux服务器大部分人刚接触Nginx不需要一上来就折腾编译参数直接用系统包管理器安装是最省事的。以常见的CentOS和Ubuntu为例CentOS/RHEL系列使用yum或dnfsudo dnf install -y nginx sudo systemctl enable --now nginxUbuntu/Debian系列使用aptsudo apt update sudo apt install -y nginx sudo systemctl enable --now nginx装完以后默认的配置文件一般在/etc/nginx/目录下主配置文件是nginx.conf站点配置通常放在conf.d/或者sites-available/目录中。很多人在这一步问为什么我用nginx -V查看时有些模块没有编译进去原因很简单系统包管理器安装的Nginx是发行版默认编译的版本模块相对固定。如果你想用sub_filter这类不太常见但很实用的模块二进制包很可能不满足需求这个时候就需要走源码编译路线。2.2 源码编译安装要定制模块时必须掌握源码编译主要解决两个问题一是需要特定模块比如sub_filter、http_sub_module、stream模块二是需要适配特殊环境比如aarch64架构的服务器或者像麒麟、统信这类国产化系统离线环境下可能找不到现成的安装包必须靠源码编译。在编译之前先确认依赖是否齐全sudo yum install -y gcc gcc-c pcre pcre-devel zlib zlib-devel openssl openssl-devel如果是离线编译需要提前把gcc、pcre-devel、zlib-devel、openssl-devel这些依赖包下载成rpm文件再通过rpm -ivh挨个安装。最常见的坑是依赖顺序不对导致后续编译报错找不到头文件建议先装gcc再装各种-devel包最后才是Nginx本体。下载Nginx源码并开始编译wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.0 ./configure --prefix/usr/local/nginx \ --with-http_sub_module \ --with-http_ssl_module \ --with-http_v2_module \ --with-stream \ --with-stream_ssl_module make -j$(nproc) sudo make install这里我要多说一句./configure的参数设计。--prefix是安装目录最好单独指定避免和系统包管理器装的Nginx混在一起--with-http_ssl_module必须带上不然后面配置HTTPS反向代理时会报unknown directive ssl--with-stream用于四层TCP/UDP转发如果你的场景只用七层HTTP转发可以不加--with-http_sub_module对应的是字符替换功能后面我会专门讲它的用法。编译完成后启动方式也需要记住/usr/local/nginx/sbin/nginx -t # 检查配置语法 /usr/local/nginx/sbin/nginx # 启动如果你的服务器是Ubuntu想用init.d方式管理Nginx可以写一个简单的启动脚本放到/etc/init.d/下核心就是执行start、stop、reload这几个动作时调用对应的Nginx命令。不过我更推荐直接写systemd服务文件管理起来更规范。2.3 Docker部署Nginx隔离环境与快速复现的首选如果后端服务本身就是用Docker部署的那Nginx也建议直接用容器跑省去服务器环境的差异问题。拉镜像非常简单docker pull nginx:1.21.5这里给一个比较标准的部署命令示例docker run -d --name nginx-proxy \ -p 80:80 -p 443:443 \ -v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \ -v /opt/nginx/logs:/var/log/nginx \ --restartalways \ nginx:1.21.5注意一点Nginx容器内的配置目录和日志目录一定要挂载到宿主机否则容器一删你辛辛苦苦写的配置和日志全部归零。另外容器里的Nginx默认以daemon off方式运行这是正常现象不需要手动改配置。Docker部署的好处是干净、可复现团队协作时把Dockerfile和配置目录放进Git仓库新环境一条命令就能拉起。缺点是排错时多了一层容器网络对容器网络原理不熟的人可能会卡在端口映射和容器间通信上。3. 配置文件逐块拆解从看懂到写出一份标准反向代理3.1 nginx.conf整体结构先过一遍不管是用哪种方式安装的Nginx主配置文件的逻辑结构都是相似的。我习惯把它拆成四块来看main块配置Nginx进程本身比如worker_processes、user、错误日志路径。events块配置连接处理模型比如worker_connections。http块配置HTTP服务相关的全部内容包括server、upstream、location等。stream块四层TCP/UDP转发的配置区反代MySQL、Redis这类非HTTP服务时会用到。刚入门的人最容易犯的错是在http块里面直接写stream相关指令或者在events块里写proxy_pass导致Nginx直接报错启动失败。记住一句话proxy_pass只能出现在server或location里upstream只能出现在http或stream里。3.2 一个最小可用的反向代理配置长什么样下面这份配置是我平时搭环境时最常用到的模板先把它跑通再谈优化server { listen 80; server_name api.example.com; location / { proxy_pass http://127.0.0.1:8080; 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; } }这份配置的含义是当外部访问http://api.example.com时Nginx把所有请求转发给本机8080端口的服务。这可能是Java应用、Node服务也可以是另一个Web服务器。Host请求头传不传影响很大。很多后端应用会校验域名如果你不传原始请求的Host后端拿到的是127.0.0.1:8080轻则日志里全是IP访问记录重则应用直接拒绝请求。X-Real-IP和X-Forwarded-For是用来传递客户端真实IP的尤其是内网的Nginx反代到后端时后端Servlet或者Spring Boot默认拿到的request.getRemoteAddr()往往是Nginx服务器的内网IP不是用户真实IP。加上这些请求头之后后端再配合X-Forwarded-For或者X-Real-IP取IP就准确了。关于用户会搜的IP头部的五元组信息Nginx转发会带吗我的回答是默认不带完整的五元组原始信息。HTTP七层转发只保留必要的原始请求头Nginx更不会默认把客户端的源IP、源端口、目标IP、目标端口、协议号这些五元组原封不动地转给后端。如果你需要保留源IP信息常规做法就是通过X-Forwarded-For、X-Real-IP传递IP源端口可以使用$remote_port变量但五元组里的目标IP和目标端口到了后端就已经是Nginx的地址了这本来就是反向代理的意义所在。3.3 proxy_pass的斜杠陷阱与proxy_redirect的用途用过Nginx的人几乎都被proxy_pass的斜杠问题坑过这也是面试里特别爱问的点。proxy_pass后面带的URI分两种情况不带路径proxy_pass http://127.0.0.1:8080;请求/api/user会完整转发成http://127.0.0.1:8080/api/user。带路径proxy_pass http://127.0.0.1:8080/;注意后面这个斜杠请求/api/user会被替换成http://127.0.0.1:8080/user也就是location匹配到的部分会被替换掉。再举一个带location的例子location /api/ { proxy_pass http://127.0.0.1:8080/; }此时请求/api/user/list后端实际收到的是/user/list。如果proxy_pass改成不带斜杠的http://127.0.0.1:8080;后端收到的还是/api/user/list。实际项目中我见过太多因为多写了一个斜杠导致接口路径全部404的案例。排查方法很简单先看Nginx的access.log确认后端实际收到的请求路径再对照后端路由定义问题基本一眼就能定位。proxy_redirect是另一个容易被忽略的指令。后端如果返回30x重定向而且Location头写的是后端自己的地址客户端拿到这个地址就会绕过Nginx直接访问后端这就违背了反向代理的初衷。典型场景是后端重定向到http://127.0.0.1:8080/login需要改写成http://api.example.com/loginproxy_redirect http://127.0.0.1:8080/ /;3.4 负载均衡的三流策略轮询、权重与IP哈希Nginx的负载均衡写在upstream块里然后在proxy_pass里使用这个upstream的名字upstream backend_servers { server 192.168.1.10:8080 weight3; server 192.168.1.11:8080 weight1; server 192.168.1.12:8080 backup; } server { listen 80; server_name app.example.com; location / { proxy_pass http://backend_servers; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里的三台后端服务器策略分别是默认模式是轮询请求按顺序轮流分发给每台服务器。weight3和weight1表示第一台服务器接收的请求量是第二台的3倍适合后端机器配置有差异的情况。backup标记的服务器平时不接收请求只有前面两台都不可用时才顶上相当于热备。如果需要同一个用户的请求始终落在同一台后端服务器比如有状态的Session场景可以改用IP哈希upstream backend_servers { ip_hash; server 192.168.1.10:8080; server 192.168.1.11:8080; }ip_hash根据客户端IP计算哈希值保证相同IP的请求总是转发到同一台后端。现在服务端无状态化趋势明显很多场景已经不需要ip_hash了但遇到老系统还必须用Session时它依然是最简单省事的方案。3.5 反向代理中的字符替换sub_filter实战热搜词里的nginx反向代理显示字符替换指的就是sub_filter模块。它的作用是Nginx拿到后端返回的响应体后把指定的字符串替换成另一段字符串再返回给客户端。这个功能在什么场景下特别有用举个例子你把一套老管理系统通过Nginx发布到公网后端页面上写死了很多CDN域名或者内部静态资源地址这些地址在公网环境不可访问。改后端代码成本高、风险大用Nginx的sub_filter直接替换响应体里的域名就快得多。配置示例server { listen 80; server_name new.example.com; location / { proxy_pass http://127.0.0.1:8080; sub_filter_once off; sub_filter http://old.example.com https://new.example.com; sub_filter src/static/ src/proxy-static/; } }sub_filter_once on表示只替换响应内容中的第一个匹配项设置为off则全部替换。实际使用中绝大部分场景都需要全局替换所以记得改成off。这个模块在默认的Linux发行版Nginx里不一定编译进去了。检查方法是在终端执行nginx -V 21 | grep sub如果输出里不带--with-http_sub_module说明你的版本没有这个模块两个解决办法一是换用OpenResty这类打包好的Nginx发行版二是自己源码编译时加上--with-http_sub_module。替换逻辑里还需要注意内容编码问题。如果后端返回的是gzip压缩过的内容sub_filter是没法直接做字符串替换的所以使用sub_filter时尽量先关闭后端的gzip输出或者在Nginx这一层用proxy_set_header Accept-Encoding identity;来告诉后端不要压缩否则会发现规则写完但死活不生效。4. 前后端分离、微服务与WebSocket场景实战4.1 在一台服务器上部署Vue3前后端分离项目前后端分离是目前最常见的部署模式我见过很多人在Windows服务器上部署Nginx然后把Vue3项目打包后的dist目录丢进去再反代后端的接口服务。Windows Server上安装Nginx非常简单从官网下载Windows版本压缩包后解压即可不需要编译也不需要安装服务。很多人在Windows上遇到的最直接问题是端口号被占用修改Nginx端口的位置在nginx.conf的listen处比如想改到8081就把listen 80改成listen 8081。改完使用以下命令重载配置nginx -t nginx -s reloadVue3项目部署的推荐配置结构如下server { listen 80; server_name www.example.com; # 静态资源也就是Vue打包出来的dist目录 root /data/projects/vue3-app/dist; index index.html; # Vue Router使用history模式时刷新页面会出现404必须配置try_files location / { 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; } # 静态资源缓存策略 location /assets/ { expires 30d; add_header Cache-Control public, immutable; } }这个配置里有三个关键点。第一try_files $uri $uri/ /index.html;是Vue Router history模式的生命线不加这一行用户在浏览器里直接刷新/user/list这样的页面时Nginx找不到对应的静态文件就会返回404加了之后所有前端路由最终都会回退到index.html由前端路由接管。第二接口路径/api/必须在静态资源之后单独配置Nginx的location是前缀匹配如果/里已经写了try_files接口请求也会被拿去匹配静态文件所以接口相关location的优先级要把握好。第三/assets/缓存配置可以让浏览器缓存打包后的JS和CSS文件减少加载时间。4.2 微服务场景下的路由转发与动态负载微服务架构下Nginx通常充当网关入口把不同前缀的请求分发给不同的微服务。比如/order开头的请求转发给订单服务/user开头的请求转发给用户服务upstream order_service { server 192.168.1.20:8081; server 192.168.1.21:8081; } upstream user_service { server 192.168.1.30:8082; server 192.168.1.31:8082; } server { listen 80; server_name gateway.example.com; location /order/ { proxy_pass http://order_service/; proxy_set_header Host $host; } location /user/ { proxy_pass http://user_service/; proxy_set_header Host $host; } }这里proxy_pass末尾带斜杠的写法非常关键它会让Nginx在转发时去掉location中匹配到的前缀。举个例子请求/order/list转发到后端时变成/list这样后端服务不需要感知自己的网关前缀部署时更灵活。但是如果后端接口本身就定义在/order路径下那proxy_pass就不能带斜杠而是写成http://order_service;。如果项目基于Spring Cloud Gateway或Dubbo这类微服务体系Nginx的作用会更偏向流量入口和TLS终结内部的服务发现和熔断由框架本身去处理。这种情况下Nginx的upstream服务器列表是静态的服务扩缩容时需要同步修改Nginx配置。想要动态感知后端节点变化可以把Nginx接入注册中心配合脚本自动更新配置好一点的团队会直接用APISIX这类网关来做动态路由。我这里要提醒一句微服务链路比较长的时候超时时间一定要在Nginx层设置合理。初始默认的proxy_connect_timeout 60s和proxy_read_timeout 60s在有些场景下并不够用比如接口内部要调用别的服务加上数据库查询一次请求可能超过一分钟。此时建议按业务调整proxy_connect_timeout 5s; proxy_read_timeout 120s; proxy_send_timeout 120s;4.3 WebSocket反向代理配置要点WebSocket的协议升级过程和普通HTTP请求不同它需要通过Upgrade和Connection这两个请求头来完成协议切换。用Nginx反代WebSocket时默认配置是不支持这个升级过程的必须显式配置location /ws/ { proxy_pass http://websocket_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; }proxy_http_version 1.1很重要因为HTTP/1.0默认不支持Upgrade请求头。proxy_read_timeout建议根据业务场景调大默认60秒的话WebSocket连接只要超过60秒没有数据交互Nginx就会主动断开连接导致前端频繁掉线。我自己在写实时聊天、消息推送或者协同编辑这类项目时踩过最深的坑就是WebSocket在这个配置上没加全前端一直报WebSocket connection failed然而一开始看日志又看不出什么异常。后来仔细检查发现是Connection头写错成了keep-alive导致握手始终不成功。这一点务必检查。5. 日志、排错与安全加固实录5.1 日志路径与自定义日志格式Nginx日志分两部分access.log记录所有访问请求error.log记录启动报错和运行异常。默认路径通常是/var/log/nginx/如果用源码编译安装默认路径是安装目录下的logs/文件夹。如果你启动Nginx时报错未找到命令大概率是环境变量里没有Nginx。直接用完整路径执行即可比如/usr/local/nginx/sbin/nginx或者配置一个软链ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx自定义日志格式在调试反向代理时非常实用。默认的combined格式看不到转发细节我建议配置一个专门用于反代的日志格式log_format proxy_log $remote_addr [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent upstream_addr$upstream_addr upstream_status$upstream_status request_time$request_time upstream_response_time$upstream_response_time; access_log /var/log/nginx/access.log proxy_log;upstream_addr会记录实际被选中的后端服务器地址upstream_status记录后端的返回状态码upstream_response_time则是后端服务处理请求的耗时。通过这些字段前端一个500错误你能立刻分辨是Nginx自己返回的500还是后端服务真的崩了网络排查时间能减少一大半。线上排查的时候我会同时开两个终端一个tail -f看access.log一个tail -f看error.log。Nginx的error.log里有些报错信息非常经典比如connect() failed (111: Connection refused)表示后端服务没启动no live upstreams while connecting to upstream表示upstream里的所有后端实例都挂了。5.2 高频报错排查速查表我把实际运维中经常遇到的问题整理成了一张速查表方便你在搜索的时候直接定位现象可能原因处理方式nginx: [emerg] unknown directive proxy_pass配置写错层级或缺少对应模块检查是否写在了http/server/location之外connect() failed (111: Connection refused)后端服务未启动或端口错误在Nginx所在服务器执行telnet 后端IP 端口验证upstream timed out后端处理时间超过proxy_read_timeout调大proxy_read_timeout或优化后端性能刷新Vue页面404缺少try_files配置在location /里加try_files $uri $uri/ /index.html;页面有样式但图片全挂静态资源路径错误或缓存问题用浏览器开发者工具看请求路径调整root或alias502 Bad GatewayNginx无法连接后端检查后端进程、端口和upstream配置504 Gateway Timeout后端处理超时调大proxy_read_timeout或排查后端慢查询WebSocket连不上缺少Upgrade和Connection请求头补全WebSocket相关的proxy_set_headersub_filter不生效模块未编译或响应被gzip压缩编译时加--with-http_sub_module关闭gzip或设置Accept-Encoding identity修改配置后不生效没有重载或nginx -t失败执行nginx -t检查再执行nginx -s reload这里再单独提一个容易忽略的问题如果服务器同时装了硬件WAF比如山石WAF这类设备Nginx通常会被部署在WAF后面由WAF做第一层反向代理和流量清洗Nginx再做第二层反向代理分发到后端。排查问题时一定要先确认请求到底是从WAF转过来的还是直接打到了Nginx否则你会看到访问日志里所有来源IP都是WAF的内网地址误以为配置出了问题。5.3 安全加固清单别等被扫了才后悔Nginx反向代理作为公网入口安全治理必须认真对待。不需要什么高深技巧就几条基础操作做与不做差别很大。第一及时升级版本。我留意到近期安全公告中出现了针对F5 Nginx的CVE漏洞风险说明比如CVE-2025-1695这类编号风险描述指向Nginx的某些版本存在安全隐患。这类漏洞虽然没有大面积攻击事件但暴露出一个现实长期不升级的Nginx一旦被发现漏洞很容易被自动化扫描工具盯上。稳妥的做法是订阅Nginx官方安全公告定期更新到最新稳定版。如果生产环境无法频繁升级至少要从访问控制和最小模块原则两方面做好加固减少暴露面。第二隐藏版本号。默认配置下Nginx的响应头会暴露nginx/1.24.0这样的版本信息攻击者可以根据版本号精准搜索已知漏洞。在http块里加一行server_tokens off;第三限制请求体大小。反向代理后端的文件上传接口如果不做限制攻击者可以通过发送超大请求体消耗服务器资源。默认值是1M很多业务场景不够用建议按需调整比如client_max_body_size 10m;第四控制访问来源。对管理后台这类敏感路径可以同时限制IPlocation /admin/ { allow 192.168.1.0/24; deny all; proxy_pass http://127.0.0.1:8080; }第五基本的超时保护。proxy_connect_timeout设置太大会让Nginx长时间保持无效连接设置太小又会影响后端启动较慢的容器。我一般设5秒到10秒后续再根据业务调整。5.4 可视化配置工具与日常运维效率很多人觉得写Nginx配置全靠记忆时间长了容易忘。如果项目规模不大又不想在服务器上反复改文件可以考虑用可视化配置工具。我试过Nginx Proxy Manager它提供Web界面支持域名添加、SSL证书申请、反向代理规则创建、访问控制等常用功能。对不熟悉命令行的朋友来说非常友好适合个人项目或小型团队。另外还有Nginx UI这类开源工具可以直接在网页上编辑配置文件、语法检查、启停服务效果类似。不过我能给的真心建议是工具可以用但核心的配置文件语法和逻辑不能丢。因为你始终会遇到工具无法覆盖的边界情况比如四层转发、复杂rewrite、动态模块加载等最终还是要回到命令行来解决。可视化工具更适合快速验证想法和托管边缘业务核心生产环境我仍然推荐手写配置加Git管理每次变更走版本控制出问题能快速回滚。最后补一句我的实际感受做了这么多年运维和全栈开发Nginx始终是我在部署任何Web项目时最先考虑的那一层。它的反向代理能力不复杂但组合起来能解决非常多实际问题隐藏内网结构、流量分发、静态资源优化、灰度发布、SSL终止、日志审计几乎每一项都离不开它。个人体会是想用好Nginx反向代理与其死记指令不如把请求链路这四个字放在脑子里。用户在浏览器输入地址到Nginx再到后端每一步发生了什么日志里能看到什么响应头里带着什么把这些想清楚了配置Nginx就成了一件很自然的事。希望这篇内容能帮你少走一些弯路也欢迎在实践中多折腾多验证——毕竟配置文件这种东西只有亲手改过、踩过坑才能真正记住为什么这么写。