Nginx反向代理与负载均衡实战:从入门到部署的完整指南

发布时间:2026/10/16 15:20:21
Nginx反向代理与负载均衡实战:从入门到部署的完整指南 刚接触 Nginx 的时候我也以为它不过是 Apache 的替代品改改端口、挂个静态页面就完事了。真正让我意识到它是接入层大脑的是一次给后端服务做流量入口改造那会儿项目还没上容器编排后端服务拆成了好几个实例一到晚上高峰就有人反馈接口特别慢某个日志导出功能能把整台机器的 CPU 打满所有请求一起遭殃。加机器容易可流量怎么分分到哪台某一台挂了难道让用户直接看 502这就是 Nginx 最擅长的三件事反向代理决定请求往哪转发负载均衡让多台服务器分担压力静态资源服务把图片、JS、CSS 这些高频请求从后端进程里彻底解放。这篇就来把从入门到部署一条线讲透不整虚的所有配置都是我自己在项目里验证过能跑的也会把文档里不写、只有踩过坑才知道的细节一并说清楚。1. 先搞清楚 Nginx 到底替你干了啥1.1 反向代理请求先到我这里再由我分发反向代理这个名词听起来抽象打个比方就明白了。你点外卖时不会直接去后厨找厨师而是先下给平台平台根据你点的菜分配给对应档口的厨师。Nginx 就是那个平台客户端只管访问 NginxNginx 再决定把请求转发给哪台服务器上的应用。它的实际价值不只是转一手而是能把后端服务隐藏起来。客户端只知道 Nginx 的地址完全不知道后面有几台服务器、分别是什么 IP。这对安全来说很友好同时也让你可以随时在后端加机器、迁移服务客户端无感知。1.2 负载均衡把流量摊开别让一台服务器累死一台后端服务再强也有扛不住的时候。常规解法是横向扩容多加几个实例一起干活但加完之后流量分配就成了新问题不能让一个实例闲着另一个实例却忙不过来吧Nginx 的负载均衡就是这么干的它维护一个后端服务列表把进来的请求按策略分发给各个实例等于是个智能分流闸口。后面我会详细讲轮询、权重、IP 哈希这些策略各自的适用场景。1.3 静态资源不该后端干的活别让它干很多新项目会把图片、CSS、JS 直接放在后端框架里由应用进程读取并返回。开发阶段没问题一上生产就露馅这些请求占用的带宽和连接数足足能拖垮业务接口的响应速度。Nginx 处理这类静态文件是纯内核级 IO 操作效率远高于应用进程。把静态资源分离出来由 Nginx 托管是部署阶段最划算的优化手段几乎没有成本但效果立竿见影。后面的配置章节会给到可以直接抄的写法。2. 从安装到第一个能跑的页面2.1 安装前先弄清目录结构不同发行版的安装方式略有差异但配置结构大同小异。我自己最常用的是 Debian/Ubuntu 系sudo apt update sudo apt install nginx -yCentOS/RHEL 系则是sudo yum install nginx -y装完之后先别急着改配置花两分钟看一眼目录后面所有排错都靠这个认知/etc/nginx/nginx.conf主配置文件负责全局设置比如 worker 进程数、日志路径、HTTP 模块加载。/etc/nginx/conf.d/存放站点的独立配置我在部署项目时习惯每个站点建一个 conf 文件互不干扰。/etc/nginx/sites-enabled/、sites-available/Debian 系特有的站点启用目录软链过去才生效。/var/log/nginx/access.log、error.log访问日志和错误日志生产排错的第一现场。/usr/share/nginx/html/默认静态页面目录装完浏览器访问服务器 IP 能看到欢迎页说明安装成功。新手最容易犯的错直接改 nginx.conf 里的 server 块结果管理系统模板一升级配置全没了。多站点场景下把配置拆到 conf.d 目录里管理是更规范的做法。2.2 最小可用配置长什么样清理掉默认配置新建一个干净的站点配置文件server { listen 80; server_name _; root /var/www/html; index index.html; }把任意一个 index.html 放到/var/www/html下面执行nginx -t nginx -s reloadnginx -t这条命令我每次改完配置都会跑一遍它的作用是帮你检查语法有错误会在命令行直接报出来而不是等 reload 才发现。配置无误后浏览器访问服务器 IP能看到你的页面就算跑通了。这一步是整个 Nginx 学习路径里最基础的闭环后面所有复杂功能都是在server块里叠加location、upstream等指令。3. 反向代理配置实战核心就一个 proxy_pass3.1 proxy_pass 加不加斜杠效果差很远反向代理最核心的配置就是proxy_pass。但这个指令有个让无数人栽跟头的细节后面的 URL 带不带 URI 路径转发行为完全不同。先看带斜杠的写法location /api/ { proxy_pass http://127.0.0.1:8080/; }请求/api/user/list时Nginx 会把/api/这个前缀替换成/转发给后端的是/user/list。再看不带斜杠的写法location /api/ { proxy_pass http://127.0.0.1:8080; }同样请求/api/user/list转发给后端的是完整路径/api/user/list。这两者的区别很多时候就是接口 404和一切正常之间的区别。我当时在一个项目里就踩过这个坑后端接口路径本身带着/api前缀我却用了带斜杠的 proxy_pass结果所有请求都在后端报 404排查了半天才发现是前缀被替换掉了。记法很简单只要看到 proxy_pass 的地址里带了路径哪怕是/它就对 location 的路径做了替换不带任何路径就是原样转发。3.2 请求头、Host 和真实 IP 的传递代理转发之后后端收到请求时很多东西已经变了。最典型的两个一是 Host 头二是客户端真实 IP。如果不加处理后端拿到的 Host 是 Nginx 自身的地址部分后端框架做 URL 拼接、重定向时会出错。传递原站点的 Host 头proxy_set_header Host $host;如果是多层代理场景还需要把客户端的真实 IP 透传下去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;X-Forwarded-For是每经过一层代理就追加一个 IP后端如果想拿到最原始的用户 IP取第一个值就行。有的后端框架比如需要校验用户真实来源的功能没拿到这个头权限判断会出幺蛾子所以这几行配置应成为反向代理的默认动作。3.3 WebSocket 代理和超时控制现在不少项目都用了 WebSocket 做实时推送、聊天功能Nginx 也能代理。默认的 HTTP 代理对 WebSocket 支持并不好需要显式升级协议location /ws/ { proxy_pass http://backend_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 秒客户端一段时间没发消息连接就被 Nginx 掐断表现就是总是莫名掉线。这个参数我一度调到 3600 秒基本等同于服务端超时交由业务逻辑自己控制。反向代理里的超时参数还有三个容易被忽略proxy_connect_timeout与后端建立连接的超时默认 60 秒一般调到 5 到 10 秒就够。proxy_read_timeout两次读取之间的间隔超时不是总耗时注意理解。proxy_send_timeout数据发送的超时。后端如果偶尔有慢接口别盲目把proxy_read_timeout调大先分清是连接慢还是响应慢对症设置才靠谱。4. 负载均衡多台机器怎么均分流量4.1 upstream 是什么配置骨架负载均衡在 Nginx 里靠upstream块实现它定义一组后端服务器然后在proxy_pass里指向这组服务器的名字。整体骨架如下upstream backend_servers { server 127.0.0.1:8081; server 127.0.0.1:8082; } server { listen 80; location /api/ { proxy_pass http://backend_servers; } }这样所有/api/开头的请求就会按策略分发到 8081 和 8082 这两台后端了。upstream里定义的服务器可以是本机不同端口也可以是局域网内不同机器甚至可以是混合组合。另外有个细节值得讲upstream的名字不能跟 server 块里的域名、IP 有歧义否则 Nginx 会把proxy_pass http://xxx;当成请求一个域名来处理而不是负载均衡。4.2 策略选择轮询、权重、IP 哈希、最少连接默认情况下Nginx 采用加权轮询两台服务器各 50% 的流量适合所有后端实例性能相近的场景。但真实环境里机器性能往往参差不齐就需要权重upstream backend_servers { server 127.0.0.1:8081 weight3; server 127.0.0.1:8082 weight1; }这个配置表示每 4 个请求里3 个分给 80811 个分给 8082。我刚用权重的时候总觉得权重越大越好其实不是权重应该和你后端实例的处理能力匹配比如 8081 内存多、CPU 强就给它 3。如果业务对会话保持有要求比如用户登录后 session 存在某台后端本地就需要ip_hashupstream backend_servers { ip_hash; server 127.0.0.1:8081; server 127.0.0.1:8082; }ip_hash会让同一个客户端 IP 的请求始终分配到同一台后端服务器。但这里有两个前提你要知道一是客户端可能经过代理导致 IP 变化二是某台后端挂掉时它身上的那部分用户会被重新哈希到其它机器会话还是可能丢失。所以它能缓解问题不能根治真正要解决会话一致还是得靠共享存储比如集中存储 session。还有least_conn适合请求耗时差异大的场景它会优先把请求分给当前活跃连接数最少的后端upstream backend_servers { least_conn; server 127.0.0.1:8081; server 127.0.0.1:8082; }这个策略在慢接口占比高的项目里表现很好我后来把日志导出这类耗时接口拆分出去后就用这个策略做了整体分发。4.3 后端挂了怎么办健康检查与故障转移负载均衡不能只负责分还要负责筛。某个后端实例端口挂了、进程假死了Nginx 如果还往它那边发请求用户就会看到 502、504。Nginx 自带的被动健康检查主要体现在两个参数上upstream backend_servers { server 127.0.0.1:8081 max_fails3 fail_timeout30s; server 127.0.0.1:8082 max_fails3 fail_timeout30s; }max_fails指在fail_timeout时间内最多失败几次达到这个次数后Nginx 会在接下来的fail_timeout时间内把该服务器标记为不可用不再分发请求。这个机制是被动的因为它是靠真实请求的失败来发现故障的成熟高效但不是说有主动探测所以故障发现会有延迟。如果要更主动的健康检查社区里有第三方模块能做主动探测但默认安装一般不含需要编译时自行加入配置复杂度和运维成本都会上升。我的建议是小规模场景用max_failsfail_timeout足够规模大了之后再考虑接入独立的注册中心和服务发现机制来替代 Nginx 的被动检查。5. 静态资源这块Gzip、缓存和防盗链5.1 静态资源缓存策略怎么设置客户端访问静态资源时浏览器会带着缓存头来确认资源是否过期。Nginx 配置缓存主要用expires和Cache-Control。最简单的缓存配置是这样的location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp)$ { expires 30d; add_header Cache-Control public, no-transform; }expires 30d会让 Nginx 在响应头里自动带上Expires和Cache-Control: max-age2592000。浏览器在 30 天内再次请求同样的 URL直接使用本地缓存不再访问服务器。但这里有个坑我必须提醒缓存设了之后如果你更新了 JS 或 CSS 文件但文件名没变用户的浏览器会一直用旧版本出现改了代码但线上没生效的假象。业界标准做法是打包时给文件名加内容哈希比如app.8f3a2c.js这样每次发版文件名都不同旧缓存自动失效。如果没条件改构建流程可以手动给静态资源 URL 加版本号参数也能应急破缓存。5.2 Gzip 压缩配置要点静态资源里的 JS、CSS、JSON 都是文本压缩后体积能降到原来的三分之一左右。开启 Gzip 的配置如下gzip on; gzip_vary on; gzip_comp_level 6; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript application/xml image/svgxml;gzip_comp_level是压缩等级范围 1 到 9等级越高体积越小但 CPU 消耗越大。实测下来 6 是一个性价比比较高的值图片这类已经压缩过的二进制文件完全不需要再 Gzip加了反而白费 CPU。gzip_min_length 1024表示只有超过 1KB 的内容才压缩很关键因为对几十字节的小响应压缩成本可能比传输节省的还高。另外现在主流的 HTTP/2 环境下服务端依然可以做 Gzip两者不冲突。5.3 防盗链与其它细节静态资源被其它网站上的人直接盗用一直是比较烦的问题特别是有带宽消耗的项目盗链多了就是白烧流量。防盗链的原理比较简单请求头里带上了来源页面的RefererNginx 判断这个值是否来自白名单域名。location ~* \.(gif|jpg|jpeg|png|bmp|webp)$ { valid_referers none blocked server_names *.example.test; if ($invalid_referer) { return 403; } }none表示允许无 Referer 的请求比如直接在地址栏输入图片 URLblocked表示 Referer 被隐私工具擦掉的情况server_names是允许自己的域名。这里要留个心如果用户用隐私模式或某些浏览器插件访问Referer 可能为空配置得太严会误伤正常用户。防盗链更像是一个防君子不防小人的手段真要防下载量级盗链得上业务层的签名校验Nginx 层够用但不要过度依赖。6. 认认真真部署一个项目从代码到线上6.1 规划目录和整体架构到实际项目部署这一步不再是一个简单页面而是前后端分离、多后端实例的整体方案。我以一个典型项目为例前端构建产物在dist目录后端有 8081 和 8082 两个实例项目里还有独立的static资源目录。目录规划建议/var/www/app/dist前端打包产物Nginx 作为静态站点提供。/var/www/app/static项目上传的图片、文件等由 Nginx 直接托管。后端实例跑在127.0.0.1:8081和127.0.0.1:8082。静态域名和 API 域名可以共用 80 端口。这个结构下 Nginx 的职责分得很清楚根路径走前端页面/api走反向代理到负载均衡组/static走本地静态文件。每个 location 各管一摊互不干扰扩展性也好。6.2 一个可以抄的完整配置把上述架构落地成一个完整的虚拟主机配置考虑到前后端分离场景里的 SPA 路由比如前端用了 Vue Router 的 history 模式还需要try_files做路由回退upstream app_backend { least_conn; server 127.0.0.1:8081 max_fails3 fail_timeout30s; server 127.0.0.1:8082 max_fails3 fail_timeout30s; } server { listen 80; server_name app.test; root /var/www/app/dist; index index.html; gzip on; gzip_vary on; gzip_comp_level 6; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript application/xml image/svgxml; location /api/ { 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_pass http://app_backend/; proxy_connect_timeout 5s; proxy_read_timeout 30s; } location /static/ { alias /var/www/app/static/; expires 30d; add_header Cache-Control public, no-transform; } location / { try_files $uri $uri/ /index.html; } }这里面有三个点需要注意一是location /api/里的proxy_pass http://app_backend/;带了末尾斜杠。按前面讲的规则这个配置会把/api/前缀替换成/再转发也就是说前端请求/api/user/list后端拿到的是/user/list。如果你的后端接口本身带着/api前缀就把这里的斜杠去掉改成proxy_pass http://app_backend;。二是try_files $uri $uri/ /index.html的作用是当请求路径在 dist 目录里找不到对应的真实文件时回退到index.html把路由交给前端框架自己处理。不加这一行刷新一个/user/123这样的前端路由页面Nginx 就会返回 404。三是alias /var/www/app/static/;末尾的斜杠。alias的作用类似文件路径映射如果少了末尾斜杠某些版本下拼接路径时会出现多层目录错位导致静态文件 404。写alias时我要求自己养成末尾必带斜杠的习惯少踩好些坑。6.3 部署后的验证流程配置写好之后不要急着直接上线按这个顺序验证一遍nginx -t先确认语法没问题然后 reload 让配置生效nginx -s reload访问项目首页确认页面能打开、样式和脚本能加载。用浏览器开发者工具的 Network 面板看一下静态资源应该返回 200 且带Cache-Control缓存头API 请求应该能被代理到后端。再找一个边缘场景测试直接访问一个/api下面的接口地址看返回的是后端数据还是 Nginx 的 404 页面访问一个前端路由地址并刷新确认try_files回退生效。如果这几项全过部署基本就稳了。我习惯再抓一下错误日志tail -f /var/log/nginx/error.log留意代理转发时有没有连接后端失败、权限不足之类的报错日志这东西上线前多看一眼效率提高不止一倍。7. 生产环境那些坑都是我亲测踩过的7.1 热加载不是重启别混淆了很多新手以为nginx -s reload会像 Tomcat 那样重启进程、短暂中断服务。实际上 reload 是平滑重载主进程会先检查配置语法没问题就启动新的 worker 进程再优雅地停掉旧的 worker已建立的连接不会立刻断开。但你也要明白一件事reload 不是万能的。某些第三方模块的配置或者对已有 worker 进程影响比较大的改动比如调整worker_processesreload 后不一定完全按预期生效稳妥起见重大变更之后看一眼访问日志、错误日志确认服务状态再放流量。7.2 client_max_body_size 太小导致的上传失败部署一个带文件上传功能的后台时会遇到用户传大文件一直报 413。默认client_max_body_size是 1MB超过这个体积的请求体直接拒收。解决方法是按业务量调大比如允许上传 50MB 的文件server { client_max_body_size 50m; ... }这个指令不仅要在后端的反向代理层设置如果前面还有一层 CDN 或网关那一层同样要调整否则请求永远到不了 Nginx。我在这上面吃过一次亏只改了应用服务器的限制没改 Nginx用户传大文件依旧失败排查了好一阵才发现是转发链路每一层都在限制体积。7.3 定位哪个环节慢的思路接口变慢时先从 Nginx 日志入手再逐层排查。访问日志里可以自定义记录响应时间比如在 log_format 里加入$request_timelog_format main $remote_addr [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for rt$request_time; access_log /var/log/nginx/access.log main;统计日志里rt值的变化规律如果rt很高说明 Nginx 到后端之间的响应很慢如果rt很低但页面体验还是慢那瓶颈大概率在数据库或前端代码。有一次项目上线后用户反馈接口特别卡我就是靠这个字段锁定了某一台后端实例的宿主机磁盘 IO 被打满直接绕开了盲目重启服务器的操作。7.4 权限问题静态文件 403 的隐性原因静态文件 403 不一定是权限真的错了很可能是 Nginx 运行用户通常是www-data或nginx对目标目录没有执行权限。Linux 的目录权限里r控制列表读取x控制能否进入目录少了执行权限照样 403。我之前把静态目录挂载到了一个新盘上目录属主是root权限是750Nginx 用户进不去页面里的图片全部 403排查半天才发现是目录权限层级的问题。排查命令直接看目录和上层所有目录的权限namei -l /var/www/app/static这条命令会把路径每一层的权限都列出来一眼就能看出卡在哪层。7.5 HTTPS 时代别忘了做跳转现在新部署的站点基本都直接上 HTTPS但如果历史原因下还有用户在用 HTTP 访问生产环境最好在 Nginx 层做统一跳转server { listen 80; server_name app.test; return 301 https://$host$request_uri; }把 HTTP 的 80 端口统一 301 到 HTTPS避免站内出现http 管一半、https 管一半的混乱。证书文件的管理跟 Nginx 的主配置解耦单独维护方便续期别把证书路径写死在某个站点 conf 里之后又到处复制后期换证书容易漏。做运维和部署这几年我最大的体会是 Nginx 其实不复杂复杂的是对整个请求链路的理解。你真正把它当成入口大脑来设计每个 location 干什么、每个 upstream 怎么配、每个响应头给不给心里都有一本账线上定位问题就会快很多。上面这些配置和坑都是我从实际项目里一件件攒出来的经验直接抄去用没问题但建议每一条都跑通、想明白再上生产。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询