Sau khi hoàn thiện LEMP và deploy được demo app, bước tiếp theo là bật HTTPS đúng cách. Một website production không nên chỉ chạy HTTP, vì mọi request, cookie, form đăng nhập, session và dữ liệu người dùng đều cần được bảo vệ bằng TLS.
Bài SSL Let’s Encrypt trên Ubuntu 24.04: Certbot, renew và OpenSSL sẽ hướng dẫn cách bật HTTPS cho Nginx trên Ubuntu 24.04 LTS bằng Let’s Encrypt và Certbot. Mục tiêu không chỉ là chạy một lệnh để có ổ khóa xanh, mà là hiểu vì sao chứng chỉ được cấp, điều kiện nào khiến cấp chứng chỉ thất bại, cách kiểm tra bằng curl và openssl, cách test auto renew, và cách tránh lỗi phổ biến như redirect loop, port 80 bị chặn hoặc chạm rate limit.
HTTPS ngày nay là mặc định. Nhưng để vận hành bền trong năm 2026 và về sau, bạn cần xem SSL/TLS như một phần của workflow vận hành: có kiểm tra trước khi cấp, có log khi lỗi, có renew tự động, có cảnh báo trước khi hết hạn và có checklist sau mỗi thay đổi Nginx.
1. Hiểu SSL Let’s Encrypt, ACME và Certbot trên Ubuntu 24.04
1.1. SSL và TLS khác nhau thế nào?
Trong thực tế, rất nhiều người vẫn gọi chung là SSL certificate, SSL website hoặc cài SSL. Về mặt kỹ thuật hiện đại, giao thức đang dùng là TLS. SSL là tên cũ, còn TLS là phiên bản kế thừa an toàn hơn.
Vì thói quen tìm kiếm và cách nói phổ biến, bài này vẫn dùng cụm SSL Let’s Encrypt. Tuy nhiên khi đọc tài liệu kỹ thuật, bạn sẽ thường thấy các thuật ngữ chính xác hơn như TLS certificate, HTTPS, TLS handshake, certificate chain và private key.
1.2. Let’s Encrypt là gì?
Let’s Encrypt là một Certificate Authority cung cấp chứng chỉ TLS miễn phí, tự động và được trình duyệt tin cậy. Với Let’s Encrypt, bạn không cần mua chứng chỉ thủ công cho từng website nhỏ. Thay vào đó, server dùng một ACME client như Certbot để chứng minh quyền kiểm soát domain và nhận chứng chỉ.
Điểm mạnh nhất của Let’s Encrypt không chỉ là miễn phí, mà là khả năng tự động hóa. Một VPS production không nên phụ thuộc vào việc “nhớ gia hạn chứng chỉ bằng tay”. Nếu quy trình renew tự động chạy ổn, website sẽ ít gặp lỗi hết hạn SSL hơn rất nhiều.
1.3. ACME là gì?
ACME là giao thức giúp ACME client và Certificate Authority làm việc với nhau. Khi bạn chạy Certbot, Certbot sẽ yêu cầu chứng chỉ từ Let’s Encrypt, Let’s Encrypt sẽ yêu cầu bạn chứng minh rằng bạn kiểm soát domain, sau đó mới cấp certificate.
Phần chứng minh quyền kiểm soát domain này gọi là challenge. Với website Nginx public thông thường, challenge dễ dùng nhất là HTTP-01. Với wildcard certificate, bạn cần DNS-01. Với một số trường hợp đặc biệt, có thể gặp TLS-ALPN-01.
1.4. Certbot làm gì?
Certbot là ACME client phổ biến, có thể lấy chứng chỉ Let’s Encrypt và tự cấu hình Nginx. Khi dùng plugin Nginx, Certbot có thể đọc server block, thêm cấu hình SSL, thêm đường dẫn certificate, cấu hình redirect HTTP sang HTTPS và tạo lịch renew tự động.
Tuy vậy, Certbot không thay thế tư duy vận hành. Bạn vẫn cần kiểm tra DNS, firewall, Nginx config, log, auto renew và certificate chain sau khi cài.
2. Chọn loại challenge SSL Let’s Encrypt trên Ubuntu phù hợp
2.1. HTTP-01 challenge: lựa chọn mặc định cho VPS chạy Nginx
HTTP-01 là lựa chọn phù hợp nhất cho phần lớn website tự host trên VPS. Khi dùng HTTP-01, Let’s Encrypt sẽ truy cập domain của bạn qua port 80 và kiểm tra một token tạm thời trong đường dẫn /.well-known/acme-challenge/.
Điều kiện cần có:
- Domain đã trỏ DNS về đúng IP của VPS.
- Port 80 mở từ internet vào server.
- Nginx đang phục vụ đúng server block cho domain.
- Không có redirect hoặc rule chặn làm hỏng đường dẫn ACME challenge.
HTTP-01 dễ tự động hóa, dễ debug và phù hợp với website thông thường. Vì vậy, nếu bạn không chắc nên dùng loại nào, hãy bắt đầu với HTTP-01.
2.2. DNS-01 challenge: dùng cho wildcard certificate
DNS-01 chứng minh quyền kiểm soát domain bằng cách tạo TXT record ở DNS, thường là dưới dạng:
_acme-challenge.example.com
DNS-01 phù hợp khi bạn cần wildcard certificate như:
*.example.com
Nhưng DNS-01 phức tạp hơn HTTP-01 vì cần thao tác với DNS. Nếu muốn renew tự động bền vững, DNS provider nên có API để Certbot hoặc ACME client tự cập nhật TXT record. Nếu phải cập nhật TXT record bằng tay, việc renew định kỳ sẽ rất dễ bị quên.
2.3. TLS-ALPN-01: trường hợp đặc biệt
TLS-ALPN-01 chạy qua port 443 và thường dùng trong các tình huống ACME client hoặc hạ tầng có hỗ trợ riêng. Với người mới dựng Nginx trên VPS, đây không phải lựa chọn cần bắt đầu.
Trong bài này, quy trình chính sẽ dùng HTTP-01 thông qua Certbot Nginx plugin.
3. Chuẩn bị trước khi cài Certbot cho Nginx
3.1. Kiểm tra DNS domain đã trỏ đúng VPS
Giả sử domain là example.com. Kiểm tra A record và AAAA record:
dig +short A example.com
dig +short A www.example.com
dig +short AAAA example.com
dig +short AAAA www.example.com
Nếu server chỉ có IPv4, hãy đảm bảo A record trỏ đúng IP. Nếu domain có AAAA record nhưng VPS chưa cấu hình IPv6 đúng, Let’s Encrypt hoặc người dùng có thể truy cập nhầm IPv6 và gặp lỗi.
Kiểm tra IP public của VPS:
curl -4 ifconfig.me
curl -6 ifconfig.me || true
Nếu DNS chưa đúng, đừng chạy Certbot vội. Hãy sửa DNS trước rồi chờ record cập nhật.
3.2. Kiểm tra Nginx server block
Certbot Nginx plugin cần đọc được server block tương ứng với domain. Kiểm tra cấu hình:
sudo nginx -t
sudo nginx -T | grep -n "server_name" | grep "example.com" || true
Một server block tối thiểu trước khi bật SSL nên có dạng:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /srv/apps/example.com/current/public;
index index.php index.html;
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;
}
}
Nếu server_name thiếu domain hoặc đang dùng _ làm default server, Certbot có thể không biết nên sửa block nào.
3.3. Kiểm tra HTTP từ bên ngoài
Từ chính server, kiểm tra local:
curl -I -H "Host: example.com" http://127.0.0.1/
Từ máy cá nhân hoặc một máy ngoài internet, kiểm tra:
curl -I http://example.com
curl -I http://www.example.com
Kết quả mong đợi là website trả về HTTP response hợp lệ, ví dụ 200, 301 hoặc 302. Nếu request timeout, hãy kiểm tra firewall, DNS và Nginx.
3.4. Mở firewall cho HTTP và HTTPS
Kiểm tra UFW:
sudo ufw status verbose
sudo ufw app list
Mở Nginx Full để cho phép cả port 80 và 443:
sudo ufw allow 'Nginx Full'
sudo ufw status verbose
Nếu trước đó chỉ mở Nginx HTTP, HTTPS sẽ không truy cập được sau khi cấp chứng chỉ. Nếu chỉ mở Nginx HTTPS mà đóng port 80, HTTP-01 challenge có thể thất bại.
3.5. Có nên đóng port 80 sau khi bật HTTPS?
Không nên đóng port 80 một cách máy móc. Với website public, port 80 thường dùng để redirect HTTP sang HTTPS và phục vụ ACME HTTP-01 challenge khi cần renew. Dữ liệu nhạy cảm vẫn đi qua HTTPS, còn HTTP chỉ là điểm vào để chuyển hướng hoặc xác minh domain.
Nếu chính sách bảo mật của bạn bắt buộc đóng port 80, hãy dùng phương thức validation khác như DNS-01, nhưng lúc đó quy trình renew sẽ phức tạp hơn.
4. Cài Certbot trên Ubuntu 24.04 LTS
4.1. Vì sao nên dùng Certbot bản Snap trong năm 2026
Trên Ubuntu, bạn có thể thấy nhiều hướng dẫn cài Certbot bằng APT:
sudo apt install certbot python3-certbot-nginx
Cách này có thể hoạt động, nhưng hướng khuyến nghị phổ biến của Certbot hiện nay là dùng bản Snap để nhận phiên bản mới hơn và có renewal automation được cấu hình sẵn. Với VPS mới, dùng Snap giúp giảm rủi ro gặp bản Certbot quá cũ so với thay đổi của Let’s Encrypt.
Nếu server đã có Certbot APT từ trước và đang quản lý certificate production, đừng gỡ vội nếu bạn chưa hiểu cấu hình hiện tại. Với server mới, có thể dùng Snap ngay từ đầu.
4.2. Cài snapd nếu chưa có
sudo apt update
sudo apt install -y snapd
sudo snap install core
sudo snap refresh core
Kiểm tra:
snap version
4.3. Gỡ Certbot APT cũ nếu đây là server mới
Nếu đây là VPS mới và bạn chưa có certificate production, có thể đảm bảo không dùng nhầm Certbot cũ từ APT:
sudo apt remove -y certbot python3-certbot-nginx || true
Nếu server đang chạy nhiều website production đã có certificate, hãy kiểm tra kỹ trước khi gỡ bất kỳ package nào.
4.4. Cài Certbot bằng Snap
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
Kiểm tra version:
certbot --version
which certbot
Kết quả mong đợi là certbot trỏ về bản Snap hoặc wrapper tương ứng.
5. Cấp SSL Let’s Encrypt cho Nginx bằng Certbot
5.1. Chạy kiểm tra cuối trước khi cấp certificate
Trước khi chạy Certbot, kiểm tra lại:
sudo nginx -t
sudo systemctl status nginx --no-pager
sudo ufw status verbose
dig +short A example.com
curl -I http://example.com
Nếu có lỗi ở bất kỳ bước nào, xử lý trước. Đừng chạy Certbot nhiều lần liên tiếp khi DNS hoặc firewall chưa đúng, vì có thể tạo nhiều validation failure không cần thiết.
5.2. Cấp certificate cho root domain và www
Chạy:
sudo certbot --nginx -d example.com -d www.example.com
Certbot có thể hỏi email, điều khoản sử dụng và lựa chọn redirect HTTP sang HTTPS. Với website production, nên bật redirect để người dùng truy cập HTTP được chuyển sang HTTPS.
Sau khi chạy thành công, Certbot sẽ tạo hoặc cập nhật cấu hình Nginx để dùng certificate trong thư mục:
/etc/letsencrypt/live/example.com/
Các file quan trọng thường gồm:
fullchain.pem
privkey.pem
cert.pem
chain.pem
Không copy private key ra nơi không cần thiết. File privkey.pem là bí mật quan trọng của TLS certificate.
5.3. Kiểm tra certificate đã được Certbot quản lý
sudo certbot certificates
Kết quả nên hiển thị domain, đường dẫn certificate, private key, ngày hết hạn và tên certificate lineage.
Khái niệm lineage rất quan trọng: Certbot không chỉ tạo một file certificate rồi bỏ đó. Nó quản lý một chuỗi certificate theo thời gian để renew và cập nhật bản mới.
5.4. Kiểm tra Nginx sau khi Certbot sửa cấu hình
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager
Nếu nginx -t fail sau khi chạy Certbot, không restart bừa. Hãy mở file server block và xem Certbot đã thêm gì.
sudo nginx -T | sed -n '/server_name example.com/,/}/p'
Trong nhiều trường hợp, lỗi đến từ server block đã có cấu trúc quá phức tạp, include sai file, hoặc có nhiều block trùng server_name.
6. Kiểm tra HTTPS bằng curl và OpenSSL
6.1. Kiểm tra redirect HTTP sang HTTPS
curl -I http://example.com
curl -I http://www.example.com
Nếu đã bật redirect, kết quả nên có status 301 hoặc 308 và header Location trỏ về URL HTTPS.
Ví dụ mong đợi:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
6.2. Kiểm tra HTTPS response
curl -I https://example.com
curl -I https://www.example.com
Kết quả mong đợi là HTTP response hợp lệ qua HTTPS, ví dụ 200, 301 hoặc 302. Nếu gặp lỗi certificate, hãy kiểm tra domain, SAN, chain và thời gian hệ thống.
6.3. Kiểm tra certificate bằng OpenSSL
Dùng openssl s_client để kiểm tra certificate server đang trình bày:
openssl s_client -connect example.com:443 -servername example.com </dev/null
Lệnh trên khá dài output. Để xem thông tin chính:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
Kiểm tra thêm SAN:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
Bạn cần thấy domain chính và các domain phụ đã yêu cầu, ví dụ example.com và www.example.com.
6.4. Kiểm tra certificate chain
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
Nếu trình duyệt báo certificate không tin cậy, có thể chain bị thiếu, server đang trình bày sai certificate hoặc request đang đi đến nhầm server. Với Nginx, thường nên dùng fullchain.pem, không chỉ cert.pem.
6.5. Kiểm tra log Nginx sau khi bật HTTPS
sudo tail -n 100 /var/log/nginx/example.com.access.log
sudo tail -n 100 /var/log/nginx/example.com.error.log
sudo journalctl -u nginx --since "30 minutes ago"
Không chỉ nhìn trình duyệt thấy ổ khóa là xong. Hãy xem error log để chắc chắn không có lỗi reload, permission, redirect hoặc upstream PHP-FPM phát sinh sau khi chuyển sang HTTPS.
7. Kiểm tra auto renew của Certbot
7.1. Vì sao auto renew quan trọng hơn lệnh cấp lần đầu
Cấp certificate lần đầu chỉ là 50% công việc. Phần quan trọng hơn là certificate phải được renew tự động trước khi hết hạn.
Từ năm 2026 trở đi, hệ sinh thái certificate đang dịch chuyển mạnh hơn về automation và certificate lifetime ngắn hơn. Vì vậy, thói quen “gần hết hạn thì SSH vào renew bằng tay” không còn phù hợp cho production.
7.2. Kiểm tra timer hoặc cron của Certbot
Với Certbot cài bằng Snap, renewal thường được cấu hình tự động. Kiểm tra timer:
systemctl list-timers | grep -E 'certbot|snap.certbot' || true
Kiểm tra service liên quan:
systemctl list-units | grep -E 'certbot|snap.certbot' || true
Không phải mọi hệ thống đều hiển thị tên timer giống hệt nhau. Điều quan trọng là Certbot có cơ chế chạy định kỳ và bạn đã test được bằng dry-run.
7.3. Test renew bằng dry-run
sudo certbot renew --dry-run
Đây là lệnh cực kỳ quan trọng. Nếu dry-run thành công, khả năng cao renewal thật cũng sẽ ổn. Nếu dry-run fail, hãy xử lý ngay thay vì chờ đến lúc certificate sắp hết hạn.
Xem log Certbot:
sudo tail -n 200 /var/log/letsencrypt/letsencrypt.log
7.4. Thêm deploy hook reload Nginx sau khi renew
Trong nhiều cấu hình, Certbot và Nginx plugin có thể xử lý reload. Tuy vậy, bạn có thể thêm deploy hook để đảm bảo Nginx reload sau khi certificate renew thành công.
Tạo file hook:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Nội dung:
#!/usr/bin/env bash
set -euo pipefail
nginx -t
systemctl reload nginx
Cấp quyền:
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Chạy lại dry-run:
sudo certbot renew --dry-run
Lưu ý: một số phiên bản Certbot có hành vi riêng với deploy hook khi dry-run. Vì vậy, ngoài dry-run, bạn vẫn nên kiểm tra log và hiểu rõ hook có chạy ở renewal thật hay không.
8. Cấu hình Nginx HTTPS sạch và dễ vận hành
8.1. Server block HTTP chỉ nên redirect và phục vụ ACME challenge
Sau khi bật HTTPS, block port 80 thường chỉ cần làm hai việc:
- Cho phép ACME challenge hoạt động khi cần.
- Redirect request còn lại sang HTTPS.
Một cấu hình thường gặp:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
Nếu Certbot đã tự tạo redirect, bạn không nhất thiết phải thay đổi thủ công. Nhưng bạn nên hiểu mục tiêu của block HTTP: không phục vụ app chính qua HTTP nữa.
8.2. Server block HTTPS phục vụ app chính
Block HTTPS sẽ có certificate và private key:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
root /srv/apps/example.com/current/public;
index index.php index.html;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
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;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Với Nginx mới hơn, cách bật HTTP/2 có thể thay đổi tùy version và package. Nếu nginx -t cảnh báo cú pháp listen ... http2, hãy theo warning của Nginx và điều chỉnh theo version đang chạy.
8.3. Không bật HSTS quá sớm
HSTS giúp trình duyệt luôn dùng HTTPS cho domain trong một khoảng thời gian. Đây là một header bảo mật tốt, nhưng không nên bật quá sớm khi bạn chưa chắc HTTPS, redirect, subdomain và renew đã ổn.
Nếu bật HSTS với thời gian dài rồi cấu hình HTTPS lỗi, người dùng có thể bị kẹt không truy cập được HTTP để tạm workaround. Vì vậy, hãy để HSTS sang bài tối ưu web hosting và security headers sau khi HTTPS đã chạy ổn định.
8.4. Không dùng TLS config copy từ bài cũ một cách mù quáng
Nhiều bài cũ trên internet vẫn hướng dẫn chỉnh cipher suite, TLS version hoặc dhparam theo thói quen cũ. Với người mới, cấu hình do Certbot và package Nginx hiện đại tạo ra thường đã đủ tốt cho phần lớn website nhỏ.
Chỉ nên tối ưu sâu TLS khi bạn có lý do rõ ràng, biết cách test bằng công cụ phù hợp và có rollback.
9. Nếu website dùng Cloudflare hoặc CDN/proxy
9.1. Hiểu hai lớp TLS khi dùng Cloudflare
Khi dùng Cloudflare proxy, kết nối HTTPS có hai đoạn:
- Trình duyệt người dùng đến Cloudflare.
- Cloudflare đến origin server của bạn.
Certificate Let’s Encrypt trong bài này nằm ở origin server, tức là lớp giữa Cloudflare và VPS. Certificate này vẫn rất quan trọng, vì nếu origin không có HTTPS đúng, kết nối từ Cloudflare về VPS có thể không đạt mức bảo mật mong muốn.
9.2. Nên dùng Full hoặc Full (strict), tránh Flexible cho production
Với production, nên dùng chế độ yêu cầu HTTPS giữa Cloudflare và origin. Nếu origin đã có Let’s Encrypt hợp lệ cho domain, chế độ phù hợp thường là Full (strict).
Không nên dùng Flexible cho website production nếu origin đã hỗ trợ HTTPS. Flexible có thể gây redirect loop và làm đoạn Cloudflare đến origin không được mã hóa bằng HTTPS.
9.3. Khi cấp Let’s Encrypt sau Cloudflare proxy
HTTP-01 challenge vẫn có thể hoạt động sau proxy nếu request đến /.well-known/acme-challenge/ được chuyển về origin bình thường. Nhưng nếu bạn có rule redirect, firewall, bot protection hoặc cache rule chặn đường dẫn này, Certbot có thể fail.
Nếu gặp lỗi khi cấp certificate sau proxy, hãy kiểm tra theo thứ tự:
- DNS record có trỏ đúng domain không.
- Proxy/CDN có chuyển request port 80 về origin không.
- UFW có mở
Nginx Fullkhông. - Nginx có phục vụ đúng server block không.
- Đường dẫn
/.well-known/acme-challenge/có bị chặn không.
10. Tránh rate limit khi dùng Let’s Encrypt
10.1. Vì sao rate limit thường bị chạm?
Người mới thường chạm rate limit không phải vì website lớn, mà vì chạy thử sai quá nhiều lần trong lúc DNS hoặc firewall chưa đúng.
Các tình huống phổ biến:
- DNS chưa trỏ về VPS nhưng vẫn chạy Certbot liên tục.
- Port 80 bị UFW hoặc cloud firewall chặn.
- Nginx server block sai
server_name. - Cloudflare/CDN rule chặn ACME challenge.
- Chạy đi chạy lại nhiều certificate cho cùng một bộ domain.
10.2. Cách làm an toàn trước khi chạy production issuance
Trước khi chạy lệnh cấp thật, hãy kiểm tra:
dig +short A example.com
curl -I http://example.com
sudo nginx -t
sudo ufw status verbose
Nếu chưa chắc, hãy test bằng staging:
sudo certbot --nginx --staging -d example.com -d www.example.com
Certificate staging không được trình duyệt tin cậy, nhưng rất hữu ích để kiểm tra flow. Khi mọi thứ ổn, chạy lại không có --staging để lấy certificate production.
10.3. Đừng xóa và cấp lại certificate nếu chỉ cần renew
Nếu certificate đã tồn tại và bạn chỉ muốn kiểm tra renewal, dùng:
sudo certbot renew --dry-run
Không nên xóa certificate rồi cấp lại chỉ để “cho sạch”. Certbot quản lý lineage để renew. Xóa sai có thể làm mất cấu hình đang chạy ổn.
11. Theo dõi ngày hết hạn certificate
11.1. Xem ngày hết hạn bằng Certbot
sudo certbot certificates
Lệnh này cho biết certificate nào đang được Certbot quản lý và ngày hết hạn là khi nào.
11.2. Xem ngày hết hạn bằng OpenSSL
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -dates
Kết quả sẽ có:
notBefore=...
notAfter=...
notAfter là thời điểm certificate hết hạn.
11.3. Tạo script kiểm tra số ngày còn lại
Tạo script:
sudo nano /usr/local/sbin/check-cert-expiry.sh
Nội dung:
#!/usr/bin/env bash
set -euo pipefail
DOMAIN="${1:-example.com}"
WARN_DAYS="${2:-21}"
end_date="$(
openssl s_client -connect "${DOMAIN}:443" -servername "${DOMAIN}" </dev/null 2>/dev/null \
| openssl x509 -noout -enddate \
| cut -d= -f2
)"
end_epoch="$(date -d "${end_date}" +%s)"
now_epoch="$(date +%s)"
days_left="$(( (end_epoch - now_epoch) / 86400 ))"
echo "${DOMAIN} certificate expires in ${days_left} days"
if [ "${days_left}" -lt "${WARN_DAYS}" ]; then
echo "WARNING: certificate is close to expiry"
exit 1
fi
Cấp quyền:
sudo chmod +x /usr/local/sbin/check-cert-expiry.sh
Chạy thử:
/usr/local/sbin/check-cert-expiry.sh example.com 21
Script này chưa thay thế monitoring thật, nhưng giúp bạn có thói quen kiểm tra certificate chủ động. Ở các bài monitoring sau, bạn có thể đưa kiểm tra này vào cron, systemd timer hoặc công cụ uptime monitor.
12. Lỗi thường gặp khi cài SSL Let’s Encrypt với Nginx
12.1. Certbot báo unauthorized
Lỗi unauthorized thường có nghĩa là Let’s Encrypt không xác minh được domain.
Kiểm tra:
dig +short A example.com
curl -I http://example.com
sudo ufw status verbose
sudo nginx -T | grep -n "server_name" | grep "example.com"
Nguyên nhân phổ biến là DNS sai, request đi đến server khác, port 80 bị chặn hoặc Nginx không phục vụ đúng domain.
12.2. Certbot báo connection timeout
Lỗi timeout thường liên quan đến network hoặc firewall.
Kiểm tra UFW:
sudo ufw status verbose
Kiểm tra Nginx có listen port 80 không:
sudo ss -ltnp | grep ':80'
sudo systemctl status nginx --no-pager
Nếu nhà cung cấp VPS có cloud firewall riêng, hãy kiểm tra cả firewall ở panel provider, không chỉ UFW trong Ubuntu.
12.3. HTTPS vào được nhưng HTTP không redirect
Kiểm tra block port 80:
sudo nginx -T | grep -n "listen 80" -A20
Kiểm tra bằng curl:
curl -I http://example.com
Nếu muốn redirect toàn bộ HTTP sang HTTPS, block port 80 nên trả về 301 hoặc 308 sang URL HTTPS.
12.4. Trình duyệt báo certificate không khớp domain
Kiểm tra certificate SAN:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -ext subjectAltName
Nếu certificate chỉ có example.com nhưng người dùng truy cập www.example.com, bạn cần cấp certificate bao gồm cả hai tên:
sudo certbot --nginx -d example.com -d www.example.com
12.5. Redirect loop khi dùng Cloudflare
Redirect loop thường xảy ra khi proxy/CDN kết nối về origin bằng HTTP trong khi origin lại redirect sang HTTPS, hoặc khi mode SSL/TLS không phù hợp.
Kiểm tra:
- Origin server có certificate hợp lệ chưa.
- Cloudflare SSL/TLS mode có đang dùng Flexible không.
- Nginx có redirect dựa trên header sai không.
- App có tự ép HTTPS thêm một lần nữa không.
Với origin đã có Let’s Encrypt hợp lệ, nên dùng chế độ mã hóa đầy đủ đến origin thay vì Flexible.
12.6. Renew dry-run fail
Chạy:
sudo certbot renew --dry-run
sudo tail -n 200 /var/log/letsencrypt/letsencrypt.log
sudo nginx -t
sudo ufw status verbose
Nếu dry-run fail, hãy xử lý ngay. Đừng chờ certificate thật gần hết hạn mới debug.
12.7. Nginx không reload sau khi renew
Kiểm tra deploy hook:
ls -la /etc/letsencrypt/renewal-hooks/deploy/
sudo certbot renew --dry-run
sudo journalctl -u nginx --since "1 hour ago"
Đảm bảo hook có quyền execute và lệnh trong hook chạy được khi dùng sudo.
13. Checklist bật HTTPS production bằng Let’s Encrypt
13.1. Checklist trước khi chạy Certbot
- Domain đã trỏ DNS về đúng VPS.
- Không có AAAA record sai nếu VPS chưa dùng IPv6.
- Nginx server block có đúng
server_name. sudo nginx -tthành công.- UFW đã mở
Nginx Full. - Port 80 truy cập được từ internet.
- Website HTTP đang trả response hợp lệ.
13.2. Checklist sau khi cấp certificate
sudo certbot certificateshiển thị certificate đúng domain.curl -I https://example.comtrả response hợp lệ.curl -I http://example.comredirect sang HTTPS nếu đã bật redirect.- OpenSSL hiển thị đúng subject, issuer, notBefore và notAfter.
- Nginx error log không có lỗi mới.
- Website chính và endpoint health-check vẫn hoạt động.
13.3. Checklist renew
- Đã chạy
sudo certbot renew --dry-runthành công. - Đã kiểm tra timer hoặc cơ chế renew tự động.
- Đã xem log
/var/log/letsencrypt/letsencrypt.log. - Đã có deploy hook reload Nginx nếu cần.
- Đã có cách theo dõi ngày hết hạn certificate.
14. FAQ
14.1. Let’s Encrypt có miễn phí thật không?
Có. Let’s Encrypt cung cấp certificate miễn phí và tự động. Tuy nhiên, miễn phí không có nghĩa là bỏ qua vận hành. Bạn vẫn cần cấu hình đúng DNS, firewall, Nginx, renew và monitoring.
14.2. Có nên dùng wildcard certificate cho website nhỏ không?
Không cần thiết trong đa số trường hợp. Nếu chỉ có example.com và www.example.com, certificate thường là đủ. Wildcard certificate cần DNS-01, phức tạp hơn và nên dùng khi bạn thật sự có nhiều subdomain động.
14.3. Có cần mua SSL trả phí không?
Với blog, landing page, website WordPress, app PHP nhỏ hoặc API cá nhân, Let’s Encrypt thường là đủ. Chứng chỉ trả phí có thể cần trong một số bối cảnh doanh nghiệp, yêu cầu hợp đồng, kiểm định hoặc hỗ trợ riêng, nhưng không phải mặc định bắt buộc cho VPS tự host.
14.4. Có nên bật HSTS ngay sau khi cài SSL không?
Chưa nên bật ngay nếu bạn mới cấu hình HTTPS lần đầu. Hãy để website chạy ổn, renew dry-run thành công, các subdomain quan trọng đều có HTTPS, rồi mới cân nhắc HSTS. Nếu bật HSTS sai với thời gian dài, rollback sẽ khó hơn.
14.5. Certbot renew có cần chạy bằng tay không?
Không nên phụ thuộc vào renew thủ công. Certbot nên có cơ chế renew tự động. Bạn chỉ cần kiểm tra định kỳ bằng certbot renew --dry-run, xem log và đảm bảo certificate chưa gần hết hạn.
14.6. Tại sao phải giữ port 80 khi website đã HTTPS?
Port 80 vẫn hữu ích để redirect HTTP sang HTTPS và phục vụ HTTP-01 challenge cho Let’s Encrypt. Đóng port 80 có thể làm renew bằng HTTP-01 thất bại. Nếu muốn đóng port 80, hãy chuyển sang phương thức validation khác như DNS-01.
14.7. Sau bài này nên học gì tiếp?
Sau khi HTTPS đã chạy ổn, bước tiếp theo là tối ưu web hosting: gzip, cache, security headers, HTTP/2, log format và cách đo trước – sau. HTTPS là nền tảng, nhưng hiệu năng và khả năng quan sát log mới giúp website vận hành bền.
15. Kết luận
SSL Let’s Encrypt Ubuntu 24.04 không chỉ là một lệnh Certbot để có ổ khóa xanh. Một cấu hình HTTPS production-ready tối thiểu cần có DNS đúng, Nginx server block sạch, port 80 và 443 mở đúng, certificate đúng domain, redirect hợp lý, kiểm tra bằng OpenSSL, renew tự động và log đủ để debug khi có lỗi.
Trong bài này, bạn đã đi qua toàn bộ workflow: chuẩn bị DNS và firewall, cài Certbot bằng Snap, cấp certificate Let’s Encrypt cho Nginx, kiểm tra HTTPS bằng curl và openssl, test auto renew bằng certbot renew --dry-run, thêm deploy hook reload Nginx, và xử lý các lỗi phổ biến như unauthorized, timeout, redirect loop, sai SAN hoặc renew fail.
Khi HTTPS đã ổn định, website LEMP của bạn đã tiến thêm một bước quan trọng từ “chạy được” sang “vận hành được”. Từ đây, các bài tiếp theo về tối ưu web hosting, logging, backup, monitoring và troubleshooting sẽ có nền tảng an toàn hơn để triển khai.

