Bạn đang vận hành VPS mượt mà thì đột nhiên website “sập”, màn hình hiện dòng chữ ám ảnh: 502 Bad Gateway. Khách hàng phàn nàn, sếp hối thúc, và áp lực đè nặng lên vai người quản trị.
Ở bài viết trước về cách dùng journalctl để xem log và gỡ lỗi (Troubleshoot) VPS Linux, chúng ta đã biết cách dùng lệnh để “bắt bệnh”. Nếu journalctl chỉ điểm “thủ phạm” là PHP-FPM (backend không phản hồi hoặc từ chối kết nối), thì bài viết này chính là đơn thuốc đặc trị.
Chúng ta sẽ không chỉ khởi động lại service bằng Systemctl để “chữa cháy” tạm thời. Bài viết này sẽ hướng dẫn bạn cách tính toán tài nguyên, sửa lỗi 502 Nginx tận gốc bằng cách tối ưu file cấu hình, và sử dụng các công cụ debug nâng cao (như strace, slowlog) mà các SysAdmin chuyên nghiệp tin dùng.

Tóm tắt nhanh: Lỗi 502 Bad Gateway trên Nginx thường xảy ra khi PHP-FPM không thể phản hồi yêu cầu từ Nginx do quá tải (hết worker), cấu hình sai hoặc process bị crash. Cách khắc phục triệt để là tính toán lại
pm.max_childrendựa trên RAM thực tế và tối ưu hóalisten.backlog.
Giải mã lỗi 502 và 504 trong log (Đừng nhầm lẫn!)
Khi gặp sự cố, phản xạ đầu tiên là xem log. Nhưng bạn cần phân biệt rõ hai mã lỗi “anh em” nhưng khác hẳn nhau về bản chất này để không sửa sai hướng.
Phân biệt 502 Bad Gateway vs 504 Gateway Time-out
Rất nhiều hướng dẫn gộp chung hai lỗi này, nhưng tài liệu kỹ thuật của Nginx chỉ ra sự khác biệt cốt lõi:
- Lỗi 504 Gateway Time-out:
- Hiện tượng: Nginx chờ đợi mòn mỏi nhưng PHP-FPM xử lý quá lâu và không trả về kết quả trong thời gian quy định (mặc định 60s).
- Nguyên nhân: Code PHP chạy quá nặng, vòng lặp vô tận, hoặc query Database bị treo.
- Lỗi 502 Bad Gateway:
- Hiện tượng: Nginx cố gắng kết nối tới PHP-FPM nhưng bị từ chối (Connection Refused) hoặc kết nối đang thiết lập thì bị ngắt đột ngột (Connection Reset by Peer).
- Nguyên nhân: PHP-FPM bị sập (crash), quá tải không còn chỗ chứa (full backlog), hoặc tiến trình worker bị hệ thống “giết” (kill) do ngốn quá nhiều RAM.
Dấu hiệu “kinh điển” trong Log
Hãy dùng lệnh journalctl hoặc tail để soi log (như đã học ở bài Rsyslog: Cấu hình log tập trung VPS):
# Xem log lỗi của PHP-FPM
tail -f /var/log/php-fpm/www-error.log
Nếu bạn thấy dòng cảnh báo này, server của bạn đang kêu cứu vì thiếu “công nhân”:
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
Điều này có nghĩa là PHP-FPM đã dùng hết số lượng worker được cấp phép. Các request mới đến phải xếp hàng chờ. Khi hàng chờ quá tải, Nginx sẽ không thể kết nối và báo lỗi 502 Nginx.
Công thức “vàng” tính toán pm.max_children

Đừng bao giờ sửa pm.max_children theo cảm tính (ví dụ: thấy lỗi thì tăng đại lên 100, 200). Nếu bạn set quá cao, VPS sẽ hết RAM (Out of Memory – OOM), dẫn đến treo cứng toàn bộ hệ thống, buộc bạn phải Hard Reboot.
Mẹo: Nếu VPS của bạn quá ít RAM vật lý, hãy cân nhắc tạo bộ nhớ Swap (Swap memory) trong VPS Linux để tránh bị crash đột ngột.
Hãy dùng toán học để tìm con số chính xác cho VPS của bạn.
Bước 1: Xác định dung lượng RAM dành cho PHP
Bạn không thể dùng 100% RAM cho PHP. Bạn phải chừa lại cho OS, Database, Nginx, Cache,…
Công thức:
$$RAM_{PHP} = RAM_{Total} – RAM_{Reserved}$$
- $RAM_{Total}$: Tổng RAM của VPS.
- $RAM_{Reserved}$: RAM dành riêng cho hệ thống và các dịch vụ khác.
- VPS 2GB – 4GB: Nên dành khoảng 1GB – 1.5GB cho System + MySQL.
- VPS lớn hơn: Tùy thuộc vào độ lớn của Database.
Bước 2: Đo lường RAM tiêu thụ của 1 Process
Mỗi website có code khác nhau (WordPress nặng hơn code thuần). Đừng đoán mò. Hãy dùng lệnh sau để đo chính xác dung lượng RAM trung bình một tiến trình PHP-FPM đang sử dụng:
ps aux | grep 'php-fpm: pool' | awk '{sum+=$6} END {if (NR>0) print sum/NR/1024 " MB"}'
Lệnh này tính trung bình cột RSS (Resident Set Size) của các tiến trình PHP.
Giả sử kết quả trả về là: 60 MB.
Bước 3: Tính toán pm.max_children
Ví dụ: VPS của bạn có 4GB RAM, chạy WordPress.
- Dành cho System/MySQL: 1.5GB.
- Còn lại cho PHP: $4GB – 1.5GB = 2.5GB = 2560MB$.
- RAM mỗi process: 60MB.
$$pm.max\_children = \frac{2560}{60} \approx 42.6$$
=> Con số an toàn bạn nên đặt là 40.
Tối ưu file cấu hình www.conf (Thực hành)
Bây giờ, hãy áp dụng con số 40 vào file cấu hình.
Mở file (ví dụ PHP 8.1):
sudo nano /etc/php/8.1/fpm/pool.d/www.conf
Tìm và chỉnh sửa các thông số sau để xử lý triệt để lỗi 502 Nginx:
Chọn chế độ quản lý (Process Manager)
pm = dynamic
- static: Số worker cố định. Tốt nhất cho hiệu suất nhưng ngốn RAM liên tục (kể cả khi không có khách). Chỉ dùng cho VPS rất mạnh.
- ondemand: Chỉ bật worker khi có khách. Tiết kiệm RAM nhất nhưng gây trễ (latency) cho người đầu tiên.
- dynamic: (Khuyên dùng) Cân bằng giữa hiệu suất và RAM. Tự động tăng giảm worker theo nhu cầu.
Bộ tham số pm.* (Dựa trên con số 40 đã tính)
Đây là cấu hình mẫu cho VPS 4GB RAM:
pm.max_children = 40 ; Giới hạn tối đa (tránh sập server)
pm.start_servers = 10 ; Khởi chạy ngay 10 worker ban đầu (25% của max)
pm.min_spare_servers = 10 ; Luôn giữ 10 worker rảnh rỗi để đón khách
pm.max_spare_servers = 15 ; Không giữ quá 15 worker rảnh (để trả RAM cho OS)
Tham số “listen.backlog”: Hàng chờ bí mật (Quan trọng cho Traffic Spike)
Bạn đã tăng pm.max_children lên 40, nhưng nếu có 50 khách truy cập cùng lúc trong 1 giây (Flash sale) thì sao? 10 khách dư ra sẽ đi đâu? Họ sẽ được đưa vào hàng đợi Backlog. Nếu hàng đợi này đầy nốt, Nginx sẽ nhận được tín hiệu “Connection Refused” và trả về lỗi 502 ngay lập tức.
Mặc định, listen.backlog thường là 511 (trên Linux). Với các website chịu tải cao (Xem thêm: Hướng dẫn cấu hình Nginx FastCGI Cache để giảm tải cho PHP), bạn nên tăng số này lên.
Trong www.conf:
listen.backlog = 4096
Lưu ý quan trọng: Tham số này sẽ vô dụng nếu Hệ điều hành giới hạn thấp hơn nó. Hãy kiểm tra giới hạn của Linux:
sysctl net.core.somaxconn
Nếu kết quả < 4096, bạn cần sửa file /etc/sysctl.conf, thêm dòng net.core.somaxconn = 4096 và chạy sysctl -p.
Ngăn chặn rò rỉ bộ nhớ (Memory Leak)
Nhiều code PHP viết không chuẩn sẽ không giải phóng hết RAM sau khi chạy xong. Theo thời gian, RAM bị đầy dần. Để khắc phục, hãy bắt buộc worker “nghỉ hưu” sau một số lượng request nhất định.
pm.max_requests = 500
Nghĩa là: Cứ xử lý xong 500 lượt truy cập, worker đó sẽ bị hủy và thay thế bằng một worker mới “sạch sẽ”.
Xử lý vấn đề Timeout (Tránh xung đột cấu hình)

Một nguyên nhân gây lỗi 502 rất phổ biến nhưng ít người để ý là sự xung đột giữa timeout của PHP và Nginx.
Trong file www.conf có tham số request_terminate_timeout. Đây là “lưỡi hái tử thần”. Nếu script chạy quá thời gian này, PHP-FPM sẽ giết worker đó ngay lập tức. Kết quả là Nginx bị ngắt kết nối đột ngột và báo 502.
Nguyên tắc: Thời gian chờ của Nginx (fastcgi_read_timeout) phải LỚN HƠN hoặc BẰNG thời gian cho phép của PHP.
Cấu hình khuyến nghị:
- Trong
www.conf:request_terminate_timeout = 60s - Trong Nginx (
nginx.confhoặc block server):fastcgi_read_timeout 60s;
Nếu bạn cần chạy các tác vụ import/export nặng, hãy tăng cả hai số này lên cùng lúc (ví dụ 300s).
Debug nâng cao (Dành cho SysAdmin)
Nếu bạn đã làm hết các bước trên mà vẫn chưa tìm ra nguyên nhân (thậm chí đã thử Không SSH được vào VPS Linux: 10 nguyên nhân và cách sửa lỗi mà vẫn bế tắc), hãy dùng đến các công cụ “hạng nặng” sau.
Bật Slowlog (Nhật ký rùa bò)
Slowlog giúp bạn chỉ mặt đặt tên xem file code nào đang làm chậm hệ thống.
Thêm vào www.conf:
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 5s
Sau khi restart PHP-FPM, bất kỳ script nào chạy lâu hơn 5s sẽ bị ghi lại kèm theo “trace” (dấu vết) cụ thể dòng code nào đang chạy.
Theo dõi Real-time với Status Page
Thay vì đoán, hãy xem biểu đồ sức khỏe server. Trong www.conf, bỏ comment:
pm.status_path = /status
Trong cấu hình Nginx, thêm block sau (Lưu ý dùng location = để tối ưu hiệu năng và tránh trùng lặp):
location = /status {
access_log off; # Tắt log truy cập để đỡ rác file log
allow 127.0.0.1; # CHỈ cho phép IP nội bộ (Bảo mật tuyệt đối)
deny all; # Chặn mọi IP khác
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
Sau đó bạn có thể dùng lệnh curl http://127.0.0.1/status để xem thông số Active processes (đang chạy) và Max children reached (đã từng chạm đỉnh chưa).
Vũ khí tối thượng: strace
Khi log không báo lỗi, status bình thường, nhưng process vẫn bị treo? Hãy dùng strace để “chụp X-quang” xem tiến trình đang làm gì (ví dụ: đang đợi kết nối Database hay bị kẹt khi ghi file).
# Tìm PID của php-fpm pool
pgrep -a php-fpm
# Gắn strace vào tiến trình con (thay 1234 bằng PID)
sudo strace -p 1234 -s 100
Kiểm tra và áp dụng (Safety First)

Một sai lầm chết người của người mới là sửa xong restart ngay, nếu gõ sai 1 dấu chấm phẩy, service sẽ sập hoàn toàn. Hãy luôn kiểm tra cú pháp trước khi restart:
# Kiểm tra lỗi cú pháp PHP-FPM
sudo php-fpm8.1 -t
# Kiểm tra lỗi cú pháp Nginx
sudo nginx -t
Nếu màn hình hiện “Test is successful”, đây là lúc bạn áp dụng thay đổi.
Lưu ý quan trọng:
- Với PHP-FPM: Nên dùng restart để đảm bảo mọi worker cũ (có thể đang bị lỗi) được dọn dẹp sạch sẽ và thay thế bằng worker mới.
- Với Nginx: Luôn dùng reload để áp dụng cấu hình mới mà không làm ngắt kết nối của người dùng hiện tại (Zero-downtime).
# Restart PHP-FPM (Làm mới hoàn toàn)
sudo systemctl restart php8.1-fpm
# Reload Nginx (Không ngắt kết nối)
sudo systemctl reload nginx
Câu hỏi thường gặp (FAQ)
1. Tại sao VPS của tôi có 16GB RAM nhưng vẫn bị lỗi 502 dù lượng truy cập không quá cao?
Rất có thể bạn vẫn đang để cấu hình mặc định của PHP-FPM. Theo mặc định, pm.max_children thường được đặt là 5. Điều này có nghĩa là dù bạn có 16GB hay 128GB RAM, server cũng chỉ cho phép tối đa 5 người được xử lý đồng thời; người thứ 6 sẽ bị chặn và gây lỗi 502. Hãy áp dụng công thức tính toán ở Phần 2 để tận dụng hết tài nguyên RAM của bạn.
2. Tại sao tôi restart PHP-FPM thì hết lỗi nhưng một lúc sau lại bị lại?
Đây là dấu hiệu điển hình của việc rò rỉ bộ nhớ (Memory Leak). Các worker của PHP sau khi xử lý code (đặc biệt là WordPress với nhiều plugin nặng) thường không giải phóng hoàn toàn RAM. Sau một thời gian, RAM bị đầy và gây crash.
- Giải pháp: Hãy kiểm tra tham số
pm.max_requeststrong file cấu hình. Đừng để giá trị là0(vô hạn). Hãy đặt nó khoảng500hoặc1000để bắt buộc worker tự tái tạo sau một số lượng request nhất định, giúp giải phóng RAM “rác”.
3. Tôi nên chọn chế độ pm = static hay pm = dynamic?
static: Tốt nhất cho hiệu suất vì số lượng worker cố định, không mất thời gian khởi tạo. Tuy nhiên, nó sẽ “chiếm dụng” RAM ngay lập tức kể cả khi không có khách. Chỉ nên dùng nếu bạn hiểu rõ VPS của mình và tài nguyên dồi dào.dynamic: (Khuyên dùng) Linh hoạt hơn, tiết kiệm RAM khi ít khách và tự động mở rộng khi đông khách. Đây là lựa chọn an toàn cho đa số VPS Web Server.
4. Tôi đã sửa file www.conf nhưng không thấy thay đổi có hiệu lực?
Có hai lý do phổ biến:
- Bạn quên khởi động lại dịch vụ. Hãy chạy lệnh
sudo systemctl restart php8.1-fpm(Xem thêm: Hướng dẫn systemctl: Sửa lỗi VPS bằng Start, Stop, Restart dịch vụ). - Bạn sửa nhầm file. Một số hệ thống có nhiều phiên bản PHP cài song song (ví dụ cả 7.4 và 8.1). Hãy kiểm tra kỹ đường dẫn
/etc/php/PHIÊN_BẢN/fpm/pool.d/.
5. Ngoài việc tăng max_children, còn cách nào giảm tải cho PHP-FPM không?
Có. PHP-FPM chỉ nên xử lý các tác vụ động. Với các nội dung tĩnh hoặc ít thay đổi, bạn nên cấu hình Caching ở tầng Nginx. Việc này giúp Nginx trả kết quả ngay lập tức mà không cần gọi đến PHP-FPM, giảm nguy cơ quá tải worker. Tham khảo chi tiết: Hướng dẫn cấu hình Nginx FastCGI Cache: Tối ưu VPS Linux từ A-Z.
6. Tôi thấy log báo lỗi connect() to unix:/run/php/php-fpm.sock failed (13: Permission denied)?
Lỗi này xảy ra do Nginx (thường chạy dưới user www-data hoặc nginx) không có quyền đọc/ghi vào file socket của PHP.
- Cách sửa: Mở file
www.conf, tìm đến phầnlisten.ownervàlisten.group, bỏ comment và sửa thành user tương ứng của Web Server (ví dụwww-data). Sau đó restart lại PHP-FPM.
7. Sự khác biệt chính giữa lỗi 502 và 504 là gì?
Tuy cả hai đều làm website không truy cập được, nhưng nguyên nhân xử lý khác nhau:
- 502 Bad Gateway: Là lỗi “Mất kết nối”. Nginx không thể kết nối được tới PHP-FPM (do PHP bị tắt, bị crash, hoặc socket bị quá tải).
- 504 Gateway Time-out: Là lỗi “Chờ quá lâu”. Kết nối vẫn thành công, nhưng PHP xử lý quá chậm (do code nặng, treo Database) khiến Nginx hết kiên nhẫn và ngắt kết nối.
Kết luận
Lỗi 502 Nginx trên VPS phần lớn xuất phát từ việc cấu hình mặc định của PHP-FPM quá thấp so với nhu cầu thực tế. Việc tính toán pm.max_children một cách khoa học, kết hợp với tinh chỉnh listen.backlog và đồng bộ Timeout sẽ giúp VPS của bạn ổn định vững chắc trước các đợt sóng traffic.
Gỡ lỗi là một nghệ thuật đi từ quan sát (journalctl), phân tích (slowlog, status), đến hành động (systemctl). Hy vọng bài viết này đã trang bị cho bạn đủ “vũ khí” để xử lý triệt để lỗi 502.
Bạn đã bao giờ gặp tình huống
pm.max_childrenvẫn đủ nhưng server vẫn 502 do hết kết nối MySQL (max_connections) chưa? Nếu có, hãy tham khảo thêm bài viết Tối ưu hóa MySQL trên VPS: Toàn tập về Caching, Indexing và chia sẻ trải nghiệm thực tế đó ở phần bình luận nhé!
