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

# Nginx Proxy Manager: نطاقات وشهادات TLS لكل خدماتك
- URL: https://arabroot.io/articles/nginx-proxy-manager-نطاقات-وشهادات-tls/
- Published: 2026-09-21T07:45:00.000Z
- Updated: 2026-10-05T10:18:22.000Z
- Description: بدلاً من فتح منفذ مستقل لكل تطبيق، يتيح لك Nginx Proxy Manager أن تربط كل خدمة بنطاقها وتمنحها شهادة HTTPS مجانية من واجهة بسيطة، دون أن تكتب إعدادات Nginx بنفسك.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, الشبكات والوصول, الاستضافة الذاتية, Nginx Proxy Manager

لنفرض أن لديك سيرفراً تشغل عليه عدة تطبيقات، فسوف تجد أن كل تطبيق يستمع على منفذ Port داخلي خاص به، هذا على 3000 وذاك على 8080، والحل البسيط الذي يخطر على البال هو أن تفتح هذه المنافذ وتعطي المستخدمين العنوان `http://203.0.113.10:8080`. ولكن هذا الحل غير مناسب إطلاقاً، فأنت لا تريد أن يكتب المستخدم رقم منفذ في المتصفح، ولا أن تفتح عشرة منافذ على الإنترنت، ولا أن تدير شهادة TLS Certificate لكل تطبيق على حدة.

وهذه المشكلات كلها يحلها **ال Reverse Proxy**، وهو خدمة واحدة تستقبل كل الطلبات على المنفذين 80 و443، فتقرأ اسم النطاق Domain في كل طلب وتوجهه إلى ال Container المقصود، وفي نفس الوقت تتولى عنك HTTPS وتجديد الشهادات Certificate Renewal.

[**Nginx Proxy Manager**](https://nginxproxymanager.com/?ref=arabroot.io) واختصاراً NPM يضع سيرفر Nginx خلف واجهة ويب Web UI بسيطة، حيث تضيف النطاق واسم ال Container ومنفذه، ثم تطلب شهادة من [Let's Encrypt](https://letsencrypt.org/?ref=arabroot.io) بنقرة واحدة، وتقصر الوصول على من يملك كلمة مرور أو على عناوين IP محددة. لذلك فهو يناسب من يفضل إدارة النطاقات من المتصفح، والفرق التي لا تريد كتابة ملفات إعداد Configuration Files الخاصة ب Nginx بيدها.

أما إذا كنت تفضل أن يكون الإعداد ملفاً نصياً في Git تراجعه وتنشره آلياً، فأدوات مثل [Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy?ref=arabroot.io) أو [Traefik](https://doc.traefik.io/traefik/?ref=arabroot.io) أنسب لك، فملف Caddy من سطرين يؤدي نفس الغرض.

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

- تثبيت NPM على شبكة Docker مشتركة بحيث لا تنشر تطبيقاتك أي منفذ على السيرفر، مع إبقاء واجهة الإدارة بعيدة عن الإنترنت.
- ربط تطبيق بنطاقه عبر Proxy Host، وطلب شهادة من Let's Encrypt بالتحقق عبر HTTP-01 أو عبر DNS-01، ثم شهادة واحدة Wildcard لكل النطاقات الفرعية.
- حماية اللوحات الداخلية بقوائم الوصول Access Lists، وتأمين واجهة NPM نفسها.
- التحقق والنسخ الاحتياطي والتحديث، وأشهر المشكلات وحلولها.

## ما تحتاجه قبل أن تبدأ Requirements

- سيرفر Linux عليه Docker وCompose، وإذا لم يكن جاهزاً فاتبع دليل [تثبيت Docker على Ubuntu](https://arabroot.io/articles/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-docker-%D8%B9%D9%84%D9%89-ubuntu/).
- الموارد Resources: يستهلك NPM نحو 100 إلى 150 ميجابايت من الذاكرة RAM، ويكفيه قدر صغير من المساحة للسجلات Logs والشهادات.
- نطاق تتحكم في سجلات DNS Records الخاصة به، حيث يحتاج كل نطاق فرعي Subdomain مثل `app.example.com` إلى سجل `A` يشير إلى العنوان العام Public IP للسيرفر `203.0.113.10`، وسجل `AAAA` إن كان لديك IPv6، ومع سجل شامل Wildcard Record مثل `*.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/).
- المنفذان `80/tcp` و`443/tcp` مفتوحان من الإنترنت إلى السيرفر، ولاحظ أن [التحقق عبر HTTP](https://letsencrypt.org/docs/challenge-types/?ref=arabroot.io#http-01-challenge) (HTTP Challenge) في Let's Encrypt يحتاج إلى المنفذ 80 حتى لو كانت كل خدماتك تعمل عبر HTTPS.
- المنفذ `81/tcp` هو منفذ واجهة الإدارة Admin UI، **ولن نفتحه على الإنترنت**.

## التثبيت

سوف نضع NPM وكل التطبيقات التي يخدمها على [شبكة Docker مشتركة](https://docs.docker.com/reference/cli/docker/network/create/?ref=arabroot.io) Docker Network اسمها `proxy`، وعلى هذه الشبكة يصل NPM إلى كل Container باسمه (`whoami`، `gitea`...)، وبالتالي لا تحتاج التطبيقات إلى نشر أي منفذ على السيرفر، ويبقى ال Reverse Proxy هو الطريق الوحيد إليها، وهذا ما [يوصي به التوثيق الرسمي](https://nginxproxymanager.com/advanced-config/?ref=arabroot.io#best-practice-use-a-docker-network). وإذا أردت أن تفهم لماذا نفعل ذلك وما أنواع الشبكات في 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/npm
sudo chown $USER: /opt/npm
cd /opt/npm
```

والملف `/opt/npm/compose.yaml` سوف يكون كما يلي:

```yaml
services:
  npm:
    image: jc21/nginx-proxy-manager:2.16.0
    container_name: npm
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:81:81"
    environment:
      TZ: UTC
    volumes:
      - npm_data:/data
      - npm_letsencrypt:/etc/letsencrypt
    networks:
      - proxy
    healthcheck:
      test: ["CMD", "/usr/bin/check-health"]
      interval: 30s
      timeout: 5s
      retries: 3

volumes:
  npm_data:
    name: npm_data
  npm_letsencrypt:
    name: npm_letsencrypt

networks:
  proxy:
    external: true
```

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

- الإعداد `127.0.0.1:81:81` ينشر واجهة الإدارة على السيرفر نفسه فقط، وسوف نصل إليها أولاً عبر نفق SSH Tunnel، ثم نضعها خلف NPM نفسه بنطاق وشهادة وقائمة وصول Access List.
- ال Volume `npm_data` فيه قاعدة بيانات Database من نوع SQLite، وملفات Nginx التي يولدها NPM، والسجلات، والشهادات المخصصة Custom Certificates، أما `npm_letsencrypt` ففيه شهادات Let's Encrypt ومفاتيح الحساب Account Keys، لذلك أدرج الاثنين في النسخ الاحتياطي Backup.
- ال Image فيها [فحص صحة](https://nginxproxymanager.com/advanced-config/?ref=arabroot.io#docker-healthcheck) Healthcheck جاهز هو `/usr/bin/check-health`، ولكنه معطل افتراضياً، فنفعله هنا.
- إذا لم يكن IPv6 مفعلاً على السيرفر فأضف `DISABLE_IPV6: "true"` إلى `environment` كما [يشرح التوثيق](https://nginxproxymanager.com/advanced-config/?ref=arabroot.io#disabling-ipv6)، والسبب أن Nginx سوف يفشل في الاستماع على `[::]` بدونه.

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

```bash
docker compose up -d
docker compose ps
docker compose logs --tail 20 npm
```

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

```bash
NAME   IMAGE                             STATUS
npm    jc21/nginx-proxy-manager:2.16.0   Up 2 minutes (healthy)
...
[Global   ] › ℹ  info      Backend PID 231 listening on port 3000 ...
```

## أول دخول وإنشاء حساب المدير

من جهازك افتح نفق SSH إلى المنفذ 81 على السيرفر، ثم افتح `http://localhost:8181` في المتصفح:

```bash
ssh -L 8181:127.0.0.1:81 deploy@203.0.113.10
```

إذا كنت قد قرأت شروحات قديمة فسوف تبحث عن الحساب الافتراضي Default Account المعروف `admin@example.com` وكلمة المرور `changeme`، ولكن الإصدارات الحديثة لم تعد تأتي به، وإنما تظهر بدلاً منه شاشة ترحيب تطلب منك إنشاء حساب المدير Admin Account مباشرة: الاسم والبريد وكلمة مرور قوية.

![شاشة Welcome في Nginx Proxy Manager لإنشاء حساب المدير الأول](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-01-create-admin.webp)

إنشاء حساب المدير عند التشغيل الأول

وإذا كنت تنشر NPM آلياً وتريد تخطي هذه الشاشة، فيمكنك إنشاء المدير عند التشغيل الأول من متغيري البيئة Environment Variables `INITIAL_ADMIN_EMAIL` و`INITIAL_ADMIN_PASSWORD` كما يبين [التوثيق الرسمي](https://nginxproxymanager.com/advanced-config/?ref=arabroot.io#auto-initial-user-creation). وبعد الدخول فعل التحقق بخطوتين Two-Factor Authentication من إعدادات حسابك.

## كيف تربط تطبيقاً بنطاقه عبر Proxy Host؟

لنأخذ مثالاً عملياً، وسوف نشغل تطبيقاً صغيراً على نفس الشبكة هو [whoami](https://github.com/traefik/whoami?ref=arabroot.io)، وهذا التطبيق يعيد تفاصيل الطلب كما وصلته، وبالتالي سوف ترى الترويسات Headers التي يمررها ال Reverse Proxy. وهذا ملفه `/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
cd /opt/whoami
docker compose up -d
```

في NPM افتح Hosts ثم Proxy Hosts ثم Add Proxy Host، واملأ تبويب Details كما يلي:

| الحقل                 | القيمة          | السبب                                                               |
| --------------------- | --------------- | ------------------------------------------------------------------- |
| Domain Names          | app.example.com | اكتب الاسم ثم اضغط Enter، ويمكنك إضافة أكثر من اسم لنفس المضيف Host |
| Scheme                | http            | البروتوكول Protocol بين NPM وال Container، وليس بين المتصفح وNPM    |
| Forward Hostname / IP | whoami          | اسم ال Container على شبكة proxy                                     |
| Forward Port          | 80              | المنفذ داخل ال Container، وليس المنفذ المنشور على السيرفر           |
| Block Common Exploits | مفعل            | يحجب أنماط الهجوم الشائعة في الروابط                                |
| Websockets Support    | مفعل            | تحتاجه تطبيقات المحادثة واللوحات الحية والطرفيات Terminals          |

![تبويب Details في نافذة Add Proxy Host مع النطاق واسم الـ Container والمنفذ](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-02-proxy-host-details.webp)

يشير النطاق إلى الـ Container whoami على المنفذ 80 عبر شبكة Docker

احفظ، فيولد NPM ملف Nginx الخاص بالمضيف ويعيد التحميل Reload خلال ثانية. وقد يتساءل البعض: هل يجب أن أنتظر [انتشار DNS Propagation](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/) حتى أختبر؟ والإجابة لا، فيمكنك الاختبار من السيرفر نفسه بتمرير اسم النطاق في الترويسة:

```bash
curl -s -H 'Host: app.example.com' http://127.0.0.1/
```

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

```bash
Hostname: 570cbf988f52
...
Host: app.example.com
X-Forwarded-For: 172.25.0.1
X-Forwarded-Proto: http
X-Forwarded-Scheme: http
X-Real-Ip: 172.25.0.1
```

ومن الترويسات `X-Forwarded-For` و`X-Real-IP` و`X-Forwarded-Proto` يعرف التطبيق عنوان الزائر والبروتوكول الأصلي، وسوف نعود إليها في قسم المشكلات.

## كيف تحصل على شهادة TLS لكل نطاق؟

افتح المضيف للتعديل من القائمة ⋮ بجانبه ثم Edit، وانتقل إلى تبويب SSL، ومن حقل SSL Certificate اختر *Request a new Certificate*. بعد ذلك فعل **Force SSL** حتى يتحول كل طلب HTTP إلى HTTPS، وفعل **HTTP/2 Support** أيضاً.

أما خيار **HSTS** فلا تفعله إلا بعد أن تتأكد أن الشهادة تعمل وتتجدد، والسبب أنه يطلب من المتصفح ألا يستخدم HTTP مع هذا النطاق مدة طويلة، وهي سنتان في إعداد NPM، والتراجع عنه صعب.

ولاحظ أن هذا الخيار يضيف `preload` دائماً، وتجد كيف تكتب HSTS بقيمتك أنت مع بقية ترويسات الأمان في دليل [ترويسات الأمان بالتفصيل: 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/).

![تبويب SSL مع خيار Request a new Certificate وخيارات Force SSL وHTTP/2 وHSTS](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-03-ssl-request-certificate.webp)

طلب شهادة جديدة من Let's Encrypt من تبويب SSL

### التحقق عبر HTTP (HTTP-01 Challenge): الخيار الافتراضي

لن تمنحك Let's Encrypt شهادة قبل أن تثبت أنك تتحكم في النطاق، ففي [طريقة التحقق HTTP-01](https://letsencrypt.org/docs/challenge-types/?ref=arabroot.io#http-01-challenge) يضع [Certbot](https://certbot.eff.org/?ref=arabroot.io) المدمج في NPM ملفاً مؤقتاً، ثم تطلبه سيرفرات Let's Encrypt من `http://app.example.com/.well-known/acme-challenge/...`. ولهذه الطريقة شروط وحدود:

- أن يشير سجل DNS للنطاق إلى هذا السيرفر فعلاً وأن يكون قد انتشر، وتحقق من ذلك بالأمر `dig +short app.example.com`.
- أن يكون المنفذ 80 مفتوحاً من الإنترنت كله، وأن يصل إلى NPM وليس إلى جهاز آخر في الطريق.
- لا تصلح هذه الطريقة لشهادات Wildcard مثل `*.example.com`.

وهذه أبسط طرق التحقق وتكفي في أغلب الحالات، لذلك اضغط Save، فيطلب NPM الشهادة ويجددها تلقائياً قبل انتهائها بثلاثين يوماً.

📌

في 29 فبراير 2020 اكتشفت Let's Encrypt خطأ Bug في الكود الذي يعيد فحص سجلات CAA قبل إصدار الشهادة، فأعلنت أنها مضطرة إلى [إلغاء Revoke نحو 3 ملايين شهادة](https://community.letsencrypt.org/t/revoking-certain-certificates-on-march-4/114864?ref=arabroot.io)، وطلبت من أصحابها تجديدها قبل 4 مارس 2020، وإلا فسوف يرى زوار مواقعهم تحذيرات أمنية. والدرس أن الشهادة قد تحتاج إلى استبدال قبل موعدها بأيام، لذلك اعتمد على التجديد الآلي واعرف كيف تطلب شهادة جديدة بنفسك عند الحاجة.

### التحقق من ملكية النطاق عبر ال DNS (DNS-01 Challenge): للشهادات الشاملة والسيرفرات الداخلية

في [طريقة التحقق DNS-01](https://letsencrypt.org/docs/challenge-types/?ref=arabroot.io#dns-01-challenge) يثبت Certbot ملكيتك للنطاق بسجل `TXT` باسم `_acme-challenge.example.com` يضيفه عبر ال API الخاص بمزود DNS Provider، وهذه الطريقة لا تحتاج إلى أي منفذ مفتوح، لذلك هي الخيار الوحيد للشهادات الشاملة Wildcard Certificates، وللسيرفرات الداخلية أو المنزلية التي لا يصل إليها الإنترنت. فعل *Use DNS Challenge* واختر المزود، ثم الصق بيانات الاعتماد Credentials بالصيغة التي تعرضها الواجهة، وتجد إضافات DNS Plugins المتاحة في [توثيق NPM الخاص ب Certbot](https://nginxproxymanager.com/certbot/?ref=arabroot.io):

![خيار Use DNS Challenge مع اختيار مزود DNS وحقل Credentials File Content](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-04-dns-challenge.webp)

DNS Challenge: المزود وملف بيانات الاعتماد ومدة الانتظار حتى ينتشر السجل

🛑

لا تستخدم مفتاح الحساب الكامل لدى مزود DNS، وإنما أنشئ رمز API Token بأضيق صلاحية Permission ممكنة، أي تعديل سجلات DNS لمنطقة Zone واحدة فقط، والسبب أن NPM يحفظ بيانات الاعتماد نصاً غير مشفر Plaintext في قاعدة بياناته كما تنبه الواجهة. وتذكر أن النسخ الاحتياطية من `npm_data` تحتوي هذا الرمز أيضاً.

### هل لديك نطاقات فرعية كثيرة؟ شهادة واحدة تكفي (Wildcard Certificate)

لنفرض أن لديك عشر خدمات على السيرفر، لكل منها نطاق فرعي مثل `git.example.com` و`wiki.example.com` و`status.example.com`، فالطريقة التي رأيناها حتى الآن تطلب شهادة مستقلة لكل Proxy Host، وهذا يعمل، ولكن مع كثرة الخدمات سوف تجد في صفحة Certificates عشر شهادات تتجدد كل منها في موعد مختلف، وكل خدمة جديدة تعني طلباً جديداً إلى Let's Encrypt يحسب من حدود عدد الطلبات. والحل الأنسب هنا هو الشهادة الشاملة Wildcard Certificate، وهي شهادة واحدة باسم `*.example.com` تغطي كل نطاق فرعي تحت النطاق الرئيسي Root Domain، فتطلبها مرة واحدة ثم تختارها في كل Proxy Host.

ولكن لاحظ أمرين قبل أن تطلبها، الأول أن النجمة تغطي مستوى واحداً فقط، فالشهادة `*.example.com` تصلح لـ `a.example.com` ولا تصلح لـ `a.b.example.com`، ولا تغطي النطاق الرئيسي `example.com` نفسه، لذلك نضيفه إلى الشهادة نفسها كاسم ثان. والثاني أن Let's Encrypt [لا تصدر شهادات Wildcard إلا عبر DNS-01](https://letsencrypt.org/docs/faq/?ref=arabroot.io)، والسبب أن الشهادة تغطي كل الأسماء تحت النطاق، فلا يكفي أن تثبت أنك تتحكم في سيرفر واحد يرد على اسم واحد، وإنما يجب أن تثبت أنك تتحكم في سجلات DNS للنطاق نفسه.

**الطريقة الأولى: شهادة من Let's Encrypt عبر DNS Challenge.** سوف نأخذ Cloudflare مثالاً، والخطوات نفسها مع أي مزود آخر في القائمة، وما يتغير هو صيغة بيانات الاعتماد فقط:

1. في لوحة Cloudflare افتح My Profile ثم API Tokens، وأنشئ رمزاً API Token بصلاحية `Zone:DNS:Edit` على منطقة النطاق `example.com` وحدها، كما [يطلب توثيق إضافة certbot-dns-cloudflare](https://certbot-dns-cloudflare.readthedocs.io/en/stable/?ref=arabroot.io)، ولا تستخدم المفتاح العام Global API Key، والسبب أنه يفتح حسابك كله لمن يحصل عليه.
2. في NPM افتح Certificates ثم Add Certificate، واختر Let's Encrypt via DNS.
3. في حقل Domain Names اكتب `*.example.com` ثم اضغط Enter، ثم `example.com` ثم Enter.
4. من حقل DNS Provider اختر Cloudflare، أو مزودك إن كان غيره، فالقائمة فيها عشرات المزودين.
5. في حقل Credentials File Content ضع السطر `dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567` بعد أن تستبدل القيمة بالرمز الذي أنشأته.
6. اترك Propagation Seconds فارغاً حتى تستخدم الإضافة قيمتها الافتراضية، وإذا فشل التحقق لأن السجل لم ينتشر بعد فاكتب فيه عدد ثوان أكبر وحاول مرة أخرى، ثم اضغط Save.

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

- الاسمان في شهادة واحدة، فتغطي الشهادة النطاق الرئيسي وكل نطاق فرعي من مستوى واحد تحته.
- تعرض الواجهة تحت حقل بيانات الاعتماد العبارة *This data will be stored as plaintext in the database and in a file!*، أي أن الرمز محفوظ نصاً غير مشفر، لذلك ينطبق عليه التحذير السابق عن أضيق صلاحية ممكنة.
- يجدد NPM الشهادة تلقائياً قبل انتهائها، ويستخدم كل مضيف مرتبط بها الشهادة الجديدة دون أي خطوة منك.

بعد ذلك افتح كل Proxy Host ثم تبويب SSL، واختر من حقل SSL Certificate الشهادة الشاملة بدلاً من *Request a new Certificate*، ثم فعل Force SSL كما سبق، ومن الآن فإن أي خدمة جديدة تحتاج فقط إلى Proxy Host تختار فيه الشهادة نفسها، دون طلب جديد إلى Let's Encrypt.

وعلى جانب DNS أضف سجلاً شاملاً من نوع `A` باسم `*`، أي `*.example.com`، يشير إلى `203.0.113.10`، فيصل أي نطاق فرعي إلى السيرفر دون أن تضيف سجلاً لكل خدمة، أما النطاق الرئيسي فيحتاج إلى سجل خاص به. وإذا كان النطاق على Cloudflare مع تفعيل ال Proxy (السحابة البرتقالية) فاجعل وضع التشفير SSL/TLS Encryption Mode هو Full (strict)، والسبب أن Cloudflare يتحقق عندها من شهادة سيرفرك، أما وضع Flexible فيسبب حلقة التحويل التي نشرحها في قسم المشكلات، وطريقة الحصول على عنوان الزائر الحقيقي خلف Cloudflare تجدها هناك أيضاً.

**الطريقة الثانية: رفع شهادة مخصصة Custom Certificate.** إذا كانت كل نطاقاتك تمر دائماً عبر Cloudflare مع تفعيل ال Proxy، فيمكنك أن تصدر من لوحة Cloudflare، من SSL/TLS ثم Origin Server ثم Create Certificate، [شهادة Origin CA](https://developers.cloudflare.com/ssl/origin-configuration/origin-ca/?ref=arabroot.io) تغطي `*.example.com` و`example.com`، وتصل مدتها إلى 15 سنة فلا تحتاج إلى تجديد، ولا تحتاج إلى وضع أي رمز API داخل NPM. ولكن هذه الشهادة لا يثق بها إلا Cloudflare، والمتصفحات لا تعرفها، لذلك إذا أوقفت ال Proxy على أي نطاق فرعي أو أوقفت Cloudflare مؤقتاً فسوف يرى الزوار خطأ شهادة غير موثوقة، كما ينبه توثيق Cloudflare. ويمكنك كذلك أن تشتري شهادة Wildcard من جهة إصدار تجارية Certificate Authority، فتعمل في كل المتصفحات، ولكنك تتولى تجديدها ورفعها بنفسك قبل انتهائها.

ولرفع أي منهما افتح في NPM صفحة Certificates ثم Add Certificate ثم Custom Certificate، واكتب اسماً للشهادة في Name، ثم ارفع ملف المفتاح الخاص في Certificate Key، وملف الشهادة في Certificate، وملف الشهادات الوسيطة في Intermediate Certificate إن وجد، ولاحظ أن NPM لا يقبل مفتاحاً محمياً بعبارة مرور Passphrase كما تنبه الواجهة. بعد ذلك تختارها في تبويب SSL لكل Proxy Host كما في الطريقة الأولى.

وقد يتساءل البعض: أي الطريقتين أختار؟ والإجابة أن Let's Encrypt عبر DNS Challenge هي الأنسب في أغلب الحالات، لأنها تعمل في كل المتصفحات وتتجدد وحدها، أما شهادة Origin CA فتناسبك إذا كانت كل نطاقاتك خلف Cloudflare دائماً ولا تريد أن تضع رمز API في NPM.

### حدود عدد الطلبات Rate Limits في Let's Encrypt

تضع Let's Encrypt [حدوداً على عدد الشهادات](https://letsencrypt.org/docs/rate-limits/?ref=arabroot.io) وعلى المحاولات الفاشلة لكل نطاق، لذلك إذا فشل الطلب فلا تكرر الضغط على Save على أمل أن ينجح في المرة الخامسة، وإنما اقرأ السبب أولاً في Logs أو في سجل ال Container ثم أصلح DNS أو المنفذ، وسوف تجد رسالة Certbot كاملة بالأمر `docker compose logs npm | grep -i -A5 certbot`.

## كيف تحمي اللوحات الداخلية بقوائم الوصول Access Lists؟

لوحات الإدارة وأدوات المراقبة Monitoring وبيئات الاختبار Staging يجب ألا تكون متاحة للعموم، وقائمة الوصول Access List في NPM تجمع لحمايتها طبقتين: **مستخدمين بكلمات مرور** عبر [HTTP Basic Auth](https://nginx.org/en/docs/http/ngx%5Fhttp%5Fauth%5Fbasic%5Fmodule.html?ref=arabroot.io)، و**قواعد IP** للسماح والمنع (allow/deny). افتح Access Lists ثم Add Access List:

1. تبويب Details: اكتب الاسم (`team-only`). وخيار **Satisfy Any** يعني أن إحدى الطبقتين تكفي وفق [توجيه satisfy في Nginx](https://nginx.org/en/docs/http/ngx%5Fhttp%5Fcore%5Fmodule.html?ref=arabroot.io#satisfy)، فمن جاء من عنوان مسموح يدخل مباشرة، ومن جاء من غيره يطلب منه NPM كلمة المرور، وبدون هذا الخيار يلزم الزائر أن يستوفي الشرطين معاً. أما **Pass Auth to Upstream** فيمرر ترويسة Authorization إلى التطبيق، لذلك اتركه معطلاً ما لم يحتج التطبيق إليها.
2. تبويب Authorizations: أضف المستخدمين وكلمات المرور.
3. تبويب Rules: أضف العناوين أو الشبكات المسموحة مثل شبكة المكتب أو ال VPN، ولاحظ أنك بمجرد أن تضيف قاعدة واحدة يضيف NPM القاعدة `deny all` في النهاية.

![تبويب Authorizations في قائمة الوصول مع اسم مستخدم وكلمة مرور](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-05-access-list-users.webp)

مستخدم في قائمة الوصول team-only

![تبويب Rules مع قاعدة Allow لشبكة ثم Deny all](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-06-access-list-rules.webp)

قاعدة تسمح لشبكة المكتب، أما ما عداها فيرفضه NPM أو يطلب له كلمة المرور بحسب Satisfy Any

بعد ذلك اربط القائمة بالمضيف من حقل Access List في تبويب Details، ثم اختبر من عنوان خارج الشبكة المسموحة، مرة بدون كلمة المرور ومرة معها:

```bash
curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/
curl -s -o /dev/null -w '%{http_code}\n' -u 'team:كلمة-المرور' https://app.example.com/
```

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

```bash
401
200
```

وسوف تعرض قائمة المضيفين وجهة كل مضيف ونوع شهادته وقائمة الوصول المرتبطة به وحالته:

![قائمة Proxy Hosts تعرض app.example.com مع الوجهة والشهادة وقائمة الوصول والحالة Online](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-07-proxy-hosts-list.webp)

المضيف مع شهادة وقائمة وصول، وحالته Online

⚠️

لا تستخدم Basic Auth إلا مع HTTPS، والسبب أنه يرسل كلمة المرور مع كل طلب. وهو حماية مقبولة لأداة داخلية، ولكنه لا يغني عن الدخول الموحد SSO، حيث لا يوفر تحققاً بخطوتين ولا جلسات Sessions ولا سجلاً للدخول، وللحصول على حماية أقوى راجع دليل [حماية أي تطبيق باستخدام Authentik Forward Auth](https://arabroot.io/articles/%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D8%A3%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-authentik-forward-auth/).

## Websockets

تطبيقات مثل Mattermost وUptime Kuma والطرفيات في Portainer تفتح اتصال WebSocket يبقى مفتوحاً، وحتى يعمل هذا الاتصال يجب أن يمرر ال Reverse Proxy [الترويستين Upgrade وConnection](https://nginx.org/en/docs/http/websocket.html?ref=arabroot.io)، وهذا ما يفعله خيار Websockets Support. وللتحقق أرسل طلب ترقية Upgrade Request يدوياً، والنتيجة المتوقعة هي `101 Switching Protocols`:

```bash
curl -s -i -N --max-time 3 -H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' https://app.example.com/echo | head -n 1
```

```bash
HTTP/1.1 101 Switching Protocols
```

وإذا عاد الرمز `400` أو `426`، أو انقطع الاتصال بعد 60 ثانية، فالخيار غير مفعل، أو أن في الطريق Proxy آخر لا يمرر الترقية.

## كيف تؤمن واجهة NPM نفسها؟

فتح نفق SSH في كل مرة يصبح مرهقاً بسرعة، لذلك أضف مضيفاً لواجهة الإدارة بالنطاق `npm.example.com`، واجعل Forward Hostname هو `npm`، وهو اسم ال Container نفسه، والمنفذ `81`، ثم أضف شهادة وقائمة وصول مقصورة على عناوين الفريق أو ال VPN. وبعد أن يعمل أبق المنفذ 81 منشوراً على `127.0.0.1` فقط احتياطاً.

وهنا قد يبدو خيار Satisfy Any مريحاً، ولكن لا تفعله في قائمة هذه الواجهة، وإنما اطلب العنوان المسموح وكلمة المرور معاً، والسبب أن NPM يثق بترويسة `X-Real-IP` إذا جاء الطلب من شبكة Docker الداخلية (`172.16.0.0/12`). فإذا كان للنطاق سجل `AAAA` وشبكة Docker لا تدعم IPv6، [يمرر Docker طلبات IPv6 عبر ال userland proxy](https://docs.docker.com/engine/network/port-publishing/?ref=arabroot.io)، فتصل إلى NPM من عنوان داخلي، وعندها يستطيع أي زائر أن يرسل عنواناً مسموحاً في هذه الترويسة فيتجاوز قواعد IP.

## كيف تتأكد أن كل شيء يعمل؟ Verification

- الأمر `docker compose ps` يعرض ال Container `npm` بحالة `healthy`.
- الأمر `curl -I http://app.example.com` يعود بالرمز `301` إلى `https://`.
- الأمر `curl -I https://app.example.com` يعود بالرمز `200`، أو بالرمز `401` إن كانت هناك قائمة وصول.
- الأمر `echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -issuer -dates` يعرض تفاصيل الشهادة، ومنها جهة الإصدار Issuer وهي Let's Encrypt وتاريخ الانتهاء.
- العنوان `http://203.0.113.10:81` لا يستجيب من خارج السيرفر.
- الشهادة تظهر في صفحة Certificates وتنتهي بعد نحو 90 يوماً، ثم تتجدد بعد شهرين دون تدخل منك.

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

حالة NPM كلها محفوظة في ال Volumes `npm_data` و`npm_letsencrypt`، لذلك أوقف ال Container لحظات حتى تحصل على نسخة متسقة من قاعدة SQLite:

```bash
cd /opt/npm
docker compose stop npm
docker run --rm -v npm_data:/data:ro -v npm_letsencrypt:/letsencrypt:ro -v "$PWD":/backup alpine:3.24 tar czf /backup/npm-$(date +%F).tar.gz -C / data letsencrypt
docker compose start npm
```

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

```bash
docker network create proxy
docker volume create npm_data
docker volume create npm_letsencrypt
docker run --rm -v npm_data:/data -v npm_letsencrypt:/letsencrypt -v "$PWD":/backup alpine:3.24 tar xzf /backup/npm-2026-09-26.tar.gz -C /
docker compose up -d
```

**تذكر:** النسخة تحتوي المفاتيح الخاصة Private Keys للشهادات وأي رموز DNS API، لذلك خزنها مشفرة Encrypted خارج السيرفر.

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

1. اقرأ ملاحظات الإصدار Release Notes أولاً، فمثلاً [الإصدار 2.16.0](https://github.com/NginxProxyManager/nginx-proxy-manager/releases/tag/v2.16.0?ref=arabroot.io) حدث Certbot ونبه إلى أن بعض إضافات DNS قد تحتاج إلى تعديل، لذلك إذا كنت تعتمد على DNS Challenge فاختبر التجديد بعد التحديث.
2. خذ نسخة احتياطية كما سبق.
3. غير الوسم Tag في `compose.yaml` إلى رقم الإصدار الجديد كاملاً وليس `latest`، ثم نفذ:

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

وعند الإقلاع سوف يطبق NPM ترحيلات Migrations قاعدة البيانات، وتظهر في السجل بالوسم `[Migrate]`، ثم يعيد توليد ملفات Nginx، فافتح بضعة مضيفين وتأكد أنها تعمل. وإذا احتجت إلى الرجوع Rollback فأعد الوسم القديم، واستعد النسخة الاحتياطية إذا كانت الترحيلات قد غيرت قاعدة البيانات.

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

### 502 Bad Gateway

هذا الخطأ يعني أن الطلب وصل إلى NPM ولم يصل إلى التطبيق، وسجل المضيف يخبرك بالسبب: `docker exec npm tail -n 20 /data/logs/proxy-host-1_error.log`، والرقم فيه هو معرف المضيف. فإذا كان ال Container متوقفاً سوف ترى رسالة مثل `whoami could not be resolved`. والأسباب الشائعة هي:

- ال Container متوقف أو يعيد التشغيل باستمرار: `docker ps -a`.
- ال Container ليس على شبكة `proxy`: `docker network inspect proxy`.
- كتبت المنفذ المنشور على السيرفر بدلاً من منفذ ال Container الداخلي.
- Scheme خاطئ: التطبيق يستمع داخلياً عبر HTTPS (مثل Portainer على 9443) وأنت اخترت `http`، أو العكس.
- التطبيق يستمع على `127.0.0.1` داخل ال Container الخاص به، والصحيح أن يستمع على `0.0.0.0`.

### ERR\_TOO\_MANY\_REDIRECTS

سوف ترى هذا الخطأ عادةً عندما يقف أمام NPM Proxy آخر ينهي TLS Termination، مثل Cloudflare بوضع Flexible أو Load Balancer، حيث يصل الطلب إلى NPM عبر HTTP فيحوله Force SSL إلى HTTPS، ثم يعود عبر HTTP من جديد، وتتكرر الدورة بلا نهاية. وللمشكلة حلان بهذا الترتيب: الأول أن تجعل وضع Cloudflare [**Full (strict)**](https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/full-strict/?ref=arabroot.io) حتى يتصل ب NPM عبر HTTPS، كما [يوضح توثيق Cloudflare](https://developers.cloudflare.com/ssl/troubleshooting/too-many-redirects/?ref=arabroot.io)، والثاني أن تفعل في تبويب SSL ضمن Advanced الخيار **Trust Upstream Forwarded Proto Headers**، فيثق NPM بترويسة `X-Forwarded-Proto: https` القادمة من ال Proxy الأمامي.

![تبويب SSL في Edit Proxy Host مع خيار Trust Upstream Forwarded Proto Headers](https://arabroot.io/content/images/2026/09/nginx-proxy-manager-08-trust-forwarded-proto.webp)

خيار الثقة بترويسة البروتوكول القادمة من الـ Proxy الأمامي

قبل تفعيل الخيار يعود طلب HTTP يحمل `X-Forwarded-Proto: https` بالرمز `301`، وبعد تفعيله يصل إلى التطبيق. ولكن لا تفعله إلا إذا كان أمام NPM Proxy تثق به، والسبب أن أي عميل Client يستطيع إرسال هذه الترويسة. وإذا استمرت الحلقة Redirect Loop فتأكد أن التطبيق نفسه لا يفرض HTTPS بطريقته، كأن يكون `APP_URL` أو `ROOT_URL` مضبوطاً على `http://`.

### لماذا فشل إصدار الشهادة؟ المنفذ 80 مغلق

رسائل مثل `Timeout during connect` أو `Connection refused` في سجل Certbot تعني أن Let's Encrypt لم تصل إلى المنفذ 80، لذلك راجع جدار الحماية Firewall لدى المزود وعلى السيرفر، وتأكد أن المنفذ ليس محجوزاً لخدمة أخرى (`sudo ss -ltnp | grep ':80 '`). ولاحظ أن بعض مزودي الإنترنت المنزلي ISP يحجبون المنفذ 80 الوارد، وفي هذه الحالة استخدم DNS Challenge، أو اتبع دليل [تشغيل خادمك من إنترنت المنزل](https://arabroot.io/articles/%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%AE%D8%A7%D8%AF%D9%85%D9%83-%D9%85%D9%86-%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D8%A7%D9%84%D9%85%D9%86%D8%B2%D9%84/). وإذا كان النطاق يمر عبر Cloudflare وخيار ال Proxy (السحابة البرتقالية Orange Cloud) مفعل، فتأكد أن الطلب يصل فعلاً إلى سيرفرك.

### التطبيق يرى عنوان Docker بدلاً من عنوان الزائر

التطبيق الذي يعمل خلف Reverse Proxy يرى في الاتصال عنوان ال Proxy (`172.x.x.x`) دائماً، أما العنوان الحقيقي فيصله في الترويستين `X-Real-IP` و`X-Forwarded-For`. والحل في إعداد التطبيق نفسه، حيث تخبره أن يثق بال Proxy ويقرأ الترويسة عبر إعداد مثل `TRUSTED_PROXIES` أو `trusted_proxies` أو `REMOTE_IP_HEADER` بحسب التطبيق، واجعل الشبكة الموثوقة مقصورة على شبكة Docker.

وإذا كان أمام NPM Proxy آخر مثل Cloudflare، فإن NPM يثق مسبقاً بنطاقات عناوين Cloudflare وCloudFront التي يجلبها عند الإقلاع، ولكنه يقرأ العنوان من الترويسة `X-Real-IP`، وCloudflare لا يرسلها، وبالتالي يصل عنوان Cloudflare إلى تطبيقك. والحل أن تضيف السطر `real_ip_header CF-Connecting-IP;` في تبويب Advanced الخاص بال Proxy Host، وتجد الشرح الكامل مع إعداد كل إطار عمل في دليل [تطبيقك خلف Reverse Proxy](https://arabroot.io/articles/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%83-%D8%AE%D9%84%D9%81-reverse-proxy/). وإذا كان سيرفرك لا يتصل بالإنترنت عند الإقلاع، فيمكنك [تعطيل هذا الجلب](https://nginxproxymanager.com/advanced-config/?ref=arabroot.io#disabling-ip-ranges-fetch) بالمتغير `IP_RANGES_FETCH_ENABLED: "false"`.

### 413 Request Entity Too Large

حد حجم الطلب Request Size Limit في NPM افتراضياً هو `2000m`، لذلك إذا ظهر هذا الخطأ فالأرجح أن الحد مفروض في التطبيق نفسه (مثل `upload_max_filesize` في PHP) أو في Proxy أمامي آخر. وإذا احتجت إلى حد أكبر لمضيف معين، فأضف في تبويب Advanced الخاص به (رمز الترس) سطراً مثل `client_max_body_size 5g;`، وهو [توجيه Nginx المعروف](https://nginx.org/en/docs/http/ngx%5Fhttp%5Fcore%5Fmodule.html?ref=arabroot.io#client%5Fmax%5Fbody%5Fsize).

### المضيف يظهر Offline بعد إعداد مخصص

السبب خطأ في إعداد Advanced يمنع Nginx من إعادة التحميل، ومنذ الإصدار 2.16 يحتفظ NPM بالملف الفاشل بالامتداد `.conf.err` حتى تقرأه: `docker exec npm ls /data/nginx/proxy_host/`، فأصلح الإعداد واحفظ من جديد.

## الخلاصة

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

- ضع NPM وتطبيقاتك على شبكة Docker مشتركة، فلا تنشر التطبيقات أي منفذ ويبقى ال Reverse Proxy هو الطريق الوحيد إليها.
- انشر واجهة الإدارة على `127.0.0.1` فقط، ثم ضعها خلف NPM بقائمة وصول تطلب العنوان المسموح وكلمة المرور معاً.
- التحقق عبر HTTP-01 يكفي في أغلب الحالات ويحتاج المنفذ 80، أما شهادات Wildcard والسيرفرات الداخلية فتحتاج التحقق عبر DNS-01 برمز API بأضيق صلاحية.
- إذا كانت لديك نطاقات فرعية كثيرة فاطلب شهادة Wildcard واحدة لـ `*.example.com` و`example.com`، ثم اخترها في كل Proxy Host، فيتجدد الجميع معها.
- لا تفعل HSTS إلا بعد أن تتأكد أن الشهادة تعمل وتتجدد.
- انسخ ال Volumes `npm_data` و`npm_letsencrypt` وخزن النسخة مشفرة خارج السيرفر، لأنها تحتوي المفاتيح الخاصة.

## سجل التحديثات

- سبتمبر 2026: كتابة الدليل واختباره على Nginx Proxy Manager 2.16.0.
- أكتوبر 2026: إضافة قسم الشهادة الشاملة Wildcard Certificate، إما من Let's Encrypt عبر DNS Challenge وإما شهادة مخصصة Custom مثل Cloudflare Origin CA.