Cấu hình Nginx Reverse Proxy là việc bạn cần làm khi đã deploy ứng dụng Node.js, Python hay Docker lên VPS nhưng không muốn mở thẳng port nội bộ ra Internet. Thay vì để người dùng truy cập IP:3000, bạn cho họ vào domain qua port 80/443, còn Nginx đứng giữa chuyển tiếp request vào backend. Ở hướng dẫn này, InterData sẽ đi từ cài đặt, viết file config, thêm SSL, đến xử lý lỗi 502 và 504 trên Ubuntu, Debian, CentOS, AlmaLinux và Rocky Linux.
NỘI DUNG BÀI VIẾT
- Nginx Reverse Proxy là gì? Tại sao cần cấu hình?
- So sánh Nginx với các Reverse Proxy phổ biến khác
- Cấu hình Nginx Reverse Proxy trên Ubuntu / Debian
- Cấu hình Nginx Reverse Proxy trên CentOS / AlmaLinux / Rocky Linux
- Use case 1: Nginx Reverse Proxy cho Node.js và Python
- Use case 2: Nginx Reverse Proxy cho Docker Container
- Thêm SSL/HTTPS và bảo mật nâng cao
- Xử lý lỗi thường gặp khi cấu hình Nginx Reverse Proxy
Nginx Reverse Proxy là gì? Tại sao cần cấu hình?
Hiểu đơn giản, Nginx Reverse Proxy là cách bạn đặt Nginx ngồi trước ứng dụng. Client gõ domain, request chạm vào Nginx trước, rồi Nginx mới chuyển tiếp vào backend đang chạy ở port 3000, 8000 hay 8080. Backend không bao giờ lộ trực tiếp ra ngoài. Nếu muốn nắm rõ kiến trúc event-driven của Nginx, bạn đọc thêm bài Nginx là gì. Còn khái niệm proxy đảo chiều nói chung được giải thích ở bài Reverse Proxy là gì.

Reverse Proxy khác gì Forward Proxy?
Forward Proxy đại diện cho client đi ra Internet. Ví dụ điển hình là VPN công ty hoặc proxy của trường học. Reverse Proxy làm ngược lại: nó đại diện cho server, đứng trước backend, và client không hề biết backend thật nằm ở đâu. Gần như mọi hệ thống web production đều dùng mô hình reverse proxy vì nó gom SSL, logging, caching và rate limiting về một chỗ.
Tại sao không mở thẳng port ứng dụng ra Internet?
Khi bạn chạy node app.js và bind vào 0.0.0.0:3000, bất kỳ ai quét port đều có thể chạm tới ứng dụng. Đặt Nginx lên trước giải quyết chuyện đó: chỉ port 80 và 443 mở ra ngoài, backend bind vào 127.0.0.1:3000, tức chỉ Nginx trên cùng máy gọi được. Bốn lợi ích thực tế mà bạn sẽ thấy ngay:
- Ẩn IP và port backend, giảm bề mặt tấn công. Kết hợp UFW hoặc firewalld chặn hết port nội bộ từ bên ngoài.
- Chỉ cần cài chứng chỉ Let’s Encrypt một lần trên Nginx. Backend không phải xử lý TLS.
- Phân phối request đến nhiều instance backend qua block
upstreamkhi traffic tăng. - Nhiều domain hoặc nhiều app chạy cùng VPS đều đi qua port 80/443, không cần mở thêm port nào.
So sánh Nginx với các Reverse Proxy phổ biến khác
Năm công cụ dưới đây đều làm được reverse proxy, nhưng mỗi cái có chỗ mạnh riêng. Bảng này dựa trên tài liệu chính thức và trải nghiệm chạy thực tế trên VPS. Nếu bạn chỉ cần một câu trả lời ngắn: với VPS Linux thông thường, Nginx là lựa chọn mặc định.
| Công cụ | Hiệu năng | Cấu hình | Phù hợp với |
|---|---|---|---|
| Nginx | Cao, event-driven, giữ được hàng nghìn kết nối đồng thời | File config dạng khối, tài liệu nhiều | Hầu hết dự án web trên VPS |
| Apache mod_proxy | Thấp hơn do mô hình process/thread | .htaccess hoặc virtual host |
Ứng dụng PHP/LAMP cũ cần giữ stack |
| Caddy | Khá, viết bằng Go, HTTPS tự động qua ACME | Caddyfile ngắn, SSL tự lo | Dự án muốn HTTPS tự động, ngại cấu hình |
| HAProxy | Rất cao ở tầng TCP/L4, health check sâu | Config riêng, không phải web server | Hệ thống cần load balancing chuyên dụng |
| Traefik | Khá, tự phát hiện service qua Docker/K8s | Label trên container | Hạ tầng container động, microservices |
Caddy đáng cân nhắc nếu bạn không muốn động tay vào chứng chỉ SSL. HAProxy chỉ nên thêm vào khi Nginx không kham nổi yêu cầu load balancing. Traefik hợp với ai chạy nhiều container và muốn service tự đăng ký. Còn lại, Nginx vẫn là điểm khởi đầu an toàn.
Cấu hình Nginx Reverse Proxy trên Ubuntu / Debian
Đây là phần chính. Nếu chưa có Nginx trên máy, bạn xem trước bài hướng dẫn cài đặt Nginx trên Ubuntu để nắm các bước cài từ repository. Dưới đây là quy trình đầy đủ từ cài đặt tới reload config.
Bước 1. Cài Nginx và mở port 80, 443
sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Kiểm tra Nginx đã chạy chưa:
sudo systemctl status nginx
curl -s http://localhost | head

Bước 2. Tạo server block reverse proxy
Trên Ubuntu và Debian, file config nằm trong /etc/nginx/sites-available/, sau đó symlink sang /etc/nginx/sites-enabled/. Tạo file mới:
sudo nano /etc/nginx/sites-available/app.example.com
Dán cấu hình dưới đây. Thay app.example.com bằng domain của bạn và 127.0.0.1:3000 bằng địa chỉ backend:
server {
listen 80;
listen [::]:80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Bước 3. Enable site, kiểm tra cú pháp, reload
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Luôn chạy nginx -t trước khi reload. Nếu cú pháp sai, Nginx từ chối nạp config mới nhưng site đang chạy vẫn sống bình thường. Chỉ khi nào nginx -t báo syntax is ok và test is successful thì lệnh reload mới thật sự an toàn.

Bảng tóm tắt các directive proxy_set_header quan trọng
| Directive | Giá trị | Tác dụng |
|---|---|---|
proxy_set_header Host |
$host |
Backend biết domain client gõ vào, cần cho virtual host |
proxy_set_header X-Real-IP |
$remote_addr |
Backend nhận IP thật của client |
proxy_set_header X-Forwarded-For |
$proxy_add_x_forwarded_for |
Giữ chuỗi IP khi request đi qua nhiều proxy, dùng cho log |
proxy_set_header X-Forwarded-Proto |
$scheme |
Backend biết request gốc là HTTP hay HTTPS |
proxy_set_header Upgrade |
$http_upgrade |
Bắt buộc để WebSocket hoạt động |
proxy_set_header Connection |
"upgrade" |
Giữ kết nối WebSocket mở |
Cấu hình Nginx Reverse Proxy trên CentOS / AlmaLinux / Rocky Linux
Trên họ RHEL, Nginx tự đọc mọi file trong /etc/nginx/conf.d/, bạn không cần symlink như Ubuntu. Firewall mặc định là firewalld chứ không phải UFW. Ba điểm khác biệt chính cần nhớ:
- Config đặt thẳng ở
/etc/nginx/conf.d/*.confvà được load tự động. - Không có bước symlink từ sites-available sang sites-enabled.
- Mở port bằng
firewall-cmd, không dùngufw.
Bước 1. Cài Nginx và mở firewall
sudo dnf install -y nginx
sudo systemctl enable --now nginx
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Bước 2. Tạo file config trong conf.d
sudo nano /etc/nginx/conf.d/app.example.com.conf
Nội dung config giống Ubuntu, chỉ bỏ bước symlink:
server {
listen 80;
listen [::]:80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Bước 3. Kiểm tra và reload
sudo nginx -t
sudo systemctl reload nginx
Nếu SELinux đang bật (mặc định trên CentOS, AlmaLinux, Rocky), Nginx có thể bị chặn khi kết nối ra backend port dù config đúng. Kiểm tra trạng thái và mở quyền:
getenforce
sudo setsebool -P httpd_can_network_connect 1
Lệnh setsebool ở trên cho phép Nginx thực hiện outbound connection. Khá nhiều bạn mất cả buổi debug chỉ vì thiếu bước này.
Use case 1: Nginx Reverse Proxy cho ứng dụng Node.js và Python
Đây là tình huống gặp nhiều nhất: app Node.js chạy bằng PM2 trên port 3000, Nginx nhận request ở port 80 rồi chuyển vào 127.0.0.1:3000. Nếu mới deploy lần đầu, bài deploy Node.js trên VPS với Nginx đi từ cài Node.js đến PM2 và SSL.
Cấu hình Nginx cho Node.js kèm WebSocket
Ứng dụng real-time như chat, notification hay dashboard cần thêm hai header Upgrade và Connection. Config đầy đủ:
upstream nodejs_backend {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
Hai timeout đặt cao (3600 giây) để WebSocket không bị ngắt khi idle. Nếu để mặc định, kết nối có thể bị đóng sau 60 giây và client tự reconnect liên tục, log đầy lỗi trông rất khó chịu.
Cấu hình cho Python (Gunicorn / Uvicorn)
Django hoặc Flask chạy qua Gunicorn ở port 8000, FastAPI qua Uvicorn ở port 8001, cấu hình tương tự Node.js nhưng không cần header WebSocket nếu app không dùng:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 20m;
}
}
client_max_body_size 20m cho phép upload file tới 20 MB. Mặc định Nginx giới hạn 1 MB, ai làm chức năng upload thường dính lỗi 413 Request Entity Too Large mà không hiểu tại sao.
Săn Ưu Đãi VPS Tiết Kiệm
Canh Me tổng hợp khuyến mãi, mã giảm giá, Hot Deals VPS và Cloud Server mới nhất của InterData. Xem qua trước khi quyết định để chọn được cấu hình hợp túi tiền.
Use case 2: Nginx Reverse Proxy cho Docker Container
Khi ứng dụng chạy trong Docker container, có hai cách để Nginx proxy tới. Cách thứ nhất là map port container ra host rồi proxy qua localhost. Cách thứ hai là cho Nginx và container vào cùng Docker network, proxy qua tên container.
Cách 1. Map port ra host (đơn giản nhất)
Chạy container với port mapping:
docker run -d --name myapp -p 127.0.0.1:3000:3000 myapp:latest
Để ý dùng 127.0.0.1:3000 thay vì 0.0.0.0:3000. Port chỉ bind vào localhost, không lộ ra ngoài Internet. Nginx proxy qua http://127.0.0.1:3000 giống hệt trường hợp Node.js chạy trực tiếp trên host.
Cách 2. Docker network (khi Nginx cũng chạy trong container)
Nếu Nginx chạy trong Docker, tạo network chung rồi proxy qua tên container:
docker network create reverse_proxy
docker network connect reverse_proxy myapp
docker network connect reverse_proxy nginx_container
Config Nginx trỏ đến tên container thay vì IP:
location / {
proxy_pass http://myapp:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Docker DNS nội bộ (thường là 127.0.0.11) phân giải tên myapp thành IP container. Cách này ổn định hơn dùng IP vì IP container có thể đổi sau mỗi lần restart. Nhớ thêm --restart unless-stopped để container tự bật lại khi VPS reboot:
docker run -d --name myapp --restart unless-stopped \
--network reverse_proxy myapp:latest
Thêm SSL/HTTPS và bảo mật nâng cao cho Nginx Reverse Proxy
Sau khi reverse proxy HTTP chạy ổn, bước tiếp theo là bật HTTPS. Certbot và Let’s Encrypt cho chứng chỉ miễn phí, tự gia hạn. Điều kiện để chạy Certbot: Nginx đang chạy và domain đã trỏ về IP VPS.
Cài Certbot và lấy chứng chỉ SSL
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
Certbot tự sửa file config Nginx, thêm block listen 443 ssl và redirect HTTP sang HTTPS. Kiểm tra tự động gia hạn:
sudo certbot renew --dry-run

Thêm security headers
Thêm các header này vào block server chạy HTTPS:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
X-Frame-Options chống clickjacking. X-Content-Type-Options ngăn trình duyệt tự đoán MIME type. Strict-Transport-Security buộc trình duyệt chỉ dùng HTTPS trong 1 năm. Không có HSTS, kẻ tấn công có thể chèn HTTP request trước khi redirect kịp xảy ra.
Kết hợp UFW và Fail2ban
Chỉ mở port 22, 80, 443 theo bài cấu hình UFW firewall trên Ubuntu. Đóng hết port nội bộ (3000, 8000, 8080) khỏi Internet. Sau đó cài đặt Fail2ban để chống brute-force SSH và các endpoint đăng nhập:
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
Fail2ban đọc log Nginx rồi ban IP sau nhiều lần đăng nhập thất bại. Nếu chạy production, xem thêm 13 cách bảo mật VPS để biết các lớp bảo vệ bổ sung như SSH key, đổi port SSH và giám sát log định kỳ.
Xử lý lỗi thường gặp khi cấu hình Nginx Reverse Proxy
Ba lỗi dưới đây chiếm phần lớn sự cố khi cấu hình Nginx reverse proxy. Mỗi lỗi sẽ đi kèm triệu chứng, nguyên nhân và lệnh debug cụ thể để bạn chạy ngay.
Lỗi 502 Bad Gateway
Triệu chứng là trình duyệt hiện 502, log Nginx có dòng upstream sent invalid header hoặc connect() failed (111: Connection refused). Nguyên nhân hầu như luôn là backend không chạy trên port Nginx proxy tới, hoặc backend vừa crash sau khi khởi động. Debug:
curl -v http://127.0.0.1:3000
sudo systemctl status myapp
sudo journalctl -u myapp -n 50
sudo tail -f /var/log/nginx/error.log
Nếu curl báo Connection refused, backend chưa chạy. Với PM2 thì kiểm tra pm2 list và pm2 logs. Với Docker thì docker ps xem container có đang Up không.
Lỗi 504 Gateway Timeout
Request treo hơn 60 giây rồi trả 504. Backend có thể vẫn sống nhưng xử lý quá lâu. Thường gặp với API gọi database chậm hoặc chức năng xuất báo cáo lớn. Kiểm tra log xem request nào timeout:
sudo grep "upstream timed out" /var/log/nginx/error.log
Tăng timeout trong block location:
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
Chỉ tăng timeout khi backend thật sự cần thời gian xử lý. Nếu request timeout liên tục, gốc rễ nằm ở backend, phải tối ưu query hoặc thêm cache. Khi có nhiều instance backend, cân nhắc load balancing với Nginx để chia request thay vì dồn vào một node.
Lỗi nginx: [emerg] bind() to 0.0.0.0:80 failed (permission denied)
Khi systemctl start nginx thất bại với dòng bind() to 0.0.0.0:80 failed (13: Permission denied), có hai khả năng. Một là tiến trình Nginx chạy dưới user không có quyền bind port dưới 1024. Hai là port 80 đã bị phần mềm khác chiếm, thường là Apache. Kiểm tra:
sudo ss -tlnp | grep :80
Nếu Apache đang giữ port 80, dừng Apache: sudo systemctl stop apache2. Nếu Nginx bị chạy bằng user thường, xem lại file /etc/nginx/nginx.conf, dòng user nginx; phải để Nginx khởi động qua systemd (chạy root để bind port rồi drop xuống user nginx). Đừng chạy lệnh nginx trực tiếp bằng user thường.
Câu Hỏi Thường Gặp về Cấu Hình Nginx Reverse Proxy
Nginx Reverse Proxy khác gì Forward Proxy?
Forward Proxy đứng về phía client, đại diện client gửi request ra Internet và ẩn IP client. Reverse Proxy đứng về phía server, nhận request rồi chuyển vào backend. Client chỉ nhìn thấy Nginx, không biết backend nằm đâu. Web production gần như luôn dùng reverse proxy để bảo vệ backend và gom SSL về một chỗ.
Nginx Reverse Proxy có hỗ trợ WebSocket không?
Có, từ Nginx 1.3.13. Bạn cần thêm proxy_set_header Upgrade $http_upgrade và proxy_set_header Connection "upgrade" trong block location, kèm proxy_http_version 1.1. Đặt thêm proxy_read_timeout cao để kết nối không bị ngắt khi idle.
Lỗi 502 Bad Gateway trong Nginx do đâu và sửa nhanh thế nào?
502 xảy ra khi Nginx không kết nối được backend. Nguyên nhân phổ biến nhất là backend chưa chạy hoặc vừa crash. Kiểm tra bằng curl http://127.0.0.1:PORT và sudo systemctl status tên-app. Xem log ở /var/log/nginx/error.log để biết chi tiết.
Chạy nhiều domain trên cùng một Nginx Reverse Proxy được không?
Được. Tạo nhiều file config trong /etc/nginx/sites-available/ (Ubuntu) hoặc /etc/nginx/conf.d/ (CentOS), mỗi file có server_name riêng và proxy_pass trỏ đến backend tương ứng. Nginx dùng server_name để route đúng domain. Nhớ chạy nginx -t và reload sau khi thêm.
Nginx Proxy Manager có tốt hơn cấu hình thủ công không?
Nginx Proxy Manager hợp với ai muốn giao diện web để thêm domain, bật SSL và quản lý proxy mà không phải sửa file config. Cấu hình thủ công cho quyền kiểm soát đầy đủ, dễ debug và không phụ thuộc công cụ ngoài. Với sysadmin quen terminal, sửa file trực tiếp thường nhanh hơn.
Lời kết
Đến đây bạn đã có đủ để cấu hình Nginx Reverse Proxy cho hầu hết tình huống thường gặp: tạo server block với proxy_pass, xử lý WebSocket, proxy qua Docker container, bật SSL bằng Certbot, thêm security headers và debug lỗi 502/504. Ba thứ đáng nhớ nhất: luôn chạy nginx -t trước khi reload, đặt proxy_read_timeout cao cho WebSocket, và giữ backend bind vào 127.0.0.1 chứ đừng bind vào 0.0.0.0. Việc còn lại là kiếm một VPS có quyền root để thực hành.
Sẵn sàng thực hành cấu hình Nginx Reverse Proxy?
VPS InterData với CPU thế hệ mới, SSD NVMe U.2 và quyền root đầy đủ để bạn cài Nginx, thử reverse proxy và deploy ứng dụng.
Nội dung mang tính tham khảo. Lệnh, cú pháp, đường dẫn file và tùy chọn cấu hình có thể thay đổi theo phiên bản hệ điều hành, phiên bản Nginx và môi trường cụ thể. Nên kiểm thử, sao lưu dữ liệu và đánh giá rủi ro trước khi áp dụng lên server production.
