Khắc phục lỗi Nginx 504 Gateway Timeout

intermediate Nginx2026-03-18| Nginx 1.18+ làm reverse proxy phía trước PHP-FPM, Node.js, Gunicorn, hoặc bất kỳ upstream app server nào. Linux (Ubuntu 20.04/22.04, Debian, CentOS).

Error Message

504 Gateway Timeout
#nginx#timeout#gateway#proxy

Lỗi

504 Gateway Timeout

Nginx đang hoạt động như một reverse proxy, và upstream server của bạn — PHP-FPM, Node.js, Gunicorn, hay bất cứ thứ gì — không phản hồi kịp thời. Nginx chờ mãi, chạm đến giới hạn timeout, rồi từ bỏ.

Cần lưu ý: đây không giống với lỗi 502. Lỗi 502 có nghĩa là upstream trả về dữ liệu không hợp lệ hoặc hoàn toàn không thể truy cập. Còn 504 có nghĩa là kết nối đã được thiết lập — upstream chỉ đơn giản là mất quá nhiều thời gian để hoàn thành.

Nguyên nhân

  • Một truy vấn database chậm hoặc lời gọi API bên ngoài đang giữ chân request — câu query DB mất 10 giây trên trang dashboard là thủ phạm điển hình
  • Upstream process (PHP-FPM worker, Node process) bị quá tải hoặc bị kẹt trong vòng lặp
  • Timeout mặc định 60 giây của Nginx quá ngắn cho khối lượng công việc của bạn — chẳng hạn upload file, xuất CSV, hoặc tạo báo cáo
  • max_execution_time của PHP tắt script trước khi timeout của Nginx kịp kích hoạt, khiến upstream bị ngắt giữa chừng

Bước 1 — Kiểm tra Nginx Error Logs trước

Chưa cần động vào file config nào. Hãy xem Nginx đang thực sự báo gì:

sudo tail -f /var/log/nginx/error.log

Thông thường bạn sẽ thấy gì đó như:

[error] upstream timed out (110: Connection timed out) while reading response header from upstream

Dòng đó xác nhận Nginx đã hết thời gian chờ response header. Upstream bắt đầu xử lý nhưng không hoàn thành đủ nhanh. Đó là bằng chứng rõ ràng nhất.

Bước 2 — Tăng thời gian timeout của Nginx Proxy

Giá trị mặc định của proxy_read_timeout trong Nginx là 60 giây. Với những tác vụ thực sự nặng — tạo PDF, gọi API bên thứ ba, xử lý upload lớn — 60 giây tan biến rất nhanh.

Mở file cấu hình server block (thường là /etc/nginx/sites-available/yoursite hoặc /etc/nginx/conf.d/yoursite.conf):

sudo nano /etc/nginx/sites-available/yoursite

Thêm các chỉ thị sau vào trong block location của bạn:

location / {
    proxy_pass http://127.0.0.1:8000;

    proxy_connect_timeout 60s;
    proxy_send_timeout    120s;
    proxy_read_timeout    120s;
}
  • proxy_connect_timeout — thời gian chờ khi mở kết nối đến upstream
  • proxy_send_timeout — thời gian chờ khi gửi request lên upstream
  • proxy_read_timeout — thời gian chờ upstream phản hồi (đây là nguyên nhân gây ra lỗi 504 của bạn)

120s đủ cho hầu hết các trường hợp. Với batch job hoặc upload file lớn, tăng lên 300s. Đừng đặt 600s cho toàn bộ — phần mẹo bên dưới sẽ giải thích tại sao.

Kiểm tra và reload:

sudo nginx -t
sudo systemctl reload nginx

Bước 3 — Sửa timeout của PHP-FPM (nếu dùng PHP)

PHP có giới hạn thực thi riêng. Chúng có thể tắt script trước khi timeout của Nginx kịp kích hoạt. Kiểm tra /etc/php/8.x/fpm/php.ini:

max_execution_time = 120
request_terminate_timeout = 120

Cũng cập nhật file cấu hình pool PHP-FPM (/etc/php/8.x/fpm/pool.d/www.conf):

request_terminate_timeout = 120

Restart để áp dụng:

sudo systemctl restart php8.2-fpm

Bước 4 — Kiểm tra hiệu suất ứng dụng upstream

Tăng timeout chỉ là giải pháp tạm thời. Bạn vẫn cần biết tại sao upstream lại chậm.

Upstream process có đang chạy không?

# Với PHP-FPM
sudo systemctl status php8.2-fpm

# Với Node.js / Gunicorn
ps aux | grep node
ps aux | grep gunicorn

Tất cả worker có đang bị bão hòa không? Một website xử lý 50 request đồng thời với chỉ 5 PHP-FPM worker sẽ xếp hàng các request cho đến khi chúng hết timeout. Kiểm tra worker đang hoạt động:

# Trạng thái PHP-FPM (nếu đã bật trong cấu hình pool)
curl http://127.0.0.1/fpm-status

# Kiểm tra process tổng quát
top -bn1 | grep -E "(php|node|gunicorn|uwsgi)"

Worker bị đầy? Tăng pool lên. Chỉnh sửa /etc/php/8.x/fpm/pool.d/www.conf:

pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10

Bước 5 — Query chậm? Kiểm tra Database

Lỗi 504 chỉ xuất hiện ở một số trang cụ thể — dashboard, báo cáo, endpoint xuất dữ liệu — hầu như đều có thể truy nguyên về một câu query DB chậm. Bật slow query log của MySQL để tìm thủ phạm:

# Trong /etc/mysql/mysql.conf.d/mysqld.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
sudo systemctl restart mysql
sudo tail -f /var/log/mysql/slow.log

Query mất 5–60 giây? Đó là nguyên nhân gây 504. Một index bị thiếu trên bảng 2 triệu dòng có thể biến một query 2ms thành query mất 45 giây. Hãy thêm index, viết lại query, hoặc cache kết quả.

Xác minh

Sau khi thực hiện thay đổi, chạy qua danh sách kiểm tra này:

# Kiểm tra cú pháp cấu hình Nginx
sudo nginx -t

# Reload
sudo systemctl reload nginx

# Theo dõi error log trực tiếp
sudo tail -f /var/log/nginx/error.log

# Đo thời gian của endpoint chậm trực tiếp
curl -w "\nTime total: %{time_total}s\n" -o /dev/null -s https://yoursite.com/slow-endpoint

Nếu time_total luôn dưới ngưỡng timeout và error log không còn ghi lỗi mới, bạn đã hoàn tất.

Mẹo nhanh

  • Giới hạn phạm vi timeout — đừng tăng toàn cục. Chỉ áp dụng timeout dài cho các block location thực sự cần (/api/export, /upload). Timeout toàn cục 300s là mời gọi tấn công slow-loris DoS.
  • Đẩy công việc dài sang hàng đợi — với những tác vụ thực sự mất vài phút, đừng cố chiến đấu với timeout. Đẩy công việc vào background queue (Redis + worker), trả về job ID ngay lập tức, và để client tự poll kết quả. Không còn timeout nữa.
  • fastcgi_read_timeout cho FastCGI — dùng fastcgi_pass thay vì proxy_pass cho PHP? Bạn cần chỉ thị này, không phải proxy_read_timeout:
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    fastcgi_read_timeout 120s;
    include fastcgi_params;
}

Related Error Notes