CDN、负载均衡、反向代理:核心区别与Nginx实战指南
发布时间:2026/10/8 9:15:21 锦皓数字建站

做运维这几年被问得最多的一个问题就是CDN、负载均衡、反向代理这三个到底有什么区别。每次都有朋友拿着 Nginx 配置来问说我明明加了反向代理为什么还是很卡我套了 CDN为什么源站 IP 还是暴露的。说实话这三个词经常被放在一起是因为它们都站在用户和后端服务器之间都涉及转发加速分发这些动作但各自的职责完全不同。今天我就不按教科书那套讲了准备用一台服务器、一个域名把三者的原理和实战串起来讲清楚。文章里会有可以直接抄走的 Nginx 配置、验证 CDN 是否生效的命令、以及我在排障时踩过的坑。适合刚接触 Web 架构、在 1Panel 或纯 Nginx 里配过反代但没搞懂背后逻辑的同学也适合想搞清楚线上链路每一层在干什么的人。1. 三个高频词到底分别解决什么问题1.1 反向代理站在用户和服务器之间的前台接待反向代理Reverse Proxy可以理解成一个公司的前台接待。用户不直接找后端员工应用服务器而是把所有请求统一递到前台由前台根据请求内容转交给对应的人。这个前台不生产内容但它决定了谁能见到后端、以什么方式见。为什么需要这个前台真实场景里理由很多隐藏后端细节后端可能是内网地址比如 192.168.x.x不能直接暴露公网。反代作为唯一入口替后端挡住所有直连尝试。统一入口与域名管理多个应用跑在不同端口8080、3000、11434用户不可能每个端口都记。反代可以把子域名或路径映射到不同端口外面看起来就是一个站点。附加能力集中下沉HTTPS 证书终止、访问控制、限流、日志、请求头改写这些都可以在反代层统一做不用塞进每个应用代码里。反向代理里最重要的配置是 proxy_pass也是最容易写错的一行。它的核心规则是location 匹配到的 URI 是否被保留取决于 proxy_pass 后面是否带了路径。举个例子。用户请求 https://example.com/api/v1/users# 带路径写法location 中的 /api 会被替换成 /v1 location /api { proxy_pass http://backend:8080/v1; } # 实际转发到 http://backend:8080/v1/users # 不带路径写法location 匹配到的完整 URI 原样转发 location /api { proxy_pass http://backend:8080; } # 实际转发到 http://backend:8080/api/v1/users这个细节我踩过太多次。之前帮同事排查接口 404查半天发现就是 proxy_pass 末尾多了一个斜杠导致路径前缀被吞掉所有接口都从 /api 变成了 /路由全乱。后来我养成了个习惯每次改完 proxy_pass先自己默念一遍带不带斜杠、带不带 URI再去 reload。1.2 负载均衡把流量分给多个后端那如果后端不是一台而是三台机器跑同一个服务呢这时候前台接待一个人忙不过来就需要一组分诊台——这就是负载均衡Load Balancing。负载均衡的核心逻辑很简单同一个入口地址背后的请求被按照某种策略分发到多台后端服务器上。每台后端分摊一部分流量单台挂了也有其他机器顶上这就是高可用最朴素的理解。注意负载均衡不一定是一个独立软件。Nginx 的反向代理模块本身就自带负载均衡能力你只要在 upstream 里写多个后端轮流转发这就已经是负载均衡了。所以很多人以为负载均衡 云厂商的 SLB/ELB其实 Nginx 配置里早就内嵌了这个功能而且对于中小流量场景完全够用不额外花钱。简单总结分工如果请求固定只去一台服务器那是反代如果请求按规则在多台服务器之间分发那就是在反代基础上叠加了负载均衡。1.3 CDN把内容搬到离用户更近的地方第三个是 CDNContent Delivery Network内容分发网络。它解决的是另一个维度的痛用户离服务器太远。打个比方你的源站在广州一个黑龙江的用户直接访问源站一个请求要跨大半个中国。就算链路很顺物理延迟也摆在那里。CDN 的思路是把静态资源图片、CSS、JS、视频提前复制到各地机房让用户从最近的节点拿数据而不是每次千里迢迢回源。关键动作是两个复制和就近。CDN 节点本质上是分布在全国甚至全球的缓存服务器用户请求会被 DNS 调度到离自己最近的节点节点上有缓存就直接返回没有缓存才回源站取一次然后缓存下来供后续请求用。所以 CDN 和负载均衡不是一回事负载均衡解决的是谁来处理CDN 解决的是从哪儿拿。生产环境里两者经常一起出现——静态资源走 CDN动态接口走负载均衡加反代各管各的。角色核心解决什么问题典型工具生活类比反向代理统一入口、隐藏后端、附加能力Nginx、Caddy、HAProxy公司前台负载均衡多后端分摊流量、故障转移Nginx upstream、云 SLB、LVS医院分诊台CDN内容就近分发、缓存加速Cloudflare、阿里云 CDN、腾讯云 CDN小区门口的便利店2. Nginx 反向代理实战从单站点到多站点2.1 最简配置看懂一个 server 块先来一个能直接跑起来的最简反代。假设我有一台服务器IP 是 1.2.3.4上面跑了两个服务一个 Node.js 应用监听 3000 端口一个静态博客放在 /var/www/blog。我只想给用户暴露一个入口 https://example.com并按路径区分/ 是博客/api 是 Node 服务。server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/certs/example.com.crt; ssl_certificate_key /etc/nginx/certs/example.com.key; # 静态博客Nginx 直接处理 location / { root /var/www/blog; index index.html; } # 动态接口转发给 Node location /api/ { proxy_pass http://127.0.0.1:3000; 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 做路由或生成链接不传的话后端拿到的是 127.0.0.1可能触发重定向到错误地址。X-Real-IP $remote_addr记录直连 Nginx 的真实客户端 IP后端日志里能直接看到用户 IP。X-Forwarded-For标准链路透传字段。$proxy_add_x_forwarded_for 会自动把现有 XFF 值和当前 remote_addr 拼接多层代理时也不会丢前面的 IP。注意反代之后后端看到的所有请求都来自 Nginx 的 IP。如果不透传这些头应用的访问日志、风控、限流全会失效。这是我过去几年见过最多的问题没有之一。如果你在 Windows 上装 Nginx配置思路完全一样只是启动方式不同。去官网下载 Windows 二进制包解压后修改 conf/nginx.conf然后用nginx.exe -t检查语法nginx.exe -s reload重载。生产环境不建议 Windows 长期跑 Nginx问题排查和性能调优都不如 Linux 顺手但本地开发练手完全没问题。2.2 多站点场景1Panel 与纯 Nginx 的两种做法现在很多人用 1Panel 管理服务器。它自带一个网站功能可以可视化创建站点、申请 SSL 证书、配置反向代理底层依然是帮你生成 Nginx 配置文件。它的逻辑很简单一个站点对应一个 server 块。在 1Panel 里给多个网站做反代最常见套路就是按域名拆站点在网站里分别创建 example.com 和 blog.example.com 两个站点。每个站点单独指定反向代理类型填入目标地址比如 http://127.0.0.1:3000。面板会自动帮你生成 server 块证书也在同一个页面申请和续期。按域名拆是最直观的方式多个站点互不影响出错也好定位。如果是纯手写 Nginx我强烈建议用 sites-available / sites-enabled 目录按站点拆分文件而不是把所有域名堆在一个 nginx.conf 里/etc/nginx/ ├── nginx.conf # 主配置只做全局设置 ├── sites-available/ │ ├── example.com │ └── blog.example.com └── sites-enabled/ # 里面放指向 sites-available 的软链接 ├── example.com - ../sites-available/example.com └── blog.example.com - ../sites-available/blog.example.com新增一个站点就是写配置 建软链 reload删除站点也不用小心翼翼去大文件里删块。我维护过十几个站点的机器单文件方案到后面会让你崩溃这个目录拆分法能救命。2.3 给本机 AI 服务做反代Ollama 实测案例Ollama 是现在很流行的本地大模型运行工具默认监听 11434 端口。如果你有台带 GPU 的服务器想给团队统一提供一个入口反代就很合适。配置如下server { listen 443 ssl; server_name ollama.example.com; ssl_certificate /etc/nginx/certs/example.com.crt; ssl_certificate_key /etc/nginx/certs/example.com.key; location / { proxy_pass http://127.0.0.1:11434; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # Ollama API 是流式输出必须关掉缓冲 proxy_buffering off; proxy_read_timeout 300s; } }两个关键点需要特别说一下一是 proxy_buffering off。大模型接口走流式生成SSE如果 Nginx 默认开启缓冲它会攒一批数据再一次性返回前端看到的就不是逐字输出而是卡一下、刷一片体验非常糟糕。开了 off 之后数据能第一时间流向客户端。二是超时时间。大模型生成可能要几十秒甚至几分钟Nginx 默认的 proxy_read_timeout 是 60 秒很容易触发 504。我一般调到 300 秒以上具体看模型大小和显存情况。如果是给公网访问记得在 Nginx 层加鉴权最简单的做法是 HTTP Basic Authhtpasswd。别把本地 Ollama 裸暴露到公网别人可以白嫖你的 GPU 算力这个坑我见过不止一次。3. 负载均衡实战从单点故障到高可用3.1 常用负载策略轮询、最少连接、IP 哈希Nginx upstream 支持多种策略日常够用的核心就三个轮询默认round-robin请求按顺序轮流分给每台后端。适合各后端性能相近、请求处理时间差不多的场景配置最简单。least_conn谁当前连接数少就分给谁。适合请求耗时差异大的场景比如有的接口 10ms有的要 2 秒轮询会导致慢请求堆积在某台机器上。ip_hash按客户端 IP 做哈希同一个 IP 固定到同一台后端。适合需要会话保持、后端又无法共享会话的场景。还有 weight 权重参数可以在任意策略基础上给不同机器不同比重。比如新机器性能好权重给 3老机器给 1流量就大致按 3:1 分配。upstream backend_app { least_conn; server 10.0.0.11:8080 weight3; server 10.0.0.12:8080 weight1; server 10.0.0.13:8080 backup; }上面这段里 backup 表示备用节点只有前两台都不可用时流量才会切到 backup。这在同城容灾、灰度发布场景里很实用——平时它不接流量关键时刻顶上去。补充一点least_conn 判断的是连接数不是 CPU 负载或响应时间所以它只是一个更好一点的默认选择不是银弹。如果各后端处理能力差异很大更稳的方案是用云厂商的负载均衡配合健康检查和权重动态调整毕竟 Nginx 原生策略在复杂场景下确实有些单调。所谓低开销负载均衡对于中小站点来说Nginx upstream 就是性价比最高的方案零额外组件、零额外费用几十行配置就能拿到基本的高可用能力。我见过很多一天几千请求的小站架一台 SLB 花了钱却发挥不出价值真不如先在 Nginx 层用起来。3.2 健康检查与故障转移是怎么工作的负载均衡能实现高可用靠的是自动剔除故障后端。Nginx 原生的检查是被动式的它不主动去探测而是根据转发请求后的表现来判断。关键参数就两个upstream backend_app { server 10.0.0.11:8080 max_fails2 fail_timeout10s; server 10.0.0.12:8080 max_fails2 fail_timeout10s; }含义是在 10 秒内如果这台后端有 2 次转发失败连接超时、返回 502/504 等Nginx 就认为它挂了接下来 10 秒内不再向它转发10 秒后重新试探请求成功则慢慢恢复。被动检查的优点是零额外请求成本缺点是发现问题时已经有部分用户踩到了失败请求。主动健康检查需要 Nginx Plus 或额外编译模块比如 nginx_upstream_check_module云厂商的负载均衡一般是主动探测这也是很多人最终换云产品的原因。如果是自己维护我建议被动检查参数配合应用日志告警一起用负载均衡负责在阈值内快速摘除故障节点告警负责通知你去修机器。两者结合比单纯依赖 Nginx 的检查机制靠谱得多。3.3 会话保持与真实 IP 透传负载均衡之后最大的两个工程问题就是会话和 IP。会话问题。如果后端是 PHP 原生 session 或某些老框架用户第一次请求落在 A 机第二次落在 B 机B 机没有这份 session用户就被踢下线了。解决思路有三个方向让同一用户固定落在同一台机器ip_hash 或 sticky cookie这是最简单的治标把 session 外置到 Redis所有后端共享这是更彻底的治本改造为无状态服务JWT 之类从根上消灭服务端会话。我的建议是新项目直接走第三条路老项目如果改不动优先把 session 挪到 Redisip_hash 只作为临时止血手段。因为它会导致某台机器热度不均一台挂了会集中甩掉一批用户。IP 问题。后端需要知道真实客户端 IP不能只看到负载均衡的 IP。Nginx 做负载均衡时同样要透传 X-Forwarded-For而且我们还能让 Nginx 把真实 IP 直接替换进 $remote_addrset_real_ip_from 10.0.0.1; # 上行设备的地址按需调整 real_ip_header X-Forwarded-For;set_real_ip_from 的含义是来自这个地址的请求XFF 里的第一个 IP 可以被信任为真实客户端。这个配置在多层代理、WAF 负载均衡的架构里几乎是必需的否则应用看到的全是一个奇怪的代理 IP限流和日志分析都会乱套。我排查过一次线上限流误伤最后发现就是没配 real_ip所有用户被算成同一个入口 IP触发了几千次误拦。4. CDN 加速原理与验证手段4.1 DNS 调度 边缘缓存CDN 为什么快CDN 的核心机制就两步。第一步是 DNS 调度。普通域名解析时DNS 服务器直接返回源站 IP接入 CDN 后域名 CNAME 到 CDN 厂商的调度域名调度系统根据用户出口 IP、位置、运营商返回一个最优节点的 IP。这就是为什么不同地区的用户 ping 同一个 CDN 域名得到的 IP 可能完全不一样。第二步是边缘缓存。节点收到请求后如果本地有对应缓存副本且没过期直接返回这叫缓存命中如果没有就回源站取一次拿回来缓存同时按响应头里的 Cache-Control 决定缓存多久。图片、CSS、JS 这类静态资源非常适合缓存命中率上去之后源站负载可以下降一个数量级。所以判断一个服务适不适合套 CDN核心就看两点内容是否可缓存静态还是动态、用户分布是否很广。如果用户都在公司局域网套 CDN 意义不大如果用户遍布全国CDN 带来的体感提升会非常明显尤其图片和视频类站点。4.2 怎么查一个域名到底有没有走 CDN如何查询本地 CDN这个场景我遇到过很多次。想确认的通常是两件事这个域名接入 CDN 了吗本地解析到的 IP 到底是不是 CDN 节点第一招看 DNS 解析结果dig example.com nslookup example.com如果解析出来的是多个不同 IP或者解析速度极快大概率是 CDN。更明显的是同一个 CDN 域名你在手机流量和家里宽带的网络下分别解析拿到不同 IP这就是本地调度在起作用。第二招看响应头。用 curl 拉一次请求检查返回头curl -I https://example.com/static/app.js常见的 CDN 特征字段有Via、X-Cache、X-Cache-Lookup、Age、X-Via、X-Served-By 等。比如有些 CDN 会返回 Via: cache某些厂商直接标 X-Cache: HIT 或 MISS。Age 字段表示缓存已经存在了多少秒也是重要参考。第三招看节点归属。把解析出来的 IP 拿到 IP 库比如 ipip.net 或本地 whois查归属如果厂商名明显是 CDN 服务商、或者 AS 号是云厂商的基本可以确认。还可以对比 TTLCDN 域名的 DNS TTL 一般不长几十秒到几分钟因为节点调度的 IP 可能随时变源站 A 记录的 TTL 动辄几小时甚至一天。提醒一句CDN 调度结果和你所在的网络环境强相关。在公司查到的节点和你家里手机 4G 网络查到的未必一致别因为 IP 不同就怀疑配置有问题。4.3 回源、缓存刷新与版本更新策略CDN 最能坑人的地方是更新了但用户看不到。原因很简单旧文件还在节点缓存里用户请求被缓存直接命中压根没到源站。我的习惯是双管齐下。第一从源头控制缓存时间。静态资源按类型区分设置 Cache-Control带 hash 的文件app.abc123.js可以设一年因为文件名变了就是新文件天然防缓存不带 hash 的入口文件index.html设 no-cache 或很短的 max-age保证每次都回源确认图片按内容性质设置比如 7 天到 30 天频繁换图的运营位单独处理。第二版本更新后主动刷新 CDN 缓存。各家 CDN 控制台都有刷新缓存 / URL 预热功能把更新过的 URL 提交刷掉。批量刷新时注意配额我有一回误操作把整个目录刷新了结果高峰期回源流量直接把源站打满源站 Nginx 报警刷屏这个教训很深刻。排查更新为什么没生效时别只盯着源站。按顺序查先用 curl 本地直连源站确认新内容没问题再看 CDN 控制台有没有缓存记录最后看响应头里的 Age 字段。如果 Age 很大就是缓存没刷或者 TTL 太长跟你的源站代码一点关系都没有。5. 高频坑位排查实录证书、WebSocket 与缓存5.1 浏览器报 err_cert_common_name_invalid 怎么办这个报错在反代场景里非常常见尤其是自己签证书或套了多层代理时。英文直译是证书通用名无效意思是浏览器请求的域名和服务器返回证书里写的域名对不上。最常见的三种成因证书只签了 www.example.com用户却用 example.com 访问。现在主流 CA 一般同时签两个域名但老证书或自己签的很容易漏。反代转发到 https 后端而后端证书是自签的或域名对不上。浏览器到 Nginx 这段证书没问题但 Nginx 转发时默认会校验上游证书校验不过报错就会在浏览器侧暴露出来。直接拿 IP 访问一个只签了域名的证书。修复思路按层来先查入口证书的 SAN 是否覆盖所有对外域名openssl x509 -in example.com.crt -noout -text | grep -A1 Subject Alternative Name如果缺域名找 CA 重新签或者补全证书。再看后端是 https 且证书不可信、域名不一致的情况。内网环境如果确认可信可以让 Nginx 转发时不校验上游证书location / { proxy_pass https://10.0.0.11:8443; proxy_ssl_verify off; proxy_ssl_server_name on; proxy_ssl_name 10.0.0.11; # 指向上游证书实际签发的域名或 IP }注意proxy_ssl_verify off 只解决转发链路的校验问题浏览器到你 Nginx 这一段仍然必须持有有效证书。生产环境尽量给上游也用受信任证书别长期挂着关闭校验否则哪天上游证书过期你会在半夜被报警电话叫醒。我在 Windows 上练手反代时被这个报错卡过一晚上最后发现就是后端自签证书没加 SAN和 Nginx 配置半毛钱关系都没有。所以遇到证书报错先分清楚是浏览器到 Nginx 这一段还是 Nginx 到上游这一段再动手改配置。5.2 WebSocket 反代必须补的三行配置给 WebSocket 服务做反代时很多人把普通 HTTP 配置直接搬过来结果前端一直连接失败。原因是 WebSocket 握手需要 Upgrade 请求头而 Nginx 默认不传递这个头。标准做法是配合 map 指令实现map $http_upgrade $connection_upgrade { default upgrade; close; } server { # ... location /ws/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 3600s; } }解释一下原理$http_upgrade 是客户端发来的 Upgrade 头的值如果是 WebSocket就是 websocket。map 块把它的值转换到 Connection 头没有升级请求时保持 close避免污染普通 HTTP 请求。proxy_read_timeout 也要拉长因为 WebSocket 长连接可能好几分钟不说一句话默认 60 秒内没有任何数据交换就会断开前端就会频繁掉线重连。这个坑隐蔽在普通请求看起来都正常只有 WebSocket 用户反映一会儿就断。如果你加了上面三行配置还断就抓包看是不是中间还有一层防火墙或四层负载均衡在掐长连接。5.3 更新不生效、偶发超时的通用排查顺序最后分享一个通用的排查习惯在 CDN、负载均衡、反向代理三类场景里都适用。不管现象是更新不生效还是一会儿通一会儿不通我都按这个顺序来先确认问题在哪一层。本地直接 curl 源站 IP绕过 DNS测试如果也有问题先修应用如果正常问题在链路。再确认 DNS 解析的最终结果。dig 看是不是被 CDN 接管解析到的 IP 是不是预期的节点。然后看中间层日志。Nginx 的 access log 能看到请求是否到达、上游是哪台、返回什么状态码CDN 控制台的日志能看到命中与回源情况。最后才动配置。改配置前先备份改完 reload 并保留旧配置目录方便随时回滚。这个习惯帮我避免了很多次瞎改一通、越弄越糟的情况。尤其是三层都配了的时候直接在源站和浏览器之间来回猜效率极低按层切分问题通常能在十几分钟内定位。有一次线上偶发 502我按这个顺序查到是某台后端机器的 keepalive 连接被防火墙静默断开Nginx 复用连接失败导致定位过程不超过十分钟而之前同事在应用代码里找了两天都没头绪。我个人还有一个小但很值钱的习惯给每个域名建一份链路档案记清楚从 DNS 到 CDN、到负载均衡、到 Nginx、再到后端应用每一层用的什么产品、证书什么时候到期、上次变更是什么时候。平时不觉得有用一旦线上出问题这份档案能帮你直接砍掉一半排查时间。CDN、负载均衡、反向代理这三个词分开看原理都不复杂复杂的是它们叠加在真实链路里之后的状态。把每一层都想成一个人、一件事问题来了按层拆你就不会慌。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。