Cấu hình Nginx Reverse Proxy và SSL (HTTPS) bằng Docker cho trang web
Hướng dẫn cấu hình Nginx Reverse Proxy và SSL HTTPS bằng Docker Compose, Certbot và Let's Encrypt, kèm bảo mật TLS, tự động gia hạn và troubleshooting production.
ShanverseUpdated 8 min read— viewsLoading views
Docker
Nginx
HTTPS & TLS
Kiến trúc tổng thể
Mermaid
Loading diagram…
Nginx và app giao tiếp qua tên service Docker, ví dụ app:3000. TLS kết thúc tại Nginx; traffic nội bộ không cần publish ra host.
Điều kiện trước khi bắt đầu
Một server có IP public và đã cài Docker Engine cùng Docker Compose.
Domain, ví dụ example.com, có bản ghi A trỏ về IPv4 của server.
Comments
Nếu có bản ghi
AAAA
, IPv6 cũng phải trỏ đúng và server phải nhận được traffic IPv6.
Firewall/security group mở TCP 80 và 443.
Không có process khác chiếm hai port này.
Email hợp lệ để nhận cảnh báo hết hạn chứng chỉ.
Kiểm tra DNS:
Kiểm tra port đang dùng:
Cấu trúc thư mục
Không commit nội dung certbot/conf vì có private key:
Backup certificate directory bằng cơ chế được mã hóa và giới hạn quyền truy cập.
Docker Compose
expose chỉ mô tả port cho network nội bộ; nó không mở port app trên host. Chỉ Nginx publish 80/443.
Bước 1: cấu hình HTTP để cấp chứng chỉ lần đầu
Tạo nginx/conf.d/site.conf chưa tham chiếu certificate:
Khởi động app và Nginx:
Test ACME path:
Kết quả phải là ok trước khi gọi Certbot.
Bước 2: cấp certificate với Certbot
Certificate được lưu tại:
Không copy private key vào Docker Image và không đưa vào Git.
Bước 3: bật HTTPS và redirect HTTP
Thay site.conf bằng cấu hình hoàn chỉnh:
resolver 127.0.0.11 cho phép Nginx cập nhật IP của service Docker sau khi app container được recreate. WebSocket cần HTTP/1.1 cùng header Upgrade và Connection.
Kiểm tra rồi reload không downtime:
Kiểm tra HTTPS:
Canonical domain: www hay không www
Nếu chọn example.com làm canonical, redirect www:
Certificate vẫn phải chứa cả hai hostname. Chỉ nên có một canonical URL để tránh duplicate content và cookie behavior khó kiểm soát.
HSTS: bật sau khi HTTPS đã ổn định
Sau khi xác nhận toàn bộ subdomain cần thiết hỗ trợ HTTPS:
Bắt đầu với thời gian ngắn, sau đó tăng:
Không thêm preload hoặc includeSubDomains nếu còn subdomain chỉ chạy HTTP. HSTS bị browser cache và khó rollback ngay.
Tự động gia hạn Let's Encrypt
Chứng chỉ Let's Encrypt có vòng đời ngắn; chạy renew hằng ngày là bình thường vì Certbot chỉ gia hạn khi gần hết hạn.
Cron trên host:
Test renewal mà không chạm certificate thật:
Sau renewal, Nginx phải reload để đọc certificate mới. Không cần restart container.
Reverse Proxy cho nhiều ứng dụng
Mỗi domain/subdomain có server block riêng. Chỉ kết nối Nginx vào network của backend tương ứng.
Xử lý IP thật khi đứng sau CDN
Nếu có Cloudflare hoặc load balancer, $remote_addr thường là IP proxy. Chỉ tin header real-IP từ CIDR chính thức của proxy:
Không trust X-Forwarded-For từ toàn Internet, vì client có thể tự giả mạo header.
Security hardening quan trọng
Không publish port app/database trên host nếu không cần.
Không mount Docker socket vào Nginx hay app.
Dùng Image version cụ thể thay vì phụ thuộc latest ở production.
Scan Nginx Image và cập nhật bản vá định kỳ.
Giữ private key ngoài Image/Git và giới hạn quyền host.
Drop capability không cần; nếu dùng Nginx unprivileged, publish 80:8080 và 443:8443.
Thiết lập CSP theo asset/API thực tế; không copy CSP chung có thể làm hỏng website.
Rate limit theo endpoint và traffic thật; tránh tự DoS người dùng hợp lệ.
App phải hiểu X-Forwarded-Proto khi sinh absolute URL hoặc secure cookie.
Lỗi thường gặp
502 Bad Gateway
Kiểm tra app đang healthy và cùng Docker Network:
Không dùng localhost:3000 trong Nginx: bên trong Nginx container, localhost là chính Nginx.
ERR_TOO_MANY_REDIRECTS
App không trust proxy và cho rằng request vẫn là HTTP. Cấu hình framework đọc X-Forwarded-Proto; không để cả Nginx và app tạo redirect mâu thuẫn.
Certbot báo challenge thất bại
DNS chưa trỏ đúng.
Port 80 bị chặn.
AAAA trỏ sai.
ACME path bị redirect/rewrite hoặc không cùng shared volume.
CDN proxy/cache can thiệp challenge.
WebSocket không kết nối
Kiểm tra proxy_http_version 1.1, Upgrade, Connection, timeout và đường dẫn WebSocket.
Upload trả 413 Request Entity Too Large
Tăng có kiểm soát:
App và upstream cũng phải chấp nhận cùng giới hạn.
Trình duyệt báo mixed content
HTML HTTPS vẫn gọi asset/API qua http://. Dùng URL HTTPS hoặc relative URL; kiểm tra X-Forwarded-Proto và biến môi trường frontend lúc build.
Hướng dẫn HTTPS và TLS từ nền tảng đến production: rủi ro MITM của HTTP, chứng chỉ Let's Encrypt, Certbot với Nginx, ACME challenges, HSTS, renewal và troubleshooting.
Hướng dẫn Docker Hardening thực tế với non-root user, multi-stage builds, minimal image, read-only filesystem, Linux capabilities, seccomp, scanning và supply-chain security.
Hướng dẫn production cấu hình Nginx và Cloudflare Reverse Proxy cho WebSocket, xử lý timeout, sticky sessions, SSL/TLS termination, IP thật và lỗi 400/502/1006.