SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-28

วิธีติดตั้ง 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/certbot

snap จะมาพร้อมกับตัวตั้งเวลาของตัวเองคือ 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) เมื่อคุณขอใบรับรองจาก Let's Encrypt สำหรับ example.com ระบบจะทำการ resolve ชื่อโดเมนใน public DNS จากนั้นจะเปิดการเชื่อมต่อไปยัง พอร์ต 80 ที่หมายเลข IP ที่พบ และร้องขอ http://example.com/.well-known/acme-challenge/<token> เซิร์ฟเวอร์ของคุณจะต้องตอบกลับด้วยเนื้อหาของ token ชุดเดียวกับที่ Certbot เขียนลงในดิสก์ นี่คือกลไกทั้งหมดของกระบวนการนี้ ซึ่งนำไปสู่ผลลัพธ์ 3 ประการที่เป็นสาเหตุหลักของความล้มเหลวในการออกใบรับรอง

  • พอร์ต 80 ต้องสามารถเข้าถึงได้จากอินเทอร์เน็ตสาธารณะ ไม่ใช่แค่จากแล็ปท็อปของคุณ กฎของ ufw, security group ของผู้ให้บริการคลาวด์ หรือไฟร์วอลล์ในคอนโซลของ VPS ที่เปิดเฉพาะพอร์ต 443 จะทำให้การออกใบรับรองและการต่ออายุในอนาคตทั้งหมดล้มเหลว
  • DNS ต้องชี้มาที่เครื่องนี้อยู่แล้ว เซิร์ฟเวอร์ตรวจสอบจะทำการ lookup จากภายนอกด้วยตัวเอง ดังนั้นรายการ /etc/hosts ในเครื่องคุณและแคชของเบราว์เซอร์จึงไม่มีผลใดๆ ต่อกระบวนการนี้
  • หากคุณประกาศ AAAA record ระบบจะพยายามใช้ IPv6 ก่อน Let's Encrypt จะลองเชื่อมต่อผ่าน IPv4 หากการเชื่อมต่อ IPv6 ล้มเหลวโดยสิ้นเชิง แต่หากมี AAAA record ที่ล้าสมัยชี้ไปยังโฮสต์ที่ ตอบรับ การเชื่อมต่อและแสดงผลอย่างอื่น จะทำให้เกิดความล้มเหลวทันที

การทำ redirect สามารถทำได้ โดยระบบตรวจสอบจะติดตาม HTTP redirect ไปยัง 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;
    }
}

คำนำหน้า ^~ ในตำแหน่ง ACME มีความสำคัญ: มันช่วยป้องกันไม่ให้บล็อก return 301 กลืนคำขอตรวจสอบความถูกต้อง (challenge request) การคงตำแหน่งนี้ไว้บนพอร์ต 80 ช่วยให้การต่ออายุใบรับรองทำงานได้ต่อไปแม้ว่าส่วนที่เหลือของเว็บไซต์จะถูกบังคับให้เป็น HTTPS เท่านั้นแล้วก็ตาม

บล็อกทั้งสองด้านบนให้บริการไฟล์จากดิสก์ หาก nginx ทำหน้าที่เป็นหน้าด่านให้กับแอปพลิเคชัน location / จะกลายเป็นบล็อก proxy_pass และ เซิร์ฟเวอร์บล็อกของ reverse proxy ทีละบรรทัด จะครอบคลุมถึงเฮดเดอร์ที่แอปนั้นต้องการ ในขณะที่ตำแหน่ง ACME และคำสั่ง TLS จะยังคงเหมือนเดิมทุกประการ

ไวยากรณ์ของ HTTP/2 ขึ้นอยู่กับเวอร์ชันของ nginx ที่คุณใช้งาน และการใช้รูปแบบผสมกันจะทำให้เกิดข้อผิดพลาดขณะเริ่มทำงาน Ubuntu 24.04 มาพร้อมกับ nginx 1.24 ซึ่งต้องการการตั้งค่าแบบบรรทัดเดียวคือ listen 443 ssl http2; ส่วน Debian 13 มาพร้อมกับ nginx เวอร์ชันใหม่กว่า ซึ่งต้องการคำสั่ง http2 on; แยกต่างหาก ให้ตรวจสอบ nginx -v ก่อนเสมอ

ชี้ nginx ไปที่ live/ ห้ามชี้ไปที่ archive/ โดยเด็ดขาด ไฟล์ symlink ใน live/ จะถูกเปลี่ยนเป้าหมายใหม่ทุกครั้งที่มีการต่ออายุใบรับรอง การใช้พาธแบบตายตัวไปยัง 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 วัน Certbot จะดำเนินการต่ออายุเมื่อเหลือเวลาไม่ถึง 30 วัน ซึ่งทำให้คุณมีช่วงเวลา 30 วันในการแก้ไขปัญหาหากการต่ออายุล้มเหลว แทนที่จะเกิดเหตุการณ์ระบบหยุดทำงาน Let’s Encrypt ไม่ส่งอีเมลแจ้งเตือนวันหมดอายุอีกต่อไป และไม่มีใครมาคอยเตือนคุณ ดังนั้นการตรวจสอบสถานะจึงเป็นหน้าที่ของคุณ

ตรวจสอบตัวตั้งเวลาที่มาพร้อมกับการติดตั้งของคุณ:

systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificates

certbot renew จะตรวจสอบไฟล์ config ทุกไฟล์ใน /etc/letsencrypt/renewal/ โดยจะข้ามรายการที่ยังไม่ถึงช่วง 30 วันสุดท้าย และดำเนินการต่ออายุรายการที่เหลือโดยใช้ flag เดียวกันกับการรันครั้งแรก นั่นคือเหตุผลว่าทำไมการรันครั้งแรกจึงสำคัญ เพราะเป็นครั้งที่ระบบจะบันทึกค่าไว้

การต่ออายุไฟล์บนดิสก์เพียงอย่างเดียวไม่ส่งผลใดๆ ต่อระบบ เนื่องจาก Nginx จะยังคงให้บริการใบรับรองเก่าที่อยู่ในหน่วยความจำจนกว่าจะมีคำสั่งให้ reload คุณควรตั้งค่า 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/ จะถูกเรียกใช้งานหลังจากที่การต่ออายุสำเร็จ หากใช้ flag --deploy-hook จะเป็นการทำงานในลักษณะเดียวกันสำหรับใบรับรองหนึ่งรายการ โดยจะจัดเก็บ renew_hook = ... ไว้ในไฟล์ config ของการต่ออายุนั้นๆ certbot --nginx จะทำการ reload ให้คุณโดยอัตโนมัติ แต่การตั้งค่าแบบ --webroot และ --standalone จะไม่ทำเช่นนั้น การลืมตั้งค่า hook คือสาเหตุที่ทำให้เว็บไซต์ยังคงแสดงใบรับรองที่หมดอายุแล้ว ทั้งที่ certbot certificates รายงานว่ามีการต่ออายุเรียบร้อยแล้ว บริการอื่นๆ ที่ต้องอ่านใบรับรองตอนเริ่มต้นระบบก็ต้องการ hook นี้เช่นกัน ตัวอย่างเช่น แอปพลิเคชันที่รันในคอนเทนเนอร์อย่าง การติดตั้ง Nextcloud บน VPS ด้วย Docker, TLS และการสำรองข้อมูล จำเป็นต้องมีการตั้งค่าขั้นตอนการ restart หรือ reload ไว้ที่นี่ด้วยเช่นกัน

การทดสอบการต่ออายุจริง

sudo certbot renew --dry-run

คำสั่งนี้จะดำเนินการตรวจสอบความถูกต้องเต็มรูปแบบกับสภาพแวดล้อม staging ของ Let's Encrypt โดยใช้เส้นทางการทำงานของโค้ดชุดเดียวกัน, firewall ชุดเดียวกัน, DNS ชุดเดียวกัน, ไม่เสียโควตา rate-limit และไม่มีการเขียนข้อมูลลงดิสก์ หากการทดสอบนี้ผ่านในวันนี้ การต่ออายุแบบอัตโนมัติในอีก 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 หากมี AAAA record ที่ล้าสมัย ก็จะทำให้เกิดข้อความนี้เช่นกัน

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 แสดงว่าปัญหาไม่ได้อยู่ที่ใบรับรอง

DNS problem: NXDOMAIN looking up A for example.com, ชื่อโดเมนไม่สามารถ resolve ได้จากภายนอก อาจเกิดจาก record ใหม่ที่ยังไม่กระจายตัว หรือมีการตั้งค่า record ใน zone ที่ผู้ให้บริการจดทะเบียนโดเมนของคุณไม่ได้ใช้งานอยู่

too many certificates already issued for: example.com, เป็นการจำกัดอัตราการร้องขอ (rate limit) ซึ่งมักพบเมื่อผู้ใช้พยายามแก้ไขปัญหาซ้ำๆ Let's Encrypt จำกัดการออกใบรับรองซ้ำสำหรับชุดชื่อโดเมนเดิมไว้ที่ 5 ฉบับต่อสัปดาห์ และอนุญาตให้ขอใบรับรองใหม่ได้ 50 ฉบับต่อโดเมนหลักต่อสัปดาห์ ไม่มีวิธีใดปลดล็อกได้นอกจากต้องรอเวลา ให้ใช้ --dry-run เพื่อทดสอบกับสภาพแวดล้อม staging แทน

nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory, nginx ถูกตั้งค่าให้ใช้ใบรับรองที่ยังไม่ได้ออกให้ หรือใบรับรองที่ถูกลบออกด้วย certbot delete ไปแล้ว ให้คอมเมนต์ส่วน TLS server block ออกก่อน จากนั้นเริ่ม nginx ใหม่ ทำการออกใบรับรอง แล้วจึงคืนค่า 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 ใหม่ ตัวตั้งเวลาต่ออายุจะไม่ติดตามคุณไปโดยอัตโนมัติ หลังจากการย้ายข้อมูล การกู้คืน snapshot หรือการอัปเกรด distro ให้รัน systemctl list-timers 'certbot*' และ --dry-run หนึ่งครั้ง การละเลยขั้นตอนนี้จะทำให้เว็บไซต์ใช้งานไม่ได้ในอีก 89 วันถัดมา ตอนตี 3 ด้วยใบรับรองที่ทุกคนเข้าใจว่ามันต่ออายุเองได้

ทั้งหมดนี้ตั้งอยู่บนสมมติฐานว่าคุณเป็นผู้ควบคุมเครื่อง มี IP สาธารณะ และเปิดพอร์ต 80 สู่สาธารณะ หรือกล่าวอีกนัยหนึ่งคือการใช้ VPS กลไกข้างต้นทั้งหมดสามารถนำไปใช้ได้เหมือนกันกับ VPS ทุกประเภท

ขั้นตอนการจัดการใบรับรองแบบเดียวกันนี้สามารถนำไปใช้ บน Apache แทน nginx ได้ และในกรณีที่ไม่สามารถใช้ใบรับรองสาธารณะได้ การใช้ self-signed certificate บน Ubuntu ก็เพียงพอสำหรับบริการภายในองค์กร

FAQ

ฉันจำเป็นต้องเปิดพอร์ต 80 หรือไม่ หากเว็บไซต์ให้บริการเฉพาะ HTTPS เท่านั้น?

จำเป็น สำหรับการทำ HTTP-01 challenge โดย Let’s Encrypt จะเริ่มต้นการตรวจสอบผ่านพอร์ต 80 เสมอ และ Certbot ไม่มีฟีเจอร์ TLS-ALPN-01 ดังนั้นหาก firewall เปิดเฉพาะพอร์ต 443 จะทำให้ไม่สามารถออกใบรับรองครั้งแรกและไม่สามารถต่ออายุอัตโนมัติได้ การตั้งค่า redirect จากพอร์ต 80 ไปยัง HTTPS สามารถทำได้ตามปกติเพราะการตรวจสอบจะติดตามการ redirect ไปด้วย วิธีเดียวที่จะไม่ต้องใช้พอร์ต 80 เลยคือการใช้ DNS-01 ร่วมกับ plugin ของผู้ให้บริการ DNS

ระหว่าง 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 plugin ที่มีให้เฉพาะในรูปแบบ snap เท่านั้น ไม่ว่าจะเลือกทางใด ให้เลือกเพียงวิธีเดียวเท่านั้น เพราะการติดตั้งสองวิธีจะทำให้มีตัวจับเวลาการต่ออายุสองชุดที่ชี้ไปยัง /etc/letsencrypt เดียวกัน และตัวที่คุณลืมมักจะเป็นตัวที่ทำให้เกิดปัญหาในภายหลัง

Certbot สามารถออกใบรับรองแบบ wildcard สำหรับ nginx ได้หรือไม่?

ทำได้ผ่าน DNS-01 เท่านั้น ใบรับรองแบบ wildcard เช่น *.example.com ไม่มีชื่อโฮสต์เฉพาะสำหรับวางไฟล์ challenge ดังนั้น --nginx, --webroot และ --standalone จึงไม่สามารถใช้งานได้ ให้ติดตั้ง plugin สำหรับผู้ให้บริการ 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 สามารถยืนยันได้หรือไม่ว่าการต่ออายุจะทำงานได้จริง?

เกือบทั้งหมด เพราะเป็นการรัน challenge จริงกับสภาพแวดล้อม staging โดยใช้ firewall, DNS และ code path เดียวกัน โดยไม่มีการจำกัดอัตรา (rate-limit) และไม่มีการเขียนไฟล์ลงดิสก์ ดังนั้นหากผ่านการทดสอบ แสดงว่าส่วนของเครือข่ายทำงานได้ถูกต้อง อย่างไรก็ตาม วิธีนี้ไม่สามารถยืนยันได้อย่างสมบูรณ์ว่า deploy hook ของคุณจะทำงานหรือไม่ ควรทดสอบแยกต่างหากโดยการรันสคริปต์ hook ด้วยตนเองและตรวจสอบ sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf