Trên một VPS duy nhất, bạn hoàn toàn có thể chạy nhiều website, microservice và ứng dụng nội bộ bằng Nginx reverse proxy. Bài viết này là hướng dẫn thực chiến cho quản trị VPS Ubuntu và developer (Laravel/Node.js/Docker) muốn tự host nhiều domain một cách gọn gàng, an toàn và dễ vận hành.
Câu trả lời nhanh
Reverse proxy là cách đặt Nginx ở phía trước để nhận mọi request HTTP/HTTPS từ internet, sau đó chuyển tiếp tới ứng dụng chạy phía sau (upstream) như Node.js (127.0.0.1:3000) hoặc PHP-FPM (FastCGI). Mỗi domain có một server block riêng với server_name đúng, SSL riêng, và cấu hình proxy hoặc FastCGI phù hợp. Không dùng proxy_pass cho PHP, hãy dùng fastcgi_pass tới PHP-FPM.
1) Kiến trúc nhiều domain trên một VPS
Ở cấp cao, Nginx đóng vai trò “cổng”:
- DNS: mỗi domain/subdomain trỏ về địa chỉ IP public của VPS.
- Firewall: mở cổng 80 (HTTP) và 443 (HTTPS).
- Nginx: phân tuyến theo server_name (domain) đến backend phù hợp:
Client ---> Nginx (80/443) --->
- Static site (serve file tĩnh)
- Node.js app (reverse proxy 127.0.0.1:3000)
- Laravel/PHP (FastCGI tới PHP-FPM)
Điểm khác biệt quan trọng:
- Reverse proxy HTTP (proxy_pass) dùng cho ứng dụng nói HTTP như Node.js, Go, Python (Gunicorn/Uvicorn khi có HTTP), v.v.
- PHP-Laravel chạy qua PHP-FPM (FastCGI), dùng fastcgi_pass, không dùng proxy_pass.
Tài liệu tham khảo chuẩn: Nginx Admin Guide (reverse proxy), Nginx server_names, và Ubuntu Server docs (configure Nginx). Hãy kiểm tra phiên bản/distro của bạn trước khi áp dụng, vì đường dẫn và gói có thể khác.
2) Chuẩn bị DNS và firewall
DNS
- Trỏ A record của example.com về IP public của VPS. Nếu có IPv6, thêm AAAA record.
- Với nhiều site: tạo các record cho mỗi domain/subdomain (vd: www.example.com, api.example.com).
- TTL mặc định 300–600 giây là hợp lý khi đang triển khai.
Firewall
- Ubuntu với UFW: mở HTTP/HTTPS.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
sudo ufw status
Nếu dùng cloud firewall (VPC/SG), mở cổng 80/443 inbound. Với Docker, nếu container publish port, đảm bảo không xung đột với Nginx lắng nghe 80/443.
3) Cài đặt Nginx và bố cục cấu hình
Trên Ubuntu:
sudo apt update
sudo apt install nginx -y
Kiểm tra dịch vụ:
systemctl status nginx
Bố cục thường dùng trên Ubuntu:
- /etc/nginx/nginx.conf: cấu hình chính, include các phần còn lại.
- /etc/nginx/sites-available/: chứa file cấu hình từng site (server block).
- /etc/nginx/sites-enabled/: symlink tới các site đang bật.
- /var/www/<domain>/: nơi đặt mã nguồn/asset tĩnh.
Mẹo: đặt các biến/chung (map, log_format) trong /etc/nginx/conf.d/*.conf hoặc ngay trong nginx.conf để tái sử dụng.
4) Cấu hình site tĩnh (static) cho www.example.com
Giả sử bạn muốn một website tĩnh ở www.example.com:
sudo mkdir -p /var/www/www.example.com/html
sudo chown -R www-data:www-data /var/www/www.example.com
sudo chmod -R 755 /var/www/www.example.com
Tạo file /etc/nginx/sites-available/www.example.com.conf:
server {
listen 80;
listen [::]:80;
server_name www.example.com;
root /var/www/www.example.com/html;
index index.html index.htm;
access_log /var/log/nginx/www.example.com.access.log;
error_log /var/log/nginx/www.example.com.error.log warn;
location / {
try_files $uri $uri/ =404;
}
}
Kích hoạt site và kiểm tra:
sudo ln -s /etc/nginx/sites-available/www.example.com.conf \
/etc/nginx/sites-enabled/www.example.com.conf
sudo nginx -t
sudo systemctl reload nginx
5) Reverse proxy Node.js cho example.com (127.0.0.1:3000)
Giả sử ứng dụng Node.js lắng nghe ở 127.0.0.1:3000. Ta tạo upstream và server block để proxy:
# Có thể đặt upstream trong file site hoặc conf.d (đảm bảo được include)
upstream app_example_com {
server 127.0.0.1:3000; # Ứng dụng Node.js nội bộ
keepalive 16; # Giữ kết nối tới upstream để giảm chi phí TCP
}
# Map hỗ trợ WebSocket Connection header an toàn (đặt ở http{})
# map $http_upgrade $connection_upgrade { default upgrade; '' close; }
server {
listen 80;
listen [::]:80;
server_name example.com;
# Logs riêng cho domain
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log warn;
# Giới hạn kích thước upload (tránh 413)
client_max_body_size 20m;
location / {
proxy_http_version 1.1;
proxy_set_header Host $host; # Bảo toàn Host
proxy_set_header X-Real-IP $remote_addr; # IP gốcClient
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # http hoặc https
# WebSocket (nếu app sử dụng). Nếu có map $connection_upgrade, dùng biến đó.
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_connect_timeout 5s;
proxy_pass http://app_example_com; # Trỏ tới upstream ở trên
}
}
Lưu ý:
- proxy_set_header Host là bắt buộc để app biết đúng domain (hữu ích cho multi-tenant, link tuyệt đối, cookie domain).
- Nếu bạn muốn Health Check đơn giản, có thể cấu hình location /healthz tới upstream hoặc serve tĩnh.
- Nếu tách file, đảm bảo khối map đặt trong context http để dùng biến $connection_upgrade.
6) Laravel/PHP: dùng FastCGI (không proxy_pass)
Nếu chạy Laravel (PHP) hãy dùng PHP-FPM. Trên Ubuntu, cài php-fpm phù hợp (kiểm tra phiên bản PHP bạn đang dùng). Ví dụ server block cho laravel.example.com:
server {
listen 80;
listen [::]:80;
server_name laravel.example.com;
root /var/www/laravel.example.com/public;
index index.php index.html;
access_log /var/log/nginx/laravel.example.com.access.log;
error_log /var/log/nginx/laravel.example.com.error.log warn;
# Laravel yêu cầu try_files tới index.php
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.(php|phar)$ {
include snippets/fastcgi-php.conf; # Trên Ubuntu: chuyển các biến FastCGI chuẩn
fastcgi_pass unix:/run/php/php-fpm.sock; # Hoặc 127.0.0.1:9000 tuỳ cài đặt
fastcgi_read_timeout 60s;
}
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)$ {
expires max;
access_log off;
}
}
Đừng dùng proxy_pass cho PHP. PHP-FPM nói giao thức FastCGI, không phải HTTP. Sử dụng fastcgi_pass tới socket hoặc cổng do PHP-FPM phục vụ.
7) Bật HTTPS và redirect 80 → 443
Triển khai TLS cho từng domain. Bạn có thể dùng Let’s Encrypt với Certbot. Hãy kiểm tra package phù hợp với distro của bạn trước khi cài.
sudo apt update
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
Certbot sẽ tự thêm server block 443 và tạo redirect 80→443. Nếu tự cấu hình, mẫu thủ công:
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$host$request_uri; # Bắt buộc HTTPS
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Thêm cấu hình TLS an toàn theo khuyến nghị hiện hành của bạn
location / {
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 60s;
proxy_send_timeout 60s;
proxy_connect_timeout 5s;
proxy_pass http://app_example_com;
}
}
Sau khi chỉnh sửa cấu hình:
sudo nginx -t # Kiểm tra cú pháp và khả dụng của upstream
sudo systemctl reload nginx # Reload không làm gián đoạn kết nối đang mở
8) Quản lý log và giám sát
- Log truy cập: /var/log/nginx/<domain>.access.log
- Log lỗi: /var/log/nginx/<domain>.error.log
- Có thể tuỳ biến log_format trong nginx.conf để thêm $request_time, $upstream_response_time cho việc chẩn đoán hiệu năng.
- Dùng tail -f để theo dõi thời gian thực khi debug:
sudo tail -f /var/log/nginx/example.com.error.log
sudo tail -f /var/log/nginx/example.com.access.log
9) Quy trình deploy an toàn và reload không downtime
Khi thay đổi cấu hình:
- Sao lưu file cũ hoặc dùng Git để version hóa /etc/nginx.
- Kiểm tra cấu hình: nginx -t
- Reload: systemctl reload nginx
- Kiểm thử: curl -I https://example.com hoặc truy cập trình duyệt.
Với ứng dụng Node.js, deploy zero-downtime thường được xử lý ở process manager (pm2, systemd socket activation, blue/green với port khác) trong khi Nginx trỏ tới upstream ổn định.
10) Xử lý sự cố thường gặp (502/404/413/504) và rollback
502 Bad Gateway
- Nguyên nhân: upstream không chạy, sai IP/port, app crash, firewall nội bộ chặn, sai proxy_pass scheme.
- Khắc phục: kiểm tra systemctl status app, netstat/ss xem port 3000 có lắng nghe, fix proxy_pass đúng http://127.0.0.1:3000, xem error_log.
404 Not Found
- Nguyên nhân: đường dẫn root sai, thiếu try_files cho Laravel, request path không tồn tại ở static site.
- Khắc phục: sửa root; với Laravel thêm try_files $uri $uri/ /index.php?$query_string; kiểm tra phân quyền thư mục.
413 Request Entity Too Large
- Nguyên nhân: upload vượt quá client_max_body_size.
- Khắc phục: tăng client_max_body_size (vd 50m) trong server hoặc http block; với PHP tăng post_max_size, upload_max_filesize tương ứng.
504 Gateway Timeout
- Nguyên nhân: upstream phản hồi chậm hơn proxy_read_timeout; truy vấn DB nặng; network nội bộ bị chặn.
- Khắc phục: tối ưu app/DB; tăng proxy_read_timeout; đảm bảo upstream khỏe; cân nhắc queue/background job cho tác vụ lâu.
Rollback an toàn
- Giữ bản sao cấu hình trước khi thay đổi (site.conf.bak hoặc quản lý bằng Git).
- Nếu reload gây lỗi, khôi phục file cũ, nginx -t lại rồi reload.
- Dùng sites-available + symlink atomic: chuẩn bị file mới, test, rồi đổi symlink khi đã chắc chắn.
11) Mở rộng cho nhiều website
Thêm mỗi domain là thêm một file trong sites-available với server_name tương ứng. Ví dụ api.example.com cho Node.js và blog.example.com cho static. Lưu ý:
- server_name phải khớp chính xác (xem: nginx.org/en/docs/http/server_names.html để biết wildcard/regex). Trên Ubuntu, kiểm tra kỹ DNS đã trỏ đúng trước khi debug Nginx.
- Mỗi domain nên có log riêng, SSL riêng, limit/timeout riêng nếu cần.
- Nếu dùng Cloudflare/Proxy CDN, cân nhắc real IP: set_real_ip_from và real_ip_header CF-Connecting-IP (khi thực sự cần), đảm bảo tin cậy đúng IP của CDN.
12) Tối ưu proxy headers và WebSocket
Bộ header khuyến nghị tối thiểu:
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;
WebSocket:
# Trong http {}
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
# Trong server/location reverse proxy
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
Giải thích:
- X-Forwarded-Proto giúp app phân biệt http/https để tạo URL tuyệt đối, secure cookie.
- Upgrade/Connection cần cho handshake WebSocket; dùng map để tránh gửi sai header khi không upgrade.
13) Điều chỉnh timeout, body size và keepalive
- client_max_body_size 20m (tăng nếu cần upload lớn).
- proxy_connect_timeout 5s: thời gian chờ kết nối tới upstream.
- proxy_read_timeout 60s, proxy_send_timeout 60s: đọc/ghi tới upstream.
- keepalive trong upstream giúp tái sử dụng kết nối; tránh thiết lập quá cao nếu upstream ít lưu lượng.
14) Quy ước thư mục và quyền
- Chạy Nginx dưới user www-data trên Ubuntu mặc định.
- Thư mục /var/www/<domain> thuộc sở hữu www-data nếu Nginx cần đọc/ghi (tuỳ use-case).
- Log rotate: đảm bảo logrotate cho Nginx hoạt động để tránh đầy đĩa.
15) Checklist production
- DNS trỏ chính xác (A/AAAA), TTL hợp lý.
- Firewall mở 80/443, chặn management ports từ internet.
- Nginx đã tách site theo server_name, có logs riêng.
- HTTPS bật, redirect 80→443, kiểm tra chuỗi chứng chỉ.
- proxy headers chuẩn: Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto.
- WebSocket hoạt động (nếu dùng), có map Connection upgrade.
- client_max_body_size và timeout phù hợp workload.
- nginx -t clean; reload không downtime.
- Giám sát: access/error log, metric (tuỳ công cụ), cảnh báo dung lượng đĩa.
- Backup/rollback cấu hình; quản lý cấu hình bằng Git.
16) FAQ
1) Tôi có thể dùng một server block để bắt mọi domain không?
Có thể dùng server_name _ hoặc default_server để bắt tất cả, nhưng không khuyến nghị cho production nhiều domain vì khó quản lý SSL, log và policy từng site. Hãy tạo server block riêng cho mỗi domain.
2) Bật HTTP/2 hoặc HTTP/3 thế nào?
Cho HTTP/2: thêm tham số http2 vào listen 443 ssl. HTTP/3 (QUIC) yêu cầu Nginx bản hỗ trợ và cấu hình bổ sung. Hãy kiểm tra tài liệu phiên bản bạn đang dùng trước khi bật.
3) Làm sao giới hạn tốc độ hoặc bảo vệ chống brute-force?
Dùng limit_req_zone/limit_req để giới hạn rate theo IP, hoặc kết hợp fail2ban. Triển khai ở layer Nginx sẽ giảm tải cho ứng dụng.
4) Tôi dùng Cloudflare/Proxy CDN, header và IP thật xử lý thế nào?
Bật proxy headers như bình thường. Để lấy IP thật, dùng set_real_ip_from với dải IP của CDN và real_ip_header theo tài liệu của nhà cung cấp, chỉ đặt khi bạn tin tưởng nguồn đó.
5) Blue/Green deploy với Node.js trên cùng VPS?
Chạy phiên bản mới ở port 3001, thêm vào upstream hoặc chuyển proxy_pass sau khi health check OK, nginx -t và reload. Cách này giảm downtime và cho phép rollback nhanh.
6) Upstream đặt ở đâu là tốt nhất?
Nếu upstream dùng chung cho nhiều site, đặt trong /etc/nginx/conf.d/upstreams.conf (được include bởi nginx.conf). Nếu chỉ phục vụ một domain, có thể đặt ngay trong file site để độc lập.
7) Tôi gặp lỗi “too many open files”, xử lý sao?
Tăng worker_connections trong nginx.conf và fs.file-max/limits hệ thống; nhưng hãy đo đạc trước khi thay đổi. Đảm bảo logrotate hoạt động để không bị đầy đĩa.
17) Ví dụ tổng hợp: nhiều website trên cùng VPS
Giả sử bạn có: www.example.com (static), example.com (Node.js ở 127.0.0.1:3000), laravel.example.com (Laravel/PHP-FPM). Kịch bản triển khai:
- Thiết lập DNS cho cả ba domain trỏ tới IP VPS.
- Mở firewall 80/443.
- Cài Nginx, tạo 3 file site trong sites-available, bật symlink sang sites-enabled.
- Test nginx -t, reload.
- Cấp SSL bằng Certbot cho từng domain, xác nhận redirect 80→443 hoạt động.
- Giám sát logs, kiểm thử tính năng, tối ưu timeout/body size theo nhu cầu.
Kết luận và bước tiếp theo
Bằng việc cấu hình Nginx reverse proxy đúng cách, bạn có thể vận hành nhiều website và ứng dụng trên cùng một VPS một cách linh hoạt, an toàn và có thể mở rộng. Hãy bắt đầu với cấu hình tối thiểu, bật HTTPS, thêm proxy headers chuẩn, xử lý WebSocket nếu cần, sau đó bổ sung limit/timeout và giám sát. Khi gặp sự cố, tra cứu logs và dùng quy trình nginx -t → reload để đảm bảo không downtime.
Đọc thêm các hướng dẫn liên quan trên technt.net:
- /setup-website-voi-nginx-tren-vps-ubuntu/
- /setup-ssh-an-toan-vps-centos/
- /ket-noi-github-secrets-vps-ci-cd-an-toan/
- /tang-dung-luong-disk-vps-ubuntu-trong-proxmox/
Chúc bạn triển khai thuận lợi và luôn kiểm tra kỹ cấu hình với nginx -t trước khi reload để giữ dịch vụ ổn định!
Bài liên quan trên TECHNT
Nếu bạn đang xây dựng hệ thống tự host, hãy đọc thêm hướng dẫn cài Ghost CMS production trên shared hosting cPanel, quy trình migrate Laravel sang Ghost CMS và kết nối GitHub Secrets với VPS cho CI/CD an toàn. Khi cần hardening máy chủ, tham khảo thêm hướng dẫn setup SSH an toàn cho VPS.