Thông báo lỗi
net::ERR_EMPTY_RESPONSE
Bạn đang nhìn chằm chằm vào một màn hình trắng xóa trong Chrome. Tab Network của DevTools hiển thị trạng thái là (failed) và console liệt kê lỗi net::ERR_EMPTY_RESPONSE. Đây không phải là lỗi 404 hay 500 tiêu chuẩn nơi máy chủ giải thích điều gì đã xảy ra. Thay vào đó, máy chủ về cơ bản đã "dập máy" trước khi kịp nói một lời nào. Nó đã thiết lập kết nối TCP nhưng lại gửi một gói tin FIN hoặc RST thay vì một phản hồi HTTP thực tế.
Trên các máy chủ đang hoạt động, điều này thường báo hiệu sự không khớp giữa bộ cân bằng tải (load balancer) và máy chủ web. Nó cũng có thể có nghĩa là một tiến trình backend đã bị sập hoặc một gói tin mạng đã bị rơi (dropped) trong quá trình truyền tải.
Bước 1: Điều chỉnh cài đặt Nginx Keep-Alive
Hầu hết các trường hợp ERR_EMPTY_RESPONSE bắt nguồn từ xung đột thời gian chờ keep-alive (keep-alive timeout). Hãy tưởng tượng AWS ALB hoặc Cloudflare của bạn mong đợi một kết nối duy trì mở trong 60 giây. Nếu máy chủ Nginx của bạn ngắt kết nối đó sau 5 giây, trình duyệt có thể cố gắng sử dụng lại một kết nối không còn tồn tại.
Mở tệp cấu hình Nginx của bạn, thường nằm tại /etc/nginx/nginx.conf, và kiểm tra các giá trị sau:
http {
# Đặt giá trị này cao hơn thời gian chờ của bộ cân bằng tải
keepalive_timeout 75;
keepalive_requests 1000;
}
Luôn đặt keepalive_timeout cao hơn khoảng 5 đến 10 giây so với bất kỳ proxy nào đứng trước bạn. Nếu bạn sử dụng Nginx làm reverse proxy, bạn cũng nên tinh chỉnh keep-alive ở phía upstream để ngăn chặn backend ngắt các yêu cầu sớm:
upstream backend_servers {
server 127.0.0.1:8080;
keepalive 32;
}
Bước 2: Mở rộng Upstream Read Timeouts
Nếu backend Node.js hoặc PHP của bạn mất 61 giây để xử lý một báo cáo nặng nhưng Nginx lại bị giới hạn ở mức 60 giây, kết nối sẽ bị đứt. Điều này thường gây ra lỗi phản hồi trống vì kết nối bị ngắt trước khi Nginx kịp tạo ra lỗi 504 Gateway Timeout.
Hãy thử tăng các giá trị này trong khối location của bạn lên 300 giây để xem lỗi có biến mất hay không:
location / {
proxy_read_timeout 300s;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_pass http://backend_servers;
}
Bước 3: Truy tìm sự cố sập Backend
Đôi khi Nginx vẫn hoạt động hoàn hảo, nhưng dịch vụ backend lại gặp sự cố trong khi xử lý dữ liệu. Nếu PHP-FPM gặp lỗi segmentation fault hoặc tiến trình Node.js vượt quá giới hạn 1GB RAM, socket sẽ bị ngắt ngay lập tức. Máy chủ không còn gì để gửi, vì vậy nó không gửi gì cả.
Kiểm tra nhật ký (logs) của bạn để tìm các mã thoát (exit codes) cụ thể:
- **PHP-FPM:** Chạy lệnh `tail -f /var/log/php8.1-fpm.log`. Tìm lỗi "SIGSEGV" (tín hiệu 11).
- **Node.js:** Sử dụng `journalctl -u my-app -f` hoặc kiểm tra nhật ký PM2. Tìm lỗi "Out of Memory" (OOM).
Nếu tiến trình bị sập trước khi gửi các header, bạn sẽ liên tục thấy lỗi phản hồi trống đó.
Bước 4: Khắc phục MTU và phân mảnh mạng
Trong môi trường đám mây hoặc qua VPN, Đơn vị truyền tải tối đa (MTU) có thể gây ra sự cố. Nếu máy chủ gửi một gói tin 1500 byte nhưng một trạm trung chuyển mạng chỉ cho phép 1400 byte, gói tin có thể bị rơi nếu bit "Don't Fragment" được thiết lập. Trình duyệt sẽ đợi dữ liệu không bao giờ đến và cuối cùng từ bỏ.
Chạy một bài kiểm tra ping nhanh để kiểm tra sự phân mảnh:
# Thử kích thước gói tin tiêu chuẩn
ping -M do -s 1472 yourserver.com
# Nếu thất bại, hãy thử kích thước nhỏ hơn
ping -M do -s 1372 yourserver.com
Nếu kích thước nhỏ hơn hoạt động nhưng kích thước lớn hơn thì không, bạn đang gặp nút thắt cổ chai về MTU. Bạn có thể tạm thời hạ MTU của giao diện mạng xuống 1400 để xác minh cách khắc phục:
sudo ip link set dev eth0 mtu 1400
Bước 5: Kiểm tra các can thiệp bảo mật
Các tường lửa như ModSecurity hoặc Fail2Ban có thể hoạt động rất gắt gao. Trong khi một sự từ chối thông thường của Nginx trả về lỗi 403 Forbidden, một tường lửa kiểm tra gói tin sâu (Deep Packet Inspection - DPI) có thể chỉ đơn giản là ngắt toàn bộ phân đoạn TCP nếu nó nghi ngờ có một cuộc tấn công. Điều này thường xảy ra khi gửi các payload POST lớn hoặc các từ khóa SQL cụ thể.
Hãy thử tắt Tường lửa ứng dụng Web (WAF) của bạn trong 60 giây. Nếu lỗi dừng lại, bạn cần điều chỉnh các quy tắc bảo mật của mình thay vì mã nguồn máy chủ.
Xác minh
Đừng dựa vào việc làm mới trình duyệt, vì bộ nhớ đệm cục bộ có thể che giấu kết quả thực tế. Sử dụng curl để theo dõi quá trình bắt tay (handshake) thô:
curl -v https://your-site.com/api/data
Nếu bạn thấy * Empty reply from server, kết nối vẫn đang bị đứt. Một cách khắc phục thành công sẽ hiển thị < HTTP/1.1 200 OK theo sau là nội dung dữ liệu thực tế.
Phòng ngừa và các phương pháp hay nhất
Các lỗi mạng bí ẩn thường ẩn náu trong các dải IP chồng chéo hoặc việc chia mạng con (subnetting) lộn xộn. Khi xây dựng các microservices, hãy kiểm tra kỹ các khối CIDR của bạn để tránh các vòng lặp định tuyến. Tôi thường xuyên sử dụng Subnet Calculator trên ToolCraft để lập bản đồ ranh giới mạng. Nó giúp ngăn chặn những cơn đau đầu lúc 2 giờ sáng do các lỗi toán học nhị phân đơn giản gây ra.
Finalmente, hãy kiểm tra giới hạn bộ mô tả tệp (file descriptor). Nếu máy chủ của bạn xử lý lưu lượng truy cập cao, Nginx có thể đạt đến giới hạn worker_connections. Chạy lệnh ulimit -n để xem giới hạn hiện tại của bạn. Nếu nó vẫn ở mức mặc định 1024, hãy tăng nó lên 65535 trong /etc/security/limits.conf để đảm bảo hệ điều hành không buộc đóng các kết nối khi đang tải nặng.

