วิธีติดตั้ง Certbot บน Ubuntu 24.04 สำหรับ Nginx
เรียนรู้วิธีติดตั้ง Certbot บน Ubuntu 24.04 ด้วยคำสั่ง apt install certbot python3-certbot-nginx พร้อมวิธีแก้ไขปัญหาพอร์ต 80 หมดเวลาและข้อควรระวังในการเลือกใช้ระหว่าง apt หรือ snap
การติดตั้ง Certbot: apt หรือ snap
บน Ubuntu 24.04 ตัว sudo apt install certbot python3-certbot-nginx จะให้ Certbot ที่ใช้งานได้จริงและออกใบรับรอง Let's Encrypt ที่เชื่อถือได้ในระดับสาธารณะ เอกสารต้นทางของ Certbot แนะนำให้ใช้ snap ความแตกต่างมีเพียงเล็กน้อย คือ snap จะติดตามรุ่นจากต้นทาง ส่วนแพ็กเกจใน archive จะยึดตามเวอร์ชันที่มาพร้อมกับ LTS และได้รับการแก้ไขด้านความปลอดภัย
เลือกเพียงวิธีเดียว การมี Certbot สองชุดหมายถึงมีตัวตั้งเวลาต่ออายุสองตัวที่มุ่งเป้าไปยังโครงสร้าง /etc/letsencrypt เดียวกัน และชุดที่คุณลืมไปนั่นแหละที่จะสร้างปัญหาให้คุณในภายหลัง
วิธีใช้ apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxคำสั่งนี้จะติดตั้ง /usr/bin/certbot, ปลั๊กอินสำหรับ nginx, คู่ของ certbot.service + certbot.timer และรายการ /etc/cron.d/certbot ซึ่งไม่มีผลการทำงานภายใต้ systemd
วิธีใช้ snap:
sudo apt remove certbot python3-certbot-nginx
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotSnap จะมาพร้อมกับตัวตั้งเวลาของตัวเองคือ snap.certbot.renew.timer ให้ลบแพ็กเกจ apt ออก ก่อน ที่จะติดตั้ง snap
การติดตั้งทั้งสองวิธีจะทำงานเหมือนกันในภายหลัง ยกเว้นช่วงเวลาที่ตัดสินใจต่ออายุ (อธิบายไว้ในหัวข้อการต่ออายุ) Certbot 2.x ใช้กุญแจ ECDSA (P-256) เป็นค่าเริ่มต้น ให้ส่งผ่าน --key-type rsa เฉพาะกรณีที่ไคลเอนต์ไม่รองรับ ECDSA เท่านั้น สถานะทั้งหมดจะถูกเก็บไว้ภายใต้ /etc/letsencrypt โดยที่ archive/ เก็บไฟล์กุญแจและใบรับรองจริง, live/ เป็น symlink ไปยังไฟล์ปัจจุบัน, renewal/ เก็บไฟล์คอนฟิกหนึ่งไฟล์ต่อหนึ่งใบรับรอง และ accounts/ เก็บกุญแจบัญชี ACME ของคุณ
การทำงานจริงของ HTTP-01 และเหตุผลที่พอร์ต 80 ไม่ใช่ทางเลือก
การตรวจสอบแบบ HTTP-01 คือการเรียกกลับ (callback) คุณร้องขอใบรับรองสำหรับ example.com จาก Let's Encrypt ซึ่งจะทำการตรวจสอบชื่อโดเมนใน public DNS จากนั้นจะเปิดการเชื่อมต่อไปยัง พอร์ต 80 ที่ที่อยู่ IP ที่พบ และร้องขอ http://example.com/.well-known/acme-challenge/<token> เซิร์ฟเวอร์ของคุณต้องตอบกลับด้วยเนื้อหาของโทเค็นที่ Certbot เขียนลงในดิสก์ไว้อย่างถูกต้อง นี่คือกลไกทั้งหมดของมัน ซึ่งนำไปสู่ผลลัพธ์ 3 ประการที่เป็นสาเหตุหลักของความล้มเหลวในการออกใบรับรอง
- พอร์ต 80 ต้องสามารถเข้าถึงได้จากอินเทอร์เน็ตสาธารณะ ไม่ใช่แค่จากแล็ปท็อปของคุณ กฎ
ufw, security group ของผู้ให้บริการคลาวด์ หรือไฟร์วอลล์ของ VPS ที่เปิดเฉพาะพอร์ต 443 จะทำให้การออกใบรับรองและการต่ออายุในอนาคตทั้งหมดล้มเหลว - DNS ต้องชี้มาที่เครื่องนี้อยู่แล้ว เซิร์ฟเวอร์ตรวจสอบจะทำการ lookup จากภายนอกด้วยตัวเอง รายการ
/etc/hostsและแคชในเบราว์เซอร์ของคุณไม่มีผลใดๆ ต่อกระบวนการนี้ - หากคุณประกาศ AAAA record ระบบจะพยายามใช้ IPv6 ก่อน Let's Encrypt จะลองใช้ IPv4 ก็ต่อเมื่อการเชื่อมต่อ IPv6 หมดเวลาเท่านั้น การเชื่อมต่อ IPv6 ที่ถูกปฏิเสธจะไม่ถูกลองใหม่ และหากมี AAAA record ที่ล้าสมัยชี้ไปยังโฮสต์ที่ ยอมรับ การเชื่อมต่อแต่ให้บริการอื่น ระบบจะถือว่าล้มเหลวทันที
การทำ redirect สามารถทำได้ โดยระบบตรวจสอบจะติดตามการเปลี่ยนเส้นทาง HTTP ไปยัง HTTPS และไม่สนใจว่าใบรับรองที่ปลายทางจะขาดหายไป หมดอายุ หรือเป็นแบบ self-signed แต่สิ่งที่ระบบจะไม่ทำคือการเริ่มต้นที่พอร์ตอื่นนอกจากพอร์ต 80 เนื่องจาก Certbot ไม่มีการใช้งาน TLS-ALPN-01 ดังนั้นการ "ใช้แค่พอร์ต 443" จึงไม่ใช่ทางออกสำหรับกรณีนี้
การเลือกตัวยืนยันตัวตน: --nginx, --webroot, --standalone
--nginx เป็นค่าเริ่มต้นที่เหมาะสมเมื่อ nginx ทำงานอยู่แล้วและให้บริการโดเมนนั้นอยู่ Certbot จะทำการวิเคราะห์ไฟล์ config ของคุณ แทรกตำแหน่งสำหรับทำ challenge ชั่วคราว สั่ง reload nginx เพื่อตรวจสอบความถูกต้อง จากนั้นจึงเขียนคำสั่ง TLS ลงใน server block ของคุณ โดยไม่มีช่วงเวลาที่ระบบหยุดทำงาน (downtime)
sudo certbot --nginx -d example.com -d www.example.comสคริปต์สำหรับเครื่องเซิร์ฟเวอร์ใหม่:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot เหมาะสมเมื่อคุณไม่ต้องการให้ Certbot เข้าไปยุ่งกับไฟล์ config ของ nginx โดยคุณอาจสร้างไฟล์ config จาก template เก็บไว้ใน git หรือจัดการผ่าน Ansible แทน Certbot จะเขียนเฉพาะไฟล์ challenge ลงในไดเรกทอรีที่คุณเปิดให้บริการอยู่เท่านั้น
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone เหมาะสมเมื่อไม่มีบริการใดใช้งานพอร์ต 80 อยู่ เช่น เมลเซิร์ฟเวอร์, API ที่สื่อสารผ่านพอร์ต 443 เท่านั้น หรือสคริปต์ที่รันตอนเริ่มระบบครั้งแรกก่อนที่ nginx จะถูกติดตั้ง Certbot จะทำการ bind พอร์ต 80 ด้วยตัวเองเป็นเวลาสั้นๆ หาก nginx กำลังทำงานอยู่ วิธีนี้จะล้มเหลว ดังนั้นควรหยุดการทำงานของ nginx ก่อนรันคำสั่ง:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"hook เหล่านี้จะถูกบันทึกไว้ในไฟล์ config สำหรับการต่ออายุใบรับรอง ดังนั้นการหยุดและเริ่มบริการใหม่จะเกิดขึ้นโดยอัตโนมัติเมื่อถึงกำหนดต่ออายุ
บล็อกเซิร์ฟเวอร์ที่ทำงานได้ทั้งก่อนและหลังมีใบรับรอง
ปัญหาไก่กับไข่คือ nginx จะปฏิเสธการเริ่มทำงานหาก ssl_certificate ชี้ไปยังไฟล์ที่ไม่มีอยู่จริง และ Certbot ไม่สามารถตรวจสอบความถูกต้องได้ในขณะที่ nginx ปิดอยู่ ให้เปิดเว็บไซต์บนพอร์ต 80 ก่อน
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
}รัน sudo nginx -t && sudo systemctl reload nginx, ยืนยันว่า curl -I http://example.com/ ตอบกลับจาก ภายนอก เครื่อง จากนั้นจึงออกใบรับรอง หลังจากนั้น:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}prefix ของ ACME ที่ยาวกว่าจะมีความสำคัญเหนือกว่า location / ดังนั้น return 301 จึงไม่เห็นคำขอ challenge ตัวปรับแต่ง ^~ มีประโยชน์ในกรณีอื่น: เมื่อ prefix นั้นตรงกัน nginx จะข้าม location แบบ regex ทั้งหมด ดังนั้นกฎที่เพิ่มเข้ามาภายหลังเช่น location ~ /\. { deny all; } จึงไม่สามารถบล็อก challenge ได้ การคง location นั้นไว้บนพอร์ต 80 หมายความว่าการต่ออายุจะยังคงทำงานได้หลังจากที่เว็บไซต์ส่วนที่เหลือเปลี่ยนเป็น HTTPS-only แล้ว
บล็อกทั้งสองด้านบนให้บริการไฟล์จากดิสก์ หาก nginx ทำหน้าที่เป็นหน้าด่านให้กับแอปพลิเคชัน location / จะกลายเป็นบล็อก proxy_pass และ บล็อกเซิร์ฟเวอร์ reverse proxy ทีละบรรทัด จะครอบคลุมถึง header ที่แอปนั้นต้องการ ในขณะที่ location ของ ACME และคำสั่ง TLS จะยังคงเหมือนเดิมทุกประการ
ไวยากรณ์ของ HTTP/2 ขึ้นอยู่กับเวอร์ชันของ nginx ที่คุณใช้ Ubuntu 24.04 มาพร้อมกับ nginx 1.24 ซึ่งต้องการให้เขียนแบบ inline คือ listen 443 ssl http2; และจะเริ่มทำงานไม่ได้หากคุณใช้คำสั่ง http2 on; แยกต่างหาก ส่วน Debian 13 มาพร้อมกับ nginx 1.26 ซึ่งต้องการ http2 on; และจะแจ้งเตือนเพียงว่ารูปแบบ inline นั้นเลิกใช้แล้ว ให้ตรวจสอบ nginx -v ก่อน
ชี้ nginx ไปที่ live/ ห้ามชี้ไปที่ archive/ โดยเด็ดขาด เพราะ live/ จะถูกเปลี่ยนเป้าหมาย symlink ในทุกครั้งที่มีการต่ออายุ การใช้ path ตรงไปยัง archive/ จะทำให้คุณติดอยู่กับใบรับรองที่หมดอายุโดยไม่รู้ตัว
Wildcard หมายถึง DNS-01 และ DNS-01 หมายถึงการใช้ปลั๊กอิน
Wildcard certificate (*.example.com) ไม่สามารถตรวจสอบความถูกต้องผ่าน HTTP-01 ได้ เนื่องจากไม่มีชื่อโฮสต์เดียวให้ดึงไฟล์ตรวจสอบ DNS-01 จึงเป็นหนทางเดียว: คุณต้องพิสูจน์การควบคุมโดเมนด้วยการประกาศ TXT record ใน _acme-challenge.example.com Certbot จำเป็นต้องใช้ข้อมูลรับรอง API ของผู้ให้บริการ DNS เพื่อดำเนินการดังกล่าวโดยอัตโนมัติ ซึ่งเป็นเหตุผลว่าทำไมจึงต้องมีปลั๊กอินสำหรับผู้ให้บริการ คำแนะนำฉบับสมบูรณ์สำหรับ Wildcard certificate ครอบคลุมกลไกของ TXT record และกับดักในการต่ออายุแบบ manual; ต่อไปนี้เป็นเวอร์ชันย่อสำหรับ Cloudflare
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareบนพาธของ apt จะใช้ sudo apt install python3-certbot-dns-cloudflare แทน ข้อมูลรับรองควรเก็บไว้ในไฟล์ที่เข้าถึงได้เฉพาะ root เท่านั้น:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereจำกัดขอบเขตของ token ให้มีสิทธิ์แก้ไข DNS เฉพาะโซนนั้นๆ เท่านั้น นี่คือคีย์สำหรับ DNS ของคุณ โปรดดูแลรักษาให้ปลอดภัย
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'ใส่เครื่องหมายคำพูดครอบ wildcard เพื่อป้องกันไม่ให้ shell ทำการ glob ค่าดังกล่าว DNS-01 ยังช่วยแก้ปัญหาที่ HTTP-01 ทำไม่ได้ เช่น การออกใบรับรองสำหรับโฮสต์ที่ไม่มีพอร์ต 80 เปิดสาธารณะ, บริการภายใน, เครื่องที่เข้าถึงได้ผ่าน self-hosted WireGuard VPN บน VPS เท่านั้น หรือแผงควบคุมผู้ดูแลระบบบนอินเทอร์เฟซส่วนตัว
การต่ออายุ: ระยะเวลา 90 วัน, ตัวตั้งเวลา, และ deploy hook
ใบรับรองจาก Let's Encrypt มีอายุการใช้งาน 90 วัน แพ็กเกจ apt (Certbot 2.9.0) จะทำการต่ออายุเมื่อเหลือเวลาไม่ถึง 30 วัน ส่วน snap จะติดตามเวอร์ชันล่าสุดของ Certbot ซึ่งตั้งแต่เวอร์ชัน 4.1 เป็นต้นไป จะมีการสอบถามเวลาการต่ออายุที่แนะนำจาก Let's Encrypt ผ่าน ACME Renewal Info (ARI) และอาจต่ออายุก่อนกำหนด สำหรับใบรับรองของ Let's Encrypt โดยทั่วไปหมายถึงช่วงเวลาประมาณสองในสามของอายุใบรับรอง ไม่ว่ากรณีใด คุณจะมีช่วงเวลาประมาณ 30 วันที่หากการต่ออายุล้มเหลว คุณยังสามารถแก้ไขได้ก่อนที่จะเกิดปัญหาบริการหยุดชะงัก คาดว่าการต่ออายุจะเกิดขึ้นบ่อยขึ้นตั้งแต่ปีหน้าเป็นต้นไป เนื่องจากโปรไฟล์เริ่มต้นของ Let's Encrypt จะเปลี่ยนเป็นใบรับรองอายุ 64 วันในวันที่ 10 กุมภาพันธ์ 2027 และเป็น 45 วันในวันที่ 16 กุมภาพันธ์ 2028 ทาง Let's Encrypt จะไม่ส่งอีเมลแจ้งเตือนวันหมดอายุอีกต่อไป ไม่มีใครมาคอยเตือนคุณ ดังนั้นการตรวจสอบสถานะจึงเป็นหน้าที่ของคุณ
ตรวจสอบตัวตั้งเวลาที่มาพร้อมกับการติดตั้งของคุณ:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew จะไล่ตรวจสอบไฟล์ config ทุกไฟล์ใน /etc/letsencrypt/renewal/ โดยจะข้ามรายการที่ยังไม่ถึงกำหนด และทำการต่ออายุรายการที่เหลือโดยใช้ตัวเลือกที่บันทึกไว้ใน config นั้นๆ ซึ่งตัวเลือกเหล่านี้คือ flag เดียวกันกับที่ใช้ในการรันครั้งแรก เว้นแต่คุณจะแก้ไขในภายหลังด้วย certbot reconfigure นี่คือเหตุผลว่าทำไมการรันครั้งแรกจึงสำคัญ เพราะเป็นค่าที่ถูกบันทึกไว้
การต่ออายุไฟล์บนดิสก์เพียงอย่างเดียวไม่ส่งผลใดๆ ต่อระบบ เพราะ nginx จะยังคงให้บริการใบรับรองเก่าที่อยู่ในหน่วยความจำจนกว่าจะมีคำสั่งให้โหลดใหม่ ให้ตั้งค่า deploy hook ไว้ดังนี้:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shคำสั่งใดก็ตามที่สามารถรันได้ใน renewal-hooks/deploy/ จะทำงานหลังจากที่การต่ออายุสำเร็จ --deploy-hook flag ทำหน้าที่เดียวกันสำหรับใบรับรองหนึ่งรายการ โดยจะบันทึก renew_hook = ... ไว้ในไฟล์ config การต่ออายุนั้นๆ certbot --nginx จะทำการโหลดใหม่ให้คุณโดยอัตโนมัติ ในขณะที่การตั้งค่าแบบ --webroot และ --standalone จะไม่ทำเช่นนั้น การลืมตั้งค่า hook คือสาเหตุที่ทำให้เว็บไซต์ยังคงแสดงใบรับรองที่หมดอายุแล้ว ทั้งที่ certbot certificates รายงานว่ามีการต่ออายุใบรับรองใหม่เรียบร้อยแล้ว บริการอื่นๆ ที่ต้องอ่านใบรับรองตอนเริ่มต้นระบบก็ต้องการ hook เดียวกันนี้ เช่น แอปที่รันในคอนเทนเนอร์อย่าง การติดตั้ง Nextcloud บน VPS ด้วย Docker, TLS และการสำรองข้อมูล ก็จำเป็นต้องตั้งค่าขั้นตอนการ restart หรือ reload ไว้ที่นี่เช่นกัน
การทดสอบการต่ออายุจริง
sudo certbot renew --dry-runคำสั่งนี้จะดำเนินการตรวจสอบตามเงื่อนไขทั้งหมดกับสภาพแวดล้อม staging ของ Let's Encrypt ซึ่งใช้เส้นทางการทำงานของโค้ดเดียวกัน, firewall เดียวกัน, DNS เดียวกัน โดยไม่มีการนับรวมในขีดจำกัดอัตราการใช้งาน (rate limits) ของระบบผลิตจริง (เนื่องจาก staging มีขีดจำกัดของตนเองที่สูงกว่ามาก) และไม่มีการบันทึกใบรับรองลงในดิสก์ หากการทดสอบนี้ผ่านในวันนี้ การต่ออายุอัตโนมัติในอีก 60 วันข้างหน้าก็จะผ่านเช่นกัน โดยมีเงื่อนไขว่าไม่มีการเปลี่ยนแปลงสภาพแวดล้อมของเซิร์ฟเวอร์ในภายหลัง
การทดสอบแบบ dry run ไม่สามารถยืนยันได้ว่า reload hook ของคุณจะทำงานหรือไม่ เนื่องจากพฤติกรรมในส่วนนี้จะแตกต่างกันไปตามเวอร์ชันของ Certbot ให้ทดสอบส่วนนี้ด้วยตนเองโดยการรันสคริปต์ hook โดยตรง, ตรวจสอบว่า systemctl reload nginx ทำงานสำเร็จ และตรวจสอบ sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf
ข้อผิดพลาดที่คุณจะพบจริง
Could not bind to IPv4 or IPv6., --standalone ในขณะที่ nginx กำลังใช้งานพอร์ต 80 อยู่ ให้ใช้ --nginx หรือ --webroot หรือหยุดการทำงานของ nginx ชั่วคราวก่อนเริ่มรัน ตรวจสอบว่าโปรเซสใดถือพอร์ตอยู่ด้วย sudo ss -lntp | grep ':80'
Timeout during connect (likely firewall problem), Let's Encrypt ไม่สามารถเข้าถึงพอร์ต 80 ได้ ให้ไล่ตรวจสอบจากภายในออกไปภายนอก: sudo ufw status (เปิดพอร์ตด้วย sudo ufw allow 'Nginx Full'), จากนั้นตรวจสอบ firewall ของผู้ให้บริการ VPS และสุดท้ายคือ DNS ให้ทดสอบจากเครือข่ายภายนอกที่ไม่ใช่เซิร์ฟเวอร์ของคุณด้วย curl -sSv http://example.com/.well-known/acme-challenge/test ทั้งนี้ record ประเภท AAAA ที่ล้าสมัยอาจทำให้เกิดข้อความแจ้งเตือนนี้ได้เช่นกัน
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404, สามารถเข้าถึงพอร์ต 80 ได้ แต่ไม่พบไฟล์ token ที่ต้องการ คำขออาจถูกส่งไปยัง server block อื่น (ตรวจสอบว่า server block ใดเป็นเจ้าของ default_server) หรือไดเรกทอรีที่ระบุใน -w ไม่ใช่ไดเรกทอรีที่ nginx ใช้งานจริง ให้ลองวางไฟล์ไว้ที่ /var/www/example.com/.well-known/acme-challenge/test แล้วเรียกดูจากภายนอก หากได้ผลลัพธ์เป็น 404 แสดงว่าปัญหาไม่ได้เกิดจาก certificate
DNS problem: NXDOMAIN looking up A for example.com, ชื่อโดเมนไม่สามารถ resolve ได้ในระดับสาธารณะ อาจเกิดจาก record ใหม่ที่ยังกระจายข้อมูลไม่ทั่วถึง หรือ record นั้นอยู่ใน zone ที่ผู้ให้บริการจดทะเบียนโดเมนของคุณไม่ได้ดูแลอยู่
too many certificates (5) already issued ..., เป็นข้อจำกัดด้านอัตราการใช้งาน (rate limit) ซึ่งมักพบเมื่อผู้ใช้พยายามแก้ไขปัญหาซ้ำๆ ข้อความจะระบุประเภทของขีดจำกัดและลงท้ายด้วยเวลา retry after Let's Encrypt จำกัดการออก certificate ซ้ำสำหรับชื่อโดเมนชุดเดิมไว้ที่ 5 ฉบับต่อสัปดาห์ และไม่มีวิธีข้ามขีดจำกัดนี้ได้ ต้องรอให้ครบกำหนดเวลาเท่านั้น นอกจากนี้ยังจำกัดการออก certificate ใหม่ไว้ที่ 50 ฉบับต่อโดเมนหลักต่อสัปดาห์ ซึ่งสามารถขอเพิ่มขีดจำกัดได้ ทั้งนี้การขอ certificate สำหรับชื่อชุดเดิมที่เคยมีอยู่แล้วจะนับเป็นการต่ออายุ (renewal) ซึ่งไม่ถูกนับรวมในขีดจำกัดนี้ และการต่ออายุผ่าน ARI จะได้รับการยกเว้นจากทุกขีดจำกัด ให้ทดสอบการตั้งค่ากับ staging ด้วย --dry-run
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory, nginx ถูกตั้งค่าให้ใช้ certificate ที่ยังไม่เคยถูกออกให้ หรือ certificate ที่ถูกลบออกไปแล้วด้วย certbot delete ให้คอมเมนต์ server block ส่วน TLS ออกก่อน จากนั้นเริ่มการทำงานของ nginx, ออก certificate ใหม่ แล้วจึงนำ server block กลับมาใช้งาน
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed, ไฟล์นี้มาพร้อมกับแพ็กเกจปลั๊กอินของ nginx บนระบบ certonly ที่ไม่มี python3-certbot-nginx ให้คุณติดตั้งปลั๊กอินเพิ่ม หรือแทนที่บรรทัด include ด้วยการตั้งค่า ssl_protocols และ ssl_ciphers ของคุณเอง
การจัดการในระดับสเกล
ใบรับรองหนึ่งใบสามารถระบุชื่อโดเมนได้สูงสุด 100 ชื่อ และการใช้ certbot --nginx -d a.example.com -d b.example.com ... เพียงใบเดียวอาจดูน่าสนใจ แต่หากมีระเบียน DNS รายการใดรายการหนึ่งล้าสมัยและทำให้การตรวจสอบความถูกต้องล้มเหลว จะส่งผลให้ชื่อโดเมนทั้งหมดบนใบรับรองนั้นใช้งานไม่ได้ การแยกใบรับรองต่อเว็บไซต์จะช่วยให้แต่ละไซต์ทำงานเป็นอิสระต่อกัน ซึ่งเป็นสิ่งที่ควรทำหากเซิร์ฟเวอร์ของคุณโฮสต์บริการมากกว่าสองสามอย่างขึ้นไป เมื่อมีจำนวนเว็บไซต์มากขึ้น การใช้ reverse proxy ที่รองรับ ACME จะคุ้มค่ากว่า เช่น Traefik reverse proxy ที่รันหลายแอปผ่าน Docker Compose ซึ่งจะร้องขอและต่ออายุใบรับรองด้วยตัวเอง ทำให้ไม่ต้องใช้ Certbot อีกต่อไป การเลือกว่า proxy ตัวใดควรอยู่หน้าสุดเป็นเรื่องของการตัดสินใจเฉพาะหน้า และ การเปรียบเทียบ Nginx กับ Caddy และ Traefik ส่วนใหญ่ขึ้นอยู่กับว่าคุณต้องการให้ proxy จัดการงานด้านใบรับรองและการตั้งค่ารายแอปให้คุณมากน้อยเพียงใด
ให้สำรองข้อมูล /etc/letsencrypt ทั้งหมด รวมถึง sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt โดยคงค่า symlinks ไว้ โครงสร้างไดเรกทอรีนั้นเก็บ accounts/ ซึ่งเป็นคีย์บัญชี ACME ของคุณ ซึ่งคุณไม่สามารถสร้างขึ้นใหม่ให้เหมือนเดิมได้ การย้ายไปยัง VPS ใหม่จึงลดขั้นตอนเหลือเพียง: ใช้ rsync คัดลอกโครงสร้างไดเรกทอรีด้วย -a, ติดตั้ง Certbot, ชี้ DNS ใหม่ และรัน certbot renew --dry-run ก่อนที่จะเริ่มใช้งานจริง
หากคุณสร้างเซิร์ฟเวอร์ใหม่หรือย้ายไปยังเวอร์ชัน LTS ตัวใหม่ ตัวตั้งเวลาการต่ออายุ (renewal timer) จะไม่ติดตามคุณไปด้วย หลังจากทำการย้ายข้อมูล กู้คืน snapshot หรืออัปเกรด distro ให้รัน systemctl list-timers 'certbot*' และ --dry-run หนึ่งครั้ง การละเลยขั้นตอนนี้คือสาเหตุที่ทำให้เว็บไซต์ใช้งานไม่ได้ในอีก 89 วันต่อมา ตอนตี 3 ด้วยใบรับรองที่ทุกคนเข้าใจว่ามันต่ออายุเองได้
เส้นทาง HTTP-01 ข้างต้นตั้งอยู่บนสมมติฐานว่าคุณควบคุมเครื่องที่มี Public IP และเปิดพอร์ต 80 สู่สาธารณะ หรือกล่าวอีกนัยหนึ่งคือ VPS ส่วน DNS-01 ไม่จำเป็นต้องมีเงื่อนไขดังกล่าว กลไกการทำงานข้างต้นเหมือนกันในทุกกรณี
ขั้นตอนการจัดการใบรับรองแบบเดียวกันนี้สามารถนำไปใช้ บน Apache แทน nginx ได้ และในกรณีที่ไม่สามารถใช้ใบรับรองสาธารณะได้ ใบรับรองแบบ self-signed บน Ubuntu สามารถใช้กับบริการภายในได้เช่นกัน
FAQ
ฉันจำเป็นต้องเปิดพอร์ต 80 หรือไม่ หากเว็บไซต์ของฉันให้บริการเฉพาะ HTTPS?
จำเป็น สำหรับการตรวจสอบแบบ HTTP-01 โดย Let’s Encrypt จะเริ่มต้นคำขอตรวจสอบผ่านพอร์ต 80 เสมอ และ Certbot ไม่มีฟังก์ชัน TLS-ALPN-01 ดังนั้นหากไฟร์วอลล์เปิดเฉพาะพอร์ต 443 จะทำให้ทั้งการออกใบรับรองครั้งแรกและการต่ออายุอัตโนมัติในภายหลังล้มเหลว การตั้งค่า redirect จากพอร์ต 80 ไปยัง HTTPS สามารถทำได้และระบบตรวจสอบจะติดตามไปเอง วิธีเดียวที่จะไม่ต้องใช้พอร์ต 80 เลยคือการใช้ DNS-01 โดยผ่านปลั๊กอินของผู้ให้บริการหรือใช้ --manual --preferred-challenges dns ของ Certbot ซึ่งคุณต้องเพิ่มระเบียน TXT ด้วยตนเอง และการต่ออายุจะไม่เป็นแบบอัตโนมัติเว้นแต่คุณจะจัดเตรียมสคริปต์ --manual-auth-hook ไว้
ระหว่าง apt กับ snap ควรติดตั้ง Certbot ตัวไหนสำหรับ nginx บน Ubuntu 24.04?
ให้ใช้ apt เนื่องจาก sudo apt install certbot python3-certbot-nginx จะติดตั้ง Certbot เวอร์ชัน 2.9.0 บน Ubuntu 24.04 ซึ่งเป็นเวอร์ชันที่ทันสมัยเพียงพอสำหรับทุกขั้นตอนในคู่มือนี้ ได้รับการอัปเดตความปลอดภัยผ่าน unattended-upgrades และไม่จำเป็นต้องใช้ snapd ให้เลือกใช้ snap เฉพาะในกรณีที่คุณต้องการเวอร์ชันล่าสุดทันทีหรือต้องการปลั๊กอิน DNS ที่แจกจ่ายในรูปแบบ snap เท่านั้น ไม่ว่าจะเลือกวิธีใด ให้เลือกเพียงวิธีเดียวเท่านั้น เพราะการติดตั้งสองชุดหมายถึงการมีตัวตั้งเวลาต่ออายุสองชุดที่ชี้ไปยังต้นไม้ /etc/letsencrypt เดียวกัน และชุดที่คุณลืมมักจะเป็นชุดที่สร้างปัญหาให้คุณในภายหลัง
Certbot สามารถออกใบรับรองแบบ wildcard สำหรับ nginx ได้หรือไม่?
ทำได้เฉพาะผ่าน DNS-01 เท่านั้น ใบรับรองแบบ wildcard เช่น *.example.com ไม่มีชื่อโฮสต์เฉพาะสำหรับดึงไฟล์ตรวจสอบ ดังนั้น --nginx, --webroot และ --standalone จึงใช้ไม่ได้ ให้ติดตั้งปลั๊กอินสำหรับผู้ให้บริการ DNS ของคุณ ใส่ API token ที่จำกัดสิทธิ์ไว้ในไฟล์ credentials ที่เข้าถึงได้เฉพาะ root และรัน certbot certonly --dns-cloudflare -d example.com -d '*.example.com' โดยใส่เครื่องหมายคำพูดครอบ wildcard เพื่อป้องกันไม่ให้ shell ทำการ glob
ทำไม nginx ยังคงแสดงใบรับรองเก่าหลังจากต่ออายุสำเร็จ?
nginx เก็บใบรับรองไว้ในหน่วยความจำและจะไม่ทราบว่ามีไฟล์ใหม่บนดิสก์จนกว่าจะทำการ reload โดย certbot --nginx จะทำการ reload ให้คุณ แต่การรัน --webroot และ --standalone จะไม่ทำเช่นนั้น ดังนั้นการต่ออายุอาจสำเร็จในขณะที่เบราว์เซอร์ยังคงเห็นใบรับรองที่กำลังจะหมดอายุ ให้วางสคริปต์ที่รันได้ไว้ใน /etc/letsencrypt/renewal-hooks/deploy/ ซึ่งรันคำสั่ง nginx -t && systemctl reload nginx เพื่อให้ระบบทำงานหลังจากต่ออายุสำเร็จทุกครั้ง
certbot renew --dry-run เป็นการยืนยันว่าการต่ออายุจะทำงานได้จริงหรือไม่?
ส่วนใหญ่ใช่ มันจะรันการตรวจสอบจริงกับสภาพแวดล้อม staging โดยใช้ไฟร์วอลล์เดียวกัน DNS เดียวกัน และเส้นทางโค้ดเดียวกัน โดยไม่กระทบต่อขีดจำกัดอัตรา (rate limits) ของระบบจริงและไม่บันทึกใบรับรองลงดิสก์ ดังนั้นหากผ่านแสดงว่าส่วนของเครือข่ายทำงานได้ปกติ อย่างไรก็ตาม มันไม่ได้ยืนยันอย่างชัดเจนว่า deploy hook ของคุณจะทำงาน ให้ทดสอบแยกต่างหากโดยการรันสคริปต์ hook ด้วยตนเองและตรวจสอบ sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf