> ## 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.

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

عندما تضيف تطبيقاً جديداً إلى السيرفر Server فهو يحتاج إلى ثلاثة أمور حتى يصل إليه المستخدم: نطاق Domain يكتبه في المتصفح، وشهادة TLS Certificate تحمي الاتصال، وقاعدة في ال Reverse Proxy توجه الطلبات إلى ال Container الصحيح، والمشكلة الحقيقية في هذه القاعدة الأخيرة، فإذا كتبتها يدوياً في ملف إعداد بعيد عن التطبيق فسوف تنسى تعديلها يوماً ما عندما يتغير اسم ال Container أو منفذه، أو تبقى القاعدة في مكانها بعد أن تحذف التطبيق نفسه بشهور ولا يتذكر أحد لماذا هي موجودة.

ولهذه المهمة أكثر من أداة، فمع [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 ويحفظ الإعداد في قاعدة بياناته، أما [**Traefik Proxy**](https://traefik.io/traefik/?ref=arabroot.io) فيقرأ الإعداد من التطبيقات نفسها، وتجد الأدوات الثلاث جنباً إلى جنب في [مقارنة Nginx Proxy Manager وTraefik وHAProxy](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/)، وهذا الدليل عن Traefik.

وTraefik هو Reverse Proxy مفتوح المصدر Open Source يراقب Docker باستمرار، فعندما يبدأ Container يحمل وسوماً Labels معينة ينشئ له Traefik قاعدة توجيه Router ويطلب له شهادة من [Let's Encrypt](https://letsencrypt.org/?ref=arabroot.io)، وعندما يتوقف ال Container يحذف القاعدة، ويسمى هذا السلوك الاكتشاف التلقائي للخدمات Service Discovery. ولاحظ أنه لا توجد هنا واجهة لتعديل الإعداد، وإنما تكتبه في الوسوم وفي ملفات نصية تحفظها في Git وتراجعها كأي كود Code، لذلك فهو مناسب للفريق الذي ينشر تطبيقاته بملفات Compose ويريد إعداد التوجيه Routing بجانبها، أما إذا كنت تفضل الإعداد بالنقر فابق مع Nginx Proxy Manager.

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

- كيف يعمل Traefik، والفرق بين الإعداد الثابت والإعداد الديناميكي.
- تثبيت Traefik بملف Compose مع تحويل HTTP إلى HTTPS وشهادات Let's Encrypt التلقائية وترويسات الأمان.
- نشر تطبيق عبر الوسوم، واستخدام ال Middlewares لتعديل الطلب قبل وصوله إلى التطبيق.
- تأمين لوحة التحكم Dashboard، وحماية ال Docker socket بوسيط Socket Proxy.
- التحقق من النجاح، والنسخ الاحتياطي، والتحديث، وأشهر المشكلات وحلولها.

⚠️

لا يستمع على المنفذين 80 و443 في السيرفر الواحد إلا برنامج واحد، فإذا كان Nginx Proxy Manager أو Nginx أو Apache يعمل على السيرفر نفسه فلن يبدأ Traefik، وعليك أن تختار أحدهما وتنقل إليه جميع النطاقات، ولا تشغل الاثنين معاً إلا إذا وضعت أحدهما خلف الآخر عن قصد.

💡

منصتا [Coolify](https://coolify.io/docs/core/networking/proxy/traefik/overview?ref=arabroot.io) و[Dokploy](https://docs.dokploy.com/docs/core/architecture?ref=arabroot.io) تأتيان ومعهما Traefik مدمجاً تديرانه بنفسيهما، فإذا كنت تنوي تثبيت إحداهما على هذا السيرفر فلا تثبت Traefik منفصلاً، واتبع دليل [تثبيت 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/) أو [تثبيت Dokploy](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-dokploy-%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/) مباشرة، وسوف يفيدك هذا الدليل حينئذ في فهم ما تفعله المنصة خلف الواجهة.

## المتطلبات 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 الخاصة به، وفيه سجل `A` لكل اسم فرعي Subdomain يشير إلى العنوان العام Public IP للسيرفر `203.0.113.10`، وسوف نستخدم في هذا الدليل `app.example.com` للتطبيق و`traefik.example.com` للوحة التحكم، وإذا كانت هذه السجلات جديدة عليك فقد شرحناها في [شرح 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 لدى المزود وعلى السيرفر معاً، ولاحظ أن المنفذ 80 ضروري [للتحقق من ملكية النطاق عبر HTTP](https://letsencrypt.org/docs/challenge-types/?ref=arabroot.io#http-01-challenge) (HTTP-01 Challenge) حتى لو كانت خدماتك كلها عبر HTTPS.
- لا يستمع أي برنامج آخر على المنفذين، وتحقق من ذلك بالأمر التالي، فإذا لم يطبع شيئاً فالمنفذان متاحان:

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

أما عن الموارد Resources فإن Traefik برنامج واحد مكتوب بلغة Go ويكفيه أصغر خادم افتراضي خاص VPS، واستهلاكه يزيد مع عدد الطلبات أكثر مما يزيد مع عدد التطبيقات التي يخدمها.

## كيف يعمل Traefik؟ الإعداد الثابت والإعداد الديناميكي

يصل الطلب إلى Traefik على نقطة دخول EntryPoint، أي منفذ يستمع عليه مثل 80 أو 443، فيطابقه Traefik مع قاعدة Router مثل اسم النطاق `app.example.com`، ثم يمر الطلب على Middleware واحد أو أكثر يعدل فيه، كأن يضيف ترويسات Headers أو يطلب كلمة مرور، وفي النهاية يصل إلى ال Service، أي ال Container الذي يخدم هذا الطلب.

وللإعداد Configuration في Traefik قسمان كما يشرح [توثيق الإعداد الرسمي](https://doc.traefik.io/traefik/getting-started/configuration-overview/?ref=arabroot.io)، والجدول التالي يبين الفرق بينهما:

| القسم                                    | ما يحدده                                                                                | مصدره                                             | متى يطبق                                                     |
| ---------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------ |
| الإعداد الثابت Static Configuration      | نقاط الدخول، ومصادر الإعداد Providers، وحساب Let's Encrypt، ولوحة التحكم، والسجلات Logs | الملف traefik.yml أو خيارات سطر الأوامر CLI Flags | عند الإقلاع فقط، وأي تغيير فيه يحتاج إلى إعادة تشغيل Traefik |
| الإعداد الديناميكي Dynamic Configuration | ال Routers وال Services وال Middlewares                                                 | وسوم Docker، أو ملفات يقرؤها File Provider        | فوراً ودون إعادة تشغيل                                       |

وسوف نكتب الإعداد الثابت في الملف `traefik.yml` وليس في خيارات سطر الأوامر، والسبب أن الملف أسهل في القراءة والمراجعة من سطر طويل من الخيارات، أما الإعداد الديناميكي فنكتبه في وسوم كل تطبيق، ومعه مجلد صغير لل Middlewares المشتركة بين التطبيقات.

## التثبيت Installation

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

سوف نضع Traefik وكل تطبيق يخدمه على [شبكة Docker](https://docs.docker.com/reference/cli/docker/network/create/?ref=arabroot.io) Docker Network واحدة اسمها `proxy`، وبهذا الشكل يصل Traefik إلى كل 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
```

### أين نحفظ الإعداد والشهادات؟

```bash
sudo mkdir -p /opt/traefik/dynamic /opt/traefik/letsencrypt
sudo chown -R $USER: /opt/traefik
cd /opt/traefik
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json
```

يحفظ Traefik في الملف `acme.json` حساب Let's Encrypt والشهادات ومفاتيحها الخاصة Private Keys، ولهذا يرفض استخدام الملف إذا كانت صلاحياته Permissions أوسع من `600`، ويكتب في السجل رسالة مثل `permissions 644 for /letsencrypt/acme.json are too open, please use 600`.

### نكتب الإعداد الثابت في traefik.yml

الآن سوف نكتب الإعداد الثابت في الملف `/opt/traefik/traefik.yml` كما يلي:

```yaml
global:
  checkNewVersion: false
  sendAnonymousUsage: false

log:
  level: INFO

accessLog: {}

api:
  dashboard: true

ping: {}

entryPoints:
  web:
    address: ":80"
    http:
      aliasHeadersStrategy: delete
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"
    http:
      aliasHeadersStrategy: delete
      tls:
        certResolver: letsencrypt

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@example.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

providers:
  docker:
    exposedByDefault: false
    network: proxy
  file:
    directory: /etc/traefik/dynamic
    watch: true
```

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

- تستقبل [نقطة الدخول](https://doc.traefik.io/traefik/reference/install-configuration/entrypoints/?ref=arabroot.io) `web` المنفذ 80، وتحول كل طلب HTTP إلى HTTPS تحويلاً دائماً Permanent Redirect. وقد يتساءل البعض: إذا كان كل طلب على المنفذ 80 يتحول إلى HTTPS، فكيف تصل Let's Encrypt إلى ملف التحقق Challenge File عبر HTTP؟ والإجابة أن Traefik يعالج طلبات `/.well-known/acme-challenge/` بمسار داخلي له أعلى أولوية Priority ممكنة، فيصل طلب التحقق إليه قبل أن يمر على قاعدة التحويل.
- تستقبل `websecure` المنفذ 443، ومعنى `certResolver: letsencrypt` أن كل Router عليها يحصل على شهادة تلقائياً، فلا داعي لتكرار ذلك في وسوم كل تطبيق.
- الخيار [aliasHeadersStrategy: delete](https://doc.traefik.io/traefik/reference/install-configuration/entrypoints/?ref=arabroot.io#aliasheadersstrategy) يحذف كل ترويسة في اسمها حرف غير الحروف والأرقام والشرطة، مثل الشرطة السفلية أو النقطة في `X_Auth_User`، والسبب أن تطبيقات PHP وغيرها تقرأ هذا الاسم على أنه `X-Auth-User`، وبالتالي يستطيع المهاجم أن ينتحل ترويسة يثق بها التطبيق. وقد أضيف هذا الخيار في الإصدار v3.7.12 بديلاً عن الخيار الأقدم `underscoreHeadersStrategy`، ويحذرك Traefik في السجل عند الإقلاع عن كل نقطة دخول تركته فيها على قيمته الافتراضية `keep`.
- القسم [certificatesResolvers](https://doc.traefik.io/traefik/reference/install-configuration/tls/certificate-resolvers/acme/?ref=arabroot.io) يعرف حساب Let's Encrypt باسم `letsencrypt`، ويستخدم التحقق عبر HTTP (HTTP-01) على نقطة الدخول `web`، ويجدد Traefik كل شهادة تلقائياً قبل انتهائها بثلاثين يوماً، فالشهادة صالحة 90 يوماً ويبدأ التجديد في يومها الستين.
- مع `exposedByDefault: false` في [Docker Provider](https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/?ref=arabroot.io) لا ينشر Traefik إلا ال Container الذي يحمل الوسم `traefik.enable=true`، والسبب أن القيمة الافتراضية `true` تنشر كل Container على السيرفر، ومنها قواعد البيانات Databases والأدوات الداخلية، وهذا بكل تأكيد ما لا تريده.
- يحدد `network: proxy` الشبكة التي يصل منها Traefik إلى ال Containers، ويهمك هذا عندما يكون التطبيق على أكثر من شبكة.
- يقرأ [File Provider](https://doc.traefik.io/traefik/reference/install-configuration/providers/others/file/?ref=arabroot.io) كل ملف في المجلد `/etc/traefik/dynamic` ويطبق أي تعديل فيه فوراً.
- يفعل `ping` نقطة الفحص `/ping` التي يستخدمها [فحص الصحة](https://doc.traefik.io/traefik/reference/install-configuration/observability/healthcheck/?ref=arabroot.io) Healthcheck داخل ال Container، ولا ننشر منفذها على السيرفر.

📌

في 3 فبراير 2020 توقفت خدمة Microsoft Teams قرابة ثلاث ساعات، وكان المستخدمون يرون رسالة تقول إن التطبيق فشل في إنشاء اتصال HTTPS مع سيرفرات Microsoft، ثم أعلنت Microsoft أن السبب [شهادة مصادقة انتهت صلاحيتها](https://www.theverge.com/2020/2/3/21120248/microsoft-teams-down-outage-certificate-issue-status?ref=arabroot.io) ولم يجددها أحد في وقتها. والدرس أن تجديد الشهادات يدوياً سوف يفشل يوماً ما مهما كان الفريق كبيراً، لذلك اترك التجديد ل Traefik، وراقب تاريخ الانتهاء بالأمر الموجود في قسم التحقق أدناه.

### ترويسات الأمان المشتركة بين كل التطبيقات

في الملف `/opt/traefik/dynamic/middlewares.yml` سوف نعرف Middleware اسمه `security-headers` يضيف [ترويسات الأمان](https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/headers/?ref=arabroot.io) Security Headers إلى كل استجابة، ثم نربطه بالتطبيقات لاحقاً:

```yaml
http:
  middlewares:
    security-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        contentTypeNosniff: true
        frameDeny: true
        referrerPolicy: "strict-origin-when-cross-origin"
```

يطلب `stsSeconds` من المتصفح Browser ألا يستخدم HTTP مع النطاق مدة سنة، وهذا ما يسمى HSTS، ويمنع `contentTypeNosniff` المتصفح من تخمين نوع الملف، ويمنع `frameDeny` عرض الصفحة داخل إطار iframe في موقع آخر، ويقلل `referrerPolicy` ما يرسله المتصفح من روابط صفحاتك إلى المواقع الأخرى، وتجد شرح كل ترويسة وكيف تضيف CSP دون أن تكسر تطبيقك في دليل [ترويسات الأمان بالتفصيل: 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/).

⚠️

يصعب التراجع عن HSTS بعد أن يحفظه المتصفح، ومع `stsIncludeSubdomains` يشمل كل اسم فرعي تحت النطاق، لذلك ابدأ بقيمة صغيرة مثل `300` ثانية، ثم ارفعها إلى سنة بعد أن تتأكد أن الشهادات تصدر وتتجدد لكل الأسماء، وإذا كان أحد التطبيقات يحتاج إلى العرض داخل إطار فعرف له Middleware آخر دون `frameDeny`.

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

سوف نحمي لوحة التحكم بمصادقة HTTP الأساسية Basic Authentication عبر [BasicAuth Middleware](https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/basicauth/?ref=arabroot.io)، وهذا ال Middleware يقبل كلمة المرور بصيغة هاش Hash مثل BCrypt، وتولدها الأداة [htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html?ref=arabroot.io) كما يلي:

```bash
sudo apt install -y apache2-utils
htpasswd -nB admin
```

تطلب الأداة كلمة المرور مرتين ثم تطبع سطراً مثل `admin:$2y$05$...`، فانسخ السطر كاملاً إلى الملف `/opt/traefik/.env` بين علامتي تنصيص مفردتين:

```ini
TRAEFIK_DASHBOARD_USERS='admin:$2y$05$Jm0mK1x5Q8bW0eQnI3zHVe4mR2sL7kT9pYcA1dF6gH8jN0uV3wXyZ'
```

```bash
chmod 600 /opt/traefik/.env
```

ولاحظ أن الهاش يحتوي على العلامة `$`، وهي علامة المتغيرات في Compose، ولكن [Compose يقرأ القيمة حرفياً](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/?ref=arabroot.io) إذا وضعتها بين علامتي تنصيص مفردتين في ملف `.env`، أما إذا كتبت الهاش داخل `compose.yaml` مباشرة فضاعف كل علامة `$` لتصبح `$$`.

### نجمع كل شيء في ملف Compose

الآن نكتب الملف `/opt/traefik/compose.yaml`، وفيه الإصدار [v3.7.13](https://github.com/traefik/traefik/releases/tag/v3.7.13?ref=arabroot.io) مثبتاً برقمه الكامل:

```yaml
services:
  traefik:
    image: traefik:v3.7.13
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./dynamic:/etc/traefik/dynamic:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s
      start_interval: 5s
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=dashboard-auth,security-headers@file"
      - "traefik.http.middlewares.dashboard-auth.basicauth.users=${TRAEFIK_DASHBOARD_USERS}"

networks:
  proxy:
    external: true
```

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

- يبحث Traefik تلقائياً عن إعداده الثابت في `/etc/traefik/traefik.yml`، فلا نحتاج إلى أي خيار في `command`.
- يمنع `no-new-privileges` أي عملية داخل ال Container من رفع صلاحياتها، وتستخدمه [أمثلة Traefik الرسمية](https://doc.traefik.io/traefik/setup/docker/?ref=arabroot.io) أيضاً.
- إذا كان لل Container فحص صحة فلا يوجه Traefik إليه الطلبات قبل أن تصبح حالته `healthy`، وهذا يشمل Traefik نفسه ولوحة التحكم، لذلك أضفنا `start_interval` ليجري الفحص كل 5 ثوان في البداية، فتظهر اللوحة خلال ثوان وليس بعد نصف دقيقة.
- تنشئ الوسوم Router اسمه `dashboard` يوجه النطاق `traefik.example.com` إلى الخدمة الداخلية `api@internal`، ويمر الطلب أولاً على كلمة المرور ثم على ترويسات الأمان، والتفاصيل في قسم لوحة التحكم أدناه.

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

```bash
cd /opt/traefik
docker compose up -d
docker compose ps
docker compose logs --tail 30 traefik
```

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

```bash
NAME      IMAGE             STATUS
traefik   traefik:v3.7.13   Up 20 seconds (healthy)
```

وعند قراءة السجل سوف تجد غالباً تحذيرين بمستوى `WRN` عند الإقلاع، الأول عن رفض بعض الحروف المرمزة Encoded Characters في مسار الطلب، وهو تنبيه عام يظهر لكل من يستخدم القيم الافتراضية، والثاني يقول إن `aliasHeadersStrategy` غير مضبوط على نقطة الدخول `traefik`، وهي نقطة دخول داخلية على المنفذ 8080 ينشئها Traefik ل `ping` ولوحة التحكم ولا ننشرها على السيرفر، لذلك لا يؤثر عليك هذا التحذير، وإذا أردت إسكاته فعرف نقطة الدخول `traefik` بنفسك في `entryPoints` وأضف إليها الخيار نفسه.

💡

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

## كيف تنشر تطبيقاً عبر الوسوم؟

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

```yaml
services:
  whoami:
    image: traefik/whoami:v1.12.0
    container_name: whoami
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`app.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.middlewares=security-headers@file"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"

networks:
  proxy:
    external: true
```

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

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

- يحدد `rule` متى يطابق الطلب هذا ال Router، وتجد بقية الصيغ مثل `PathPrefix` في [صفحة القواعد](https://doc.traefik.io/traefik/reference/routing-configuration/http/routing/rules-and-priority/?ref=arabroot.io)، واكتب اسم النطاق بين علامتي `` ` `` وليس بين علامتي تنصيص.
- `loadbalancer.server.port` هو المنفذ داخل ال Container وليس منفذاً على السيرفر، وإذا أعلن ال Image عن منفذ واحد بالتعليمة `EXPOSE` فسوف يكتشفه Traefik وحده، ولكن كتابته صراحة تحميك من الخطأ عندما يعلن ال Image عن أكثر من منفذ.
- يشير `security-headers@file` إلى ال Middleware الذي عرفناه في الملف، أما ال Middleware المعرف في وسوم Docker فلاحقته `@docker`، وإذا كان ال Middleware في المصدر نفسه فلك أن تحذف اللاحقة كما فعلنا مع `dashboard-auth`.

بعد ثوان من تشغيل ال Container ينشئ Traefik ال Router ويطلب الشهادة، ولا تحتاج إلى انتظار انتشار DNS Propagation لكي تختبر، وإنما وجه النطاق إلى العنوان المحلي من السيرفر نفسه:

```bash
curl -s -D - --resolve app.example.com:443:127.0.0.1 https://app.example.com/
```

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

```bash
HTTP/1.1 200 OK
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
...
Hostname: 3f2a9c1b7d4e
Host: app.example.com
X-Forwarded-For: 203.0.113.50
X-Forwarded-Proto: https
X-Real-Ip: 203.0.113.50
```

وإذا لم تصدر الشهادة بعد فسوف يرفض `curl` الاتصال، والسبب أن Traefik يقدم في هذه الحالة شهادته الافتراضية الموقعة ذاتياً Self-signed باسم `TRAEFIK DEFAULT CERT`، فأضف الخيار `-k` للتجربة فقط، وراجع قسم الشهادات في المشكلات الشائعة أدناه.

## كيف تعدل الطلب قبل وصوله إلى التطبيق بال Middlewares؟

يقع ال Middleware في الطريق بين ال Router وال Service، ولك أن تربط أكثر من Middleware بال Router الواحد مفصولة بفاصلة، فتنفذ بالترتيب الذي كتبتها به. لنفرض أن لديك أداة داخلية تريد قصرها على شبكة المكتب أو الشبكة الخاصة الافتراضية VPN، فالحل أن تضيف إليها [IPAllowList](https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/ipallowlist/?ref=arabroot.io) كما يلي:

```yaml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.tools.rule=Host(`tools.example.com`)"
      - "traefik.http.routers.tools.entrypoints=websecure"
      - "traefik.http.routers.tools.middlewares=office-only,security-headers@file"
      - "traefik.http.middlewares.office-only.ipallowlist.sourcerange=203.0.113.0/24,10.8.0.0/24"
```

وأي عنوان خارج هذه النطاقات سوف يتلقى الرمز `403`، أما ال Middlewares التي تستخدمها أكثر من خدمة فضعها في مجلد `dynamic` كما فعلنا مع ترويسات الأمان، حتى تعرفها مرة واحدة وتعدلها في مكان واحد.

## كيف تؤمن لوحة التحكم؟

تعرض [لوحة تحكم Traefik](https://doc.traefik.io/traefik/reference/install-configuration/api-dashboard/?ref=arabroot.io) كل ال Routers وال Services وال Middlewares وحالتها، وهي مفيدة جداً في تتبع الأخطاء، ولكنها تكشف أيضاً كل نطاقاتك وبنية خدماتك لمن يصل إليها.

والحل السريع الذي تجده في كثير من الأمثلة هو الخيار `api.insecure`، وهذا الحل غير مناسب إطلاقاً لسيرفر على الإنترنت، والسبب أنه يعرض اللوحة وال API على المنفذ 8080 دون أي مصادقة Authentication، فإذا نشرت هذا المنفذ أصبح كل ما في اللوحة متاحاً لأي شخص. لذلك لا نستخدمه، وإنما نضع اللوحة خلف Router له ثلاثة شروط:

- نطاق خاص بها هو `traefik.example.com`، عبر HTTPS فقط.
- كلمة مرور عبر `dashboard-auth` الذي يقرأ الهاش من `.env`.
- الخدمة `api@internal`، وهي الخدمة الداخلية التي تقدم اللوحة وال API معاً.

افتح `https://traefik.example.com/dashboard/` مع الشرطة المائلة في آخره، وأدخل اسم المستخدم وكلمة المرور، وإذا فتحت العنوان الرئيسي للنطاق Root URL فسوف يحولك Traefik إلى `/dashboard/` تلقائياً.

وإذا لم ترد أن تصل إلى اللوحة من الإنترنت أصلاً فأمامك خياران، الأول أن تضيف إليها Middleware من نوع IPAllowList كما في القسم السابق، فلا يفتحها إلا من على شبكة الفريق أو ال VPN، والثاني أن تحذف وسومها كلها وتجعل `dashboard: false` في `traefik.yml`، وتكتفي بالسجلات وبالأمر `docker compose logs`.

⚠️

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

## لماذا يحتاج ال Docker socket إلى حماية خاصة؟

يقرأ Traefik الوسوم من ال API الخاص بـ Docker عبر الملف `/var/run/docker.sock`، ومن يصل إلى هذا الملف يستطيع إنشاء Container بصلاحيات root على السيرفر كله كما ينبه [توثيق أمان Docker](https://docs.docker.com/engine/security/?ref=arabroot.io#docker-daemon-attack-surface)، وTraefik هو البرنامج المعرض للإنترنت مباشرة، فإذا استغل مهاجم ثغرة Vulnerability فيه فقد يصل عبر ال socket إلى السيرفر بأكمله.

وقد يبدو أن اللاحقة `:ro` في ملف Compose تحل المشكلة، ولكن في الواقع فهي غير كذلك، فهي تمنع تعديل الملف نفسه داخل ال Container ولا تمنع إرسال أوامر إلى Docker عبره، وبالتالي فهي خطوة صغيرة وليست حماية كاملة. والحل الأنسب كما [يوصي توثيق Traefik](https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/?ref=arabroot.io#docker-api-access) هو أن تضع بينهما Socket Proxy مثل [docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy?ref=arabroot.io)، وهو وسيط يمرر طلبات القراءة التي يحتاج إليها Traefik فقط ويرفض غيرها بالرمز `403`.

أضف الخدمة التالية إلى `/opt/traefik/compose.yaml`، ثم احذف سطر `docker.sock` من خدمة `traefik` وأضف إليها الشبكة `socket`:

```yaml
services:
  socket-proxy:
    image: tecnativa/docker-socket-proxy:v0.5.0
    container_name: socket-proxy
    restart: unless-stopped
    environment:
      CONTAINERS: 1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - socket

  traefik:
    # ... الإعداد السابق نفسه دون سطر docker.sock
    networks:
      - proxy
      - socket

networks:
  proxy:
    external: true
  socket:
    internal: true
```

بعد ذلك وجه Docker Provider إلى ال Socket Proxy في `traefik.yml`، وأعد إنشاء الخدمتين بالأمر `docker compose up -d`:

```yaml
providers:
  docker:
    endpoint: "tcp://socket-proxy:2375"
    exposedByDefault: false
    network: proxy
```

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

- يسمح `CONTAINERS: 1` بقراءة قائمة ال Containers ووسومها، أما الأحداث Events و`PING` و`VERSION` فمسموحة افتراضياً، وطلبات التعديل `POST` ممنوعة افتراضياً، فلو حاول أحد إيقاف Container عبر ال Socket Proxy لتلقى الرمز `403`.
- يعزل `internal: true` الشبكة `socket` عن الخارج، فلا يصل إلى ال Socket Proxy إلا Traefik.

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

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

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

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

- يعيد الأمر `curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/` الرمز `200` دون الخيار `-k`، وهذا يعني أن الشهادة موثوقة.
- يعرض الأمر التالي جهة الإصدار Issuer وهي Let's Encrypt، وتاريخ انتهاء بعد نحو 90 يوماً:

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

- تعيد لوحة التحكم الرمز `401` دون كلمة المرور والرمز `200` معها، ويعرض ال API ال Routers النشطة وإصدار Traefik:

```bash
curl -s -u admin https://traefik.example.com/api/version
curl -s -u admin https://traefik.example.com/api/http/routers
```

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

```bash
{"Version":"3.7.13","Codename":"langres",...}
```

- أي نطاق لا يطابق Router يعيد الرسالة `404 page not found`، ولا يظهر في `/api/http/routers` أي Container لم تضف إليه `traefik.enable=true`.

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

لا توجد قاعدة بيانات ل Traefik، فحالته كلها ملفات في `/opt/traefik`: الإعداد الثابت، ومجلد `dynamic`، وملف Compose، والملف `.env`، والملف `acme.json`، أما توجيه كل تطبيق فمحفوظ في ملف Compose الخاص به، وبالتالي يدخل في النسخة الاحتياطية لذلك التطبيق.

انسخ المجلد دون أن توقف Traefik، والسبب أن الملف `acme.json` لا يتغير إلا عند إصدار شهادة أو تجديدها:

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

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

```bash
docker network create proxy
sudo tar xzf /root/traefik-2026-10-02.tar.gz -C /opt
sudo chown -R $USER: /opt/traefik
chmod 600 /opt/traefik/letsencrypt/acme.json /opt/traefik/.env
cd /opt/traefik
docker compose up -d
```

ومع وجود `acme.json` يستخدم Traefik الشهادات الحالية مباشرة ولا يطلب من Let's Encrypt شهادات جديدة لكل النطاقات دفعة واحدة، وهذا يهمك إذا كانت نطاقاتك كثيرة حتى لا تصطدم بحدود الإصدار، وإذا نقلت التطبيقات إلى السيرفر الجديد أيضاً فانقلها مع ملفات Compose الخاصة بها، ثم غير سجلات DNS.

🛑

تحتوي النسخة على المفاتيح الخاصة لكل شهاداتك ومفتاح حساب Let's Encrypt وهاش كلمة مرور اللوحة، لذلك خزنها مشفرة Encrypted خارج السيرفر، ولا تضعها في مستودع Git عام مع بقية ملفات الإعداد.

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

1. راجع [صفحة الإصدارات](https://github.com/traefik/traefik/releases?ref=arabroot.io)، واقرأ قسم الإصدار الجديد في [ملاحظات الترحيل Migration ضمن السلسلة 3](https://doc.traefik.io/traefik/migrate/v3/?ref=arabroot.io)، والسبب أن الإصدارات التصحيحية تغير أحياناً سلوكاً افتراضياً لأسباب أمنية، كما فعل [الإصدار v3.7.13](https://doc.traefik.io/traefik/v3.7/migrate/v3/?ref=arabroot.io#v3713) مع بعض الترويسات وصيغ الطلبات.
2. أنشئ نسخة احتياطية كما في القسم السابق.
3. غير الوسم Tag في `compose.yaml` إلى الإصدار الجديد برقمه الكامل، وليس `latest` ولا `v3`، ثم نفذ:

```bash
cd /opt/traefik
docker compose pull
docker compose up -d
docker compose logs --tail 30 traefik
```

يتوقف استقبال الطلبات بضع ثوان أثناء إعادة إنشاء ال Container، فاختر وقتاً قليل الحركة، وبعد ذلك افتح اللوحة وتأكد أن كل ال Routers خضراء، وابحث في السجل عن `WRN` و`ERR` لأن Traefik ينبهك فيه إلى الخيارات المهملة Deprecated، وإذا ظهرت مشكلة فأعد الوسم القديم ونفذ `docker compose up -d`.

أما الانتقال من السلسلة 2 إلى 3 فهو ترحيل كبير يغير صيغة بعض القواعد والخيارات، ولا يكفي فيه تغيير رقم الإصدار، لذلك اتبع فيه [دليل الترحيل من v2 إلى v3](https://doc.traefik.io/traefik/migrate/v2-to-v3/?ref=arabroot.io) خطوة بخطوة.

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

### يعيد Traefik الرسالة 404 page not found

معنى هذه الرسالة أن الطلب وصل إلى Traefik ولكن لم يطابقه أي Router، فافتح `/api/http/routers` أو اللوحة وتحقق مما يلي:

- أن ال Container يحمل `traefik.enable=true`، فبسبب `exposedByDefault: false` يتجاهل Traefik ما عداه.
- أن النطاق في `Host()` يطابق النطاق الذي تطلبه حرفاً بحرف، وأنه بين علامتي `` ` ``.
- أن ال Router مربوط بنقطة الدخول `websecure` وأنك تطلب HTTPS.
- أن ال Container في حالة `healthy` إذا كان له فحص صحة، فTraefik لا يوجه الطلبات إلى Container حالته `starting` أو `unhealthy`، وتظهر الحالة في `docker ps`.
- أن ال Middleware المذكور في الوسوم معرف فعلاً، فإذا أشرت إلى Middleware غير موجود ظهر ال Router في اللوحة بحالة خطأ ولم يعمل.

### لا تصدر الشهادة، أو تظهر TRAEFIK DEFAULT CERT

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

```bash
docker compose logs traefik | grep -i -A3 "acme"
```

غالباً سوف تجد رسالة `Unable to obtain ACME certificate for domains` ومعها السبب الذي أرسلته Let's Encrypt، فإذا كان السبب `Timeout during connect` أو `Connection refused` فالمنفذ 80 لا يصل إلى Traefik من الإنترنت، وعليك مراجعة جدار الحماية لدى المزود وعلى السيرفر، وإذا كان السبب `NXDOMAIN` أو عنواناً غير عنوان سيرفرك فسجل DNS لم ينتشر بعد أو يشير إلى مكان خاطئ، وتحقق منه بالأمر `dig +short app.example.com`.

وتفرض Let's Encrypt [حدوداً على عدد الشهادات](https://letsencrypt.org/docs/rate-limits/?ref=arabroot.io) وعلى المحاولات الفاشلة لكل نطاق، لذلك لا تقم بإعادة تشغيل Traefik مرة بعد مرة على أمل أن ينجح الطلب، والسبب أن كل محاولة فاشلة تقربك من الحد، وإنما أصلح السبب أولاً وجرب على بيئة Staging كما وصفنا في قسم التثبيت، وإذا ظهرت رسالة `too many certificates` أو `too many failed authorizations` فلا حل إلا أن تنتظر انتهاء المدة المذكورة في الرسالة.

### 504 Gateway Timeout أو 502 Bad Gateway

الطلب هنا وصل إلى Traefik وطابق ال Router، ولكن Traefik لم يصل إلى ال Container، وأشيع الأسباب أن ال Container ليس على الشبكة `proxy`، فيحاول Traefik الوصول إليه عبر شبكة أخرى لا يراها، وينتظر 30 ثانية ثم يعيد `504`، وسوف تجد في السجل تحذيراً مثل:

```bash
Could not find network named "proxy" for container "/whoami". Maybe you're missing the project's prefix in the label?
```

والحل أن تضيف الشبكة `proxy` إلى الخدمة في ملف Compose مع `external: true` كما في مثال whoami، وإذا كانت الخدمة على عدة شبكات فأضف إليها الوسم `traefik.docker.network=proxy`. أما الرمز `502` فمعناه غالباً أن المنفذ في `loadbalancer.server.port` خاطئ، أو أن التطبيق يستمع على `127.0.0.1` داخل ال Container وليس على `0.0.0.0`.

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

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

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

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

### يتجاهل Traefik ملف acme.json

إذا ظهرت في السجل رسالة `permissions 644 for /letsencrypt/acme.json are too open, please use 600` فلن يحفظ Traefik أي شهادة، وسوف يطلبها من جديد مع كل تشغيل حتى تصطدم بالحدود، والحل أن تنفذ `chmod 600 /opt/traefik/letsencrypt/acme.json` ثم `docker compose restart traefik`، وتتكرر المشكلة عادة بعد نسخ الملف بأداة لا تحفظ الصلاحيات.

### تطلب اللوحة كلمة المرور ثم ترفضها

السبب في الغالب أن علامات `$` في الهاش لم تصل كما هي، فتحقق من القيمة التي وصلت إلى الوسم بالأمر `docker inspect traefik --format '{{ index .Config.Labels "traefik.http.middlewares.dashboard-auth.basicauth.users" }}'`، وإذا ظهر الهاش ناقصاً فضع القيمة في `.env` بين علامتي تنصيص مفردتين، أو ضاعف كل `$` إذا كتبتها في `compose.yaml`، ثم نفذ `docker compose up -d`.

## الخلاصة

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

- يقرأ Traefik التوجيه من وسوم كل Container، فيبقى إعداد التطبيق ونطاقه وشهادته في ملف Compose واحد، ويختفي التوجيه مع التطبيق عندما تحذفه.
- اجعل `exposedByDefault: false` حتى لا ينشر Traefik إلا ما تختاره، وفعل `aliasHeadersStrategy: delete` على نقاط الدخول المعرضة للإنترنت.
- لا تستخدم `api.insecure`، وضع اللوحة خلف نطاق خاص وكلمة مرور، أو أوقفها إذا لم تحتج إليها.
- ال Docker socket يعادل صلاحية root على السيرفر، فضع بينه وبين Traefik وسيط Socket Proxy لا يسمح إلا بالقراءة.
- اترك تجديد الشهادات ل Traefik، وجرب على بيئة Staging أولاً، وانسخ `acme.json` مشفراً خارج السيرفر.

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

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