Thiết lập SSL miễn phí với Let’s Encrypt trên Nginx Proxy Manager

P P T P Chung

Thiết lập SSL miễn phí bằng Let’s Encrypt trên Nginx Proxy Manager từ A-Z

Website không có HTTPS hiện nay gần như “mất điểm” ngay từ lần truy cập đầu tiên: trình duyệt cảnh báo không an toàn, người dùng ngại nhập thông tin, SEO kém hơn, API dễ bị chặn vì mixed content. Tin tốt: bạn không cần mua chứng chỉ SSL đắt tiền. Let’s Encrypt cung cấp SSL miễn phí, tự động gia hạn. Nginx Proxy Manager giúp cấu hình reverse proxy + SSL bằng giao diện web, không cần viết file Nginx thủ công.

Bài này hướng dẫn từ A-Z: chuẩn bị domain, trỏ DNS, cấu hình Proxy Host, cấp SSL Let’s Encrypt, ép HTTPS, xử lý lỗi thường gặp, vận hành an toàn.


Nginx Proxy Manager là gì?

Nginx Proxy Manager, thường viết tắt là NPM, là giao diện quản trị cho Nginx reverse proxy.

Thay vì tự viết cấu hình kiểu:

server {
    listen 443 ssl;
    server_name example.com;
    ...
}

Bạn thao tác qua web UI:

– Thêm domain. – Chỉ định IP/port service nội bộ. – Bật SSL Let’s Encrypt. – Bật redirect HTTP → HTTPS. – Quản lý nhiều website/app trên cùng một VPS.

Ví dụ bạn có:

app.example.com → container app port 3000api.example.com → API port 8080blog.example.com → WordPress port 80

NPM đứng trước tất cả, nhận traffic từ internet, rồi chuyển tiếp về đúng service.


Mô hình hoạt động

Luồng cơ bản:

Người dùng
→ https://app.example.com
→ VPS public IP
→ Nginx Proxy Manager
→ app nội bộ:3000

SSL được cài ở tầng NPM. App phía sau có thể chạy HTTP nội bộ, miễn là kết nối từ internet vào NPM đã được mã hóa bằng HTTPS.


Chuẩn bị trước khi cài SSL

Trước khi bấm “Request a new SSL Certificate”, cần chắc các điều kiện sau đã đúng. Phần này quan trọng nhất; đa số lỗi Let’s Encrypt đến từ DNS hoặc port.

1. Có domain hợp lệ

Bạn cần sở hữu domain, ví dụ:

example.com

Hoặc dùng subdomain:

app.example.com

Let’s Encrypt không cấp SSL cho IP trần kiểu:

https://123.123.123.123

Bạn bắt buộc cần domain/subdomain.


2. Trỏ DNS về VPS

Trong trang quản lý DNS của nhà cung cấp domain, tạo bản ghi:

Type: A
Name: app
Value: IP_PUBLIC_VPS
TTL: Auto

Ví dụ:

app.example.com → 203.0.113.10

Nếu dùng IPv6, thêm bản ghi AAAA nếu cần.

Kiểm tra DNS:

nslookup app.example.com

Hoặc:

dig app.example.com

Kết quả phải trả về đúng IP VPS.


3. Mở port 80 và 443

Let’s Encrypt thường xác minh domain qua HTTP challenge. Vì vậy port 80 phải truy cập được từ internet. HTTPS dùng port 443.

Cần mở:

80/tcp
443/tcp

Nếu dùng UFW:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status

Nếu dùng cloud firewall như AWS Security Group, Oracle Cloud, Hetzner Firewall, DigitalOcean Firewall: mở cả ở tầng cloud.

Lỗi phổ biến:

Timeout during connect

Nguyên nhân thường là port 80 bị chặn.


4. Nginx Proxy Manager đang chạy đúng

Nếu dùng Docker Compose, cấu hình phổ biến:

services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: npm
    restart: unless-stopped
    ports:
      - "80:80"
      - "81:81"
      - "443:443"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

Chạy:

docker compose up -d

Truy cập admin UI:

http://IP_PUBLIC_VPS:81

Thông tin mặc định thường là:

Email: [email protected]
Password: changeme

Sau khi đăng nhập, đổi email/password ngay.


Tạo Proxy Host trong Nginx Proxy Manager

Vào giao diện NPM:

Hosts → Proxy Hosts → Add Proxy Host

Tab Details:

Domain Names

Nhập domain:

app.example.com

Có thể nhập nhiều domain nếu cùng trỏ về một backend:

example.com
www.example.com

Scheme

Chọn backend scheme:

http

Nếu service phía sau đã có HTTPS nội bộ, chọn:

https

Thông thường app Docker nội bộ dùng HTTP.

Forward Hostname / IP

Điền IP hoặc tên container/service.

Nếu app chạy trên host:

127.0.0.1

Nhưng lưu ý: nếu NPM chạy trong Docker, 127.0.0.1 là chính container NPM, không phải host.

Cách tốt hơn: cho app và NPM chung Docker network, rồi dùng service name:

app

Hoặc dùng IP LAN/private:

172.17.0.1

Forward Port

Điền port app:

3000

Ví dụ:

app.example.com → http://app:3000

Bật các tùy chọn thường dùng:

Block Common Exploits: nên bật. – Websockets Support: bật nếu app dùng WebSocket, Socket.IO, realtime dashboard. – Cache Assets: chỉ bật khi hiểu cache; không bắt buộc.

Bấm Save.

Kiểm tra trước HTTP:

http://app.example.com

Nếu truy cập được, mới sang bước SSL. Nếu HTTP còn lỗi, SSL thường cũng lỗi.


Cấp SSL Let’s Encrypt miễn phí

Mở lại Proxy Host vừa tạo:

Edit → SSL

Chọn:

Request a new SSL Certificate

Bật:

Force SSLHTTP/2 SupportHSTS Enabled nếu chắc chắn site luôn dùng HTTPS – HSTS Subdomains chỉ bật nếu tất cả subdomain đều đã HTTPS

Nhập email thật:

Tick:

I Agree to the Let's Encrypt Terms of Service

Bấm Save.

Nếu thành công, NPM sẽ tự:

– Gửi yêu cầu cấp cert tới Let’s Encrypt. – Xác minh domain. – Lưu certificate. – Reload Nginx. – Dùng HTTPS cho domain.

Kiểm tra:

https://app.example.com

Xem chứng chỉ trong trình duyệt. Issuer thường là:

Let's Encrypt

Force SSL, HTTP/2, HSTS nên bật thế nào?

Force SSL

Nên bật gần như luôn.

Kết quả:

http://app.example.com → https://app.example.com

Tránh người dùng truy cập bản HTTP không mã hóa.


HTTP/2 Support

Nên bật.

HTTP/2 giúp tối ưu nhiều request trên một kết nối. Hầu hết trình duyệt hiện đại hỗ trợ tốt.


HSTS

HSTS báo cho trình duyệt: domain này chỉ được dùng HTTPS.

Ưu điểm:

– Tăng bảo mật. – Chống downgrade attack. – Trình duyệt tự ép HTTPS.

Nhược điểm:

– Nếu cấu hình SSL sai, người dùng từng truy cập có thể bị kẹt, khó quay về HTTP. – Nếu bật cho subdomain khi chưa chuẩn bị đủ, các subdomain khác có thể lỗi.

Khuyến nghị:

– Site cá nhân/app ổn định: bật HSTS Enabled. – Chưa chắc toàn bộ subdomain HTTPS: không bật HSTS Subdomains. – Không bật preload nếu chưa hiểu rõ hậu quả.


Gia hạn SSL tự động

Let’s Encrypt certificate thường có hạn khoảng 90 ngày. NPM tự động gia hạn trước khi hết hạn.

Bạn không cần cron riêng nếu NPM chạy ổn định.

Điều kiện gia hạn thành công:

– Domain vẫn trỏ đúng IP. – Port 80/443 vẫn mở. – NPM vẫn chạy. – Không có reverse proxy/CDN chặn challenge.

Kiểm tra container:

docker ps

Xem log:

docker logs npm

Nếu dùng volume như dưới, cert không mất khi recreate container:

volumes:
  - ./data:/data
  - ./letsencrypt:/etc/letsencrypt

Không xóa thư mục letsencrypt nếu không muốn mất cert local.


Dùng Cloudflare: cần chú ý gì?

Nếu domain đi qua Cloudflare, bạn có 2 lựa chọn.

Cách đơn giản: tắt proxy khi cấp SSL

Trong DNS Cloudflare, chuyển bản ghi từ mây cam sang mây xám:

Proxied → DNS only

Sau đó cấp SSL trong NPM. Khi xong có thể bật lại proxy nếu cấu hình đúng.

SSL mode trong Cloudflare

Nên dùng:

Full

Hoặc tốt hơn:

Full (strict)

Tránh dùng:

Flexible

Vì Flexible dễ gây vòng lặp redirect:

ERR_TOO_MANY_REDIRECTS

Mô hình đúng:

Browser → HTTPS → Cloudflare → HTTPS → NPM

Không nên:

Browser → HTTPS → Cloudflare → HTTP → NPM

nếu NPM đang Force SSL.


Lỗi thường gặp và cách xử lý

“Internal Error” khi request certificate

Nguyên nhân thường gặp:

– DNS chưa trỏ đúng. – Port 80 bị chặn. – Domain đang đi qua proxy/CDN sai cấu hình. – NPM không truy cập được internet. – Let’s Encrypt rate limit do thử quá nhiều.

Cách xử lý:

dig app.example.com
curl -I http://app.example.com
docker logs npm

Đảm bảo HTTP truy cập được từ ngoài internet trước.


“Timeout during connect”

Let’s Encrypt không kết nối được tới domain.

Fix:

– Mở firewall port 80. – Kiểm tra security group cloud. – Kiểm tra router/NAT nếu self-host tại nhà. – Đảm bảo IP DNS đúng public IP.


“Invalid response from http://domain/.well-known/acme-challenge/…”

Challenge bị trả về sai nội dung.

Fix:

– Tắt redirect lạ ở backend. – Kiểm tra custom Nginx config. – Tạm tắt Cloudflare proxy. – Đảm bảo domain đang đi vào NPM, không đi vào app khác.


HTTPS báo lỗi 502 Bad Gateway

SSL đã đúng, nhưng NPM không kết nối được backend.

Fix:

– Kiểm tra app đang chạy. – Kiểm tra port. – Nếu Docker, kiểm tra network. – Không dùng 127.0.0.1 sai ngữ cảnh.

Ví dụ app container tên web cùng network với NPM:

Forward Hostname/IP: web
Forward Port: 3000

ERR_TOO_MANY_REDIRECTS

Thường gặp khi Cloudflare SSL mode là Flexible hoặc app tự redirect HTTPS trong khi NPM cũng Force SSL.

Fix:

– Cloudflare dùng Full/Full strict. – Chỉ để một tầng chịu trách nhiệm redirect. – Kiểm tra biến môi trường app như APP_URL, TRUST_PROXY, X-Forwarded-Proto.


Checklist cấu hình chuẩn

Trước khi coi là xong, kiểm tra nhanh:

DNS A record trỏ đúng IP VPS. – Port 80/443 mở từ internet. – NPM ports map đúng: 80:80, 443:443, 81:81. – Proxy Host trỏ đúng backend. – HTTP truy cập được trước khi cấp SSL. – SSL Let’s Encrypt cấp thành công. – Force SSL bật. – HTTP/2 bật. – HSTS chỉ bật khi chắc. – Volume letsencrypt được lưu bền. – Cloudflare SSL mode không dùng Flexible.


Bảo mật tối thiểu nên làm

SSL chỉ là một phần. Với NPM, nên làm thêm:

– Đổi mật khẩu admin mặc định ngay. – Không public port 81 nếu không cần. – Giới hạn admin UI bằng firewall hoặc VPN. – Cập nhật Docker image định kỳ. – Backup thư mục dataletsencrypt. – Bật 2FA nếu bản bạn dùng hỗ trợ. – Không cấp wildcard hoặc expose service không cần thiết.

Nếu chỉ bạn quản trị, tốt nhất chặn port 81 khỏi internet:

sudo ufw deny 81/tcp

Rồi truy cập admin qua SSH tunnel/VPN.


Kết luận

Let’s Encrypt + Nginx Proxy Manager là combo rất thực dụng: miễn phí, dễ dùng, đủ mạnh cho homelab, VPS cá nhân, staging, thậm chí nhiều hệ thống production nhỏ. Phần khó nhất không phải SSL, mà là chuẩn bị đúng DNS, firewall, port và backend.

Quy trình nên nhớ:

Domain đúng → DNS đúng → port mở → Proxy Host chạy HTTP → cấp SSL → Force HTTPS

Đừng bật quá nhiều thứ ngay từ đầu. Làm từng bước, kiểm tra từng lớp. Khi HTTP ổn, SSL gần như chỉ còn một cú bấm.

Tác giả

P T P

Chia sẻ

Bài viết liên quan

Bình luận (0)

Email của bạn sẽ không được hiển thị công khai.

Chưa có bình luận. Hãy là người đầu tiên!