Tới nội dung
Cloudj

Hướng dẫn VPS

Cấu hình Nginx reverse proxy SSL trên VPS với Let's Encrypt

Hướng dẫn cấu hình Nginx reverse proxy SSL trên VPS Ubuntu hoặc Debian: trỏ DNS, viết server block, lấy chứng chỉ Let's Encrypt miễn phí và tự gia hạn.

Cập nhật · 5 phút đọc

Cấu hình Nginx reverse proxy SSL là cách phổ biến nhất để đưa một ứng dụng web trên VPS ra Internet. Ứng dụng của bạn chỉ lắng nghe ở một cổng nội bộ như 3000 hoặc 8000, còn Nginx nhận traffic công khai ở cổng 80 và 443, xử lý HTTPS bằng chứng chỉ Let's Encrypt miễn phí rồi chuyển request vào ứng dụng. Bài này đi trọn quy trình trên Ubuntu hoặc Debian: bản ghi DNS, server block, chứng chỉ, và bước kiểm tra gia hạn mà nhiều người chỉ nhớ ra khi chứng chỉ đã hết hạn.

Cần chuẩn bị gì

  • Một máy chủ Linux có địa chỉ IPv4 công khai. Bất kỳ image Ubuntu hoặc Debian nào trên cloud server của Cloudj đều dùng được; cấu hình nhỏ nhất là đủ để làm proxy cho một ứng dụng nhỏ.
  • Một tên miền hoặc subdomain bạn quản lý, ví dụ app.example.com.
  • Ứng dụng đã chạy trên máy chủ và lắng nghe ở 127.0.0.1 (bài này dùng cổng 3000).
  • Quyền SSH với một user có sudo. Nếu chưa bảo mật máy chủ, hãy làm theo checklist bảo mật VPS mới trước.

Nếu chưa chọn vị trí đặt máy chủ, hướng dẫn chọn vị trí VPS giúp bạn chọn thành phố gần người dùng, và bài chọn vị trí VPS theo độ trễ chỉ cách đo thực tế. Với người dùng chủ yếu ở Việt Nam, hãy đo từ chính các nhà mạng mà khách của bạn dùng.

Bước 1: trỏ DNS về máy chủ

Let's Encrypt kiểm tra xem tên miền có trỏ về đúng máy chủ đang xin chứng chỉ hay không, vì vậy DNS phải làm trước tiên. Tại nhà cung cấp DNS, tạo bản ghi A cho app.example.com với IPv4 công khai của máy chủ (xem trên trang máy chủ trong console Cloudj). Nếu máy chủ có IPv6 và bạn muốn phục vụ qua IPv6 thì thêm bản ghi AAAA; nếu không thì đừng tạo, vì một bản ghi AAAA sai là nguyên nhân rất hay gặp khiến việc xác thực thất bại.

Kiểm tra bản ghi ngay trên máy chủ trước khi đi tiếp:

dig +short app.example.com

Trên Debian, lệnh dig nằm trong gói bind9-dnsutils. Kết quả phải là IP của máy chủ. Nếu trả về rỗng hoặc IP cũ, hãy chờ bản ghi cập nhật xong.

Bước 2: cài Nginx và mở firewall

sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx

Nếu dùng UFW, hãy mở HTTP và HTTPS. Profile Nginx Full mở cả cổng 80 và 443:

sudo ufw allow 'Nginx Full'
sudo ufw status

Cổng 80 vẫn phải mở kể cả khi bạn đã chuyển hết sang HTTPS, vì certbot dùng HTTP challenge qua cổng này mỗi lần gia hạn. Bài cấu hình firewall UFW hướng dẫn phần còn lại của bộ rule.

Bước 3: cấu hình Nginx reverse proxy (SSL ở bước sau)

Trước hết, thêm một đoạn cấu hình nhỏ để WebSocket hoạt động mà không ảnh hưởng tới request thông thường. Khối map này phải nằm trong context http, và các file trong conf.d được include đúng chỗ đó:

sudo tee /etc/nginx/conf.d/connection-upgrade.conf > /dev/null <<'EOF'
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
EOF

Sau đó tạo file site /etc/nginx/sites-available/app.example.com:

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    client_max_body_size 20m;

    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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 60s;
    }
}

Vài dòng cần giải thích thêm. Host giữ nguyên tên miền gốc để ứng dụng tạo link đúng. X-Forwarded-Proto cho ứng dụng biết khách truy cập bằng HTTPS hay HTTP; nhiều framework cần header này để không bị vòng lặp chuyển hướng. client_max_body_size nâng giới hạn upload mặc định 1 MB; hãy đặt theo đúng mức ứng dụng của bạn cho phép.

Bật site, kiểm tra cú pháp rồi reload:

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Mở http://app.example.com, bạn sẽ thấy ứng dụng chạy qua HTTP thường. Nếu gặp lỗi 502 Bad Gateway nghĩa là Nginx không kết nối được tới ứng dụng: kiểm tra bằng curl -I http://127.0.0.1:3000.

Bước 4: lấy chứng chỉ SSL miễn phí bằng certbot

Cài certbot và plugin Nginx từ kho gói của hệ điều hành:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com --redirect

Lần chạy đầu, certbot hỏi email và yêu cầu đồng ý điều khoản. Sau đó nó xác minh quyền sở hữu tên miền qua cổng 80, lưu chứng chỉ vào /etc/letsencrypt/live/app.example.com/, thêm khối listen 443 ssl vào file site, và với cờ --redirect sẽ chuyển toàn bộ HTTP sang HTTPS. Nginx được reload tự động.

Muốn dùng cả tên miền gốc lẫn www, truyền hai cờ -d: -d example.com -d www.example.com. Cả hai tên đều phải có bản ghi DNS trỏ về máy chủ.

Bước 5: kiểm tra tự gia hạn

Chứng chỉ Let's Encrypt có thời hạn ngắn, nên việc gia hạn bắt buộc phải tự động. Gói certbot trên Debian và Ubuntu cài sẵn một systemd timer chạy kiểm tra gia hạn hai lần mỗi ngày. Hãy xác nhận timer và chạy thử:

systemctl list-timers | grep certbot
sudo certbot renew --dry-run

Nếu lệnh chạy thử thành công thì lần gia hạn thật cũng sẽ thành công, miễn là DNS và cổng 80 không thay đổi. Với chứng chỉ cấp qua plugin Nginx, certbot tự reload Nginx sau khi gia hạn.

Gia cố thêm sau khi chạy ổn

  • Giữ ứng dụng ở chế độ nội bộ. Cho ứng dụng lắng nghe ở 127.0.0.1 thay vì 0.0.0.0, để đường vào công khai duy nhất là qua Nginx. Nếu dùng Docker, publish cổng dạng 127.0.0.1:3000:3000; xem thêm bài cài Docker trên VPS.
  • HSTS. Khi HTTPS đã chạy ổn ở mọi nơi, thêm add_header Strict-Transport-Security "max-age=31536000" always; vào server block cổng 443. Đừng thêm sớm, vì trình duyệt sẽ ghi nhớ thiết lập này.
  • Ẩn phiên bản Nginx. Đặt server_tokens off; trong /etc/nginx/nginx.conf.
  • Nhiều ứng dụng trên một máy chủ. Lặp lại bước 3 và 4 với file site và tên miền mới cho mỗi ứng dụng. Nginx chọn block dựa trên server_name.

Tài liệu chính thức về module proxy của Nginx liệt kê đầy đủ các directive ở trên nếu bạn cần tinh chỉnh buffer hay timeout.

Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách kiểm tra
certbot báo challenge thất bạiDNS trỏ sai hoặc cổng 80 bị chặndig +short, sudo ufw status
502 Bad GatewayỨng dụng chưa chạy hoặc chạy ở cổng kháccurl -I http://127.0.0.1:3000
Vòng lặp chuyển hướng sau khi bật HTTPSỨng dụng bỏ qua X-Forwarded-ProtoBật thiết lập trusted proxy của framework
nginx -t báo lỗiGõ sai hoặc thiếu dấu chấm phẩyĐọc số dòng trong thông báo lỗi
Chạy thử gia hạn thất bạiDNS hoặc firewall đã thay đổiKiểm tra lại DNS và cổng như ở bước 1 và 2

Câu hỏi thường gặp

Mỗi tên miền có cần một IP riêng không?

Không. Nginx và trình duyệt hiện đại đều hỗ trợ SNI, nên nhiều website HTTPS có thể dùng chung một IPv4. Mỗi tên miền chỉ cần server block và chứng chỉ riêng.

Có lấy được chứng chỉ wildcard theo cách này không?

Không với HTTP challenge. Chứng chỉ wildcard như *.example.com cần DNS challenge, tức là phải dùng plugin DNS của certbot tương ứng với nhà cung cấp DNS của bạn.

Có nên cài certbot bằng snap thay vì apt?

Cách nào cũng được. Gói apt đơn giản hơn trên Ubuntu và Debian và có sẵn timer gia hạn. Dự án certbot khuyến nghị snap để có bản mới nhất; chỉ cần chọn một cách và đừng cài cả hai.

Vị trí VPS có ảnh hưởng tới SSL không?

Không ảnh hưởng tới chứng chỉ. Vị trí chỉ quyết định độ trễ tới người truy cập, bạn có thể đo theo bài kiểm tra độ trễ VPS bằng ping và mtr.

Triển khai trên máy chủ của bạn

Khi DNS đã cập nhật, dựng reverse proxy kèm SSL chỉ mất khoảng mười lăm phút, và các bước giống hệt nhau ở mọi vị trí của Cloudj. Bạn có thể tạo máy chủ Ubuntu hoặc Debian trong console Cloudj, nạp số dư bằng chuyển khoản VND, và xem giá trực tiếp cho từng cấu hình trên trang bảng giá.

Triển khai máy chủ hoặc mua proxy

Chọn vị trí trong console, xem giá trực tiếp và thanh toán từ số dư tài khoản.