> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# تثبيت Caddy على خادمك: Reverse Proxy بشهادات HTTPS تلقائية
- URL: https://arabroot.io/articles/تثبيت-caddy-reverse-proxy/
- Published: 2026-09-29T08:30:00.000Z
- Updated: 2026-10-05T10:18:25.000Z
- Description: يمنح Caddy كل موقع شهادة HTTPS تلقائياً بملف إعداد من بضعة أسطر. نثبته على Ubuntu مع Docker Compose، ونربط به تطبيقاتك بأمان، ونجهز النسخ الاحتياطي والتحديث وحلول المشكلات الشائعة.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, الشبكات والوصول, الاستضافة الذاتية, Caddy

لنفرض أن لديك تطبيقاً يعمل على السيرفر Server في المنفذ 3000، وتريد أن يصل إليه المستخدم عبر نطاق Domain خاص به وبشهادة HTTPS، فالطريقة التقليدية أن تثبت Nginx وتكتب له ملف إعداد Configuration لكل نطاق، ثم تشغل certbot لتطلب الشهادة، ثم تضيف مهمة مجدولة Cron Job تجددها كل ثلاثة أشهر، ثم تتذكر أن تعيد تحميل Nginx بعد كل تجديد، وكل خطوة من هذه الخطوات قابلة للنسيان، والنتيجة المعروفة موقع يتوقف فجأة لأن شهادته انتهت في يوم لم ينتبه له أحد. لذلك ظهرت أدوات تجمع ال Reverse Proxy وإدارة الشهادات في مكان واحد، والفرق الأول بينها هو طريقة الإعداد، ففي [Nginx Proxy Manager](https://arabroot.io/articles/nginx-proxy-manager-%D9%86%D8%B7%D8%A7%D9%82%D8%A7%D8%AA-%D9%88%D8%B4%D9%87%D8%A7%D8%AF%D8%A7%D8%AA-tls/) تدير النطاقات من واجهة ويب Web UI ويحفظ الإعداد في قاعدة بيانات Database، وفي [Traefik](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-traefik-reverse-proxy/) تكتب التوجيه وسوماً Labels على كل Container، أما [HAProxy](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-haproxy-load-balancer/) فهو موازن حمل Load Balancer قبل كل شيء ويعتمد غالباً على أداة خارجية لإصدار الشهادات.

وهذا الدليل عن [**Caddy**](https://caddyserver.com/?ref=arabroot.io)، وهو خادم ويب Web Server وReverse Proxy مفتوح المصدر Open Source مكتوب بلغة Go، يقرأ إعداده من ملف نصي واحد قصير اسمه [Caddyfile](https://caddyserver.com/docs/caddyfile/concepts?ref=arabroot.io)، وHTTPS فيه مفعل افتراضياً لكل موقع تذكره، فأنت تكتب اسم النطاق وعنوان التطبيق في ثلاثة أسطر، ثم يطلب Caddy الشهادة من [Let's Encrypt](https://letsencrypt.org/?ref=arabroot.io) ويجددها قبل انتهائها ويحول زوار HTTP إلى HTTPS دون أي خطوة منك، ولهذا تختاره فرق كثيرة عندما تريد أقصر طريق إلى موقع آمن.

وسوف نناقش في هذا المقال ما يلي:

- متى يناسبك Caddy ومتى يناسبك غيره، وكيف يعمل HTTPS التلقائي وأين يحفظ الشهادات.
- تثبيت Caddy على Ubuntu 24.04 من [ال Image الرسمي](https://hub.docker.com/%5F/caddy?ref=arabroot.io) مع Docker Compose، وكتابة Caddyfile يوجه نطاقين إلى تطبيقين.
- ترويسات الأمان Security Headers، وكلمة مرور لمسار الإدارة، والضغط Compression، والسجلات Logs، وتوزيع الحمل Load Balancing.
- حماية ال Admin API، وإعادة التحميل دون انقطاع، والإضافات وشهادات Wildcard.
- التحقق من النجاح، والنسخ الاحتياطي، والتحديث ولماذا نتخطى الإصدار 2.11.6، وأشهر المشكلات وحلولها.

⚠️

لا يستمع على المنفذين 80 و443 في السيرفر الواحد إلا برنامج واحد، لذلك اختر Reverse Proxy واحداً لكل سيرفر: Nginx Proxy Manager أو Traefik أو HAProxy أو Caddy، فإذا كان أحدها يعمل الآن فانقل نطاقاته إلى Caddy ثم أوقفه قبل تشغيل Caddy، وتجد الفروق بينها في [مقارنة أدوات ال Reverse Proxy](https://arabroot.io/articles/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-nginx-proxy-manager-%D9%88-traefik-%D9%88-haproxy/).

💡

تشغل منصة [Coolify](https://coolify.io/docs/core/networking/proxy/overview?ref=arabroot.io) على كل سيرفر Reverse Proxy خاصاً بها، وTraefik هو خيارها الافتراضي، ولكنها تتيح [اختيار Caddy بدلاً منه](https://coolify.io/docs/core/networking/proxy/caddy/overview?ref=arabroot.io) فتولد له وسوم Docker وتدير شهاداته بنفسها، ويذكر توثيقها أن Traefik هو ما يستخدمه فريقها في الإنتاج وأن توثيقه ودعمه أوسع. فإذا كنت تنوي تثبيت Coolify على هذا السيرفر فلا تثبت Caddy منفصلاً، واتبع دليل [تثبيت Coolify](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-coolify-%D9%84%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/) مباشرة.

## متى يناسبك Caddy، ومتى يناسبك غيره؟

اختر Caddy إذا أردت أن يكون الإعداد ملفاً نصياً تحفظه في Git وتراجعه كأي كود Code، وتريد أن تعمل الشهادات دون خطوة إضافية، وهو مناسب أيضاً لمن يشغل مواقع ثابتة Static Sites إلى جانب بضعة تطبيقات، لأنه خادم ملفات File Server كذلك وليس Reverse Proxy فقط. والجدول التالي يلخص الفرق بينه وبين الأدوات الثلاث الأخرى:

| المعيار                                         | Caddy                                       | Nginx Proxy Manager | Traefik                                | HAProxy                  |
| ----------------------------------------------- | ------------------------------------------- | ------------------- | -------------------------------------- | ------------------------ |
| طريقة الإعداد                                   | ملف Caddyfile قصير                          | واجهة ويب           | وسوم Docker وملفات YAML                | ملف haproxy.cfg          |
| شهادات HTTPS                                    | تلقائية افتراضياً لكل نطاق في الملف         | من الواجهة لكل مضيف | تلقائية بعد تعريف Certificate Resolver | عبر أداة خارجية غالباً   |
| اكتشاف ال Containers تلقائياً Service Discovery | لا يوجد في النسخة القياسية، ومتاح عبر إضافة | لا يوجد             | نعم، من الوسوم                         | لا يوجد                  |
| الوصول إلى Docker socket                        | لا يحتاج إليه                               | لا يحتاج إليه       | يحتاج إليه                             | لا يحتاج إليه            |
| توزيع الحمل وفحوص الصحة Health Checks           | متوفرة                                      | محدودة              | متوفرة                                 | متقدمة، وهي غرضه الأساسي |

وفي المقابل، إذا كان من يدير السيرفر لا يريد أن يفتح ملفاً أصلاً فNginx Proxy Manager أيسر له، وإذا كانت تطبيقاتك تظهر وتختفي كل يوم وتريد أن يعلن كل منها عن نطاقه في ملف Compose الخاص به فTraefik أنسب، وإذا كانت لديك عدة سيرفرات خلفية Backend Servers وحركة كثيفة وخدمات TCP فHAProxy مصمم لهذا بالتحديد.

## المتطلبات Requirements

- سيرفر يعمل بنظام Ubuntu 24.04 وعليه Docker Engine وCompose v2، وإذا لم يكن جاهزاً فاتبع دليل [تثبيت Docker على Ubuntu](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-docker-%D8%B9%D9%84%D9%89-ubuntu/)، ثم دليل [تأمين خادم VPS من أول دخول](https://arabroot.io/articles/%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D8%AE%D8%A7%D8%AF%D9%85-vps-%D9%85%D9%86-%D8%A3%D9%88%D9%84-%D8%AF%D8%AE%D9%88%D9%84/).
- نطاق تدير سجلات DNS Records الخاصة به، وفي هذا الدليل يشير السجلان `app.example.com` و`api.example.com` إلى العنوان العام Public IP للسيرفر `203.0.113.10`، وإذا كانت هذه السجلات جديدة عليك فقد شرحناها في [شرح DNS وسجلاته للمبتدئين](https://arabroot.io/articles/%D8%B4%D8%B1%D8%AD-dns-%D9%88%D8%B3%D8%AC%D9%84%D8%A7%D8%AA%D9%87-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).
- المنفذان Ports `80/tcp` و`443/tcp` مفتوحان من الإنترنت في جدار الحماية Firewall لدى المزود وعلى السيرفر معاً، وإذا أردت HTTP/3 فافتح أيضاً `443/udp` لأنه مفعل في Caddy افتراضياً.
- لا يستمع أي برنامج آخر على المنفذين 80 و443، فإذا لم يطبع الأمر التالي شيئاً فالمنفذان متاحان:

```bash
sudo ss -ltnp | grep -E ':(80|443) '
```

أما الموارد Resources فCaddy برنامج واحد صغير يكفيه أصغر خادم افتراضي خاص VPS لعدد من المواقع والتطبيقات، وإذا كانت بعض المصطلحات هنا جديدة عليك فراجع [مصطلحات الاستضافة الذاتية للمبتدئين](https://arabroot.io/articles/%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%B0%D8%A7%D8%AA%D9%8A%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

## كيف يعمل HTTPS التلقائي Automatic HTTPS؟

كلما وجد Caddy اسم نطاق في عنوان موقع داخل ال Caddyfile فعل له ما يسميه توثيقه [HTTPS التلقائي](https://caddyserver.com/docs/automatic-https?ref=arabroot.io)، حيث يطلب لكل اسم شهادة من جهة إصدار Certificate Authority عبر بروتوكول ACME، ويجددها في الخلفية، ويضيف تحويلاً Redirect من HTTP إلى HTTPS على المنفذ 80\. وجهة الإصدار الافتراضية الأولى هي Let's Encrypt والثانية ZeroSSL، فإذا فشل الطلب من الأولى جرب الثانية، وهذا يعني أن تعطل جهة إصدار واحدة لا يوقف شهاداتك.

ولكي تثبت أنك تملك النطاق تطلب منك جهة الإصدار أن تمر بعملية تحقق Challenge، وCaddy يفعل طريقتين للتحقق افتراضياً ويختار بينهما عشوائياً في البداية، ثم يتعلم مع الوقت أيهما ينجح أكثر فيفضله:

- [التحقق عبر HTTP](https://letsencrypt.org/docs/challenge-types/?ref=arabroot.io#http-01-challenge) (HTTP-01 Challenge): تطلب جهة الإصدار ملفاً مؤقتاً من النطاق على المنفذ 80.
- [التحقق عبر TLS](https://letsencrypt.org/docs/challenge-types/?ref=arabroot.io#tls-alpn-01) (TLS-ALPN-01 Challenge): تتحقق جهة الإصدار عبر مصافحة TLS Handshake خاصة على المنفذ 443.
- أما التحقق من ملكية النطاق عبر ال DNS (DNS-01 Challenge) فلا يحتاج إلى أي منفذ مفتوح، ولكنه يتطلب إضافة Plugin لمزود DNS، وسوف نشرحه في قسم الإضافات أدناه.

وبالتالي فلكي ينجح الإصدار يذكر التوثيق هذه الشروط: سجل `A` أو `AAAA` يشير إلى سيرفرك، والمنفذان 80 و443 مفتوحان من الخارج ويصلان إلى Caddy، واسم النطاق مكتوب في الإعداد، ومجلد البيانات قابل للكتابة ولا يضيع، والسبب في الشرط الأخير سوف تراه في القسم التالي.

ويجدر الانتباه إلى أن Caddy يلغي طلبات ACME الجارية كلما غيرت الإعداد وأعدت تحميله، كما يذكر التوثيق، لذلك إذا كنت تضيف عدة نطاقات فاجمع تعديلاتك في إعادة تحميل واحدة بدلاً من إعادة التحميل بعد كل سطر، حتى تعطي Caddy الوقت ليكمل إصدار الشهادات في الخلفية.

📌

في 6 ديسمبر 2018 توقفت خدمة البيانات لدى مشغل الاتصالات البريطاني O2 يوماً كاملاً تقريباً، وتأثر بها 25 مليون مشترك إضافة إلى سبعة ملايين آخرين على شبكات تعتمد عليه، وأعلنت Ericsson المزودة للبرمجيات أن السبب [شهادة انتهت صلاحيتها في برمجياتها](https://www.bbc.com/news/business-46499366?ref=arabroot.io)، فاضطرت O2 إلى تعويض المشتركين عن يومين من الخدمة. والدرس أن الشهادة التي يعتمد تجديدها على تذكر شخص ما سوف تنتهي يوماً ما، لذلك اترك التجديد ل Caddy، وتابع تاريخ الانتهاء بالأمر الموجود في قسم التحقق أدناه.

### أين يحفظ Caddy الشهادات؟

يحفظ Caddy الشهادات ومفاتيحها الخاصة Private Keys وحساب ACME في [مجلد البيانات](https://caddyserver.com/docs/conventions?ref=arabroot.io#data-directory) Data Directory، وهذا المجلد في ال Image الرسمي هو `/data` والشهادات تحت `/data/caddy/certificates`، أما في `/config` فيحفظ نسخة من آخر إعداد عمل به باسم `autosave.json`.

🛑

ينص التوثيق على أن مجلد البيانات ليس ذاكرة مؤقتة Cache، فإذا شغلت Caddy دون Volume دائم ل `/data` فسوف يفقد الشهادات كلها عند كل إعادة إنشاء لل Container، ثم يطلبها من جديد لكل النطاقات دفعة واحدة، ومع تكرار ذلك تصطدم ب[حدود الإصدار في Let's Encrypt](https://letsencrypt.org/docs/rate-limits/?ref=arabroot.io) Rate Limits، وقد يتوقف إصدار الشهادات لنطاقك مدة تصل إلى أسبوع بحسب الحد الذي تجاوزته.

## التثبيت Installation

سوف نثبت الوسم Tag `caddy:2.11.4` برقمه الكامل، وهو آخر إصدار سليم متاح في ال Image الرسمي على Docker Hub عند كتابة هذا الدليل، وتجد تغييراته في [صفحة الإصدار v2.11.4](https://github.com/caddyserver/caddy/releases/tag/v2.11.4?ref=arabroot.io)، والسبب في أننا لم نختر الإصدار الأحدث 2.11.6 تجده في قسم التحديث. ولا تستخدم `latest` ولا `2`، والسبب أن الإصدار سوف يتغير حينئذ دون علمك مع أول `docker compose pull`، وهذا ما حدث بالفعل لكل من كان يستخدم هذين الوسمين في 2 أكتوبر 2026، حيث انتقل إلى 2.11.6 بمشكلاته دون أن يقرر ذلك.

ولاحظ أن الإصدار 2.11.4 يتجاهل ترويسات الطلب التي في أسمائها شرطة سفلية Underscore مثل `X_Custom` لأسباب أمنية، فإذا كان تطبيقك يعتمد على ترويسة من هذا النوع فغير اسمها في التطبيق، لأن الخيار الذي يسمح بها لم يظهر إلا في الإصدارات التالية.

### نبدأ بشبكة Docker مشتركة

نضع Caddy وكل تطبيق يخدمه على [شبكة Docker](https://docs.docker.com/reference/cli/docker/network/create/?ref=arabroot.io) Docker Network واحدة اسمها `proxy`، وبهذا يصل Caddy إلى كل Container باسمه ولا يحتاج أي تطبيق إلى نشر منفذ على السيرفر. وإذا أردت أن تفهم لماذا نفعل ذلك وما أنواع الشبكات في Docker، ولماذا نضع قاعدة البيانات على شبكة داخلية خاصة بها، فقد شرحنا ذلك بالتفصيل في دليل [شبكات Docker وأفضل الممارسات](https://arabroot.io/articles/%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-docker-%D9%88%D8%A3%D9%81%D8%B6%D9%84-%D8%A7%D9%84%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA/). أنشئ الشبكة مرة واحدة:

```bash
docker network create proxy
```

### كيف نرتب مجلد Caddy؟

```bash
sudo mkdir -p /opt/caddy/conf /opt/caddy/data /opt/caddy/config /opt/caddy/logs
sudo chown -R $USER: /opt/caddy
cd /opt/caddy
```

- `conf/Caddyfile`: ملف الإعداد.
- `data`: الشهادات والمفاتيح وحساب ACME، ويقابله `/data` داخل ال Container.
- `config`: آخر إعداد محفوظ، ويقابله `/config`.
- `logs`: سجلات الوصول Access Logs لكل موقع.

وسوف نستخدم مجلدات على السيرفر وليس Volumes يديرها Docker، والسبب أن النسخ الاحتياطي يصبح حينها نسخ مجلد واحد. وقد يتساءل البعض: لماذا نربط مجلد `conf` كاملاً وليس الملف وحده؟ والإجابة أن [توثيق ال Image](https://hub.docker.com/%5F/caddy?ref=arabroot.io) ينبه إلى ذلك صراحة، لأن أغلب المحررات Editors مثل vim تحفظ الملف بإنشاء نسخة جديدة منه، فإذا ربطت الملف مباشرة بقي ال Container يرى النسخة القديمة، ولن تعمل إعادة التحميل Reload كما تتوقع حتى تعيد إنشاء ال Container.

### كيف تولد كلمة مرور مسار الإدارة؟

سوف نحمي المسار `/admin/` في التطبيق بكلمة مرور، وCaddy لا يقبل كلمة المرور نصاً صريحاً، وإنما تحولها إلى هاش Hash بالأمر [caddy hash-password](https://caddyserver.com/docs/command-line?ref=arabroot.io#caddy-hash-password) الموجود داخل ال Image نفسه:

```bash
docker run --rm -it caddy:2.11.4 caddy hash-password
```

يطلب الأمر كلمة المرور مرتين ثم يطبع هاش bcrypt يبدأ ب `$2a$14$`، فانسخه كما هو لأنك سوف تضعه في ال Caddyfile في الخطوة التالية.

### نكتب ملف الإعداد Caddyfile

الملف `/opt/caddy/conf/Caddyfile` يوجه النطاق `app.example.com` إلى ال Container `whoami` على المنفذ 80، والنطاق `api.example.com` إلى ال Container `api` على المنفذ 3000، وهذا محتواه كاملاً:

```nginx
{
	email admin@example.com
}

(security) {
	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "DENY"
		Referrer-Policy "strict-origin-when-cross-origin"
		-Server
	}
}

app.example.com {
	import security
	encode zstd gzip
	log {
		output file /var/log/caddy/app.example.com.log
	}
	basic_auth /admin/* {
		admin $2a$14$WMIMdolrjA3dw5cd3Es81.ZiXtipyQTZ3KuVrCB00Tjx1KMs7j6X6
	}
	reverse_proxy whoami:80
}

api.example.com {
	import security
	encode zstd gzip
	log {
		output file /var/log/caddy/api.example.com.log
	}
	reverse_proxy api:3000
}
```

في الإعداد أعلاه لاحظ التالي:

- الكتلة الأولى بين قوسين دون اسم هي [الخيارات العامة](https://caddyserver.com/docs/caddyfile/options?ref=arabroot.io) Global Options، والخيار `email` يربط حساب ACME ببريدك، ويوصي به التوثيق حتى تصلك رسائل جهة الإصدار إذا ظهرت مشكلة في شهاداتك.
- الكتلة `(security)` مقتطف Snippet لا يعمل وحده، وإنما تدرجه في أي موقع بالأمر [import security](https://caddyserver.com/docs/caddyfile/directives/import?ref=arabroot.io)، فتكتب ترويسات الأمان مرة واحدة وتعدلها في مكان واحد.
- كل كتلة تبدأ باسم نطاق هي موقع Site، ويكفي أن تذكر الاسم ليطلب Caddy شهادته ويضيف التحويل من HTTP.
- التوجيه [reverse\_proxy whoami:80](https://caddyserver.com/docs/caddyfile/directives/reverse%5Fproxy?ref=arabroot.io) يمرر كل طلب إلى ال Container باسمه عبر الشبكة `proxy`، لأن Docker يحل أسماء ال Containers على الشبكات التي ينشئها المستخدم، ويضيف Caddy الترويسات `X-Forwarded-For` و`X-Forwarded-Proto` و`X-Forwarded-Host` فيعرف التطبيق عنوان الزائر وأنه جاء عبر HTTPS، ويتجاهل Caddy القيم التي يرسلها الزائر نفسه في هذه الترويسات حتى لا ينتحلها.

أما بقية الأسطر، أي الترويسات وكلمة المرور والضغط والسجلات، فسوف نشرحها في أقسامها أدناه، وترتيبها داخل الموقع لا يهم لأن Caddy يرتب التوجيهات Directives [ترتيباً ثابتاً](https://caddyserver.com/docs/caddyfile/directives?ref=arabroot.io#directive-order)، فهو يطبق `basic_auth` مثلاً قبل `reverse_proxy` أينما كتبته.

### نشغل Caddy بملف Compose

الملف `/opt/caddy/compose.yaml`:

```yaml
services:
  caddy:
    image: caddy:2.11.4
    container_name: caddy
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./conf:/etc/caddy:ro
      - ./data:/data
      - ./config:/config
      - ./logs:/var/log/caddy
    networks:
      - proxy
    healthcheck:
      test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:2019/config/"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
      start_interval: 2s

networks:
  proxy:
    external: true
```

في الإعداد أعلاه لاحظ التالي:

- يشغل ال Image افتراضياً الأمر `caddy run --config /etc/caddy/Caddyfile --adapter caddyfile`، لذلك لا نحتاج إلى `command`.
- المنفذ `443/udp` ضروري ل HTTP/3، والصلاحية Capability `NET_ADMIN` تسمح ل Caddy برفع حجم مخازن UDP Buffers فيتحسن أداء HTTP/3، وهي اختيارية بحسب توثيق ال Image، فإذا لم تحتج إلى HTTP/3 فاحذف السطرين معاً.
- لا نربط `/var/run/docker.sock`، لأن Caddy يقرأ التوجيه من ال Caddyfile وليس من Docker، وبذلك يبقى باب حساس مغلقاً يحتاج Traefik إلى فتحه.
- يفحص `healthcheck` ال Admin API داخل ال Container على `127.0.0.1:2019`، ولا ننشر هذا المنفذ على السيرفر، والسبب في قسم ال Admin API.
- نربط `conf` للقراءة فقط `:ro`، فلا شيء داخل ال Container يستطيع تعديل إعداده.

### نجرب بتطبيق صغير

سوف نختبر بالتطبيق الصغير [whoami](https://github.com/traefik/whoami?ref=arabroot.io)، وهو يعيد تفاصيل الطلب كما وصلته فترى الترويسات التي أضافها Caddy. وهذا هو الملف `/opt/whoami/compose.yaml`، ولاحظ أنه لا ينشر أي منفذ:

```yaml
services:
  whoami:
    image: traefik/whoami:v1.12.0
    container_name: whoami
    restart: unless-stopped
    networks:
      - proxy

networks:
  proxy:
    external: true
```

```bash
sudo mkdir -p /opt/whoami
sudo chown $USER: /opt/whoami
cd /opt/whoami
docker compose up -d
```

وبالطريقة نفسها أضف تطبيقك الحقيقي إلى الشبكة `proxy` باسم `api` ليستمع على المنفذ 3000 داخل ال Container، وإذا اختلف اسم ال Container أو منفذه فعدل سطر `reverse_proxy` ليطابقه.

### افحص الإعداد قبل التشغيل

قبل التشغيل الأول افحص الإعداد بالأمر [caddy validate](https://caddyserver.com/docs/command-line?ref=arabroot.io#caddy-validate)، حيث يقرأ الإعداد ويجهز كل وحداته Modules كأنه سيعمل ثم يخرج دون أن يستمع على أي منفذ:

```bash
cd /opt/caddy
docker compose run --rm --no-deps caddy caddy validate --config /etc/caddy/Caddyfile
```

والمخرج سوف يكون كما يلي:

```bash
Valid configuration
```

بعد ذلك شغل الخدمة وانتظر حتى تصبح حالتها `healthy`:

```bash
docker compose up -d
docker compose ps
docker compose logs --tail 30 caddy
```

```bash
NAME    IMAGE          STATUS
caddy   caddy:2.11.4   Up 10 seconds (healthy)
```

ويطلب Caddy الشهادات في الخلفية بعد الإقلاع مباشرة، فلا يتأخر بدء الخدمة بسببها، وسوف ترى في السجل لكل نطاق رسالة `obtaining certificate` ثم `certificate obtained successfully`.

💡

إذا كانت هذه أول مرة تجرب فيها الإعداد فأضف إلى الخيارات العامة السطر `acme_ca https://acme-staging-v02.api.letsencrypt.org/directory`، فيستخدم Caddy [بيئة الاختبار Staging في Let's Encrypt](https://letsencrypt.org/docs/staging-environment/?ref=arabroot.io) وحدودها أوسع بكثير، ولكن شهاداتها غير موثوقة في المتصفح Browser. وبعد نجاح التجربة احذف السطر وأعد التحميل فيطلب Caddy شهادات حقيقية، ولا يلزمك حذف شيء من `data` لأن Caddy يحفظ شهادات كل جهة إصدار في مجلد مستقل.

### وإذا أردت التثبيت من مستودع apt الرسمي

إذا أردت تشغيل Caddy كخدمة systemd عادية دون Docker، فالمشروع يوفر [مستودع apt رسمياً](https://caddyserver.com/docs/install?ref=arabroot.io#debian-ubuntu-raspbian) للإصدارات المستقرة، وهذه أوامر إضافته كما في صفحة التثبيت:

```bash
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
```

وتشغل الحزمة الخدمة `caddy` تلقائياً بالمستخدم `caddy`، والإعداد في `/etc/caddy/Caddyfile`، والبيانات في `/var/lib/caddy/.local/share/caddy`، وإعادة التحميل بالأمر `sudo systemctl reload caddy`، كما يشرح [دليل التشغيل](https://caddyserver.com/docs/running?ref=arabroot.io). ويثبت المستودع أحدث إصدار مستقر، وقد يسبق ال Image الرسمي بأيام، فعند كتابة هذا الدليل كان المستودع يقدم الإصدار 2.11.7 بينما لم يصل Docker Hub بعد إلا إلى 2.11.6\. ولكن انتبه إلى أن التطبيقات هنا تعمل في Containers وCaddy خارجها، فلا يصل إليها بأسمائها، وإنما ينشر كل تطبيق منفذه على `127.0.0.1` وتكتب `reverse_proxy 127.0.0.1:8080`.

## ترويسات الأمان في مقتطف واحد

يستخدم المقتطف `security` التوجيه [header](https://caddyserver.com/docs/caddyfile/directives/header?ref=arabroot.io) ليضيف أربع ترويسات إلى كل استجابة ويحذف واحدة، وتجد شرح كل ترويسة وبقية الترويسات التي تحتاجها في دليل [ترويسات الأمان بالتفصيل: HSTS وCSP](https://arabroot.io/articles/%D8%AA%D8%B1%D9%88%D9%8A%D8%B3%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%86-hsts-%D9%88-csp/):

- `Strict-Transport-Security`: يطلب من المتصفح ألا يستخدم HTTP مع النطاق مدة سنة، وهذا ما يسمى HSTS.
- `X-Content-Type-Options: nosniff`: يمنع المتصفح من تخمين نوع الملف.
- `X-Frame-Options: DENY`: يمنع عرض الصفحة داخل إطار iframe في موقع آخر.
- `Referrer-Policy`: يقلل ما يرسله المتصفح من روابط صفحاتك إلى المواقع الأخرى.
- `-Server`: العلامة `-` تعني حذف الترويسة، فلا يعلن السيرفر اسمه في كل استجابة.

⚠️

يصعب التراجع عن HSTS بعد أن يحفظه المتصفح، ومع `includeSubDomains` يشمل كل نطاق فرعي Subdomain تحت النطاق، لذلك ابدأ بقيمة صغيرة مثل `max-age=300` ثم ارفعها إلى سنة بعد التأكد من أن الشهادات تصدر وتتجدد لكل الأسماء، وإذا كان أحد التطبيقات يحتاج إلى العرض داخل إطار فاكتب له مقتطفاً آخر دون `X-Frame-Options`.

## حماية مسار الإدارة بكلمة مرور

يطلب التوجيه [basic\_auth](https://caddyserver.com/docs/caddyfile/directives/basic%5Fauth?ref=arabroot.io) اسم مستخدم وكلمة مرور بمصادقة HTTP الأساسية Basic Authentication، والمطابق Matcher `/admin/*` يقصر الحماية على هذا المسار وتبقى بقية التطبيق مفتوحة، فإذا أردت حماية الموقع كله فاحذف المسار واكتب `basic_auth {` مباشرة.

ويمكنك أن تضيف أكثر من مستخدم كل واحد في سطر داخل الكتلة، ولاحظ أن اسم التوجيه تغير في الإصدار 2.8 من `basicauth` إلى `basic_auth`، فإذا وجدت الاسم القديم في أمثلة على الإنترنت فاعلم أنها كتبت قبل ذلك الإصدار.

⚠️

يرسل Basic Authentication كلمة المرور مع كل طلب، لذلك لا يصلح إلا عبر HTTPS، وهو ما يضمنه Caddy هنا. وهو حماية مقبولة لمسار يدخله شخص أو اثنان، ولكنه لا يوفر تحققاً بخطوتين Two-Factor Authentication ولا سجلاً للدخول، ولا تضع ال Caddyfile الذي يحمل الهاش في مستودع Git عام.

## كيف يضغط Caddy الاستجابات ويكتب السجلات؟

يضغط التوجيه [encode zstd gzip](https://caddyserver.com/docs/caddyfile/directives/encode?ref=arabroot.io) الاستجابات بخوارزمية Zstandard للمتصفحات التي تدعمها وب gzip لغيرها، ولا يضغط Caddy الاستجابة التي يقل حجمها عن 512 بايت لأن الفائدة فيها ضئيلة.

ويكتب التوجيه [log](https://caddyserver.com/docs/caddyfile/directives/log?ref=arabroot.io) سطراً بصيغة JSON لكل طلب يصل إلى الموقع في ملف خاص بالموقع داخل `/opt/caddy/logs`، وعند 100 ميجابايت ينتقل Caddy بنفسه إلى ملف جديد Log Rotation ويحتفظ بعشرة ملفات قديمة مدة أقصاها 90 يوماً، فلا يمتلئ القرص. ويخفي افتراضياً قيم الترويسات الحساسة مثل `Cookie` و`Authorization` فتظهر في السجل بالقيمة `REDACTED`، وبالتالي لا تتسرب كلمة مرور مسار الإدارة إلى ملفات السجلات.

```bash
tail -n 1 /opt/caddy/logs/app.example.com.log
```

أما سجل Caddy نفسه، أي رسائل الإقلاع والشهادات والأخطاء، فيكتبه على المخرج القياسي Standard Output وتقرؤه بالأمر `docker compose logs caddy`.

## كيف توزع الحمل على أكثر من نسخة من التطبيق؟

إذا شغلت من التطبيق نسختين فاذكرهما معاً في `reverse_proxy` ويوزع Caddy الطلبات عليهما، وهذا موقع `api.example.com` بعد تعديله ليخدمه ال Containers `api1` و`api2`:

```nginx
api.example.com {
	import security
	encode zstd gzip
	log {
		output file /var/log/caddy/api.example.com.log
	}
	reverse_proxy api1:3000 api2:3000 {
		lb_policy round_robin
		health_uri /health
		health_interval 10s
		health_timeout 2s
	}
}
```

في الإعداد أعلاه لاحظ التالي:

- يحدد [lb\_policy](https://caddyserver.com/docs/caddyfile/directives/reverse%5Fproxy?ref=arabroot.io#load-balancing) طريقة التوزيع، والقيمة الافتراضية `random`، واخترنا هنا `round_robin` أي التناوب، ومن الخيارات الأخرى `least_conn` للنسخة الأقل اتصالات، و`ip_hash` و`cookie` لإبقاء الزائر على النسخة نفسها.
- يفعل [health\_uri](https://caddyserver.com/docs/caddyfile/directives/reverse%5Fproxy?ref=arabroot.io#active-health-checks) الفحص النشط Active Health Check، حيث يطلب Caddy المسار `/health` من كل نسخة كل 10 ثوان، والنسخة التي لا تعيد الرمز 200 خلال ثانيتين يستبعدها حتى تتعافى.
- وإذا لم يكن في تطبيقك مسار للفحص فاستخدم الفحص السلبي Passive Health Check بإضافة `fail_duration 30s`، وعندها يستبعد Caddy النسخة التي فشل فيها طلب حقيقي مدة 30 ثانية.

وإذا احتجت إلى توزيع الحمل على عدة سيرفرات مع فحوص صحة دقيقة ووضع TCP فراجع دليل [HAProxy](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-haproxy-load-balancer/) لأنه مصمم لهذا.

## أبق ال Admin API داخل السيرفر

يدير Caddy إعداده عبر [Admin API](https://caddyserver.com/docs/api?ref=arabroot.io) يستمع افتراضياً على `localhost:2019`، ومن خلاله يطبق الأمر `caddy reload` الإعداد الجديد، وتستطيع قراءة الإعداد الحالي بصيغة JSON من داخل ال Container:

```bash
docker compose exec caddy wget -qO- http://127.0.0.1:2019/config/
```

ولكن هذا ال API لا يطلب أي مصادقة Authentication في إعداده الافتراضي، فمن يصل إليه يستطيع أن يستبدل الإعداد كله أو يوجه نطاقاتك إلى أي مكان يريده، لذلك لا تضف `2019:2019` إلى `ports`، ولا تغير عنوانه إلى `0.0.0.0:2019` بالخيار `admin`. وفي ملف Compose أعلاه يستمع ال API على `127.0.0.1` داخل ال Container وحده، فلا يصل إليه أحد من السيرفر ولا من الإنترنت.

وقد يتساءل البعض: إذا كان ال API بهذه الخطورة فلماذا لا نعطله تماماً بالخيار العام `admin off`؟ والإجابة أن الأمر `caddy reload` يتوقف عن العمل عندها لأنه يرسل الإعداد الجديد عبر هذا ال API نفسه، فلا يبقى أمامك لتطبيق أي تعديل إلا إيقاف Caddy وتشغيله، ويفشل كذلك فحص الصحة في ملف Compose، لذلك نبقيه مفعلاً ومغلقاً.

## التحقق من الإعداد وإعادة التحميل دون انقطاع

بعد كل تعديل على ال Caddyfile رتبه أولاً بالأمر [caddy fmt](https://caddyserver.com/docs/command-line?ref=arabroot.io#caddy-fmt)، والخيار `--diff` يعرض الفروق بين ملفك والصيغة المعتمدة دون أن يغير شيئاً:

```bash
cd /opt/caddy
docker compose exec caddy caddy fmt --diff /etc/caddy/Caddyfile
```

والمجلد مربوط للقراءة فقط، فإذا أردت كتابة الصيغة المرتبة في الملف فاكتبها من Container مؤقت:

```bash
docker run --rm -v /opt/caddy/conf:/etc/caddy caddy:2.11.4 caddy fmt --overwrite /etc/caddy/Caddyfile
```

ثم افحص الإعداد وطبقه بالأمر [caddy reload](https://caddyserver.com/docs/command-line?ref=arabroot.io#caddy-reload) داخل ال Container العامل، والخيار `-w /etc/caddy` يحدد مجلد العمل Working Directory فيجد الأمر الملف `Caddyfile` فيه:

```bash
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
docker compose exec -w /etc/caddy caddy caddy reload
```

ويطبق Caddy الإعداد الجديد دون أن يقطع الاتصالات المفتوحة، وهذا ما يوصي به [توثيق ال Image](https://hub.docker.com/%5F/caddy?ref=arabroot.io) بدلاً من إعادة تشغيل ال Container. والآن لنكسر الإعداد عمداً لنرى ما يحدث: إذا كتبت `reverse_proxx` بدلاً من `reverse_proxy` في موقع `app.example.com` ثم أعدت التحميل، فسوف يرفض Caddy الإعداد الجديد ويستمر في العمل بالقديم، ويطبع سبب الرفض مع رقم السطر:

```bash
Error: adapting config using caddyfile: Caddyfile:24: unrecognized directive: reverse_proxx
```

ولكن تذكر أن الملف الخاطئ يبقى على القرص، ولن يقلع به Caddy عند أول إعادة تشغيل للسيرفر، لذلك أصلح الخطأ فوراً ولا تتركه لوقت لاحق.

## متى تحتاج إلى الإضافات Plugins وشهادات Wildcard؟

النسخة القياسية من Caddy لا تضم وحدات لمزودي DNS، وأنت تحتاج إلى إحداها في حالتين: الأولى شهادة Wildcard مثل `*.example.com` لأن Let's Encrypt لا تصدرها إلا بالتحقق عبر ال DNS (DNS-01 Challenge)، والثانية شهادة لخدمة داخلية لا يصل إليها الإنترنت. وفي الحالتين تبني نسخة من Caddy تضم وحدة مزودك عبر [xcaddy](https://caddyserver.com/docs/build?ref=arabroot.io#xcaddy) الموجود في ال Image ذي الوسم `builder`.

وهذا الملف `/opt/caddy/Dockerfile` يضيف وحدة [Cloudflare](https://github.com/caddy-dns/cloudflare?ref=arabroot.io) كمثال:

```dockerfile
FROM caddy:2.11.4-builder AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare@v0.2.4

FROM caddy:2.11.4
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```

بعد ذلك استبدل في `compose.yaml` السطر `image: caddy:2.11.4` بالسطر `build: .`، ومرر توكن ال API الخاص بمزود DNS عبر متغير بيئة Environment Variable في `.env`، ثم اكتب في موقع ال Wildcard:

```nginx
*.example.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	reverse_proxy whoami:80
}
```

وتجد وحدات المزودين الأخرى في [صفحة التنزيل](https://caddyserver.com/download?ref=arabroot.io)، ومن الإضافات المعروفة أيضاً [caddy-docker-proxy](https://github.com/lucaslorentz/caddy-docker-proxy?ref=arabroot.io) التي تقرأ التوجيه من وسوم Docker كما يفعل Traefik، وهي التي تشغلها Coolify عندما تختار Caddy. ولكن تذكر أن كل إضافة هي كود Code من طرف آخر يعمل داخل البرنامج المعرض للإنترنت مباشرة، ويحتاج إلى إعادة بناء مع كل تحديث، لذلك لا تضف إلا ما تحتاج إليه فعلاً.

## التحقق من النجاح Verification

- يعرض الأمر `docker compose ps` في `/opt/caddy` ال Container `caddy` بحالة `healthy`.
- يعيد طلب HTTP تحويلاً دائماً إلى HTTPS بالرمز `308`:

```bash
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://app.example.com/
```

```bash
308 https://app.example.com/
```

- يعيد طلب HTTPS الرمز `200` دون الخيار `-k`، أي أن الشهادة موثوقة، ومعه ترويسات الأمان:

```bash
curl -s -D - -o /dev/null https://app.example.com/
```

```bash
HTTP/2 200
alt-svc: h3=":443"; ma=2592000
referrer-policy: strict-origin-when-cross-origin
strict-transport-security: max-age=31536000; includeSubDomains
via: 1.1 Caddy
x-content-type-options: nosniff
x-frame-options: DENY
...
```

- يعرض الأمر التالي جهة الإصدار وتاريخ انتهاء الشهادة:

```bash
echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -issuer -dates
```

- يعيد المسار `https://app.example.com/admin/` الرمز `401` دون كلمة المرور، والرمز `200` مع الخيار `-u admin`.
- في جسم الاستجابة من whoami يظهر السطر `X-Forwarded-Proto: https`، أي أن التطبيق يعرف أن الزائر جاء عبر HTTPS.
- في `/opt/caddy/logs/app.example.com.log` سطر لكل طلب، و`http://203.0.113.10:2019` لا يرد من خارج السيرفر.

## النسخ الاحتياطي Backup والاستعادة Restore

كل ما يحتاج إليه Caddy موجود في `/opt/caddy`: ال Caddyfile في `conf`، والشهادات وحساب ACME في `data`، وآخر إعداد في `config`، وملف Compose، لذلك يكفي أن تنسخ المجلد دون أن توقف Caddy:

```bash
sudo tar czf /root/caddy-$(date +%F).tar.gz -C /opt caddy
```

وللاستعادة على سيرفر جديد ثبت Docker، وأنشئ الشبكة، وفك النسخة، ثم شغل الخدمة:

```bash
docker network create proxy
sudo tar xzf /root/caddy-2026-10-02.tar.gz -C /opt
sudo chown -R $USER: /opt/caddy
cd /opt/caddy
docker compose up -d
```

ومع مجلد `data` يستخدم Caddy الشهادات الحالية مباشرة ولا يطلب شهادات جديدة لكل النطاقات دفعة واحدة، وبعد ذلك انقل التطبيقات مع ملفات Compose الخاصة بها وغير سجلات DNS إلى العنوان الجديد، ويمكنك أن تستثني مجلد `logs` من النسخة إذا كان كبيراً ولا تحتاج إليه.

🛑

تحتوي النسخة على المفاتيح الخاصة لكل شهاداتك ومفتاح حساب ACME وهاش كلمة مرور مسار الإدارة، لذلك خزنها مشفرة Encrypted خارج السيرفر، وإذا حفظت ال Caddyfile في Git فاجعل المستودع Repository خاصاً، ولا تضع فيه مجلد `data` أبداً.

## التحديث Upgrade إلى إصدار أحدث

1. راجع [صفحة الإصدارات](https://github.com/caddyserver/caddy/releases?ref=arabroot.io) واقرأ قسم التغييرات التي تكسر التوافق Breaking Changes في كل إصدار بين إصدارك والإصدار الجديد، ثم تأكد أن الوسم الجديد ظهر في [وسوم ال Image الرسمي](https://hub.docker.com/%5F/caddy/tags?ref=arabroot.io)، لأنه يتأخر عادة أياماً عن صفحة الإصدارات.
2. أنشئ نسخة احتياطية كما في القسم السابق.
3. غير الوسم في `compose.yaml` إلى الإصدار الجديد برقمه الكامل، ثم افحص إعدادك به في Container مؤقت قبل أن تلمس ال Container العامل:

```bash
cd /opt/caddy
docker compose pull
docker compose run --rm --no-deps caddy caddy validate --config /etc/caddy/Caddyfile
```

1. إذا نجح الفحص فأعد إنشاء ال Container وتابع السجل:

```bash
docker compose up -d
docker compose logs --tail 30 caddy
```

وأثناء إعادة إنشاء ال Container تنقطع الاتصالات المفتوحة بضع ثوان، لذلك اختر وقتاً قليل الحركة، وإذا ظهرت مشكلة فأعد الوسم القديم ونفذ `docker compose up -d`، وإذا كنت تستخدم إضافات فغير الوسمين في `Dockerfile` ونفذ `docker compose build --pull` قبل ذلك.

ولنأخذ مثالاً حياً على أهمية قراءة صفحة الإصدارات قبل التحديث، فقد صدر [الإصدار v2.11.6](https://github.com/caddyserver/caddy/releases/tag/v2.11.6?ref=arabroot.io) في 1 أكتوبر 2026 ومعه إصلاحات أمنية، أحدها يخص المواقع التي تجمع `forward_auth` و`reverse_proxy`، ولكنه غير أيضاً بعض السلوك الافتراضي، فهو يرفض الطلبات التي يزيد حجم ترويساتها على 16 كيلوبايت بالرمز `431`، ويقطع الاتصال الذي تتوقف فيه قراءة جسم الطلب أو كتابة الاستجابة أكثر من دقيقة، ويحذف ترويسات الطلب التي في أسمائها نقطة، ويشترط Go 1.26 لبناء الإضافات. وبعد يومين فقط صدر [الإصدار v2.11.7](https://github.com/caddyserver/caddy/releases/tag/v2.11.7?ref=arabroot.io) في 3 أكتوبر 2026 ليصلح مشكلات سببتها هذه المهلات الزمنية Timeouts الجديدة في 2.11.6، أهمها أن Caddy قد ينهار Panic عند العمل ك Reverse Proxy عبر HTTP/2 إذا كان ما زال يقرأ جسم الطلب، وأن البث المستمر Streaming مثل SSE الذي يفتحه الكلاينت بطلب `POST` كان ينقطع بعد 60 ثانية بالضبط، ويوصي فريق Caddy كل من انتقل إلى 2.11.6 بالترقية.

لذلك تخط الإصدار 2.11.6 تماماً، والسبب أن ال Reverse Proxy الذي ينهار تحت HTTP/2 أو يقطع البث بعد دقيقة أسوأ من البقاء على إصدار سليم أقدم بشهور. وعند كتابة هذا الدليل لم يكن الوسم `caddy:2.11.7` قد ظهر بعد في وسوم ال Image الرسمي، فبقينا على 2.11.4، وعندما يظهر انتقل إليه مباشرة بالخطوات أعلاه، وإذا كان تطبيقك يعتمد على Cookies كبيرة أو اتصالات طويلة صامتة فاقرأ عن الخيارين `max_header_size` و`timeouts` في ملاحظات الإصدار 2.11.6 قبل أن تنتقل، لأن هذه التغييرات باقية في 2.11.7.

## مشكلات شائعة وحلولها Troubleshooting

### لا تصدر الشهادة

ابحث في سجل Caddy عن سبب الرفض:

```bash
docker compose logs caddy | grep -i -E 'obtain|challenge|acme' | tail -n 10
```

وغالباً سوف تجد رسالة `could not get certificate from issuer` ومعها السبب الذي ذكرته جهة الإصدار، فإذا كان السبب `Timeout during connect` أو `Connection refused` فالمنفذان 80 و443 لا يصلان إلى Caddy من الإنترنت، فراجع جدار الحماية لدى المزود وعلى السيرفر. وإذا كان السبب `NXDOMAIN` أو عنواناً غير عنوان سيرفرك فسجل DNS لم ينتشر بعد أو يشير إلى مكان خاطئ، فتحقق منه بالأمر `dig +short app.example.com`، وتحقق كذلك من سجل `AAAA` بالأمر `dig +short AAAA app.example.com`، لأن سجلاً قديماً يشير إلى عنوان IPv6 لا يصل إلى Caddy قد يفشل التحقق.

### too many certificates أو too many failed authorizations

معنى الرسالتين أنك تجاوزت [حدود Let's Encrypt](https://letsencrypt.org/docs/rate-limits/?ref=arabroot.io)، وهذا يحدث غالباً عندما يضيع مجلد `data` مرة بعد مرة أو عندما تكرر محاولة فاشلة. ويعيد Caddy المحاولة بنفسه بفواصل تطول حتى يوم كامل بين المحاولة والأخرى ولمدة تصل إلى 30 يوماً، وأثناء إعادة المحاولة مع Let's Encrypt يتحول إلى بيئة Staging، ويجرب ZeroSSL إذا فشلت الأولى. لذلك لا تعد تشغيل ال Container على أمل أن ينجح الطلب، وإنما أصلح السبب أولاً، وجرب على بيئة Staging بالخيار `acme_ca` كما وصفنا في قسم التثبيت، وتأكد أن `data` مربوط بمجلد دائم.

### لا يبدأ Caddy لأن المنفذ مستخدم

إذا ظهرت عند `docker compose up -d` رسالة مثل `Bind for 0.0.0.0:80 failed: port is already allocated` فهناك Container آخر ينشر المنفذ 80، وغالباً هو Nginx Proxy Manager أو Traefik، أما الرسالة `address already in use` فمعناها أن برنامجاً على السيرفر نفسه يستمع على المنفذ، مثل Nginx أو Apache أو Caddy مثبت من حزمة apt. واعرف صاحب المنفذ بالأمرين:

```bash
sudo ss -ltnp | grep -E ':(80|443) '
docker ps --filter publish=80 --filter publish=443
```

والحل أن تختار Reverse Proxy واحداً للسيرفر وتنقل إليه كل النطاقات ثم توقف الآخر، وإذا كان البرنامج خدمة نظام لا تحتاج إليها فأوقفها وعطلها، مثلاً `sudo systemctl disable --now nginx`.

### 502 Bad Gateway

هذا يعني أن الطلب وصل إلى Caddy ولكن Caddy لم يصل إلى التطبيق، والسبب يظهر في سجل Caddy بمستوى `error`:

- `dial tcp: lookup api: i/o timeout` أو `no such host`: لا يجد Caddy الاسم، فإما أن ال Container ليس على الشبكة `proxy` وإما أن اسمه مختلف، فأضف الشبكة إلى خدمته مع `external: true` كما في مثال whoami، وتحقق من الاسم بالأمر `docker network inspect proxy`.
- `connect: connection refused`: يجد Caddy ال Container ولكن المنفذ خاطئ، أو التطبيق يستمع على `127.0.0.1` داخل ال Container وليس على `0.0.0.0`، واكتب في `reverse_proxy` المنفذ داخل ال Container وليس منفذاً منشوراً على السيرفر.
- إذا ظهرت `no upstreams available` مع توزيع الحمل فالفحص النشط استبعد كل النسخ، فتأكد أن المسار في `health_uri` يعيد 200 فعلاً.

### permission denied في البيانات أو السجلات

داخل ال Image الرسمي يعمل Caddy بالمستخدم root، فلا تظهر هذه المشكلة عادة، وإنما تظهر إذا أضفت `user:` إلى ملف Compose لتشغيله بمستخدم آخر، أو استعدت النسخة بأداة لا تحفظ الملكية، وعندها يجب أن يملك ذلك المستخدم المجلدات `data` و`config` و`logs` وأن يقرأ `conf`. ويتحقق Caddy من أن التخزين قابل للكتابة قبل أي طلب ACME، لذلك تظهر الرسالة في السجل بعد الإقلاع مباشرة، وفي تثبيت apt تأكد أن المستخدم `caddy` يقرأ ال Caddyfile ويكتب في مجلد السجلات الذي تحدده.

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- يكفي أن تكتب اسم النطاق في ال Caddyfile ليطلب Caddy شهادته ويجددها ويحول HTTP إلى HTTPS، فلا تحتاج إلى certbot ولا إلى مهام مجدولة قد تنساها.
- اربط `/data` بمجلد دائم وانسخه مشفراً خارج السيرفر، لأن ضياعه يعني طلب كل الشهادات من جديد والاصطدام بحدود Let's Encrypt.
- اربط مجلد `conf` كاملاً وليس الملف وحده، وافحص الإعداد ب `caddy validate` ثم طبقه ب `caddy reload` دون انقطاع.
- أبق ال Admin API على `127.0.0.1` داخل ال Container ولا تنشر المنفذ 2019 أبداً.
- ثبت رقم الإصدار كاملاً، وتخط 2.11.6، وانتقل إلى 2.11.7 عندما يظهر في ال Image الرسمي بعد قراءة ملاحظات الإصدار.

## سجل التحديثات (Changelog)

- أكتوبر 2026: كتابة الدليل.