ووردبريس على VPS بـ Docker: الإعداد وحلقة التوجيه

ووردبريس على VPS بـ Docker: الإعداد وحلقة التوجيه

تشغيل ووردبريس على الاستضافة المشتركة نوع يتقاسم فيه موقعك موارد سيرفر واحد مع مئات المواقع الأخرى، فتكون أرخص الخيارات وأسهلها للبداية، لكن أداءها يتأثر إذا استهلك جار ما موارد…">الاستضافة المشتركة ويحتاج خبرة أو…">سيرفر افتراضي خاص (VPS) حاجة سهلة تبدأها في عشر دقايق. الصعب إنك تخلّصها: بيانات قاعدة البيانات، والـ volume اللي لازم يعيش بعد كل ترقية، والإعداد الواحد اللي بيرمي wp-admin في حلقة توجيه لا تنتهي أول ما تحط بروكسي عكسي قدّامه.

المقال ده بيمشي معاك الطريق للآخر: ملف Compose جاهز، شهادة HTTPS بتتجدد لوحدها، وشرح للحلقة اللي بتوقف نص الناس.

ملف Compose

ثلاث خدمات فقط: ووردبريس، MariaDB، ولا شيء غيرهما. ثبّت وسم كل صورة (Image Tag) حتى تكون الترقية سطراً تغيّره بقرار منك، لا شيئاً يهبط عليك مع إعادة تشغيل:

services:
  db:
    image: mariadb:11  # check hub.docker.com/_/mariadb/tags for the current point release
    restart: unless-stopped
    environment:
      - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MARIADB_DATABASE=wordpress
      - MARIADB_USER=wordpress
      - MARIADB_PASSWORD=${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql


  wordpress:
    image: wordpress:6-apache  # check hub.docker.com/_/wordpress/tags for the current point release
    restart: unless-stopped
    depends_on: [db]
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
      WORDPRESS_CONFIG_EXTRA: |
        if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
            $$_SERVER['HTTPS'] = 'on';
        }
        define('FORCE_SSL_ADMIN', true);
    volumes:
      - wp_content:/var/www/html/wp-content
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
    ports:
      - "127.0.0.1:8080:80"


volumes:
  db_data:
  wp_content:

كلمات المرور تعيش في ملف .env بجوار ملف Compose، ولا تُكتب داخل الـ YAML أبداً:

printf 'DB_ROOT_PASSWORD=%snDB_PASSWORD=%sn' "$(openssl rand -hex 24)" "$(openssl rand -hex 24)" > .env
chmod 600 .env

لاحظ أن الـ volume يغطي /var/www/html/wp-content فقط، لا التثبيت بأكمله. هذا المجلد هو القوالب والإضافات والوسائط — الأجزاء التي تخصك فعلاً. أما نواة ووردبريس فتعيش داخل الصورة، وهذا بالضبط ما يجعل تغيير وسم الصورة لاحقاً عملية نظيفة بدل تعارض مع ملفاتك.

HTTPS خلف Caddy، ومن أين تأتي حلقة التوجيه

blog.example.com {
    reverse_proxy 127.0.0.1:8080
}

هذا هو الملف كله. يطلب Caddy شهادة الأمان (SSL) ويجددها تلقائياً بمجرد تشغيله، بشرط أن يكون سجل A في الـ اسم النطاق مثل arabcms.com إلى عنوان IP السيرفر الذي يستضيف الموقع. أي تعديل في سجلاته قد يحتاج ساعات…">DNS يشير إلى السيرفر بالفعل. وهو أيضاً يمرر بروتوكول الطلب الأصلي في ترويسة X-Forwarded-Proto عند كل قفزة — ومن هنا تحديداً تأتي حلقة التوجيه.

ووردبريس يتحدث إلى حاوية wordpress عبر HTTP عادي داخل شبكة Docker، لأن التشفير انتهى عند Caddy قبل ثلاث قفزات. فإذا تُرك وحده، يرى HTTP_X_FORWARDED_PROTO=https قادمة من طلب المتصفح الحقيقي، لكنه يظن أن اتصاله بنفسه http. وعندها يظل FORCE_SSL_ADMIN — وأنت تريده مفعّلاً حتى لا تعمل لوحة التحكم بلا تشفير — يعيد توجيه صفحة يعتقد أنها تُحمَّل عبر HTTP إلى HTTPS، إلى ما لا نهاية.

الأسطر الثلاثة في WORDPRESS_CONFIG_EXTRA أعلاه هي العلاج: تقرأ الترويسة التي يرسلها Caddy أصلاً، وتضبط $_SERVER['HTTPS'] في PHP قبل أن يتخذ ووردبريس قراره، فيرى FORCE_SSL_ADMIN طلباً مشفّراً حقيقياً ويتوقف عن الدوران.

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

لماذا $$_SERVER وليس $_SERVER؟

لاحظ أن كل $_SERVER أعلاه مكتوبة $$_SERVER داخل ملف Compose. هذه ليست غلطة مطبعية.

Compose يجري استبداله الخاص لصيغة $VAR على كل نص في الملف — بما فيها الكتل النصية — قبل أن يُقرأ الملف كـ YAML أصلاً، و$_SERVER تبدو له تماماً كإشارة إلى متغيّر بيئة اسمه _SERVER.

اترك علامة الدولار مفردة، فيستبدلها Compose بنص فارغ بصمت، ويهبط كود PHP المكسور مباشرةً في wp-config.php. النتيجة ليست فشلاً في حل حلقة التوجيه فحسب، بل خطأ قاتل يسقط الموقع كله. مضاعفة علامة الدولار هي طريقتك لتقول لـ Compose: اتركها ومرّر $ واحدة إلى PHP.

حد الرفع الذي ينساه الجميع

سقف رفع الوسائط في ووردبريس تحدده PHP، لا أي إعداد في لوحة التحكم. الصورة الرسمية مبنية على php:apache التي تقرأ كل ملف .ini يُوضع في /usr/local/etc/php/conf.d/، فيكفي ملف صغير مربوط للقراءة فقط دون المساس بالصورة:

file_uploads = On
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300

القاعدة التي توقع الناس: post_max_size يجب أن تساوي upload_max_filesize أو تزيد عنها، وإلا فاز الأصغر بصمت وفشل رفع أرشيف قالب كبير برسالة لا تفيد في شيء.

وCaddy لا يضع سقفاً على حجم جسم الطلب افتراضياً كما تفعل بعض البروكسيات العكسية، فلا يوجد ما ترفعه من جهته مع الملف أعلاه. لكن إن أضفت يوماً توجيه request_body { max_size ... } إلى تلك الكتلة، فقد صار سقفاً ثانياً فوق post_max_size — والأصغر في السلسلة هو الفائز دائماً.

اقرأ هذا قبل الشراء: مسألة المنفذ 443

الـ VPS من نوع NAT IPv4 يعطيك عدداً محدوداً من المنافذ الموجّهة، ووجود المنفذ 443 بينها يعتمد على الباقة نفسها. تحقق من ذلك قبل أن توجّه الـ DNS إلى السيرفر. والمنافذ التي تحصل عليها موجّهة إلى الجهاز، لا إلى حاوية بعينها.

إن كان السيرفر سيشغّل موقعاً واحداً فقط فالأمر غير ذي بال: يرتبط Caddy بالمنفذ 443، ويجلس ووردبريس خلفه، وانتهى. المشكلة تظهر لحظة رغبتك في موقع HTTPS ثانٍ على نفس الجهاز، لأن المنفذ موجود مرة واحدة على مستوى الشبكة.

والحل هو ما يعطيه لك Caddy مجاناً: مثيل واحد فقط مرتبط بالمنفذ 443، يوجّه حسب اسم المضيف. أضف كتلة ثانية في نفس ملف Caddyfile لموقع آخر تشير إلى 127.0.0.1:<port> مختلف، فيختار Caddy الخلفية الصحيحة من ترويسة Host قبل أن يصل الطلب إلى أي من التطبيقين. ما لا يمكنك فعله هو ربط حاوية ووردبريس مباشرةً بـ 443:443 بينما يحتاج شيء آخر نفس المنفذ.

ملاحظة للقارئ في مصر والخليج: اختر مركز بيانات في فرانكفورت أو أمستردام لأقرب زمن استجابة، وضع Cloudflare أمام الموقع. الفارق بين مركز بيانات أوروبي وآخر أمريكي محسوس فعلياً على زوار القاهرة والرياض، وأوضح ما يكون في زمن أول بايت.

أساسيات أمان تسبق أي إضافة

لا شيء مما يلي مشكلة إضافات، وتثبيت واحدة نادراً ما يحلها:

  • إضافات قليلة. كل إضافة كود لم تراجعه يعمل بنفس صلاحيات نواة ووردبريس. موقع بخمس إضافات جيدة الصيانة أأمن من موقع بثلاثين، بصرف النظر عما تدّعيه أي منها في مجال الحماية.
  • التحديثات التلقائية الصغرى مفعّلة. ووردبريس يطبّق الإصدارات الصغرى والأمنية تلقائياً بشكل افتراضي؛ لا تعطّل هذا. وإن أردته صريحاً في الإعدادات أضف define('WP_AUTO_UPDATE_CORE', 'minor'); إلى WORDPRESS_CONFIG_EXTRA.
  • اسم مستخدم حقيقي للمشرف، لا admin. معالج التثبيت يطلب منك اختياره بالفعل، فاختره. الاسم admin هو التخمين الأول في كل قوائم حشو بيانات الاعتماد على الإنترنت.
  • تقييد محاولات الدخول المتكررة. يمكن أن يكون هذا بإضافة، لكنه لا يجب أن يكون: fail2ban يراقب سجل الوصول بحثاً عن ضربات متكررة على wp-login.php ويؤدي نفس المهمة على طبقة الشبكة، دون إضافة قطعة PHP أخرى إلى الموقع نفسه.

النسخ الاحتياطي: الـ volume وقاعدة البيانات

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

docker compose exec db sh -c 'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" wordpress' > "wordpress-$(date +%F).sql"

هذا الأمر يقرأ كلمة مرور الجذر من بيئة حاوية db نفسها، فلا يمر شيء حساس في سجل أوامر الشل. أما الـ volume — القوالب والإضافات والوسائط، أي كل ما ليس قاعدة بيانات:

docker run --rm -v wordpress_wp_content:/data -v "$(pwd)":/backup alpine:3.20 
  tar czf "/backup/wp-content-$(date +%F).tar.gz" -C /data .

عدّل اسم الـ volume ليطابق ما يطبعه docker compose config --volumes فعلياً في مشروعك، فـ Compose يسبقه باسم مجلد المشروع افتراضياً.

تنبيه: انقل الملفين خارج السيرفر بالكامل. نسخة احتياطية جالسة بجوار السيرفر الذي جاءت منه ليست نسخة احتياطية، بل ملف.

الترقية: غيّر الوسم ودع ووردبريس يكمل

docker compose pull
docker compose up -d

خذ نسخة mariadb-dump أعلاه قبل هذا مباشرةً، لا بعده. ووردبريس يشغّل ترقية قاعدة البيانات تلقائياً أول مرة يفتح فيها مشرف صفحة على الإصدار الجديد — لا تحتاج أمر ترحيل منفصل — لكن هذه الترقية رحلة في اتجاه واحد. اقرأ ملاحظات إصدار النسخة التي تقفز إليها قبل تغيير الوسم، خصوصاً عبر إصدار رئيسي.

حجم السيرفر المناسب

ووردبريس وMariaDB وCaddy حزمة خفيفة لموقع صغير مع إضافة تخزين مؤقت تعمل بكفاءة: 1 جيجابايت من الرام أرضية عملية، والمساحة غالباً مكتبة الوسائط لا التطبيق نفسه.

يتغير الرقم لحظة أن يفعل الموقع أكثر من عرض صفحات. ووكومرس يضيف وزناً حقيقياً على قاعدة البيانات — طلبات وجلسات وتنويعات منتجات — ومنشئ صفحات مثل Elementor يشغّل PHP أثقل بوضوح أثناء التحرير، حتى لو ظلت الصفحة المنشورة سريعة. الأرضية الواقعية عندها 2 جيجابايت، ويُفضّل الانتقال قبل أن يبطؤ الموقع لا بعده.

في Elementor: إن كانت هذه بيئة عمل تحرر عليها بـ Elementor Pro وJetEngine يومياً، فاعتبر 2 جيجابايت نقطة البداية لا السقف. ثقل المحرر يقع على PHP وقت التحرير، وهو ما لا تخفّفه إضافة الكاش لأنها تخدم الزوار لا المحررين.

الخلاصة

الحزمة كلها ثلاث خدمات وملف بروكسي من سطرين، وأخطر ما فيها إعدادان: الأسطر التي تخبر ووردبريس أن الاتصال مشفّر فعلاً، وعلامة الدولار المضاعفة التي تمنع Compose من ابتلاع الكود.

جهّز النسخ الاحتياطي من أول يوم وخزّنه بره السيرفر، وابدأ بمركز بيانات أوروبي لو جمهورك في مصر أو الخليج. ولو حابب حد يراجع الإعداد معاك قبل ما تنقل موقع حقيقي، احجز جلسة من هنا احجز استشارة مجانية. ولو مش عايز إدارة سيرفر من أصله، استضافة مُدارة قريبة من المنطقة أوفر لوقتك جرّب Hostinger.

مرجع المقال (بالإنجليزية): Self-hosting WordPress on a VPS: Docker Compose, HTTPS, and the redirect loop nobody explains
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.

الأسئلة الشائعة

لماذا تدخل لوحة تحكم ووردبريس في حلقة إعادة توجيه خلف Caddy؟

لأن ووردبريس يرى أن طلب المتصفح جاء عبر HTTPS، لكنه لا يعتبر اتصاله بنفسه مشفّراً، فيظل FORCE_SSL_ADMIN يحاول فرض بروتوكول يعتقد أنه غير مستخدم. الحل قراءة ترويسة HTTP_X_FORWARDED_PROTO وضبط $_SERVER['HTTPS'] قبل تنفيذ هذا الفحص.

هل أستطيع تشغيل أكثر من موقع HTTPS على نفس الـ VPS من نوع NAT IPv4؟

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

ماذا يحدث فعلياً إذا تجاهلت ملف uploads.ini؟

يفشل رفع الوسائط وتثبيت الإضافات والقوالب بمجرد تجاوز حد PHP الافتراضي — وهو رقم صغير عادةً — برسالة توحي بأن الملف تالف لا بأنه أكبر من المسموح. هذه من أكثر بلاغات “ووردبريس بايظ” التي تكون في حقيقتها إعداد PHP لم يرفعه أحد.

أحتفظ بنسخة من volume المحتوى — هل ما زلت أحتاج نسخة من قاعدة البيانات؟

نعم. الـ volume يحمل ملفاتك، وقاعدة البيانات تحمل مقالاتك وإعداداتك وكل ما تخزنه الإضافات وووكومرس كصفوف لا كملفات. الاسترجاع بأحدهما دون الآخر يعطيك موقعاً بكل ملفاته وبلا محتواه، أو العكس.

كم رام يحتاج ووردبريس على VPS؟

1 جيجابايت أرضية عملية لموقع صغير مع إضافة تخزين مؤقت تعمل بكفاءة. لكن مع ووكومرس أو منشئ صفحات مثل Elementor تصبح الأرضية الواقعية 2 جيجابايت، والأفضل الترقية قبل أن يبطؤ الموقع لا بعدها.

اترك تعليقاً