资讯动态

深入浅出Nginx:静态资源托管、反向代理与负载均衡实战指南

发布时间:2026/10/15 11:38:05 来源:尧图企业网站定制
1. 先把Nginx的使用场景想明白再动手Nginx这个名字在开发和运维圈子里几乎天天能碰到但很多人对它的理解停留在“一个Web服务器”这种模糊层面。实际上Nginx在我日常工作中最常用的三个身份是静态文件服务器、反向代理入口、负载均衡调度器。一个配置得当的Nginx可以让后端只关心业务逻辑让前端资源以最快速度到达用户终端还能在最前面挡住大量无效请求。这篇文章会围绕这三个身份展开配合最常用的命令、配置片段和两个完整部署案例把我踩过的坑和验证过的方法都写出来。适合看这篇文章的读者我觉得是这样几类人刚接触Nginx、只会nginx -s reload就想上线的同学手里有业务系统、想把静态资源和后端接口分开部署的开发者以及负责服务器运维、经常被504和502折腾到怀疑人生的朋友。这篇文章不会去逐条背诵官方文档而是按“我实际会怎么操作”的思路去讲尽量把每条命令背后为什么要这么做说清楚。读完后你可以照着案例搭出第一个线上环境也能在出问题的时候有一个自己的排查顺序而不是打开百度搜一句“Nginx 502”。先明确一个基础认知Nginx天生是事件驱动的异步架构处理高并发连接时性能和内存占用都远优于同等配置下的Apache。它擅长“转发”和“分发”而不是“执行”。你不需要在Nginx里写业务逻辑它做的事情是把请求接住、按规则走、把正确的内容回给对方。这种定位决定了我们在配置时应该遵循一条主线接收请求后尽快决定“谁能处理这个请求”然后不再让Nginx承担多余工作。所有location、upstream、proxy_pass的配置本质上都是在表达这条主线。1.1 什么时候该用Nginx静态资源、反向代理还是负载均衡新人最容易犯的错是上来就给Nginx配一大堆规则结果反而不如一个简单方案稳定。我建议先判断自己的业务属于哪一种再决定配置的复杂度。如果你的业务是个人博客、企业官网、产品落地页那核心需求就是静态资源托管。HTML、CSS、JS、图片这类文件直接交给Nginx读取磁盘返回配合gzip和浏览器缓存已经可以达到非常理想的加载速度。这种情况下后端逻辑如果存在也只是提供极少数动态接口用反向代理单独转发特定路径就行。如果你的业务有后端服务比如Java、Go、Python写的API那么Nginx的任务就变成了反向代理。它把80或443端口收到的请求转发给内网地址的后端进程同时负责HTTPS证书管理、HTTP头过滤、请求体大小限制。这时Nginx与后端之间可以考虑使用内网专用的HTTP连接通过proxy_pass指定目标地址。配置的核心不是“转发”本身而是路径映射、超时控制、错误处理。如果你的业务规模到了需要多台后端实例扛流量Nginx就升级为负载均衡入口。upstream块里定义一组后端地址Nginx按轮询、最小连接数、IP哈希等算法分发请求。这里要注意负载均衡不只是“多台机器分摊流量”还要考虑后端节点的健康检查、会话保持、优雅下线。没有健康检查的负载均衡遇到某台后端挂掉时用户会周期性看到502因为Nginx还在往宕机节点转发请求。1.2 安装方式和版本选择不要一上来就编译我第一次给服务器装Nginx时直接下载源码开始configure、make、make install折腾了半天直到后来才发现其实有更稳的路径。现在我的建议是能用系统包管理器装的就用系统包管理器。Ubuntu和Debian上用aptCentOS和Rocky上用yum或dnf装好后自动注册systemd服务直接systemctl start nginx就能跑起来。这种方式的好处是后续卸载、升级、查依赖都方便补丁能跟着系统源走。什么时候需要自己编译当你需要定制模块、或者官方预编译包里的模块不满足需求时才考虑源码编译。比如你要用到第三方模块、需要调整某些内置模块的编译开关这个时候去源码目录里 configure 才有意义。生产环境的服务器我倾向于用稳定版主线外的发布分支不要追最新版Nginx的奇数版本是主线版偶数版本是稳定版比如1.24.x、1.26.x这种。线上环境没必要追求最新功能稳定压倒一切。我自己在Ubuntu上的标准安装命令就是apt update apt install nginx -y nginx -v systemctl enable nginx systemctl start nginx装完之后配置文件在/etc/nginx/目录下主配置文件是nginx.confsites-available和sites-enabled两个目录是Debian系特有的配置管理方式。真正的站点配置放在sites-available里启用时在sites-enabled里建一个软链接指向过去禁用时删掉链接就可以。这套机制在维护大量站点时特别顺手你不需要改主配置只需要控制软链接的增删。CentOS系略有不同配置目录是/etc/nginx/conf.d/习惯上每个站点放一个.conf文件主配置里通过include /etc/nginx/conf.d/*.conf;自动加载。如果你两种系统都维护过应该能体会到理解发行版的配置文件管理哲学比死记命令重要得多。2. 常用命令每天都会敲的那十几个Nginx的命令行工具很简洁核心的可执行文件就是nginx本身通过不同参数完成不同操作。我每天高频使用的命令大概在十五个以内分四类查看信息、测试配置、启停控制、日志排查。查看版本和编译参数用nginx -v nginx -V-v只显示版本号-V会连带显示configure参数和模块列表这两个命令在线上升级或者确认模块时很有用。有时候你需要确认服务器上是否开启了某个模块不需要去翻编译文档直接nginx -V看输出里的--with-http_ssl_module这类字段就能确定。测试配置用nginx -t nginx -T-t是检查配置语法输出里会明确告诉你哪一行配置有问题这是每次修改完配置后必须执行的一步。严格来说nginx -t不能只测一次就算完要把输出里提示的warning也当回事。warning不会阻止启动但往往隐藏着性能或安全上的隐患比如server_name重复、worker_processes设置不合理等。-T是打印当前生效的完整配置通常用于快速确认线上生效的配置内容到底是什么尤其适合排查“我改了配置但行为没变”的诡异情况。启停控制方面systemctl start nginx systemctl stop nginx systemctl restart nginx nginx -s reload nginx -s quit大部分时候修改配置不需要restartreload就够了。reload会让master进程重新读取配置然后温和地拉起新worker进程旧worker进程在处理完当前请求后自动退出。这意味着请求不会中断在线更新配置非常丝滑。真正需要restart的场景是修改了监听端口、改了worker数量这类需要重建监听socket的情况或者你load了全新的动态模块、改了涉及进程本身的参数。nginx -s quit和nginx -s stop的区别也值得记一下。quit是优雅退出处理完当前连接再关stop是立即强制退出正在进行的请求会直接断掉。线上做维护的时候优先用quit避免把正在下载大文件的用户直接掐断。日志和状态查看tail -f /var/log/nginx/access.log tail -f /var/log/nginx/error.log ss -lntp | grep nginx日志是Nginx排错的核心信息来源。access.log记录每个请求的访问情况error.log记录报错和异常。ss -lntp可以确认Nginx正在监听的端口和进程状态比netstat更现代输出也更快。还有一个平时用得不多但关键时候能救命的命令lsof -i :80它可以查出80端口是否被占用、占用进程是谁。我第一次部署Nginx时就遇到过80端口被系统自带Apache占用的场景当时systemctl start nginx报错了但看日志看不出端倪最后是lsof -i:80定位到了问题根源。2.1 启停和重载命令的坑reload 不等于 restart很多人把reload当成restart用觉得都是让Nginx重新加载配置实际差别很大。reload只重读配置文件并平滑重启worker进程master进程的PID不变监听socket也不会断开。restart则是完整停止再启动新旧PID都会变化监听期间会有极短的窗口期请求如果恰好在这个窗口进来可能直接connection refused。我记得有一次在线上环境改了一个server块的listen端口执行了reload结果新端口一直没生效请求还是走旧端口。后来才反应过来reload不适用于某些变更监听地址、worker_processes数量这样的改动必须restart或者systemctl restart Nginx才行。不过restart有风险如果配置文件存在语法错误restart会导致Nginx起不来服务直接挂了。所以在执行restart之前一定要先nginx -t。我个人的操作习惯是nginx -t nginx -s reload但遇到上面说的端口变更场景就会改成nginx -t systemctl restart nginx这样既能保证配置语法没问题又能在需要完全重启时安全操作。还有一点要提醒如果Nginx是用systemd管理的nginx -s reload不会被systemd感知到但你依然可以这么用只是状态机上不会显示“reloaded”这种信息。如果希望systemd感知用systemctl reload nginx更规范。2.2 日志、测试和排错命令组合排错的时候我习惯开三个终端窗口一个跑tail -f看error.log一个跑tail -f看access.log一个手动执行curl测试。这是一种很朴素的调试方式但效率极高。你手动发起一次请求然后立刻看两个日志文件的输出基本能定位请求到底有没有到Nginx、Nginx有没有转给后端、后端有没有响应。一个很常用的测试技巧curl -I http://127.0.0.1-I只取响应头不取正文适合快速确认服务是否正常。如果你改了Nginx配置怀疑静态文件路径错误可以用curl -v http://127.0.0.1/app/index.html-v会打印完整的HTTP交互过程包括DNS解析、TCP连接、发送的请求头、接收的响应头和响应体。Nginx配置里经常会因为路径别名问题导致404curl -v能让你看到最终的URL路径是什么便于判断到底是配置错了还是文件真的不存在。还有一组诊断命令值得记住getenforce sestatus如果你的服务器是CentOS系且开启了SELinuxNginx访问非标准目录下的文件时即使权限正确也可能被拒绝报403或500。这时候用getenforce查看SELinux状态如果是Enforcing考虑用chcon调整文件的安全上下文或临时关闭验证是不是SELinux导致的。SELinux的问题很隐蔽因为它不改变文件的权限位你在文件系统层面看一切正常。3. 项目案例一搭建一个静态资源站点部署静态站可能是Nginx最基础的使用场景但要把这个“基础”做扎实需要注意的细节并不少。接下来我用一个实际项目来做示范假设我要部署一个前端构建好的单页应用目录在/data/www/demo.example.com/通过demo.example.com对外提供服务要求开启gzip压缩、强制HTTPS跳转并配置合理的浏览器缓存策略。这个场景在工作中太常见了前端同学把打包产物给你你把它扔到服务器上然后让Nginx把它跑起来。很多人会觉得“不就是扔个文件嘛”但实际部署时缓存策略、压缩策略、SPA路由处理都是容易出问题的点。3.1 目录规划和权限设置先把目录建好mkdir -p /data/www/demo.example.com把前端构建产物上传到这个目录后需要检查两个权限问题。第一是目录的读权限Nginx的worker进程默认以www-data或nginx用户运行如果目录在其他用户名下且没有给到读权限Nginx就会返回403。第二是父目录的权限不只是站点目录本身/data、/data/www父目录都要有可执行权限否则Nginx进程进不去。我通常的做法是chown -R www-data:www-data /data/www/demo.example.com chmod -R 755 /data/www/demo.example.comchmod 755表示文件所有者可读可写可执行组和其他用户可读可执行。在静态资源场景下这个权限设定已经足够。要注意的是不要为了省事直接chmod 777那会带来不必要的安全隐患尤其是根目录下如果有用户上传功能777会被利用来写入恶意文件。目录规划上还有一个值得坚持的好习惯每个站点放在独立目录不要全部堆在/var/www/html里尤其是多人维护的服务器。独立目录的好处是出问题时可以快速通过站点的访问日志和站点的配置对应起来不会出现“这个页面是哪个目录下的”这种糊涂账。3.2 server块配置逐行拆解在sites-available目录下新建文件demo.example.comserver { listen 80; listen [::]:80; server_name demo.example.com; root /data/www/demo.example.com; index index.html; location / { try_files $uri $uri/ /index.html; } location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 30d; add_header Cache-Control public, max-age2592000; access_log off; } location /favicon.ico { log_not_found off; access_log off; } gzip on; gzip_types text/plain text/css application/javascript application/json image/svgxml; gzip_min_length 1024; }这段配置我逐行拆解给你看。listen 80;监听IPv4的80端口listen [::]:80;监听IPv6的80端口。如果服务器支持IPv6不写第二行会导致IPv6地址访问不到或者如果只存在IPv6环境甚至会卡到无法正常访问。server_name是用来匹配不同域名的。Nginx收到请求后会根据请求头里的Host字段在所有server块里找一个匹配的server_name来路由找不到就落到默认server块。如果没有特殊要求第一个加载的server块会成为默认server所以我通常会把一个默认拒绝或默认跳转的server块放在最前面避免别人直接用IP访问到意外内容。root和index比较简单分别指定站点根目录和默认首页文件名。需要注意root和alias的区别很多人会在这两个指令上犯错。root会把完整路径拼接到location后面而alias会把location前缀替换掉。举一个最常见的场景如果location是/static/root配置为/data/www/demo.example.com访问/static/app.css时Nginx会去找/data/www/demo.example.com/static/app.css如果是alias配置为/data/www/demo.example.com/static/访问/static/app.css时Nginx会去找/data/www/demo.example.com/static/app.css。粗看一样但如果你写了alias /data/static/访问/static/app.css时找的是/data/static/app.css这就是两者的关键区别。location /里用了try_files $uri $uri/ /index.html;。这是单页应用部署中最关键的一行。SPA的路由由前端脚本控制比如React Router的history模式用户访问/about这个地址时服务器上其实没有about.html文件如果直接按路径找文件就会404。try_files先看$uri这个文件是否存在再看$uri/目录是否存在如果都没有就把请求指向/index.html由前端路由接管。没有这行配置你的SPA刷新后就会一片空白或者报404。静态资源的location块用的是正则匹配~*表示不区分大小写匹配.js、.css等后缀。命中后设置expires 30d让浏览器把这些资源缓存30天同时加上Cache-Control: public允许CDN和代理缓存max-age设置为2592000秒也就是30天。静态资源的文件名如果带有指纹比如app.8f3k2j.js这种长缓存策略非常合适因为文件名变了就相当于新资源不会出现旧缓存问题。access_log off是为了让资源请求不刷满访问日志图片和JS请求量太大全记到日志里会导致日志文件飞速膨胀。location /favicon.ico用了精确匹配只处理一个特定路径。log_not_found off表示这个文件找不到时不记录错误日志因为favicon不一定每个项目都有浏览器却又总会请求不关掉这个日志就是铺天盖地的404记录。最后是gzip配置。gzip on;开启压缩gzip_types列了需要压缩的MIME类型gzip_min_length 1024表示小于1024字节的文件不压缩。太小的文件压缩后体积可能不减反增所以有个阈值限制。配置压缩时别忘了包含text/html这是默认就会压缩的不需要在types里带。3.3 上线验证步骤配置写好后启用站点并验证ln -s /etc/nginx/sites-available/demo.example.com /etc/nginx/sites-enabled/ nginx -t systemctl reload nginx然后执行curl -I http://127.0.0.1/观察响应头里有没有HTTP/1.1 200 OK再确认请求头里是否带上了Content-Encoding: gzip。如果响应头里出现了Content-Encoding: gzip说明gzip生效。再测试SPA路由curl -I http://demo.example.com/about如果返回200而不是404说明try_files配置成功。最后在浏览器里访问https://demo.example.com确认页面加载无报错打开开发者工具看网络面板确认静态资源命中了Cache-Control: public, max-age2592000并且Content-Encoding为gzip。这一步我有一个强迫症级别的检查点响应头不能出现Server: nginx之外的多余服务器信息。如果有必要可以加一行server_tokens off;隐藏Nginx版本号避免暴露精确版本给潜在攻击者。4. 项目案例二反向代理和负载均衡实战第二个案例更接近现代后端架构的部署场景。假设有一个内部API服务运行在127.0.0.1:8080提供/api/路径下的接口前端业务页面由静态服务提供同时需要支持多实例负载均衡。这个部署任务本质上要解决三个问题如何把公网请求安全转发到后端、如何保持多后端的负载均衡与故障转移、如何处理前端静态资源和后端动态接口的分离。4.1 upstream 配置和调度算法在Nginx的http块或server块之外定义一组后端upstream api_backend { least_conn; server 127.0.0.1:8080 weight1 max_fails2 fail_timeout10s; server 127.0.0.1:8081 weight1 max_fails2 fail_timeout10s; }least_conn表示优先把请求分给当前连接数最少的后端实例。相比默认的轮询方式当后端实例性能不同或请求处理耗时差异较大时最少连接数能更好地平衡实际负载。weight1是权重权重越高分配的请求比例越大适合后端机器配置不一致的场景。max_fails2和fail_timeout10s组合表示10秒内如果后端失败两次Nginx会将其标记为不可用并在进入下一轮探测前不再转发请求给它。upstream里还有一个容易被忽略的配置项是keepaliveupstream api_backend { least_conn; server 127.0.0.1:8080 max_fails2 fail_timeout10s; server 127.0.0.1:8081 max_fails2 fail_timeout10s; keepalive 64; }keepalive表示每个worker进程与后端保持最多64个空闲长连接。没有这个配置每次请求都要新建和销毁TCP连接高并发下会大量消耗文件描述符还会明显增加延迟。加上keepalive之后Nginx和后端之间可以复用TCP连接性能提升非常显著。upstream的调度算法我常用的有四种round-robin轮询、least_conn最少连接、ip_hash按客户端IP哈希、url_hash按请求URL哈希。ip_hash适合需要session保持的旧系统url_hash适合缓存命中率要求高的场景普通API后端用least_conn就挺好。4.2 location 匹配规则负载均衡配置的server块是这样写的server { listen 80; server_name api.example.com; access_log /var/log/nginx/api.example.com.access.log; location /api/ { proxy_pass http://api_backend; 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_connect_timeout 5s; proxy_read_timeout 30s; proxy_send_timeout 30s; } location /healthz { access_log off; proxy_pass http://api_backend; proxy_read_timeout 2s; } }proxy_pass是反向代理的核心指令。这里有一个经常让人摸不着头脑的情况proxy_pass后面带不带URI行为完全不同。不带URI时比如proxy_pass http://api_backend;Nginx会把完整的原始请求URI传给后端带URI时比如proxy_pass http://api_backend/;location前缀会被替换为URI中的路径。这个差异在实际配置中会引发非常诡异的404问题。所以我建议如果没有明确把握proxy_pass后面不要带URI直接写upstream名。proxy_set_header用于把客户端的真实信息传给后端。后端程序经常需要获取用户真实IP尤其是做日志分析或风控的时候如果Nginx不传X-Real-IP和X-Forwarded-For后端看到的IP全是Nginx的内网地址那API的访问日志基本就废了。$proxy_add_x_forwarded_for会在原有XFF头基础上追加客户端地址即使请求已经经过多层代理也能保留下链路信息。X-Forwarded-Proto则让后端知道用户请求是http还是https很多应用会用它来判断协议类型从而生成正确的回调地址。超时参数也要根据自己的业务合理设定。proxy_connect_timeout 5s是Nginx与后端建立连接的超时时间后端正常应该毫秒级响应如果5秒还没连上要么网络有问题要么后端根本没起来没必要继续等。proxy_read_timeout 30s是等待后端响应的时间如果你的后端有长时间运行的接口比如文件导出、批量报表这个值要适当调大否则大任务做到一半就会被Nginx掐断返回504。proxy_send_timeout 30s是Nginx向后端发送请求体的超时时间一般上传大文件时才会触发。location /healthz这个块值得单独讲。健康检查在生产环境必不可少。你可以在upstream里配置health_check但那需要在商业版或者使用第三方模块才能实现。替代方案是后端提供一个轻量探活接口Nginx配置一个独立location只负责转发这个探活请求由运维脚本周期性调用或者让负载均衡器来探测。关键是不要让探活请求进入正式的业务日志和慢超时流程所以这里关闭了access_log并缩短了read_timeout。4.3 动静分离配置如果一个server块同时承担静态资源和API接口的职责可以在同一个域名下做动静分离server { listen 80; server_name www.example.com; root /data/www/www.example.com; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://api_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这样前端脚本请求/api/login时Nginx自动转发到后端服务访问/assets/app.js时直接从本地磁盘读取。动静分离的好处不仅仅是各司其职还能让静态资源命中缓存策略动态请求不受缓存干扰整体性能更可控。如果你的项目是前后端完全不同域名部署那就更简单了两个server块各管一个域名配置互不干扰。5. 性能优化和常用细节调整静态部署和反向代理都跑通之后接下来该关注性能了。很多人对性能优化的理解就是“开gzip、开缓存”但实际值得调整的参数比这多一些而且每个参数的调整都应当有明确目的不建议照搬网上的“万能配置”。5.1 gzip、缓存和安全响应头gzip在静态站点案例里已经写过基础配置这里补充几个进阶选项。gzip_comp_level是压缩级别1到9越高压缩率越大但CPU消耗也越高。我实测下来级别5是一个比较平衡的点级别6以上CPU占用明显上升压缩率提升却很小。gzip_vary会在响应头里加上Vary: Accept-Encoding提示缓存服务器区分压缩和非压缩版本防止代理服务器缓存了错误版本。如果你的前端资源通过CDN分发gzip_vary on基本是必须的。缓存方面除了静态资源的expires还可以配置open_file_cache。它的作用是缓存文件句柄、文件大小和修改时间减少重复打开文件的系统调用。配置可以放在http块或server块里open_file_cache max1000 inactive20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;max1000表示最多缓存1000个文件句柄inactive20s表示20秒内没有被访问的缓存条目会被淘汰min_uses2表示文件至少被访问2次才缓存。这些参数能减少磁盘IO但前提是文件量不要超过配置的缓存上限否则缓存反复淘汰反而增加开销。安全响应头在现在的部署里也应该加我建议在server块或http块里统一加上add_header X-Content-Type-Options nosniff; add_header X-Frame-Options SAMEORIGIN; add_header Referrer-Policy strict-origin-when-cross-origin;这几个响应头分别用于防止MIME类型嗅探、点击劫持和敏感来源泄露。安全性上不算完整体系但属于性价比极高的基础项一行配置就能挡住一类常见的浏览器攻击方式。5.2 worker进程数与系统连接参数Nginx主配置里最核心的性能参数是这几行worker_processes auto; worker_connections 1024; use epoll;worker_processes auto表示按CPU核心数自动设置worker进程数。事件的并发处理由多个worker进程分担worker数和CPU核心数一致通常是最优的。worker_connections是每个worker进程能同时保持的最大连接数1024是默认值。当并发连接数超过worker_processes * worker_connections时再进来的连接就会被拒绝。如果你的业务并发量较高需要调大这个值同时必须同步调整操作系统层面的文件描述符限制。操作系统层面需要修改的就是ulimitulimit -n 65535要让这个限制持久生效需要修改/etc/security/limits.conf加入* soft nofile 65535 * hard nofile 65535然后在nginx.conf的events块里加入worker_rlimit_nofile 65535;这三处配合好Nginx才能突破默认的1024连接限制。我曾经遇到过Nginx配置一切正常但高并发时频繁报Too many open files错误排查到最后发现是系统限制没放开Nginx的worker进程最多只能打开1024个文件描述符。连接数多大并发能力就有多受束缚。TCP层面的优化可以加这样几个参数listen 80 backlog4096;backlog是连接队列的长度高并发下如果这个值太小新连接会被内核丢弃。tcp_nodelay on;可以让小数据包立即发送降低交互式请求的延迟。tcp_nopush on;则相反会在响应到达一定大小后才一次性发出适合传输大文件时减少网络分片。在静态资源场景下tcp_nopush和sendfile配合使用效果明显。5.3 日志分割与清理Nginx默认会把所有访问日志写到同一个文件里时间久了文件体积会非常大不仅占用磁盘空间排错时不方便甚至可能影响文件写入性能。我目前使用的方法是logrotate来自动分割几乎不用自己写cron脚本。在/etc/logrotate.d/nginx下配置/var/log/nginx/*.log { daily missingok rotate 14 compress delaycompress notifempty create 640 www-data adm sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 cat /var/run/nginx.pid fi endscript }daily表示每天分割一次rotate 14表示保留14份历史日志compress表示旧日志压缩存储delaycompress的意思是分割后的第一个日志先不压缩避免正在写入的log还没完全结束就被压缩。postrotate里的kill -USR1通知Nginx重新打开日志文件这是关键一步没有它Nginx还会继续往已经被改名或压缩的旧文件里写。如果你坚持要手动分割命令组合是mv /var/log/nginx/access.log /var/log/nginx/access.log.20250616 kill -USR1 cat /var/run/nginx.pid手动分割时一定要记得发USR1信号这是Nginx文档里明确推荐的做法。6. 问题排查速查表Nginx的报错信息其实非常直白大多数问题都能在error.log里找到线索。下面是我整理的高频问题排查清单按“现象、可能原因、检查点”的结构写你在排错时可以直接对照。6.1 502、504、499是怎么来的502 Bad Gateway表示Nginx作为代理向它配置的上游服务器发出了请求但上游服务器没有给出有效响应。排查顺序我在工作中总结成了三步第一步确认后端进程是否存活。直接curl http://127.0.0.1:8080/healthz看有没有正常返回。如果连接拒绝那Nginx自然无法代理问题是后端服务挂了。第二步确认后端进程监听地址是否和nginx.conf里upstream的server参数一致。后端如果监听的是IPv6的::1而Nginx配置的是127.0.0.1:8080连接就会失败。这种不一致很常见尤其是用localhost启动服务的程序localhost会同时解析到IPv4和IPv6程序实际监听优先选了IPv6地址Nginx配置却写了IPv4。第三步如果后端进程正常、地址一致就要看Nginx和后端之间的网络是否存在防火墙拦截、SELinux阻断。可以用curl在Nginx本机测试如果本机能通但Nginx代理失败重点检查selinux和本机防火墙规则。504 Gateway Timeout表示Nginx成功连接了上游服务器但上游没有在proxy_read_timeout设置的时间内返回响应。优先检查后端是不是有慢查询、死循环、或者某个接口本来就超出预期时长。如果你确定后端这么慢是正常的比如导出大量Excel就把proxy_read_timeout调大到足够支撑业务完成。但我得提醒一句不要无脑调大到几分钟如果后端一直不回占用的是Nginx与后端的连接资源高并发下连接池会被打满。499是一个比较容易被忽略的状态码它表示客户端在Nginx拿到上游响应之前关闭了连接。出现大量499时优先考虑用户请求超时后主动断开后端压力过大也会导致这种情况因为后端的处理速度赶不上用户等待的耐心阈值。修复方向通常是后端链路优化而不是调Nginx超时参数。6.2 403、404、301循环应该先查哪里403 Forbidden的常见原因有四个。第一root目录的权限不足worker用户没有读权限或父目录缺少x权限用ls -ld查看路径每一级的权限位确认是755或至少有rx权限。第二索引文件不存在client_max_body_size超限不会报403但索引问题会访问目录却没有配置index或者索引文件不存在时Nginx会拒绝列出目录内容并返回403除非开启了autoindex线上环境请保持autoindex off。第三SELinux阻止上面已经讲过用getenforce验证。第四Nginx配置里的deny all误伤检查是否有访问控制指令比如公网IP被某条geo规则覆盖。404 Not Found的排查点按我说过的root和alias区别来查。写过location后记得预判最终会映射到哪个文件路径然后实际确认这个文件是否存在于系统里。另外try_files和location的嵌套关系也可能导致意外404尤其是正则location的优先级高于普通前缀匹配容易被干扰。301循环重定向通常发生在HTTPS跳转配置错误时。比如你写了这样两条规则把所有HTTP请求301重定向到HTTPS然后又在HTTPS server块中设置了同样的重定向逻辑就会形成循环。判断是否循环的最快方法是curl -L跟踪重定向看它最终停在哪个地址然后检查server块里的return 301和schema判断逻辑。6.3 一个从报错到修复的完整排错实例我记一个最近处理过的案例完整走一遍排错流程。某天收到监控告警业务系统的API接口502率突然升高。我先执行nginx -t确认配置无异常然后tail -f /var/log/nginx/error.log查看报错发现大量connect() failed (111: Connection refused) while connecting to upstream。接着我systemctl status api.service查看后端服务状态发现服务处于running。再用ss -lntp检查后端端口监听发现8080端口只有IPv4环境在监听而Nginx配置的是127.0.0.1:8080按理说一致。继续检查后端进程实际监听地址发现程序启动配置里绑定的是::1即IPv6回环而不是127.0.0.1Nginx用IPv4去连当然被拒绝。这个问题的根源是后端程序读了一个默认配置没有显式指定监听地址导致默认绑到了IPv6回环。修复方式也很简单在后端启动配置里显式写上127.0.0.1:8080重启后502恢复。整个排查过程不到10分钟核心工具就是ss -lntp、tail -f error.log、curl直连测试。我分享这个案例是想让你知道Nginx报错本身不可怕可怕的是不按日志提示去追根因而是在配置参数里瞎猜乱试。写在最后的一些实操心得这些年在服务器上部署Nginx我逐渐形成了一个习惯每次上线前都执行一遍nginx -t每次变更后都立刻用curl做一次冒烟测试然后把error.log和access.log的tail窗口打开至少观察两分钟。简单但很有效。如果让我给刚开始接触Nginx的人一个建议我会说不要急着背指令先把“请求进来后Nginx如何用server、location、upstream这三级逻辑做路由”想清楚。server决定去哪个站点location决定交给哪个处理逻辑upstream决定最终抛给哪个后端。这条链路通畅了报错日志里的每句话你都能迅速对应到应该是哪一环出了问题。还有一个我可以确认的经验配置管理上一定要保留注释。Nginx配置允许注释把当时为什么写这个location、为什么要调大某个超时时间写清楚三个月后你自己回来看绝对会感谢当时的注释。很多线上诡异问题就是因为有人悄悄改了一个看似无关紧要的参数没有任何交代后来的人完全摸不着头脑。这个内容后续还可以扩展的方向包括HTTPS证书自动化续期、Nginx与容器编排平台的联动、基于Nginx的限流与灰度发布。如果这篇文章对你有帮助你可以先从两个案例里的任意一个开始在自己的测试环境里完整走一遍遇到问题再翻回来对照。实践过一次之后Nginx对你来说就不再是一堆让人发怵的命令而是一套逻辑清晰的路由工具。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑