TL;DR / Checklist nhanh
- Cập nhật hệ thống, cài Nginx, mở UFW cho OpenSSH và HTTP/HTTPS.
- Tạo webroot, cấu hình server block (PHP-FPM hoặc reverse proxy tới Node, hoặc static).
- Trỏ DNS về IP VPS, xin SSL Let’s Encrypt, bật redirect HTTPS.
- Thiết lập phân quyền tối thiểu cần thiết, kiểm tra logs, verify bằng curl.
- Triển khai theo quy trình reload an toàn và có kế hoạch rollback.
Điều kiện, phiên bản tham chiếu và cảnh báo rủi ro
- Hệ điều hành: Ubuntu 22.04 LTS hoặc 24.04 LTS (amd64).
- Nginx: gói từ Ubuntu repo mặc định. PHP-FPM ví dụ: 8.3 (có thể dùng PPA nếu cần phiên bản mới).
- Quyền: user có sudo; truy cập SSH hoạt động bình thường.
- Tên miền: có thể cấu hình DNS A/AAAA trỏ về IP VPS.
Cảnh báo:
- UFW: chỉ bật sau khi đã mở rule cho SSH cổng đang dùng, tránh khóa phiên SSH. Nếu dùng console nhà cung cấp VPS, bạn có đường lui khi bị khóa.
- PPA PHP: chỉ thêm khi thật sự cần phiên bản mới; luôn kiểm tra tính tương thích trước ở môi trường staging.
- SSL/HSTS: chỉ bật HSTS sau khi xác nhận HTTPS ổn định; bật sai có thể gây lỗi truy cập lâu dài cho người dùng.
1) Cập nhật hệ thống, cài Nginx và cấu hình UFW an toàn
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx
# Mở firewall cho SSH (mặc định cổng 22). Nếu bạn dùng cổng khác, thay thế tương ứng
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full' # mở 80 và 443
sudo ufw enable # xác nhận Y khi được hỏi
sudo ufw status
# Kiểm tra Nginx hoạt động
systemctl status nginx
Kết quả mong đợi: Nginx Active (running), truy cập http://IP_VPS thấy trang mặc định.
Verify nhanh:
curl -I http://<IP_VPS>
ss -ltnp | grep -E ':80|:443' # Nginx lắng nghe cổng 80/443
Rollback nếu bị khóa SSH do firewall: đăng nhập qua console của nhà cung cấp VPS, chạy sudo ufw disable hoặc xóa rule sai và bật lại.
2) (Tuỳ chọn) Cài MariaDB
sudo apt install -y mariadb-server mariadb-client
sudo mysql_secure_installation
-- Tạo DB và user mẫu
CREATE DATABASE myapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'm@t_kh@u_m@nh';
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';
FLUSH PRIVILEGES;
Verify:
mysql -u myapp_user -p -e "SHOW DATABASES LIKE 'myapp_db';"
3) (Tuỳ chọn) Cài PHP-FPM 8.3
Chỉ thêm PPA khi cần phiên bản mới hơn repo Ubuntu:
sudo apt install -y software-properties-common ca-certificates lsb-release apt-transport-https curl
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install -y \
php8.3 php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl php8.3-mbstring \
php8.3-xml php8.3-bcmath php8.3-intl php8.3-sqlite3 php8.3-zip \
php8.3-gd php8.3-redis php-imagick php8.3-opcache php8.3-apcu
Tối ưu cơ bản cho production (/etc/php/8.3/fpm/php.ini):
memory_limit = 256M
post_max_size = 64M
upload_max_filesize = 64M
max_execution_time = 120
cgi.fix_pathinfo = 0
; Opcache
opcache.enable=1
opcache.validate_timestamps=0
opcache.max_accelerated_files=20000
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
sudo systemctl reload php8.3-fpm
systemctl status php8.3-fpm
Lưu ý: update-alternatives --config php chỉ ảnh hưởng phiên bản PHP CLI, không thay đổi socket FPM.
4) (Tuỳ chọn) Cài Node.js LTS
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v && npm -v
5) Tạo webroot và file kiểm thử
sudo mkdir -p /var/www/example.com/public
sudo chown -R $USER:$USER /var/www/example.com
cat << 'PHP' | sudo tee /var/www/example.com/public/info.php
<?php phpinfo();
PHP
6) Cấu hình server block cho PHP-FPM
Tạo file cấu hình:
sudo nano /etc/nginx/sites-available/example.com
Nội dung mẫu an toàn cơ bản:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.php index.html;
# Security headers cơ bản (áp dụng cả HTTP, khuyến nghị tinh chỉnh ở khối HTTPS)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Gzip cơ bản cho nội dung văn bản
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 120s;
}
# Chặn truy cập file ẩn
location ~ /\.[^/]+ { deny all; }
}
Kích hoạt và reload an toàn:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# Tùy chọn: giữ site mặc định cho tới khi verify OK, sau đó mới gỡ
sudo nginx -t && sudo systemctl reload nginx
Verify:
curl -I http://example.com
curl -I http://example.com/info.php
Rollback nếu lỗi cấu hình: sudo rm -f /etc/nginx/sites-enabled/example.com → sudo nginx -t && sudo systemctl reload nginx.
7) Reverse proxy cho Node/SSR
Ví dụ Next.js (có WebSocket):
upstream next_app {
server 127.0.0.1:3000;
keepalive 64;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com www.example.com;
location / {
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 $connection_upgrade;
proxy_http_version 1.1;
proxy_pass http://next_app;
}
}
Gợi ý systemd cho app Node (khởi động cùng hệ thống):
sudo tee /etc/systemd/system/nextapp.service >/dev/null <<'UNIT'
[Unit]
Description=Next.js App
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/example.com
Environment=NODE_ENV=production
ExecStart=/usr/bin/node ./.next/standalone/server.js
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl daemon-reload
sudo systemctl enable --now nextapp
systemctl status nextapp
8) Static site (React SPA/Next static export)
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/out; # thư mục build tĩnh
location / {
try_files $uri $uri/ /index.html;
}
}
9) Trỏ DNS về VPS
Thêm bản ghi tại nhà cung cấp DNS:
- A:
@→ IP VPS - A:
www→ IP VPS
Verify propagation:
dig +short A example.com
10) Cài chứng chỉ SSL (Let’s Encrypt) và redirect HTTPS
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Kiểm tra tự động gia hạn:
systemctl status certbot.timer
sudo certbot renew --dry-run
Verify:
curl -I https://example.com
Rollback nếu xin SSL lỗi: đảm bảo DNS trỏ đúng, port 80 mở; tạm thời phục vụ HTTP (gỡ cấu hình HTTPS sinh ra nếu chưa chạy) và thử lại sau khi DNS đã cập nhật.
11) Phân quyền an toàn cho web
Nguyên tắc: chỉ cấp ghi cho thành phần thực sự cần ghi (VD: thư mục runtime/cache/upload). Không chmod -R 777.
- Chủ sở hữu mã nguồn: user deploy; group:
www-datađể FPM/Node có thể đọc. - Thư mục runtime (VD: Laravel
storage,bootstrap/cache) cấp ghi cho groupwww-data.
# Ví dụ Laravel
sudo chown -R deploy:www-data /var/www/example.com
sudo find /var/www/example.com -type f -exec chmod 0644 {} +
sudo find /var/www/example.com -type d -exec chmod 0755 {} +
sudo chgrp -R www-data /var/www/example.com/storage /var/www/example.com/bootstrap/cache
sudo chmod -R 0775 /var/www/example.com/storage /var/www/example.com/bootstrap/cache
# Nếu cần ACL tinh chỉnh hơn
sudo apt install -y acl
sudo setfacl -R -m g:www-data:rwx /var/www/example.com/storage /var/www/example.com/bootstrap/cache
sudo setfacl -d -m g:www-data:rwx /var/www/example.com/storage /var/www/example.com/bootstrap/cache
12) Logs, giám sát và reload không gián đoạn
- Logs mặc định:
/var/log/nginx/access.log,/var/log/nginx/error.log. Ubuntu đã có logrotate tại/etc/logrotate.d/nginx. - Giám sát dịch vụ:
systemctl status nginx,journalctl -u nginx -f. - Reload an toàn sau khi chỉnh config:
sudo nginx -t && sudo systemctl reload nginx.
tail -f /var/log/nginx/access.log /var/log/nginx/error.log
13) Mẫu cấu hình thêm
A) Laravel / Livewire / Filament (PHP-FPM)
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \\.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
B) Next.js SSR (Node server)
upstream next_app { server 127.0.0.1:3000; keepalive 64; }
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
server {
listen 80;
server_name example.com www.example.com;
location / {
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 $connection_upgrade;
proxy_http_version 1.1;
proxy_pass http://next_app;
}
}
C) React SPA hoặc Next static export
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/out; # thư mục build tĩnh
location / { try_files $uri $uri/ /index.html; }
}
D) NestJS API
upstream nest_app { server 127.0.0.1:4000; keepalive 64; }
server {
listen 80;
server_name api.example.com;
location / {
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_pass http://nest_app;
}
}
14) Lỗi thường gặp và cách xử lý nhanh
- 502 Bad Gateway (PHP): socket FPM sai hoặc FPM chưa chạy → kiểm tra
systemctl status php8.3-fpm, chỉnh đúngfastcgi_pass. - 404 trên Laravel: thiếu
try_files→ dùng đúng snippet ở trên. - SSL cấp thất bại: domain chưa trỏ xong hoặc port 80 bị chặn → mở UFW
Nginx Full, chờ DNS propagate rồi chạy lại certbot. - Tải file lớn lỗi: thêm
client_max_body_size 64m;trong server block và reload. - Permission denied khi ghi file runtime: chỉ cấp ghi cho thư mục cần thiết (VD:
storage,bootstrap/cache), xem mục phân quyền an toàn.
15) Quy trình rollback nhanh (tránh downtime)
- Sao lưu file cấu hình trước khi sửa:
sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak.$(date +%F-%H%M). - Sau khi sửa, luôn chạy
sudo nginx -t. Nếu fail, khôi phục file backup ngay. - Nếu sau reload phát sinh lỗi 5xx:
sudo ln -sf /etc/nginx/sites-available/example.com.bak...sangsites-enabledhoặc gỡ link site mới:sudo rm /etc/nginx/sites-enabled/example.com→sudo systemctl reload nginx. - Vẫn lỗi? Tạm bật lại site mặc định:
sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default→ reload.
Xem thêm: Setup SSH GitHub cho Shared Hosting để clone dự án an toàn, nhanh gọn (cách làm tương tự trên VPS) và Tăng dung lượng disk VPS Ubuntu trong Proxmox khi cần mở rộng lưu trữ.
FAQ
- Reload khác Restart thế nào? Reload (
systemctl reload nginx) nạp lại cấu hình không cắt kết nối; Restart dừng dịch vụ rồi khởi động lại (có thể gián đoạn ngắn). - Phục vụ nhiều domain? Tạo một server block cho mỗi domain, trỏ
server_nametương ứng, kích hoạt qua symlinksites-enabled. - Đổi phiên bản PHP-FPM? Cài FPM phiên bản khác, sửa
fastcgi_passsang socket tương ứng, reload Nginx. - Force HTTPS? Khi đã có SSL ổn định, thêm server block 80 chỉ để redirect:
return 301 https://$host$request_uri;.