Hướng Dẫn Cấu Hình Nginx Reverse Proxy Trên Linux (Node.js, Docker và SSL/HTTPS)

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.

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ì.

Nginx Reverse Proxy

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 upstream khi 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.

Thuê VPS InterData

CPU thế hệ mới, SSD NVMe U.2, hỗ trợ kỹ thuật 24/7

Cần VPS Linux có quyền root để cài Nginx và thực hành ngay?

VPS InterData chạy Ubuntu, Debian hoặc CentOS với quyền root đầy đủ để bạn cài Nginx, tạo file config, chạy systemctl reload nginx và deploy Node.js hoặc Docker đúng như hướng dẫn trong bài. Ổ cứng NVMe U.2, có backup định kỳ và bảo vệ DDoS.

Xem Bảng Giá Thuê VPS ⟶

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

cài đặt Nginx

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;
    }
}

Tạo server block reverse proxy

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 oktest is successful thì lệnh reload mới thật sự an toàn.

kiểm tra cấu hình Nginx

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/*.conf và đượ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ùng ufw.

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 UpgradeConnection. 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.

Xem Hot Deals Tại Canh Me ⟶

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

Certbot homepage

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ỳ.

Cloud Server InterData

Nâng/hạ tài nguyên nhanh theo tải, mạng tốc độ cao

Hệ thống nhiều backend service cần hạ tầng ổn định hơn?

Khi Nginx reverse proxy cho nhiều backend service cùng lúc, workload tăng đột biến, hoặc cả team cùng deploy chung một môi trường production, Cloud Server InterData cho phép tăng CPU/RAM mà không phải dựng lại Nginx từ đầu. Hợp với ai cần phân tải qua block upstream nhiều node backend.

Xem Bảng Giá Cloud Server ⟶

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 listpm2 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_upgradeproxy_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:PORTsudo 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.

Xem Các Gói VPS Giá Tốt ⟶

Hoặc xem Hot Deals VPS & Cloud Server tại Canh Me

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.