Nginx 504 网关超时:完整修复指南

504 Gateway Timeout 表示 Nginx 作为反向代理时,已经成功连接到上游服务器(如 PHP-FPM、Node.js、Gunicorn),但上游在规定时间内没有返回完整响应。与 502 不同,502 是上游连接失败或返回无效响应,而 504 是连接成功了但响应太慢。

这个错误通常有两个层面的原因:一是 Nginx 的超时设置过短,无法等待慢请求完成;二是上游应用本身处理速度太慢,需要优化。本文将从日志分析到缓存配置,逐步解决 504 错误。

广告位

步骤 1:识别超时请求(Nginx 错误日志分析)

首先从 Nginx 错误日志中定位哪些请求触发了 504。日志会明确显示超时发生的上游地址和请求 URI:

# 查看 Nginx 错误日志中的 504 记录
grep "504\|upstream timed out" /var/log/nginx/error.log | tail -20

# 典型日志输出示例:
# [error] 12345#0: *6789 upstream timed out
# (110: Connection timed out) while reading response header
# from upstream, client: 1.2.3.4,
# server: example.com, request: "GET /api/heavy-query HTTP/1.1",
# upstream: "fastcgi://127.0.0.1:9000"

从日志中提取关键信息:upstream 字段显示了超时的后端地址,request 字段显示了具体是哪个 URL 触发了超时。如果只有特定接口频繁超时,问题很可能是该接口的后端逻辑需要优化,而非全局超时设置问题。

步骤 2:增加代理超时设置

如果后端处理确实需要较长时间(如大数据导出、复杂报表生成),可以通过调整 Nginx 超时参数来解决。默认的 proxy_read_timeout 为 60 秒,对于慢请求可能不够:

# 在 server 或 location 块中添加
location /api/ {
    proxy_pass http://backend;

    # 连接上游的超时时间
    proxy_connect_timeout 30s;

    # 向上游发送请求的超时时间
    proxy_send_timeout 60s;

    # 读取上游响应的超时时间(504 的关键参数)
    proxy_read_timeout 300s;

    # 对于 FastCGI(PHP-FPM)使用对应指令
    # fastcgi_read_timeout 300s;
    # fastcgi_connect_timeout 30s;
    # fastcgi_send_timeout 60s;
}

修改后测试配置并重载 Nginx:

sudo nginx -t
sudo nginx -s reload

注意:增大超时只是治标之策。如果后端响应需要 300 秒以上,应该考虑异步处理、任务队列或将长耗时操作拆分为多个短请求。

步骤 3:检查上游应用性能

504 的根本原因往往是上游应用处理太慢。定位慢请求的具体瓶颈:

# 对于 PHP 应用,启用慢日志
# 在 php-fpm.conf 或 pool.d/www.conf 中添加:
# slowlog = /var/log/php-fpm/slow.log
# request_slowlog_timeout = 5s

# 查看慢日志
tail -50 /var/log/php-fpm/slow.log

# 对于 Node.js 应用,使用 --prof 生成性能分析
node --prof app.js

# 对于 Python/Django,启用 SQL 查询日志
# 在 settings.py 中检查 DATABASES 配置
# 确保 CONN_MAX_AGE 设置合理

常见的后端性能瓶颈包括:数据库慢查询(缺少索引、全表扫描)、外部 API 调用超时(第三方支付、短信网关)、内存溢出导致频繁垃圾回收、以及文件 I/O 阻塞。使用应用性能监控工具(如 New Relic、Datadog)可以帮助持续追踪慢请求。

步骤 4:配置 Nginx upstream keepalive

每次请求都重新建立 TCP 连接会带来额外开销,在高并发场景下可能间接导致 504。配置 keepalive 连接池可以复用已有连接,减少握手延迟:

upstream backend {
    server 127.0.0.1:8080;
    # 保持最多 32 个空闲连接
    keepalive 32;
    # 空闲连接超时时间(默认 60s)
    keepalive_timeout 60s;
}

server {
    location / {
        proxy_pass http://backend;

        # 必须设置 HTTP/1.1 才能使用 keepalive
        proxy_http_version 1.1;
        # 清除 Connection 头,使连接可复用
        proxy_set_header Connection "";

        proxy_read_timeout 300s;
    }
}

配置 keepalive 后,Nginx 会维护一个到上游的持久连接池,避免频繁创建和销毁 TCP 连接。这在 PHP-FPM 使用 TCP 模式(而非 Unix socket)时效果尤为明显。

步骤 5:实施缓存

对于响应较慢但内容变化不频繁的接口,配置 Nginx 缓存可以大幅减轻后端压力,从根本上预防 504:

# 在 http 块中定义缓存区域
proxy_cache_path /var/cache/nginx
    levels=1:2
    keys_zone=api_cache:10m
    max_size=1g
    inactive=10m
    use_temp_path=off;

server {
    location /api/ {
        proxy_pass http://backend;
        proxy_cache api_cache;
        # 缓存 200 响应 5 分钟
        proxy_cache_valid 200 5m;
        # 不缓存错误响应
        proxy_cache_valid 404 1m;
        # 添加缓存命中状态头
        add_header X-Cache-Status $upstream_cache_status;
    }
}
# 创建缓存目录并设置权限
sudo mkdir -p /var/cache/nginx
sudo chown nginx:nginx /var/cache/nginx
sudo nginx -t && sudo nginx -s reload

启用缓存后,首次请求仍然会到达后端,但后续相同请求将直接从 Nginx 缓存返回,响应时间从秒级降至毫秒级,大幅降低 504 发生概率。

广告位

快速参考:原因与修复

症状 / 日志信息 根本原因 修复方法
upstream timed out (110) proxy_read_timeout 设置过短 增大 proxy_read_timeout 至 300s 或更长
仅特定 API 接口超时 后端处理逻辑过慢(慢查询、外部调用) 优化数据库查询、添加索引或使用任务队列
高并发时频繁 504 连接池耗尽,每请求新建 TCP 连接 配置 upstream keepalive 和 proxy_http_version 1.1
504 伴随高 CPU 使用率 后端应用 CPU 密集型计算 增加 worker 进程数、优化算法或水平扩展
数据库查询导致的 504 慢 SQL 查询阻塞响应 添加数据库索引、优化查询或启用查询缓存
间歇性 504,负载正常 外部 API 调用偶尔超时 为外部调用设置超时、添加重试机制或降级策略

相关指南