Trong các sự cố vận hành web server, mã lỗi 504 Gateway Timeout mang một thông điệp rất đặc thù: máy chủ làm nhiệm vụ trung gian (như Cloudflare, Load Balancer, hoặc Nginx reverse proxy) đã kết nối thành công tới dịch vụ ứng dụng phía sau, nhưng sau một khoảng thời gian chờ đợi nhất định (thường là 30s hoặc 60s), dịch vụ phía sau vẫn chưa trả về bất kỳ kết quả nào.
Nói cách khác, 504 không phải là lỗi "không tìm thấy dịch vụ" hay "dịch vụ bị sập ngay lập tức" (như 502 Bad Gateway). Bản chất của 504 là dịch vụ backend vẫn đang sống nhưng đang bị nghẽn nghẹt, treo cứng hoặc xử lý quá tải một tác vụ nào đó.
Sai lầm phổ biến nhất khi xử lý lỗi 504 là vội vã nâng các thông số proxy_read_timeout hay fastcgi_read_timeout từ 60 giây lên 300 giây. Việc tăng timeout một cách cơ học không làm ứng dụng chạy nhanh hơn; nó chỉ khiến các tiến trình backend bị giam giữ kết nối lâu hơn, làm cạn kiệt tài nguyên máy chủ nhanh hơn và người dùng phải chờ đợi màn hình xoay tròn vô ích. Hãy bắt đầu bằng việc xác định chính xác tầng nào phát sinh lỗi và request đang bị tắc ở giai đoạn nào.
504 Gateway Timeout khác 502 Bad Gateway như thế nào?
Trong mô hình triển khai thực tế, một yêu cầu web thường đi qua chuỗi:
Trình duyệt người dùng → Mạng CDN / Cloudflare → Nginx Reverse Proxy → Ứng dụng Backend (Node.js/PHP) → Database / API bên ngoài
Sự khác biệt cốt lõi giữa hai mã lỗi:
- 502 Bad Gateway: Đứt gãy giao tiếp. Nginx vừa bắt tay kết nối tới backend thì bị từ chối thẳng thừng (port chưa mở, socket không tồn tại) hoặc backend bị crash đột ngột (OOM Killer tắt tiến trình) và đóng kết nối ngay lập tức. Lỗi này được phân tích chi tiết tại bài Website lỗi 502: cách kiểm tra Nginx và upstream.
- 504 Gateway Timeout: Quá hạn thời gian chờ. Kết nối TCP giữa Nginx và backend đã được thiết lập thành công, nhưng backend phải nằm chờ một tài nguyên khác (chờ khóa dữ liệu database, chờ API cổng thanh toán trả về, hoặc worker pool bị quá tải) khiến Nginx hết kiên nhẫn và chủ động ngắt kết nối để trả lỗi cho người dùng.
Với lỗi 504, câu hỏi kỹ thuật cốt lõi luôn là: Ứng dụng backend đang phải chờ đợi điều gì?
Bước 1: Xác định tầng kiến trúc nào đang phát ra mã 504
Giao diện trang báo lỗi 504 cho bạn biết tầng nào trong hệ thống vừa ngắt kết nối:
- Màn hình lỗi mang nhận diện của Cloudflare (kèm Error 524 hoặc 504): Cloudflare đã gửi request tới Nginx trên VPS của bạn, nhưng quá 100 giây mặc định (đối với gói Free/Pro) mà VPS chưa trả về phản hồi nào.
- Màn hình trang trắng chữ đen "504 Gateway Time-out" kèm chữ nginx ở dưới: Chính Nginx trên VPS của bạn là bên phát sinh lỗi, do backend nội bộ (PHP-FPM hoặc Node.js) không kịp phản hồi theo giới hạn
proxy_read_timeout(mặc định 60 giây). - Mã 504 trả về dưới dạng JSON từ API backend: Bản thân ứng dụng backend là bên phát sinh timeout khi gọi tới một microservice hoặc API bên thứ ba.
Xác định đúng tầng phát lỗi giúp bạn không chỉnh sửa cấu hình timeout nhầm chỗ.
Bước 2: Phân biệt giữa một endpoint chậm và toàn bộ hệ thống bị nghẽn
Phạm vi xuất hiện lỗi 504 là manh mối quan trọng nhất để khoanh vùng nguyên nhân:
| Phạm vi xảy ra lỗi 504 | Nguyên nhân kỹ thuật nghi vấn hàng đầu |
|---|---|
| Chỉ xuất hiện ở tính năng xuất báo cáo (Export Excel), sao lưu hoặc import dữ liệu | Thuật toán xử lý đồng bộ quá nặng, quét hàng trăm nghìn dòng dữ liệu vượt quá thời gian timeout HTTP |
| Chỉ xuất hiện ở tính năng thanh toán hoặc đặt hàng | Khóa dữ liệu database (Row Lock / Table Lock), hoặc API cổng thanh toán bên thứ ba phản hồi quá chậm |
| Chỉ xuất hiện ở tính năng upload tệp tin lớn | Băng thông mạng tải lên chậm, hoặc tiến trình xử lý ảnh/quét virus đồng bộ làm nghẽn worker |
| Xuất hiện ngẫu nhiên trên toàn bộ các trang động | Hàng đợi worker (PHP-FPM worker pool, Node.js event loop) đã bị cạn kiệt; các request mới phải xếp hàng chờ |
| Xuất hiện cả trên trang tĩnh lẫn trang động | Ổ cứng VPS bị nghẽn I/O 100%, hoặc toàn bộ CPU máy chủ bị chiếm dụng |
Nếu trang chủ hoặc các trang xem sản phẩm vẫn mở mượt mà trong khi chỉ có trang thanh toán báo 504, máy chủ VPS của bạn hoàn toàn khỏe mạnh; vấn đề nằm ở đoạn mã xử lý hoặc câu truy vấn dữ liệu của riêng tính năng thanh toán đó.
Bước 3: Đọc log Nginx và phân tích 3 mốc thời gian Upstream Timing
Để biết request bị nghẽn ở giai đoạn nào, hãy đọc log Nginx. Nếu cấu hình log mặc định chưa đủ chi tiết, bạn cần làm quen với 3 biến thời gian cực kỳ quan trọng của module ngx_http_upstream_module:
$upstream_connect_time: Thời gian để Nginx thiết lập kết nối TCP tới backend.$upstream_header_time: Thời gian tính từ lúc kết nối tới lúc Nginx nhận được byte đầu tiên của phần header từ backend.$upstream_response_time: Tổng thời gian từ lúc gửi request tới khi nhận toàn bộ phản hồi từ backend.
Phân tích ý nghĩa dữ liệu:
- Nếu
upstream_connect_timetăng cao (vài giây tới chục giây): Vấn đề nằm ở tầng mạng nội bộ hoặc bản thân backend đã cạn kiệt khả năng tiếp nhận kết nối mới (hàng đợi kết nối TCP backlog bị đầy, hết file descriptors, hoặc container bị treo). - Nếu
upstream_connect_timerất nhỏ (0.001s) nhưngupstream_header_timechạm trần 60s: Nginx bắt tay với backend gần như tức thì, nhưng backend hoàn toàn im lặng và không trả về được bất kỳ dữ liệu nào. Đây là bằng chứng thép cho thấy logic bên trong backend (code, database, API ngoài) đang bị treo cứng.
Kiểm tra error log của Nginx quanh thời điểm lỗi:
sudo tail -n 50 /var/log/nginx/error.logBạn sẽ thường thấy thông báo dạng: upstream timed out (110: Connection timed out) while reading response header from upstream, client: 1.2.3.4, server: tenmien.com, request: "POST /checkout HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:"
Dòng log trên chỉ rõ: Nginx đã gửi request POST /checkout vào Unix socket của PHP-FPM, nhưng PHP-FPM không trả lại response header trong thời gian quy định.
Bước 4: Ứng dụng Backend đang phải chờ đợi điều gì?
Khi xác định được backend là nơi gây nghẽn, hãy điều tra theo 4 thủ phạm phổ biến nhất trong thực tế:
1. Worker Pool của ứng dụng bị bão hòa
Với PHP-FPM hoặc các mô hình web server dựa trên worker (Gunicorn, Puma), số lượng tiến trình xử lý đồng thời bị giới hạn bởi cấu hình (ví dụ pm.max_children = 20).
Nếu có 20 người dùng cùng thực hiện một tác vụ chậm mất 5 giây, toàn bộ 20 worker sẽ bị chiếm dụng. Lúc này, người dùng thứ 21 gửi request tới sẽ bị đẩy vào hàng đợi chờ (listen.backlog). Nếu hàng đợi này chờ quá lâu mà không có worker nào rảnh tay giải phóng, Nginx sẽ hết hạn chờ và trả về 504.
Kiểm tra log của PHP-FPM:
sudo tail -n 50 /var/log/php8.2-fpm.logNếu bạn nhìn thấy thông báo: WARNING: [pool www] server reached pm.max_children setting (20), consider raising it, nghĩa là worker pool của bạn đã cạn kiệt.
2. Database bị khóa dữ liệu (Deadlock hoặc Lock Wait)
Trong một giao dịch thương mại điện tử, khi trừ tồn kho sản phẩm, mã nguồn thường khóa dòng dữ liệu đó (SELECT ... FOR UPDATE). Nếu giao dịch đó bị treo hoặc chạy quá chậm, toàn bộ các request thanh toán khác muốn mua cùng sản phẩm sẽ phải xếp hàng chờ khóa được giải phóng.
Khi thời gian chờ khóa (innodb_lock_wait_timeout trong MySQL thường là 50 giây) vượt quá giới hạn timeout của Nginx (60 giây), người dùng sẽ nhận ngay mã lỗi 504.
Kiểm tra trạng thái các câu truy vấn và khóa đang diễn ra trên MySQL:
SHOW FULL PROCESSLIST;
-- Hoặc kiểm tra chi tiết bảng khóa InnoDB
SHOW ENGINE INNODB STATUS\GNếu thấy danh sách hàng chục câu query đang ở trạng thái Waiting for table metadata lock hoặc Updating kéo dài hàng chục giây, bạn đã tìm thấy nguyên nhân gốc rễ.
3. Gọi các API bên ngoài mà không cấu hình Timeout hợp lý
Đây là "cái bẫy" rất phổ biến của các lập trình viên: mã nguồn backend gọi sang API của cổng thanh toán, dịch vụ gửi SMS OTP hoặc dịch vụ giao hàng bằng hàm curl hoặc thư viện HTTP mặc định mà không thiết lập thời gian timeout cụ thể.
Nếu cổng thanh toán đối tác bị sự cố mạng và treo kết nối trong 120 giây, tiến trình backend của bạn cũng sẽ ngoan ngoãn treo theo suốt 120 giây đó. Nginx đứng phía trước chỉ chờ 60 giây là ngắt kết nối và trả về 504 cho khách hàng, trong khi worker backend bên trong vẫn tiếp tục bị giam cầm lãng phí.
4. Tác vụ nặng bị xử lý đồng bộ thay vì đẩy vào hàng đợi (Queue)
Các tác vụ xuất file Excel hàng trăm nghìn đơn hàng, nén video hoặc gửi hàng nghìn email bản tin tuyệt đối không được xử lý đồng bộ (synchronous) ngay trong vòng đời của một request HTTP thông thường.
Nếu người dùng bấm nút "Xuất báo cáo" và trình duyệt phải giữ kết nối đợi máy chủ truy vấn và kết xuất dữ liệu mất 90 giây, lỗi 504 là điều tất yếu. Mô hình chuẩn kỹ thuật bắt buộc phải là: ứng dụng tiếp nhận request, đẩy tác vụ vào hàng đợi (Redis Queue / RabbitMQ), trả về thông báo mã 202 Accepted ngay cho người dùng, và để một worker chạy nền xử lý.
Khi nào việc tăng thời gian Timeout là hợp lý?
Tăng thời gian timeout chỉ là giải pháp kỹ thuật đúng đắn khi thỏa mãn đầy đủ các điều kiện sau:
- Đây là một chức năng nội bộ hợp lệ (ví dụ: trang quản trị xuất báo cáo thuế tháng) mà bạn biết rõ nó buộc phải chạy mất 90 giây và tạm thời chưa thể chuyển sang hàng đợi nền.
- Bạn chỉ tăng thời gian timeout trên đúng phạm vi route đó, tuyệt đối không tăng trên toàn bộ website:
# Chỉ tăng timeout riêng cho đường dẫn báo cáo nội bộ
location /admin/reports/export {
proxy_read_timeout 180s;
proxy_connect_timeout 60s;
proxy_pass http://backend;
}- Bạn đã tính toán chắc chắn máy chủ có đủ RAM và dung lượng worker pool để giữ các kết nối dài này mà không làm ảnh hưởng tới người dùng mua hàng ở các trang khác.
Khi nào tăng Timeout chỉ là hành động che giấu lỗi?
Bạn tuyệt đối không được tăng timeout nếu:
- Lỗi 504 xuất hiện trên các trang nghiệp vụ người dùng bình thường (đăng nhập, xem sản phẩm, thanh toán). Người dùng thực tế sẽ không bao giờ kiên nhẫn ngồi chờ một trang web tải quá 5–10 giây.
- Ứng dụng bị chậm do câu truy vấn database thiếu index hoặc bị deadlock. Tăng timeout từ 60s lên 300s chỉ làm cho số lượng câu query bị nghẽn tăng lên gấp 5 lần và đánh sập database nhanh hơn.
- Backend bị treo do gọi API bên ngoài thiếu timeout. Giải pháp đúng là cấu hình timeout cho HTTP client bên trong code ứng dụng (ví dụ: giới hạn gọi API thanh toán tối đa 10 giây).
Các biện pháp hạ nhiệt khẩn cấp trong sự cố 504
Nếu website đang bị lỗi 504 trên diện rộng trong giờ cao điểm theo đúng Quy trình 30 phút đầu khi website production gặp sự cố, hãy thực hiện các bước hạ nhiệt sau:
- Kill các câu truy vấn database bị treo: Nếu phát hiện các câu query chạy scan bảng dữ liệu lớn hoặc gây deadlock chiếm dụng hàng chục kết nối, hãy dùng lệnh
KILL <query_id>trên MySQL để giải phóng khóa dữ liệu ngay lập tức. - Khởi động lại worker pool: Khởi động lại dịch vụ backend (
sudo systemctl restart php8.2-fpmhoặcdocker compose restart backend) để giải phóng toàn bộ các worker đang bị kẹt bởi các request treo. - Tạm dừng các cronjob và tác vụ nền nặng: Tắt các cronjob đồng bộ dữ liệu hoặc import file đang cạnh tranh tài nguyên với web server.
- Cảnh báo quan trọng về tính toàn vẹn dữ liệu (Idempotency): Với các request ghi dữ liệu (như thanh toán trừ tiền hoặc tạo đơn hàng), việc khách hàng nhận mã 504 không đồng nghĩa với việc giao dịch chưa được ghi vào database. Khách hàng thấy timeout thường có xu hướng bấm F5 hoặc bấm nút thanh toán nhiều lần. Nếu hệ thống của bạn không có cơ chế chặn trùng lặp giao dịch (Idempotency Key), việc thử lại có thể khiến khách hàng bị trừ tiền nhiều lần. Hãy kiểm tra kỹ cơ sở dữ liệu trước khi khuyên người dùng thao tác lại.
Checklist khoanh vùng và xử lý lỗi 504 Gateway Timeout
- ☐ Đã xác định rõ tầng nào đang phát ra thông báo 504 (Cloudflare, Nginx VPS hay API ứng dụng).
- ☐ Đã kiểm tra xem lỗi chỉ xảy ra ở 1 tính năng nặng cụ thể hay lan rộng trên toàn bộ website.
- ☐ Đã đọc error log của Nginx và kiểm tra 3 mốc thời gian: connect time, header time và response time.
- ☐ Đã kiểm tra log của backend xem worker pool có bị chạm trần cấu hình (
pm.max_children) hay không. - ☐ Đã kiểm tra danh sách tiến trình database (
SHOW PROCESSLIST) để phát hiện các câu query chậm hoặc deadlock giữ khóa bảng. - ☐ Đã rà soát các cuộc gọi API ra bên thứ ba xem có được thiết lập timeout ngắn hạn hay không.
- ☐ Các tác vụ xử lý tệp tin hoặc kết xuất dữ liệu dài trên 30 giây đã được tách sang hàng đợi nền (Queue) chưa.
- ☐ Nếu buộc phải tăng timeout, chỉ áp dụng cục bộ trên đúng block
locationcủa tính năng đó, không tăng cho toàn site. - ☐ Đã kiểm tra tính toàn vẹn của dữ liệu giao dịch trước khi hướng dẫn khách hàng thực hiện lại thao tác.
Mã lỗi 504 không phải là bản án hỏng hóc phần cứng, mà là lời nhắc nhở rõ ràng rằng một thành phần nào đó trong chuỗi xử lý đang làm việc kém hiệu quả. Bằng cách định vị chính xác điểm nghẽn thời gian, bạn sẽ giải quyết dứt điểm sự cố thay vì chỉ nới rộng thời gian chờ một cách tạm bợ.



