/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
上周帮人看一台机器,反馈是"网站服务器异常"。我打开浏览器一看,白底黑字一行 502 Bad Gateway,页面还有个 nginx 的版本号。他已经在控制台重启过两次了,没用。
我没急着重启,先敲了 tail -50 /var/log/nginx/error.log。第一行就写着 *37 connect() failed (111: Connection refused) while connecting to upstream,upstream 指向的是 127.0.0.1:9000。PHP-FPM 没起来。 从看到日志到修好,四分钟。
这就是本文想讲清楚的那件事:网站服务器异常怎么办这个问题的答案,几乎从不取决于浏览器上那个数字,而取决于 error_log 里那一行英文。 同一个 502,五种不同的日志原文,对应五个完全不同、且互不搭界的修法。
我自己在排障时有个习惯:遇到 5xx,第一反应不是猜,是把那一行日志原文抄下来。抄完再去查表——因为 nginx 已经把答案写出来了,只是它用的是自己的语言。
每次有人问 网站服务器异常怎么办,我都会先把这个习惯塞给他:因为同一个状态码底下压着至少五种互不相干的病因,靠猜一定会走错路。
一、先把"网站服务器异常"这个词拆开
这个词在日常反馈里被用得太宽了,我一般先把它砍成三类,因为它们走的是完全不同的排查路径:
| 白屏 / 超时 / “无法访问此网站” | 链路层,请求还没到 nginx | 33 篇讲的那三层:安全组、系统防火墙、进程端口 |
| 有 HTTP 数字:500/502/503/504 | 到达了 nginx,卡在它后面 | nginx 的 error_log |
| 浏览器红锁 / ERR_CERT_DATE_INVALID | TLS 层,没有 HTTP 状态码 | 证书有效期与服务器时间 |
中间那一类才是本文的范围:请求到了 nginx,但 nginx 没能从它背后那个进程拿到一个能用的响应。
为什么要把边界划这么死?因为第一类和第三类如果按 502 的思路去查,会查很久且毫无收获——它们的错误根本不在 nginx 的日志里。
按 MDN 的定义,502 的含义是"服务器作为网关或代理,从上游服务器收到了无效响应"。注意这个定义的主语:是 nginx 收到了无效响应,或者是压根没收到。 它自己好得很。
想明白这一点很重要:5xx 从来不是"网站服务器异常",而是"nginx 之后的那一台异常",而那一台可能就在同一台机器的另一个端口上。
二、唯一靠谱的判决书:error_log,不是 access_log
这里有个坑:绝大多数人第一反应是去翻 access_log,但那里面只有结果,没有原因。
access_log 里一行 502 长这样:
116.62.x.x – – [12/Oct/2026:09:14:22 +0800] "GET /api/order HTTP/1.1" 502 150 "-" "Mozilla/5.0 …"
能看出的只有"这个请求返回了 502"。为什么返回,一个字都没有。
error_log 里对应时刻的那一行才是答案:
2026/10/12 09:14:22 [error] 2841#0: *137 connect() failed (111: Connection refused) while connecting to upstream,
client: 116.62.x.x, server: demo.example.com, request: "GET /api/order HTTP/1.1",
upstream: "http://127.0.0.1:9000/api/order", host: "demo.example.com"
关键在中间那段 connect() failed (111: Connection refused)。这就是全文要拆解的东西。
实操上我一般用这条,把时间范围内的错误拉出来:
# 最近 200 行错误日志(默认级别 error,5xx 都会落进来)
tail -200 /var/log/nginx/error.log
# 或者盯着刷,另一边复现问题
tail -f /var/log/nginx/error.log
另外提一句容易混淆的:upstream: 字段显示的是 nginx 当时试图去连的那个地址,不是"后端真实地址"。如果 upstream 块里配了域名,这里显示的是解析后的 IP——先看清楚它是不是你以为的那个 IP,再往下排查。
三、connect() failed (111: Connection refused) —— 后端根本没在听
这是最常见的一种,也是最容易修的一种。含义很直白:nginx 去敲门,门后面没人应,对方直接回了个"滚"。
三个最常见来源,我按出现频率排:
1)后端进程压根没起来。 PHP-FPM 崩了、Node 进程被 OOM 杀了、Java 部署完还没起来。查的方法很机械:
ps aux | grep -E 'php-fpm|node|java' | grep -v grep
ss -tlnp | grep -E ':9000|:8080' # 换成你 proxy_pass 里那个端口
如果 ss 查不到任何监听,那就不是 nginx 的问题。
2)端口写错了。 这个我自己也犯过:配置里写 proxy_pass http://127.0.0.1:8080;,实际服务监听的是 9000。nginx 配置语法完全合法,nginx -t 也不会报错——它没法替你验证端口背后有没有人。
3)用 UNIX socket 时文件不在了。 PHP-FPM 崩掉时,socket 文件会消失,connect() 直接失败。
快速验证的黄金一条:在服务器本机直接请求那个后端,绕过 nginx。
这一步看着简单,但它把 网站服务器异常怎么办 这个大问题切成了一半:本机通、外面不通,问题在 nginx 到后端这一段配置;本机都不通,那就是后端自己的事,不用再碰 nginx 一行配置。我每次都先做这一步,它能立刻把责任切开,避免在错误的方向上查半小时。
四、no live upstreams while connecting to upstream —— 后端是好的,被 nginx 自己拉黑了
这条最有迷惑性。你去后端机器上看到进程好好的,curl 也正常,但 nginx 就是报 502。
原因是 nginx 的被动健康检查把这台机器标记为不可用了。
按 nginx 官方 ngx_http_upstream_module 手册 的原文,默认参数是:
“By default, the number of unsuccessful attempts is set to 1.”(max_fails) “By default, the parameter is set to 10 seconds.”(fail_timeout)
翻译过来就是:只要失败 1 次,这台后端就被拉黑 10 秒。
想想这对一台偶发抖动的服务意味着什么——一次网络抖动,这台就被摘 10 秒。如果是单台 upstream,那这 10 秒里用户看到的全是 502,而你去查的时候可能刚好过完,一切"看起来都正常"。这就是所谓"网站服务器异常怎么回事、刷新一下又好了"的典型成因。
同一份手册还写了 nginx 在全部失败时的行为:
“If an error occurs during communication with a server, the request will be passed to the next server… If a successful response could not be obtained from any of the servers, the client will receive the result of the communication with the last server.”
所以有没有下一台,结局完全不同。我的建议是别动 max_fails 的默认行为,而是:
upstream backend {
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
改完记得 nginx -t 再 nginx -s reload。顺带说一句,keepalive 32 这行看着无关紧要,但它能显著减少 nginx 与后端之间反复握手带来的失败概率。
五、upstream timed out (110: Connection timed out) —— 后端活着,只是慢
这条和第三条的区别是根本性的:connection refused 是"没人应",timed out 是"有人应了但半天不说话"。
触发它的是 ngx_http_proxy_module 里那三个超时,官方手册给的默认值清一色是 60 秒:
proxy_connect_timeout 60s; # 与后端建连最多等多久
proxy_send_timeout 60s; # 向后端发送请求最多等多久
proxy_read_timeout 60s; # 等后端回话最多等多久
这三个里绝大多数场景只需要关心最后一个 proxy_read_timeout——它是"后端算多久算超时"那把尺。一个报表导出跑了 90 秒,nginx 会在第 60 秒把连接掐断,然后给客户端返回 504。
这里要说清楚一个概念:504 Gateway Timeout 和 502 Bad Gateway 是两种不同的失败,虽然用户看都是个错误页:
- 502:连接层面就没谈成
- 504:连接谈成了,但后端没在规定时间内把话说完
所以 网站服务器异常怎么解决 这个问题,在慢的场景下答案不是"重启",而是二选一:要么让后端快起来(加索引、加缓存、异步化),要么把尺子调长——但第八条我会说,调长尺子通常是个坏主意。
六、响应收到一半就断了:另外三条日志原文
这一组我合并讲,因为它们都属于"连接建立成功,但响应不吃这一套":
| upstream prematurely closed connection while reading response header | 后端进程在处理途中崩了 | 后端自己的日志、dmesg 看 OOM |
| recv() failed (104: Connection reset by peer) while reading response header | 后端主动发 RST 断开 | 同上,或后端有连接数限制 |
| upstream sent too big header while reading response header from upstream | 响应头超过 nginx 缓冲区 | 调 proxy_buffer_size |
中间那条特别值得留意:如果只有部分请求出现、且没有规律,大概率是后端在某个特定输入下崩了。这时候去翻后端的日志的时间点,比在 nginx 这边反复看有用得多。
第三条则是个纯粹的配置问题,一条就能解决:
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
典型场景是后端往响应头里塞了超长的 Set-Cookie 或者一堆自定义头。
七、Permission denied … while connecting to upstream —— UNIX socket 的权限
这条在 PHP-FPM 用 socket 文件时特别常见,日志原文大致是:
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream
(13: Permission denied) —— 不是端口不通,是 nginx 那个用户打不开这个 socket 文件。
对账两边就行:nginx 的 user 配置 vs PHP-FPM 池里的 listen.owner / listen.group / listen.mode。
ls -l /run/php/php8.2-fpm.sock # 看属主和权限位
grep '^user' /etc/nginx/nginx.conf # nginx 跑在哪个用户下
我自己碰到过一次更隐蔽的:上层目录 /run/php 没有执行权限,导致 nginx 连文件都摸不到。所以文件权限对还不够,整条路径都要能走通。
另外三种:它们根本不会出现在 nginx 日志里
说到这里必须补一块边界,因为 有些"网站服务器异常",nginx 日志里一行都没有——请求压根没到 nginx。
1)负载均衡那一层直接返回的。 这时候 502 是 LB 给的,不是 nginx 给的。三家云的判断思路一致:对比 LB 返回给客户端的码 与 后端返回给 LB 的码。
按 阿里云《CLB异常状态码》 的口径:若 status 与 upstream_status 相同,说明是 LB 透传了后端的异常;若 upstream_status 为 -,则这个码是 LB 自己生成的,典型的是"全部后端健康检查失败"。
同一份文档还点了一个高频坑:后端 ECS 上的 iptables 或第三方安全软件拦掉了 100.64.0.0/10(这是 CLB 与后端通信用的网段),健康检查就会全部失败,直接导致 502。这一个原因我见过不止三次。
腾讯云《健康检查异常排查》 除同源网段外还给了个很硬的点:同时开启 tcp_tw_recycle 和 tcp_timestamps 会导致健康检查异常,建议关闭 tcp_tw_recycle。这条在老教程里经常被推荐"优化",实际是颗雷。
华为云《健康检查异常排查(共享型)》 明确写了它那边的源地址段:共享型 ELB 用 100.125.0.0/16 做健康检查,后端安全组必须放通这个网段,否则永远不健康。
三家在这个问题上的结论高度一致:健康检查失败是 LB 层 502 的第一大成因,而健康检查失败的第一大成因,是 LB 的探测源地址被后端的某个防火墙拦了。
2)证书过期。 浏览器报 ERR_CERT_DATE_INVALID,表现为红色警告页,而不是 502。查法:
openssl x509 -in /path/to/cert.pem -noout -subject -dates
我这在本机拿一张自签证书跑的真实输出:
subject=CN=demo.example.com
notBefore=Oct 12 02:03:42 2026 GMT
notAfter=Nov 11 02:03:42 2026 GMT
**只看过期时间就用 -enddate。**另外别忘了:证书还在有效期内也照样报错——如果服务器系统时间错了。时间不准会让所有 HTTPS 站点在同一时刻集体失效,且报错五花八门。
3)间歇性的那一类。 时不时出现、刷新又好——除了第四节的 fail_timeout,通常还剩这三个:后端连接池耗尽、PHP-FPM 的 pm.max_children 打满、listen 的 backlog 溢出。这一类要看后端自己的状态,不是 nginx 的。
八、什么时候不该查这一套:三条边界
说清楚边界,省得用错力气:
场景一:静态页正常,动态接口才报错。 那问题基本锁定在后端进程,nginx 只是传话的。别在 nginx 配置上折腾。
场景二:只有部分地区或个别用户报错。 这是链路或 DNS 的事,不是 nginx 到后端这一段。三个地区的用户同时报才是服务端问题。
场景三:错误只在业务高峰期出现。 这是容量问题——fail_timeout、超时时间调再大也只是把错误推后,不会让它消失。该扩容扩容,该加缓存加缓存。
九、没有日志权限时:用 curl 自己把这三个数测出来
这一节是我觉得最实用的一招,尤其是机器不在你手上、或者你只有访问权限的时候。
curl 的 -w 可以把一次请求拆成几段分别计时(文档见 curl 官方《Write out》):
curl -o /dev/null -sS -w 'dns=%{time_namelookup} connect=%{time_connect} firstbyte=%{time_starttransfer} total=%{time_total}\\n' \\
https://demo.example.com/api/order
我本机跑的两个对照结果(一个是正常的,一个是 sleep 3 秒的慢接口):
http=200 dns=0.000042s connect=0.000925s starttransfer(首字节)=0.001808s total=0.001996s
http=200 dns=0.000060s connect=0.001267s starttransfer(首字节)=3.002423s total=3.002719s
看第二行:connect 几乎不耗时,time_starttransfer 却整整 3 秒。 这就是后端的处理时间——它给出的正是 nginx 的 upstream_response_time 那一个量。
而连接失败时,http_code 会是 000(表示压根没拿到响应):
curl: (7) Failed to connect to 127.0.0.1 port 59999 after 2038 ms: Could not connect to server
于是不需要任何日志权限,你也能把失败分成两类:
- firstbyte 接近 0,且状态码为 000 或立刻失败 → 连接被拒绝,对应第三节
- connect 很快、firstbyte 很长 → 后端处理慢,对应第五节
阿里云那份文档里判断 502 来源的方法也是这个逻辑:upstream_response_time 接近 0 说明连接被拒,接近超时阈值说明超时。 两种方法测的是同一个量,一个在 LB 视角,一个在客户端视角。
十、顺序总结
排到这里,我可以给一个能从头走到尾的顺序了。网络连接异常网站服务器失去响应 这类模糊反馈进来时,我按这个走:
最后补一句我的个人看法:别把 proxy_read_timeout 往上调当成解决问题的手段。 60 秒的默认值的意思是"用户不可能等这么久"——把这个尺子调到 300 秒,你得到的不是修好了,而是用户看到一个永远在转圈的空白页,外加一堆挂死的连接占着后端。真正该做的是让后端在 1 秒内答完,或者把这个接口改成异步。
回到开头那句:这些 error_log 里的英文看着吓人,但它们是 nginx 给出的完整答案,不是一个待破译的密码。抄下来、对上表,剩下的都是机械动作。
我写这篇的起因就是那台被重启过两次的机器——网站服务器异常怎么办 的答案从来不在控制面板的重启按钮上,而在那一行被忽略的日志里。先读日志,再动手,这一条比本文所有的命令都值钱。
FAQ
网站服务器异常是什么原因导致的?
绝大多数是 nginx 之后的后端进程出了问题:进程崩了、端口不对、处理太慢、或者 socket 权限不足。nginx 自己出问题反而很少见——它出错时通常是启动失败、直接连不上,而不是返回 502。
怎么区分 502 和 504?
502 是连接层面没谈成(后端没听讲 connect refused),504 是连上了但后端没在 proxy_read_timeout(默认 60 秒)内把话说完。它们分居第三节和第五节,处理思路完全不搭界。
为什么重启服务器之后就好了,过一会儿又不行?
典型的资源耗尽型:进程起来后撑一段时间又被 OOM 杀掉,或连接池被慢慢占满。你去查的时候它刚好活得好好的,所以看起来"好了"。这种情况应该去看 dmesg 里的 OOM 记录,而不是继续重启。
刷新一下就好了,是不是可以忽略?
不能,这恰恰是第四节的典型症状。max_fails 默认为 1,一次抖动就摘 10 秒——刷新一次正好过了这 10 秒。真正的修法是调 fail_timeout 并加 keepalive,而不是让用户靠刷新自救。
只有 HTTPS 打不开,HTTP 能打开,是哪里的问题?
走第七节的证书与时间那部分。用 openssl x509 -noout -enddate 确认证书有效期,再用 date 和系统日志确认服务器时间有没有漂移——证书有效但系统时间错了,结果与证书过期完全一样。
已经到服务器上、时间又很紧,第一步做什么?
先跑 tail -50 /var/log/nginx/error.log,看最后那一行英文。相比之下,网站服务器异常怎么办 这类关键词搜出来的通用答案,大多会让你重启——而重启恰恰会抹掉现场。看完日志再决定要不要重启。
网站服务器不稳定怎么打开网页?
先分清是不稳定(间歇成功)还是彻底打不开。间歇成功优先怀疑 LB 健康检查抖动、fail_timeout 摘除、以及后端连接池耗尽;彻底打不开才按上面的九步从链路查起。这两者的排查路径差别很大,别混着来。
网硕互联帮助中心




评论前必须登录!
注册