ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Nginx 配置全解:从安装、反向代理到高并发调优与平滑升级

Nginx 配置全解:从安装、反向代理到高并发调优与平滑升级 说实话搜“nginx 配置”这个关键词搜出来的东西永远是一堆让你更懵的碎片。有人让你改 nginx.conf有人塞给你一大段 proxy_pass 让你照抄但很少有人把配置文件的组织逻辑、每个指令为什么这么写、改完以后出了异常该怎么定位讲清楚。我这些年从源码编译、业务分发、静态资源托管到平滑升级踩过不少坑这篇就把 nginx 配置这条线从头到尾捋一遍安装完之后文件目录怎么组织、反向代理怎么配才不容易出 502、静态资源和共享目录的 root 与 alias 差别在哪、高并发场景下 worker 和内核参数怎么调、生产环境升级二进制如何做到不掉线最后再把我排错时最常用的思路和日志定位方法分享出来。内容不追求“配置大全”而是把最常用、最容易踩坑的部分讲透适合刚接触 nginx 的运维、后端开发也包括那些已经在用 nginx 但一直没理清配置逻辑的人。1. 安装这件事决定了你后面少踩一半坑很多人在配置阶段被各种奇怪问题卡住根源其实在安装这一步就没选对路子。不同发行版、不同安装方式配置文件路径和权限模型完全不一样如果你照着网上一个教程去改结果文件路径都不对那自然怎么改都不生效。1.1 包管理器安装最快但要知道它帮你做了什么在 Ubuntu/Debian 上一条apt install nginx在 CentOS/RHEL 上一条yum install nginx装完服务就能跑。这种方式最大优势是配置目录很规范比如/etc/nginx/nginx.conf是主配置/etc/nginx/conf.d/里放自定义站点配置日志在/var/log/nginx/下systemd 管理方式也是现成的。但缺点同样明显发行版自带版本通常比较保守可能缺少你后面需要的模块比如--with-stream四层转发、--with-http_v2_moduleHTTP/2。遇到这种情况要么你装的是旧版本要么你根本不知道当前这个 nginx 编译时带了哪些模块。我的建议是装完第一件事先执行nginx -V这一步不会有人强调但它真的比什么都重要。-V会把编译参数、模块列表全部打印出来你能立刻确认手里这个 nginx 是不是你想要的版本有没有 SSL 模块有没有 stream 模块。很多人在配 HTTPS 的时候发现listen 443 ssl;直接报错就是因为源码编译时没带--with-http_ssl_module而自己还浑然不知。1.2 源码编译安装可控性最强也是最稳的生产选择如果是公司业务服务器我更推荐你自己编译安装。编译不是为了“显得专业”而是为了让你清楚知道自己装了哪些模块、二进制放在哪个路径、未来升级的时候怎么去操作。编译前先把依赖装齐Ubuntu 下一般是这几个包apt-get install -y build-essential libpcre3-dev zlib1g-dev libssl-devCentOS 下对应的是yum install -y gcc gcc-c pcre-devel zlib-devel openssl-devel注意新版 nginx1.25 之后开始用 PCRE2如果你装的是新版源码依赖包要换成libpcre2-dev或pcre2-devel否则 configure 阶段会报找不到 PCRE 库。我当年就被这个坑过一次——编译 1.25 的源码系统里只有 pcre 没有 pcre2卡了半天才发现是版本配套问题。我的常用编译参数如下./configure \ --prefix/usr/local/nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_stub_status_module \ --with-http_gzip_static_module \ --with-stream \ --with-stream_ssl_module然后编译安装make -j$(nproc) make install这里我要重点说下--prefix参数。它决定了你未来所有配置文件的路径比如/usr/local/nginx/conf/nginx.conf。很多人喜欢默认不指定--prefix结果 nginx 被装到/usr/local/nginx倒也罢了但如果你在多个环境上分别用源码编译和包管理器安装nginx 的配置路径完全不统一后续交接非常痛苦。我个人的习惯是生产服务器一律源码编译到固定 prefix配合一套手写的 systemd service 管理不用发行版自带的启动脚本。源码安装的 nginx 没有自带 systemd 管理文件需要你手动写一个这是我一直在用的模板[Unit] Descriptionnginx - high performance web server Afternetwork.target [Service] Typeforking PIDFile/usr/local/nginx/logs/nginx.pid ExecStart/usr/local/nginx/sbin/nginx ExecReload/usr/local/nginx/sbin/nginx -s reload ExecStop/usr/local/nginx/sbin/nginx -s quit PrivateTmptrue [Install] WantedBymulti-user.target写好后放到/etc/systemd/system/nginx.service执行systemctl daemon-reload之后就能用systemctl start nginx、systemctl reload nginx来管理了。这套方式的好处是统一了管理入口也方便设置开机自启。1.3 Windows 下的 nginx 和 Linux 差异不小如果你只是本地开发或者测试Windows 版也能用。官网下载对应的 zip 压缩包解压后直接双击nginx.exe就能启动默认监听 80 端口访问http://localhost能看到欢迎页。但有几个点必须清楚。第一Windows 版没有 daemon 模型没有 Linux 上那种 master-worker 进程架构一个 nginx.exe 进程扛所有事性能表现远不如 Linux。第二nginx -s reload在 Windows 上虽然能执行但某些场景下配置不生效需要人工确认进程是否真的重启了。第三Windows 下要把 nginx 做成系统服务一般要借助 NSSM 或 WinSW 这类工具不然开机不会自动启动。综合来看Windows 上玩玩可以生产环境还是老老实实用 Linux。1.4 安装完之后立刻做的检查不管用哪种方式装装完都要按这个顺序做一遍基础验证nginx -t nginx -V ss -lntp | grep 80 curl -I http://127.0.0.1nginx -t是检查配置语法nginx -V看编译参数ss确认端口监听curl -I确认 HTTP 响应正常。四步走完你才拥有一个“确定没毛病的起点”后面改配置的时候出了问题才能往配置逻辑上排查而不是怀疑环境。还有一个容易忽略的有些 Ubuntu 系统默认开了 AppArmorCentOS 可能开了 SELinux这些安全模块会拦截 nginx 访问非默认目录。最典型的表现是配置没写错、nginx -t也通过但访问静态文件就是 403。遇到这种情况先执行getenforce看看 SELinux 状态如果是 Enforcing要么setsebool -P httpd_read_user_content 1放开权限要么把文件放到允许的目录下。2. 读懂 nginx.conf 的主线main、http、server 和 location 的协作逻辑配置 nginx 之前先要把配置文件的层次结构理解透。很多人一上来就盯着 location 里的各种正则结果连 server 和 location 之间的关系都没理清楚越改越乱。2.1 四个层级的职责分配我常拿“小区物业”这套比喻来讲main上下文是物业管理处管的是整个小区的公共事务比如 nginx 运行身份、worker 进程数量、PID 文件路径这些配置在最外层所有 server、http 都要受它约束。http块就是小区里的“公共设施条例”规定所有站点共用的行为比如 MIME 类型、日志格式、默认超时时间。所有 HTTP 相关的 server 块都必须嵌套在 http 块内部。server块对应一栋楼的门牌号。它通过listen指定端口、server_name指定域名来决定“哪个请求进哪栋楼”。一台 nginx 上可以定义几十个 server只要端口和域名不冲突就行。location块则是楼里的具体房间它根据请求 URI 的路径前缀或正则规则告诉 nginx“这个路径该执行什么动作”。location 可以指向静态目录也可以把请求转到后端服务。理解这层嵌套关系以后你拿到一份别人的配置文件至少不会再一头雾水。2.2 全局参数里最常见的几个“为什么”主配置里有一行user nginx;很多新手不知道它的意义。它的作用是设置 worker 进程的运行系统用户。如果进程以 root 身份跑一旦 nginx 或第三方模块有漏洞攻击者直接拿到 root 权限用它跑即便被攻破也只是低权限用户。但这也带来另一个问题nginx 对静态文件的访问权限取决于这个 user。你 web 目录的文件假如是另一个用户创建的权限没放够nginx 读不了就会 403。排 403 的时候除了看路径还要想一下 nginx 的运行用户和文件权限之间的关系。worker_processes auto;的意思是按 CPU 核心数启动 worker 进程。它不是越大越好因为每个 worker 都独立占用内存而且进程切换也会消耗 CPU。生产环境一般设为 auto 或者等于物理核心数。我有段时间贪心在 8 核机器上设了 32 个 worker结果高并发下性能反而下降后来仔细看了监控发现大量时间耗在进程调度上。worker_connections定义的是单个 worker 进程能同时打开的连接数上限包括与客户端的连接、与后端的连接、日志文件句柄等。所以整个 nginx 理论上最大并发连接数不是这两个值直接相乘涉及反向代理时每个请求通常要占用两条连接计算公式要留出余量。这个细节放到第 5 节再展开讲。2.3 include 机制没人会把所有配置堆在一个文件里刚学 nginx 的时候我也把所有 server、upstream 全部堆进 nginx.conf结果是文件越来越长每次改一个站点都胆战心惊生怕语法写错连累所有站点。后来我才发现 nginx 早就准备好了拆分配置的方案。默认的http块里通常有一行include /etc/nginx/conf.d/*.conf;这行是关键。它把conf.d目录下所有.conf文件都加载进来。你完全可以给每个服务单独建一个文件比如api.conf、web.conf、upload.conf互不干扰。改其中某一个文件后nginx -t检查的是全部但有且只有你改动的文件会被 reload。Debian/Ubuntu 系的官方 nginx 包还有一套sites-available/sites-enabled的约定可用配置放在sites-available然后在sites-enabled里建软链接来启用。这个设计是为了方便批量启停站点如果只是个人项目直接用conf.d就够了。2.4 server_name 和 location 的匹配优先级server_name对多域名非常重要。它的匹配优先级是精确匹配 通配符前缀*.example.com 通配符后缀www.example.* 正则~^www\.example\.com$ 默认 server第一个或显式指定的default_server。这个规则特别容易被忽略比如你同时定义了server_name api.example.com;和server_name example.com;如果访问的是api.example.com而前者恰好没写对请求就会落到后一个 server现象就是你明明配了接口转发结果返回的是 HTML 页面。location的匹配优先级同样是个坑。规则按以下顺序精确匹配优先然后^~前缀匹配如果命中就不再检查正则接着是正则~/~*按顺序匹配最后才是普通前缀匹配取最长者。很多人写location /api和location /api/v1却不知道前者其实会同时匹配/api/v1如果不了解“最长前缀优先”的规则配置出来的行为会很诡异。3. 反向代理配置给后端服务套上一层“门面”反向代理是 nginx 最核心也是最常见的用途。它的本质是客户端连的是 nginxnginx 再往后端转发请求后端拿到的请求看起来就像是 nginx 发出去的。这样做的好处很多客户端不知道后端真实地址安全性提高SSL 证书统一在 nginx 层终止后端不用关注证书还可以在后端集群间做负载均衡。3.1 一个可以直接抄的反向代理模板下面这个模板我用了很多年适合大多数 Web 应用upstream backend { server 127.0.0.1:8080; server 127.0.0.1:8081; keepalive 32; } server { listen 80; server_name api.example.com; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ; 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; } }这里每个 header 都不是凑数的。Host如果不设置后端收到的 Host 是backend或127.0.0.1:8080很多框架拿这个值生成绝对链接时会出错。X-Real-IP和X-Forwarded-For是把客户端真实 IP 传下去否则后端只能看到 nginx 的 IP日志里的客户端来源全是错的。X-Forwarded-Proto告诉后端请求原本是 HTTP 还是 HTTPS否则后端拿$scheme判断时会被 nginx 的普通连接误导。proxy_http_version 1.1和Connection 这两行配了对upstream里的keepalive 32才有效果。它能让 nginx 与后端之间保持长连接避免每个请求都重新建立 TCP 连接。性能测试里这两行通常能带来非常明显的 QPS 提升。3.2 proxy_pass 的路径行为最容易出 bug 的地方proxy_pass后面带不带路径行为完全不同。这是 nginx 配置里最容易踩的坑之一。# 不带 URI location /api/ { proxy_pass http://backend; }这种情况下nginx 会把原始 URI 原样转发给后端/api/user到了后端还是/api/user。# 带 URI location /api/ { proxy_pass http://backend/; }这种写法里nginx 会用替换规则把匹配到的/api/部分替换成proxy_pass后面的/。所以请求/api/user到后端就变成了/user。如果你的后端接口本来就定义在/api路径下却写了带/的 proxy_pass大概率会拿到 404。这里我的建议是同一套服务尽量只固定一种写法。一般推荐 location 后面不带路径、proxy_pass 也不带路径让两端路径保持一致减少心智负担。3.3 超时配置、缓冲和生产里常见的 502/504反向代理模式下常见的异常状态码其实都能从配置上找原因。502 Bad Gateway 通常是后端服务根本没起来、端口没监听、或者后端进程崩了。排查命令很简单ss -lntp | grep 8080 curl http://127.0.0.1:8080/health如果本机 curl 都没响应问题不在 nginx先修后端。504 Gateway Timeout 则多半是 nginx 等后端响应等太久了。默认的proxy_read_timeout是 60 秒如果你有一个慢接口比如导出报表这种耗时超过 60 秒nginx 就先放弃等待了。处理办法不是盲目把时间调到 600 秒而是先分清慢请求是不是合理的合理就调大location /export/ { proxy_read_timeout 300s; }不合理就先去优化后端调超时只是掩盖问题。还有个容易忽略的参数是proxy_buffering。默认开启时nginx 会等后端响应攒够再一次性回给客户端这对大多场景是好事但如果你做的是流式响应比如 SSE、实时日志就必须关闭缓冲location /stream/ { proxy_buffering off; }否则客户端会感觉到数据卡顿、一直收不到像是超时一样。3.4 WebSocket 和 HTTPS 终止WebSocket 的代理跟普通 HTTP 有些差异关键在于升级协议和超时。标准配置如下location /ws/ { proxy_pass http://ws_backend; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; }Upgrade和Connection头告诉后端这是一个 WebSocket 升级请求。proxy_read_timeout必须调大因为 WebSocket 连接本质上是长连接默认 60 秒超时会让连接频繁断开。HTTPS 终止就简单了在 server 块里加两行listen 443 ssl; ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/server.key;证书文件注意权限私钥一般要chmod 600否则 nginx 启动时会报权限过宽的错误。4. 静态文件和共享目录动静分离的关键细节很多人以为 nginx 配静态文件就是写个root指向目录实际用起来总是遇到 403、404甚至配置“看起来对”但访问的是别的目录。这里面的坑基本都出在 root 和 alias、以及目录浏览、访问控制这些细节上。4.1 root 和 alias一字之差路径天差地别这是 nginx 配置里最高频的混淆点。看这两个示例location /static/ { root /data/web; }访问/static/a.css时nginx 实际读取的文件路径是root 完整URI也就是/data/web/static/a.css。location /static/ { alias /data/files/; }访问/static/a.css时nginx 实际读取的是alias URI 去掉匹配前缀的部分也就是/data/files/a.css。简单来说root是把完整的 URI 拼在根路径后面alias是用你指定的路径替换掉 location 匹配到的部分。我见过不少新手文件放在/data/files下却用root /data/files;配了location /static/结果实际找的是/data/files/static/xxx白白多了一层目录于是 404。诊断这种问题时最快的办法是打开error.log里面会直接打印实际尝试的文件路径。4.2 共享文件和文件服务的配置实践“nginx 共享文件”这块本质是 nginx 直接暴露一个目录给用户下载或预览。最基础的做法是开启目录列表location /download/ { alias /data/share/; autoindex on; autoindex_localtime on; charset utf-8; }autoindex_localime on让列表时间显示为本地时区否则默认是 UTC用户看到的修改时间会差 8 小时。charset utf-8是为了让中文文件名不乱码。如果共享目录本身是 NFS 或者网络挂载盘还需要注意 nginx 的user配置对挂载点是否有读权限。很多网络文件系统会对 uid 做映射你在挂载端看到的权限和在 nginx 进程里的实际权限可能不一致表现就是列表能开但点进去下载时报 403。下载场景里sendfile on;基本是必开的。它让 nginx 直接通过内核的 sendfile 系统调用把磁盘文件发到网卡省掉了用户态内存拷贝大文件下载性能提升非常明显。同时可以加tcp_nopush on;它在 sendfile 开启时会将响应头和数据包合并发送减少网络小包数量。4.3 静态资源缓存和压缩访客体验立刻变好静态资源天然适合缓存。简单的配置是在 location 里加expireslocation /static/ { alias /data/static/; expires 7d; add_header Cache-Control public, max-age604800; }expires 7d会同时生成Expires头和Cache-Control: max-age604800浏览器就会在 7 天内直接使用本地缓存。如果你的资源带了版本号比如a.1.2.3.js缓存时间可以放心调长因为文件变了 URL 也会变。压缩方面gzip 是性价比很高的优化gzip on; gzip_static on; gzip_comp_level 5; gzip_min_length 1k; gzip_types text/plain text/css application/javascript application/json image/svgxml;gzip_static on启用后如果磁盘上已经有预压缩好的.gz文件nginx 直接发送它不重复压缩省 CPU。这里要注意别把图片格式加进gzip_typesJPEG/PNG 本身已经压缩过了再 gzip 白耗 CPU 还减不了多少体积。4.4 访问控制IP 白名单和基础认证共享目录如果不想对所有人开放可以做两层简单控制。第一层是 IP 白名单location /admin/ { allow 10.0.0.0/8; deny all; }注意deny all必须放在allow之后才有意义nginx 是按顺序匹配的先放行白名单其余全部拒绝。第二层是 HTTP Basic 认证location /private/ { auth_basic Restricted; auth_basic_user_file /etc/nginx/.htpasswd; }.htpasswd可以用openssl passwd -apr1生成也可以用htpasswd命令。文件内容格式是用户名:加密密码注意权限要设为600否则 nginx 或系统安全策略可能拒绝读取。5. 负载均衡与高并发调优worker 数量、upstream 策略和内核参数nginx 能扛高并发靠的不只是它性能好还要你会“把并发拆给多个进程、把请求合理分给多个后端”。5.1 upstream 的三种负载策略怎么选上一节模板里已经出现了upstream backend它就是后端服务器池。nginx 默认使用加权轮询每个请求按顺序轮流分发到各个后端。这种策略在请求处理时间差不多的场景下最公平。如果你的后端业务有缓存、有本地状态轮询会导致每个请求都换后端缓存命中率很低。这时候用ip_hash同一客户端 IP 的请求会始终打到同一个后端upstream backend { ip_hash; server 127.0.0.1:8080; server 127.0.0.1:8081; }least_conn则是把请求分给当前活跃连接数最少的后端适合后端请求耗时差异大的场景比如既有秒开接口又有重计算接口。least_conn的算法会在一定权重基础上选择连接数最少的节点适合大多数混合业务。这三种策略的适用场景总结如下策略适用场景典型问题默认轮询后端无状态、处理能力均衡缓存命中率低ip_hash需要会话保持、本地缓存某 IP 容易压垮单节点least_conn请求耗时差异大配置复杂理解门槛高5.2 后端健康检查不配置502 会打得你措手不及默认情况下nginx 只有在把请求转发给一个不可用的后端时才会通过max_fails和fail_timeout判断它“挂了”然后暂时不再向它转发。默认值是max_fails 1和fail_timeout 10s意味着只要失败一次该后端会在 10 秒内被标记为不可用。这个默认值对生产环境来说太敏感偶尔一次超时就把节点摘掉会造成不必要的抖动。我一般会把参数放宽upstream backend { server 127.0.0.1:8080 max_fails3 fail_timeout30s; server 127.0.0.1:8081 max_fails3 fail_timeout30s; }意思是 30 秒内失败 3 次才摘除节点。注意这只是被动健康检查——nginx 只在实际转发失败的时候才知道后端挂了。如果需要主动探测比如定期发健康检查请求得用 nginx 商业版或者结合第三方模块来实现日常使用中被动检查配合监控告警已经足够。5.3 高并发下的 worker 与内核参数协同调整先说并发能力的大致模型。假设worker_processes是 8worker_connections是 10240理论上最大并发连接约为 8 × 10240 81920。但做反向代理时每个用户请求会占用一条客户端连接和一条到后端的连接除非启用了 upstream keepalive所以实际能承载的并发请求数会打折扣大约是连接数的一半左右。这也是为什么我会在代理场景里把worker_connections设到 20480 以上而不是习惯性的 1024。worker_connections设大了之后Linux 系统层面也要跟着调否则 nginx 会报“too many open files”或者连接队列直接溢出不响应。在/etc/security/limits.conf里把 nginx 用户的可打开文件数调大nginx soft nofile 100000 nginx hard nofile 100000然后在内核参数文件/etc/sysctl.conf里追加net.core.somaxconn 65535 net.ipv4.tcp_max_syn_backlog 65535 net.ipv4.ip_local_port_range 1024 65535改完执行sysctl -p生效。somaxconn和tcp_max_syn_backlog控制的是 TCP 连接队列的长度高并发下如果队列太短新连接会被直接丢弃用户端表现就是长时间连接不上。ip_local_port_range影响的是 nginx 主动向后端发起连接时能用的本地端口范围反向代理场景大量后端连接时很容易耗尽端口这个范围越大越好。另外别忘了在events块里显式指定use epoll;。Linux 下 epoll 是目前效率最高的事件模型nginx 在 Linux 上会默认使用 epoll但显式写出来一方面是明确意图另一方面避免某些环境自动探测出了意外。6. 平滑升级和回滚生产环境更换二进制的完整流程“nginx 平滑升级”这个热搜词我猜点进去的人大部分是遇到了重启会打断长连接的场景。确实业务运行中你不能贸然杀掉 nginx 重启WebSocket 连接会断、正在下载的文件会失败、在线用户会被强制踢下线。解决办法是让旧的 worker 进程处理完手头的请求再退出新的 worker 接替工作整个过程对用户几乎无感知。6.1 先分清楚 reload、restart、平滑升级三件事nginx -s reload是我日常改配置最常用的一步。它的本质是给 master 进程发送 HUP 信号master 会重新加载配置文件然后启动新的 worker 进程旧的 worker 进程会被优雅关闭处理完当前请求后才退出。所以改配置用 reload 就够了不会中断已有连接。nginx -s restart严格来说不存在这个命令常见的是systemctl restart nginx或手动 kill 后再启动。这会导致 master 和 worker 全部退出所有连接直接断开生产环境要尽量避免。平滑升级则是在你换了 nginx 二进制文件后如何做到不重启 master 就切换新版本。这是通过信号机制实现的比 reload 更进一层。6.2 平滑升级的完整操作步骤假设你现有的 nginx 在/usr/local/nginx现在要升级到 1.26.x 版本完整流程如下。第一步备份旧二进制cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old第二步编译新版本注意--prefix要和旧版本一致./configure --prefix/usr/local/nginx ...你的原有参数 make -j$(nproc)编译完成后不要急着make install。make install会直接覆盖二进制和配置文件如果你没备份出问题想回滚就麻烦了。更稳妥的做法是先把新二进制拷贝过去cp ./objs/nginx /usr/local/nginx/sbin/nginx注意覆盖前确认旧二进制已经备份。第三步向正在运行的 master 进程发送 USR2 信号kill -USR2 $(cat /usr/local/nginx/logs/nginx.pid)这一步会让旧的 master 把 PID 文件改名为nginx.pid.oldbin然后启动一个新的 master 进程和一组新的 worker。此时新旧两套进程同时在线新 master 用的是新二进制。第四步让旧 master 优雅退出kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid.oldbin)QUIT信号会让旧 master 和它管理的 worker 在处理完当前连接后退出。等到旧进程全部消失新版本就已经完全接管流量了。验证一下nginx -v ps aux | grep nginx如果 ps 里只剩新的 master 和 worker说明升级完成。6.3 回滚出了问题别慌十几秒就能救回来升级完发现新版本有 bug 怎么办如果你按上面步骤备份了旧二进制回滚非常快。只要把新 master 先停掉再让旧的 master 复活。kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid) kill -HUP $(cat /usr/local/nginx/logs/nginx.pid.oldbin)HUP会让旧 master 重新读取自己的配置并拉起 worker。等新 master 退出、旧 master 和 worker 稳定运行后再把旧二进制恢复回去cp /usr/local/nginx/sbin/nginx.old /usr/local/nginx/sbin/nginx签名式的顺序很关键先让进程切换再改二进制文件防止旧 master 还没起来时二进制已经被覆盖。说到这我也提醒一句很多人喜欢在源码目录里直接make upgrade这个命令本质就是把上面步骤自动化了会依次执行 USR2 和 QUIT。但它要求旧二进制必须放在objs/目录实际生产中我反而更推荐手动执行信号操作因为每一步都能看到进程状态心里有底。7. 配置不生效、502、403排错思路与日志定位最后这部分是实操里最有用的。配置报错不可怕可怕的是“nginx -t 通过了但行为完全不对”的这种玄学问题。我遇到过太多次了这里把常见问题的排查链路整理出来。7.1 第一板斧永远是 nginx -t 和日志修改任何配置后先做语法检查nginx -t它能帮你拦住语法错误、指令拼写错误、缺少模块这三大类问题。但注意nginx -t只检查语法不检查逻辑。你配置一个不存在的域名、写错了一个路径、include 了一个不在加载列表里的文件它全都不会报错。nginx -t通过后真正的问题是看日志。日志位置取决于你的安装方式和配置源码编译通常默认在--prefix下的logs/目录包管理安装通常在/var/log/nginx/。排错核心命令tail -f /var/log/nginx/error.log tail -f /var/log/nginx/access.logerror.log会告诉你权限问题、文件路径问题、连接超时问题access.log会显示每个请求的返回状态码和耗时。绝大多数排错都是靠这两个文件说话而不是靠猜。7.2 403、404、502、504 一张表说清楚状态码真实原因排查方向403nginx 没权限读文件、目录没有 index、被访问控制规则拦截确认 user 权限、目录是否存在 index 文件、allow/deny 顺序404root/alias 拼接出来的实际路径不对、location 没匹配上看 error.log 里的实际文件路径检查 location 优先级502后端没启动、端口不对、后端进程崩了ss检查端口本机 curl 后端504后端响应时间超过 proxy_read_timeout调大超时或优化后端性能499客户端提前断开通常不是配置问题关注后端日志这里我想展开讲一个最典型的 502 案例。后端明明在监听本机 curl 也通但 nginx 转发就是 502。后来发现是proxy_pass http://localhost:8080;里 localhost 在服务器上被解析成了 IPv6 的::1而后端只监听了 IPv4 的127.0.0.1nginx 连 IPv6 地址当然连不上。解决方法是把localhost显式改成127.0.0.1。这个坑在云服务器上尤其常见。还有一个“配置改了但总是不生效”的经典问题。分布式 Linux 的 nginx 包会把站点配置放在sites-enabled/里而nginx.conf默认只 includeconf.d/*.conf。你如果在nginx.conf里改了配置系统会提示你“不要再编辑这个文件的 server 块去 sites-enabled 里改”但很多人没注意。最后的结果是配置文件语法完全正确nginx -t 也通过但你修改的 server 块根本没被加载。7.3 一些偏门但值得记住的排错点第一是 upstream 的 DNS 缓存问题。nginx 在启动或者 reload 时才解析upstream里的域名如果你的后端是动态 IP比如云数据库的域名解析变了nginx 不会实时感知会一直往旧 IP 转发直到 reload。这种情况要在 upstream 里开启 resolverresolver 8.8.8.8 valid30s; upstream backend { server api.internal.example.com resolve; }但注意server ... resolve;这个写法需要 nginx 版本支持并且要谨慎使用因为它会改变 upstream 的解析行为。第二是浏览器缓存造成的“明明改了但没变”。改完静态资源配置后你本机浏览器可能还在用旧的缓存响应。此时不要用浏览器刷新判断直接命令行验证curl -I http://your-server/static/a.css?v12345看返回的Cache-Control和Last-Modified再判断。第三是多层反向代理场景下如果你在 nginx 外层还有 CDN 或者其他负载均衡注意X-Forwarded-For会叠加后端拿到的 IP 列表是原始效果还是拼接效果取决于你每层是否覆盖传递。7.4 卸载 nginx 的干净方式最后补一句卸载相关的。热词里也有“linux 系统卸载 nginx”。如果是最初用包管理器装的删除干净需要三步systemctl stop nginx systemctl disable nginx apt-get remove --purge nginx nginx-common但源码编译安装的 nginx 没有独立的卸载命令只能手动清理先停进程再删除--prefix指定目录检查是否有软链接指向/usr/local/nginx最后清理 systemd service 文件。nginx -s quit而不是kill -9让它优雅退出避免留下异常状态的监听端口。回到排错本身我的体会是nginx 的大多数“疑难杂症”追根溯源都是三层问题——路径拼错了、权限不够、或者进程之间状态没同步。只要你坚持用nginx -t验语法、用 error.log 看真实路径、再用curl分层验证九成问题都能在 10 分钟内定位。真正难的不是配置是建立一套“先验证环境再怀疑配置”的排错顺序。这套顺序我基本每天都在用也算是我写这篇 nginx 配置总结里最想传达的东西。
返回列表