Sửa lỗi Nginx 502 Bad Gateway khi deploy Node.js và Docker
Quy trình tìm nguyên nhân Nginx 502 Bad Gateway: upstream chưa chạy, sai port, bind interface, socket, Docker network, timeout và log.
Mục lục bài viết
VPS & Hosting cho dự án của bạn
Khám phá các gói máy chủ và lưu trữ phù hợp từ website cá nhân đến ứng dụng production.
502 Bad Gateway là một trong những lỗi production phổ biến khi Nginx đứng trước Node.js, PM2 hoặc Docker.
Thông báo này thường không có nghĩa Nginx bị hỏng. Nó thường cho biết Nginx không nhận được phản hồi hợp lệ từ upstream.
Kiến trúc
Client
↓
Nginx
↓
Upstream app
Nginx trả 502 khi có vấn đề giữa:
Nginx ↔ Upstream
Ví dụ:
- App chưa chạy.
- Sai port.
- App crash.
- Connection refused.
- Upstream đóng kết nối.
- Socket permission.
- DNS/container name sai.
- Protocol HTTP/HTTPS sai.
- Response header không hợp lệ.
Bước 1: Xác định server block
Xem toàn bộ config:
sudo nginx -T
Tìm domain:
sudo nginx -T 2>/dev/null |
grep -n "api.deployeasy.vn"
Kiểm tra:
server_name api.deployeasy.vn;
và:
proxy_pass http://127.0.0.1:3001;
Đảm bảo request đang vào đúng server block.
Bước 2: Xem Nginx error log
sudo tail -n 100 \
/var/log/nginx/error.log
Nếu có log riêng:
sudo tail -n 100 \
/var/log/nginx/deployeasy-api.error.log
Realtime:
sudo tail -f /var/log/nginx/error.log
Các thông báo quan trọng:
connect() failed (111: Connection refused)
upstream timed out
no live upstreams
permission denied
upstream prematurely closed connection
host not found in upstream
Log thường chỉ thẳng nhóm nguyên nhân.
Connection refused
Ví dụ:
connect() failed (111: Connection refused)
while connecting to upstream
Nginx đã tới IP/port nhưng không có process lắng nghe.
Kiểm tra:
sudo ss -ltnp | grep :3001
Test trực tiếp:
curl -v http://127.0.0.1:3001
Nếu curl cũng fail, sửa app trước, không sửa Nginx.
App chưa chạy
PM2:
pm2 list
pm2 logs deployeasy-api --lines 200
systemd:
sudo systemctl status deployeasy-api
sudo journalctl -u deployeasy-api -n 200
Docker:
docker compose ps
docker compose logs --tail=200 api
Sai port
Nginx:
proxy_pass http://127.0.0.1:3001;
Nhưng app log:
Listening on port 3000
Sửa một trong hai và giữ tài liệu rõ ràng.
Kiểm tra:
sudo ss -ltnp
App bind sai interface
App trên host
Nếu Nginx cùng host, bind:
127.0.0.1:3001
là phù hợp.
App trong container
App bên trong container thường cần bind:
0.0.0.0:3000
Nếu bind 127.0.0.1 trong container, Docker port mapping có thể không truy cập được.
NestJS:
await app.listen(3000, "0.0.0.0");
Host chỉ publish:
ports:
- "127.0.0.1:3001:3000"
Nginx proxy:
proxy_pass http://127.0.0.1:3001;
Docker container không publish port
Compose:
services:
api:
ports:
- "127.0.0.1:3001:3000"
Kiểm tra:
docker compose ps
docker port CONTAINER_NAME
Nếu Nginx chạy trên host, nó không tự truy cập được container-only port nếu không có network/port phù hợp.
Nginx cũng chạy trong Docker
Nếu Nginx và API cùng Compose network:
proxy_pass http://api:3000;
Không dùng:
proxy_pass http://127.0.0.1:3000;
vì localhost trong Nginx container là chính Nginx container.
Compose:
services:
nginx:
networks:
- app
api:
networks:
- app
networks:
app:
Kiểm tra DNS trong Nginx container:
docker compose exec nginx getent hosts api
Test:
docker compose exec nginx \
curl -v http://api:3000/health
Image Nginx có thể không có curl; dùng wget hoặc debug container phù hợp.
host not found in upstream
Ví dụ:
host not found in upstream "api"
Nguyên nhân:
- Service name sai.
- Không cùng Docker network.
- Nginx start trước DNS/service và config resolve theo cách không phù hợp.
- Container đã đổi tên.
- Dùng hostname không phân giải được.
Kiểm tra:
docker compose config
docker network inspect NETWORK_NAME
HTTP và HTTPS upstream sai
App chỉ chạy HTTP:
http://127.0.0.1:3001
Nhưng Nginx dùng:
proxy_pass https://127.0.0.1:3001;
TLS handshake sẽ fail.
Hoặc app chạy HTTPS nội bộ nhưng Nginx dùng HTTP.
Chọn đúng scheme:
proxy_pass http://127.0.0.1:3001;
Thông thường HTTPS kết thúc tại Nginx và upstream local dùng HTTP.
Unix socket permission
Nginx có thể proxy tới socket:
proxy_pass http://unix:/run/deployeasy/api.sock;
Lỗi:
permission denied while connecting to upstream
Kiểm tra:
ls -l /run/deployeasy/api.sock
namei -l /run/deployeasy/api.sock
Nginx worker user cần quyền traverse directory và truy cập socket.
Không sửa bằng chmod 777; dùng owner/group đúng.
SELinux hoặc AppArmor
Trên một số distro, MAC policy có thể chặn Nginx kết nối upstream.
Ubuntu thường dùng AppArmor; RHEL-family thường dùng SELinux.
Kiểm tra log:
sudo journalctl -k --since "30 minutes ago"
Không tắt toàn bộ security module để sửa nhanh. Tìm policy cần điều chỉnh.
upstream timed out
Ví dụ:
upstream timed out
while reading response header from upstream
Nginx kết nối được nhưng app phản hồi quá lâu.
Kiểm tra:
time curl -v http://127.0.0.1:3001/slow-endpoint
Nguyên nhân:
- Query database chậm.
- Deadlock.
- API bên thứ ba.
- CPU/RAM cạn.
- Event loop bị block.
- Timeout app.
- File upload/xử lý đồng bộ.
Tạm điều chỉnh:
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
Không tăng timeout vô hạn. Sửa nguyên nhân app chậm.
upstream prematurely closed connection
App đóng connection trước khi Nginx nhận response hoàn chỉnh.
Kiểm tra app log cùng thời điểm:
pm2 logs
sudo journalctl -u deployeasy-api
docker compose logs api
Nguyên nhân:
- App crash.
- Unhandled exception.
- OOM kill.
- Process restart.
- Response quá lớn/lỗi.
- Timeout trong app.
- Client aborted và app xử lý sai.
App bị OOM
Kiểm tra:
free -h
sudo journalctl -k |
grep -i -E 'out of memory|killed process'
Docker:
docker inspect CONTAINER \
--format '{{.State.OOMKilled}}'
Nếu app bị kill, Nginx sẽ không kết nối được và trả 502.
PM2 restart loop
pm2 list
pm2 describe deployeasy-api
Xem restart count và log.
App có thể chạy vài giây rồi crash, khiến 502 xuất hiện không liên tục.
Health endpoint
Tạo endpoint nhẹ:
GET /health
Kiểm tra upstream:
curl -f http://127.0.0.1:3001/health
Nginx:
curl -f https://api.deployeasy.vn/health
Nếu upstream health fail, lỗi nằm trước Nginx public layer.
Kiểm tra Host header
Một số app cần domain đúng.
curl -v \
-H 'Host: api.deployeasy.vn' \
http://127.0.0.1:3001
Nginx:
proxy_set_header Host $host;
Header WebSocket
Nếu lỗi chỉ xảy ra với WebSocket:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Kiểm tra cả route và CORS/origin của WebSocket server.
Buffer và response header
Nginx có giới hạn buffer cho response header. Cookie quá lớn hoặc nhiều header có thể gây lỗi.
Log có thể báo:
upstream sent too big header
Nguyên nhân thường là cookie/header quá lớn. Tốt hơn là giảm header thay vì tăng buffer vô hạn.
Có thể điều chỉnh:
proxy_buffer_size 16k;
proxy_buffers 8 16k;
Nhưng phải đo và hiểu.
DNS của upstream ngoài
Nếu proxy tới hostname:
proxy_pass https://backend.example.com;
DNS resolution và certificate upstream có thể gây lỗi.
Kiểm tra từ VPS:
dig backend.example.com
curl -Iv https://backend.example.com
Nginx config chưa reload
Sau khi sửa:
sudo nginx -t
sudo systemctl reload nginx
Kiểm tra config active:
sudo nginx -T
Đừng chỉ nhìn file vừa sửa; có thể Nginx đang dùng file khác.
Default site xung đột
Ubuntu có thể có:
/etc/nginx/sites-enabled/default
Nếu server block không khớp, request rơi vào default.
Có thể disable sau khi chắc chắn:
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
Không xóa file trước khi hiểu cấu hình.
Quy trình chẩn đoán 502
Tầng 1: DNS và Nginx
dig api.deployeasy.vn
sudo systemctl status nginx
sudo nginx -t
Tầng 2: Upstream local
curl -v http://127.0.0.1:3001/health
sudo ss -ltnp | grep :3001
Tầng 3: Process manager
PM2:
pm2 list
pm2 logs --lines 200
systemd:
sudo systemctl status deployeasy-api
sudo journalctl -u deployeasy-api -n 200
Docker:
docker compose ps
docker compose logs --tail=200 api
Tầng 4: Tài nguyên
free -h
df -h
df -i
uptime
Tầng 5: Database/dependency
pg_isready
curl -Iv https://dependency.example.com
Ví dụ lỗi Docker
Compose:
services:
api:
ports:
- "3000:3000"
App bind:
app.listen(3000, "127.0.0.1");
Host curl fail.
Sửa:
app.listen(3000, "0.0.0.0");
Host chỉ publish local:
ports:
- "127.0.0.1:3000:3000"
Ví dụ sai port
Nginx:
proxy_pass http://127.0.0.1:3000;
PM2 app:
PORT=3001
Sửa một nguồn cấu hình và dùng healthcheck để xác minh.
Ví dụ app crash do env
Log:
DATABASE_URL is required
Sửa EnvironmentFile/Compose env, rồi restart:
sudo systemctl restart deployeasy-api
hoặc:
docker compose up -d api
Không nên làm
Restart mọi thứ ngay
Restart có thể xóa trạng thái/log tạm và không tìm được nguyên nhân.
Tăng timeout vô hạn
Che app chậm và giữ connection lâu.
chmod 777
Không sửa được sai port hoặc app crash, đồng thời tăng rủi ro.
Ép status 200 trong Nginx
Che lỗi thay vì sửa upstream.
Checklist
[ ] Đúng server block
[ ] Xem error log
[ ] curl upstream trực tiếp
[ ] Port đúng
[ ] Process đang chạy
[ ] App bind đúng interface
[ ] Docker publish/network đúng
[ ] HTTP/HTTPS scheme đúng
[ ] Socket permission đúng
[ ] Health endpoint
[ ] Không OOM
[ ] Database hoạt động
[ ] Timeout được đo
[ ] nginx -t + reload
[ ] Monitoring restart count
Câu hỏi thường gặp
502 có phải do DNS không?
DNS sai thường đưa request tới server khác hoặc không kết nối. 502 thường phát sinh sau khi request tới reverse proxy nhưng upstream gặp vấn đề.
Restart Nginx có sửa 502 không?
Chỉ khi Nginx/config có vấn đề. Nếu app không chạy, restart Nginx không giải quyết gốc.
502 và 504 khác nhau thế nào?
502 thường là phản hồi upstream không hợp lệ hoặc kết nối lỗi; 504 thường liên quan timeout chờ upstream. Log là nguồn xác nhận.
App local chạy nhưng domain 502?
Kiểm tra Nginx proxy port, interface, Docker network, scheme và server block.
Kết luận
Cách sửa 502 nhanh nhất là đi theo luồng request và test từng điểm. Bắt đầu từ error log và curl trực tiếp upstream, thay vì thay đổi nhiều cấu hình cùng lúc.
Nguồn tham khảo
Đọc tiếp
Bài viết liên quan
Nginx Reverse Proxy là gì? Cấu hình cho Node.js production
Giải thích Nginx reverse proxy, server block, proxy_pass, header, timeout và cách cấu hình nhiều domain cho ứng dụng Node.js trên VPS.
Đọc bài →Cài HTTPS miễn phí bằng Certbot và Nginx trên Ubuntu
Hướng dẫn cài chứng chỉ HTTPS bằng Certbot cho Nginx, kiểm tra domain, firewall, tự động gia hạn và xử lý lỗi certificate.
Đọc bài →Deploy Node.js production bằng PM2 và Nginx trên VPS
Hướng dẫn deploy ứng dụng Node.js bằng PM2, ecosystem file, biến môi trường, startup, log, Nginx reverse proxy và rollback.
Đọc bài →