Certbot على Ubuntu 24.04: nginx وLet's Encrypt
ثبّت Certbot على Ubuntu 24.04 عبر apt أو snap، واحصل على شهادة Let's Encrypt لـ nginx، وأصلح إخفاقات المنفذ 80 التي تُفشل تجديد HTTP-01 على خادم حي.
تثبيت Certbot: apt أو snap
على Ubuntu 24.04، يمنحك الأمر sudo apt install certbot python3-certbot-nginx نسخة عاملة من Certbot تُصدر شهادات Let's Encrypt حقيقية وموثوقة على نطاق واسع. توثيق Certbot الرسمي (upstream) يوجهك بدلًا من ذلك نحو حزمة snap؛ والفرق بينهما ضيق — فحزمة snap تتبع إصدارات المصدر الرسمي أولًا بأول، بينما حزمة الأرشيف (apt) تتبع ما شُحن مع إصدار LTS وتتلقى تصحيحات الأمان.
اختر واحدة منهما. فوجود نسختين من Certbot يعني وجود مؤقّتَي (timers) تجديد يستهدفان الشجرة نفسها /etc/letsencrypt، والنسخة التي نسيتها هي التي ستفاجئك.
مسار apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxهذا يثبّت /usr/bin/certbot، وإضافة (plugin) nginx، وزوجًا من الوحدات: certbot.service وcertbot.timer، وإدخالًا في /etc/cron.d/certbot لا يقوم بأي شيء (no-op) تحت 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. توجد كل الحالة (state) تحت /etc/letsencrypt: يحمل archive/ ملفات المفتاح والشهادة الحقيقية، ويحتوي live/ على روابط رمزية (symlinks) إلى الملفات الحالية، ويضم renewal/ ملف إعداد واحدًا لكل شهادة، ويحمل accounts/ مفتاح حساب ACME الخاص بك.
ما الذي يفعله HTTP-01 فعلًا، ولماذا المنفذ 80 ليس اختياريًا
تحدي HTTP-01 هو استدعاء عكسي (callback). تطلب من Let's Encrypt شهادة تغطي example.com؛ فتحلّ الاسم عبر DNS العام، وتفتح اتصالًا إلى المنفذ 80 عند العنوان الذي تجده، وتطلب http://example.com/.well-known/acme-challenge/<token>. يرد خادمك بمحتوى الرمز بالضبط الذي كتبه Certbot للتو على القرص. هذه هي الآلية بأكملها. ثلاث نتائج تترتب عليها، وهي المسؤولة عن معظم حالات فشل الإصدار.
- يجب أن يكون المنفذ 80 قابلًا للوصول من الإنترنت العام، لا من حاسوبك المحمول فقط. قاعدة
ufw، أو مجموعة أمان (security group) لدى مزوّد السحابة، أو جدار حماية في وحدة تحكم VPS يفتح المنفذ 443 فقط، كل ذلك يعطّل الإصدار وكل تجديد لاحق معه. - يجب أن يشير DNS إلى هذا الخادم مسبقًا. فخادم التحقق يجري بحثه الخاص من الخارج؛ ولا تعني له إدخالات
/etc/hostsلديك ولا ذاكرة متصفحك المؤقتة شيئًا. - إذا نشرت سجل AAAA، تُجرَّب IPv6 أولًا. تعيد Let's Encrypt المحاولة عبر IPv4 عندما يفشل اتصال IPv6 فشلًا تامًا — لكن سجل AAAA قديم يشير إلى مضيف يقبل الاتصال ويقدّم شيئًا آخر يمنحك فشلًا صريحًا.
إعادة التوجيه مسموحة: يتبع التحقق إعادة توجيه HTTP نحو HTTPS، ولا يهمه أن تكون الشهادة في الطرف الآخر مفقودة أو منتهية الصلاحية أو موقّعة ذاتيًا. أما ما لن يفعله فهو البدء من أي مكان غير المنفذ 80. لا يملك Certbot أي تطبيق لـ TLS-ALPN-01، لذا فإن «استخدم 443 فقط» ليس مخرجًا.
اختيار مصادِق (authenticator): --nginx، --webroot، --standalone
--nginx هو الخيار الافتراضي الصحيح عندما يكون nginx يعمل ويخدم النطاق بالفعل. يحلّل Certbot إعداداتك، ويُدرج موقع تحدٍّ مؤقتًا، ويعيد تحميل nginx، ويتحقق، ثم يكتب توجيهات TLS داخل كتلة الخادم لديك. من دون أي توقف للخدمة.
sudo certbot --nginx -d example.com -d www.example.comبصيغة آلية (scripted)، على خادم جديد:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot هو الخيار الصحيح حين تريد إبعاد Certbot تمامًا عن إعداد nginx لديك — إعداد تولّده من قالب، أو تحتفظ به في git، أو تنشره عبر Ansible. لا يكتب Certbot سوى ملف التحدي، داخل مجلد تخدمه أصلًا.
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 المنفذ 80 بنفسه لبضع ثوانٍ. إن كان nginx يعمل فعلًا، يفشل هذا — أوقفه أثناء التنفيذ:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"تُسجَّل هذه الخطافات (hooks) في إعداد تجديد الشهادة، لذا يتكرر الإيقاف والتشغيل نفسه تلقائيًا بلا تدخل عند التجديد.
كتلة خادم (server block) تعمل قبل وجود الشهادة وبعده
مشكلة الدجاجة والبيضة: يرفض 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 من ابتلاع طلب التحدي. إبقاء ذلك الموقع على المنفذ 80 يعني استمرار عمل التجديدات بعد أن يتحول باقي الموقع إلى HTTPS حصرًا.
تعتمد صياغة HTTP/2 على إصدار nginx لديك، والخلط بين الصيغتين ينتج خطأ عند بدء التشغيل. يشحن Ubuntu 24.04 مع nginx 1.24 الذي يريدها مضمّنة — listen 443 ssl http2;. أما Debian 13 فيشحن مع نسخة أحدث من nginx تريد توجيه http2 on; منفصلًا. تحقق من nginx -v أولًا.
وجّه nginx إلى live/، لا إلى archive/ أبدًا. تُعاد الإشارة في روابط live/ الرمزية عند كل تجديد؛ أما المسار الثابت داخل archive/ فيثبّتك على شهادة تنتهي صلاحيتها دون أن تشعر.
شهادات wildcard تعني DNS-01، وDNS-01 يعني إضافة (plugin)
شهادة wildcard (*.example.com) لا يمكن التحقق منها عبر HTTP-01 — إذ لا يوجد اسم مضيف واحد تُجلَب منه ملف. DNS-01 هو الطريق الوحيد: تثبت السيطرة عبر نشر سجل TXT باسم _acme-challenge.example.com. يحتاج Certbot إلى بيانات اعتماد API لمزوّد DNS لديك لفعل ذلك تلقائيًا بلا تدخل، وهذا بالضبط ما وُجدت من أجله إضافات المزوّدين. الدليل الكامل لشهادة wildcard يغطي آلية سجل TXT وفخ التجديد في الوضع اليدوي؛ وتتبعه نسخة 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احصر نطاق رمز API على صلاحيات تعديل DNS لتلك المنطقة (zone) وحدها. إنه مفتاح لـ DNS الخاص بك؛ عامله على هذا الأساس.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'ضع الـ wildcard بين علامتي اقتباس حتى لا يوسّعه الشِل (shell) بنفسه. يحل DNS-01 أيضًا ما لا يستطيع HTTP-01 حله: شهادات لمضيفين بلا منفذ 80 عام — خدمة داخلية، أو خادم لا يمكن الوصول إليه إلا عبر VPN من نوع WireGuard مستضاف ذاتيًا على VPS، أو لوحة إدارة على واجهة خاصة.
التجديد: التسعون يومًا، والمؤقّت، وخطاف النشر (deploy hook)
شهادات Let's Encrypt صالحة لتسعين يومًا. يجدد Certbot حين يتبقى أقل من ثلاثين يومًا، ما يمنحك نافذة من ثلاثين يومًا يكون فيها التجديد المعطّل إزعاجًا قابلًا للإصلاح لا انقطاعًا للخدمة. لم تعد Let's Encrypt ترسل رسائل تنبيه بقرب انتهاء الصلاحية — فلن يذكّرك أحد بذلك، لذا صارت المراقبة مسؤوليتك أنت الآن.
تحقق من المؤقّت الذي جاء مع تثبيتك:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatesيمر أمر certbot renew على كل إعداد في /etc/letsencrypt/renewal/، ويتجاوز كل ما هو خارج نافذة الثلاثين يومًا، ويجدد الباقي باستخدام الخيارات نفسها تمامًا التي استُخدمت في التنفيذ الأصلي. لهذا السبب يهم التنفيذ الأول: فهو الذي يُسجَّل.
تجديد الملف على القرص لا يغيّر شيئًا بمفرده — يواصل nginx تقديم الشهادة القديمة من الذاكرة إلى أن يخبره شيء بإعادة التحميل. اربط خطاف نشر واحدًا يكفي للأبد:
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 المهمة نفسها لشهادة واحدة، فيخزّن renew_hook = ... في إعداد تجديدها. يعيد certbot --nginx التحميل نيابة عنك؛ أما إعدادات --webroot و--standalone فلا تفعل ذلك. غياب الخطاف هو بالضبط سبب أن يقدّم موقع شهادة منتهية الصلاحية بينما يبلّغ certbot certificates بسرور عن شهادة جديدة تمامًا. أي شيء آخر يقرأ الشهادة عند بدء التشغيل يحتاج إلى الخطاف نفسه — تطبيق يعمل داخل حاوية (containerised) مثل تثبيت Nextcloud على VPS مع Docker وTLS ونسخ احتياطية يحتاج إلى ربط خطوة إعادة التشغيل أو إعادة التحميل الخاصة به هنا أيضًا.
اختبار التجديد فعليًا
sudo certbot renew --dry-runهذا ينفّذ التحدي كاملًا مقابل بيئة الاختبار (staging) الخاصة بـ Let's Encrypt: مسار الشيفرة نفسه، وجدار الحماية نفسه، وDNS نفسه، بلا أي تكلفة من حدّ المعدل، ودون كتابة أي شيء على القرص. إذا نجح اليوم، فسينجح التجديد التلقائي بعد ستين يومًا أيضًا، بافتراض ألا يتغير شيء في الخادم خلال تلك المدة.
التنفيذ التجريبي لا يثبت أن خطاف إعادة التحميل لديك يعمل فعلًا — فالسلوك هنا يختلف باختلاف إصدار Certbot. اختبر هذا النصف يدويًا: نفّذ سكربت الخطاف مباشرة، وتأكد من نجاح 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')، ثم جدار الحماية الخاص بمزوّد VPS، ثم DNS. اختبر من مكان ليس خادمك: curl -sSv http://example.com/.well-known/acme-challenge/test. سجل AAAA قديم ينتج هذه الرسالة نفسها.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404 — المنفذ 80 قابل للوصول، لكن الرمز لا يُقدَّم. وصل الطلب إلى كتلة خادم مختلفة (تحقق من أيها يملك default_server)، أو أن المجلد الممرَّر إلى -w ليس المجلد الذي يخدمه nginx فعلًا. ضع ملفًا في /var/www/example.com/.well-known/acme-challenge/test واجلبه من الخارج؛ فإن ظهر 404 هناك أيضًا، فلم تكن الشهادة يومًا هي المشكلة.
DNS problem: NXDOMAIN looking up A for example.com — الاسم لا يُحلّ علنًا. سجلات جديدة لم تنتشر بعد، أو سجل في منطقة لا يخدمها مسجّل نطاقك (registrar).
too many certificates already issued for: example.com — حدّ معدل (rate limit)، وهو الحد الذي يصطدم به الناس أثناء تصحيح الأخطاء في حلقة متكررة. تحدّ Let's Encrypt الشهادات المكرَّرة — المجموعة نفسها تمامًا من الأسماء — بخمس شهادات أسبوعيًا، وتسمح بشكل منفصل بخمسين شهادة جديدة لكل نطاق مسجَّل أسبوعيًا؛ ولا شيء يرفع أيًا من الحدين سوى الوقت. صحّح الأخطاء مقابل بيئة staging باستخدام --dry-run.
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory — nginx مضبوط لشهادة لم تُصدَر قط، أو شهادة حُذفت بالأمر certbot delete. علّق كتلة خادم TLS، وشغّل nginx، وأصدر الشهادة، ثم استعد الكتلة.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed — هذا الملف يأتي مع حزمة إضافة nginx. في خادم يستخدم certonly بلا python3-certbot-nginx، إما أضف الإضافة أو استبدل سطر include بإعدادات ssl_protocols وssl_ciphers الخاصة بك.
إدارة الشهادات على نطاق واسع
يمكن أن تحمل الشهادة الواحدة حتى مئة اسم، وتبدو certbot --nginx -d a.example.com -d b.example.com ... مغرية — إلى أن يُفشل سجل DNS قديم واحد التحقق ويسحب معه كل الأسماء الأخرى على تلك الشهادة. الشهادات المنفصلة لكل موقع تفشل باستقلالية، وهذا ما تريده على خادم يستضيف أكثر من شيء أو شيئين. بعد حفنة من المواقع، تثبت بوابة أمامية تفهم ACME جدارتها: بروكسي Traefik العكسي (reverse proxy) الذي يشغّل تطبيقات متعددة عبر Docker Compose يطلب الشهادات ويجدّدها بنفسه، ويخرج Certbot من الصورة كليًا.
انسخ /etc/letsencrypt احتياطيًا بالكامل — sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt — مع بقاء الروابط الرمزية سليمة. تحمل هذه الشجرة accounts/، مفتاح حساب ACME الخاص بك، الذي لا يمكنك إعادة توليده مطابقًا للأصل. الانتقال إلى VPS جديد يختصر عندها إلى: نسخ الشجرة بـ rsync باستخدام -a، وتثبيت Certbot، وإعادة توجيه DNS، وتنفيذ certbot renew --dry-run قبل التحويل الفعلي.
أعد بناء الخادم أو انتقل إلى إصدار LTS جديد، ولن يتبعك مؤقّت التجديد. بعد أي عملية ترحيل، أو استعادة من لقطة (snapshot)، أو ترقية توزيعة، نفّذ systemctl list-timers 'certbot*' وتنفيذًا واحدًا لـ --dry-run. تجاوز هذه الخطوة هو بالضبط كيف يتعطل موقع بعد 89 يومًا، عند الساعة الثالثة فجرًا، بشهادة افترض الجميع أنها تجدد نفسها.
كل هذا يفترض جهازًا تتحكم فيه، بعنوان IP عام ومنفذ 80 مفتوح للعالم — خادم VPS بعبارة أخرى. الآلية أعلاه هي نفسها على أي منها.
تنطبق خطوات الشهادة نفسها على Apache بدلًا من nginx، وحين لا تكون الشهادة العامة خيارًا متاحًا، تغطي شهادة موقّعة ذاتيًا على Ubuntu الخدمات الداخلية.
FAQ
هل أحتاج إلى فتح المنفذ 80 إذا كان موقعي يقدّم HTTPS فقط؟
نعم، من أجل تحدي HTTP-01. تبدأ Let's Encrypt طلب التحقق دائمًا على المنفذ 80، ولا يملك Certbot أي تطبيق لـ TLS-ALPN-01، لذا فإن جدار حماية يفتح المنفذ 443 فقط يحجب كلًا من الإصدار الأول وكل تجديد تلقائي لاحق. إعادة التوجيه من المنفذ 80 إلى HTTPS أمر مقبول — فالتحقق يتبعها. الطريقة الوحيدة لتجاوز المنفذ 80 كليًا هي DNS-01 مع إضافة مزوّد.
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 محدود النطاق في ملف بيانات اعتماد مقصور على root، ونفّذ certbot certonly --dns-cloudflare -d example.com -d '*.example.com'، مع وضع الـ wildcard بين علامتي اقتباس لمنع الشِل من توسيعه.
لماذا يستمر nginx في تقديم الشهادة القديمة بعد تجديد ناجح؟
يحتفظ nginx بالشهادة في الذاكرة ولا يلاحظ الملف الجديد على القرص إلا حين يعيد التحميل. يعيد certbot --nginx التحميل نيابة عنك، لكن تنفيذات --webroot و--standalone لا تفعل ذلك، لذا قد ينجح التجديد بينما لا يزال المتصفح يرى شهادة توشك على الانتهاء. ضع سكربتًا قابلًا للتنفيذ في /etc/letsencrypt/renewal-hooks/deploy/ ينفّذ nginx -t && systemctl reload nginx، وسيعمل بعد كل تجديد ناجح.
هل يثبت certbot renew --dry-run أن التجديد سينجح؟
في الغالب نعم. فهو ينفّذ التحدي الحقيقي مقابل بيئة staging — جدار الحماية نفسه، وDNS نفسه، ومسار الشيفرة نفسه — بلا أي تكلفة من حدّ المعدل ودون كتابة أي شيء على القرص، لذا فإن نجاحه يعني أن النصف الشبكي سليم. لكنه لا يثبت بشكل موثوق أن خطاف النشر لديك يعمل. اختبر ذلك بشكل منفصل: نفّذ سكربت الخطاف يدويًا وتحقق من sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.