الحاجة / مشكلة الخادم

إذا سبق لك أن رأيت ارتفاعات "غير مبررة" في استخدام وحدة المعالجة المركزية على PHP-FPM أثناء حملة إعلانية، متبوعة بظهور أخطاء 504 على جانب الخادم الوكيل العكسي، فقد واجهت بالفعل الحد الأقصى لـ WordPress خادم واحد. نادرًا ما تنشأ المشكلة من WordPress "في حد ذاتها": إنها مزيج من PHP + الإدخال/الإخراج + الجلسات + التحميلات + ذاكرة التخزين المؤقت التي تشبع نقطة واحدة.

الهدف هنا واضح وملموس: وضع WordPress 6.9.4 (PHP 8.1+) خلف HAProxy، وتوزيع الحمل على عدة خوادم ويب، والحفاظ على اتصالات TLS نظيفة، وإجراء فحوصات صحية تكشف بالفعل عن عقدة معطلة، وتجنب المزالق الكلاسيكية (التحميلات غير المشتركة، وذاكرة التخزين المؤقت غير المتناسقة، وضعف ضبط خاصية الثبات، وقاعدة البيانات التي تصبح عنق الزجاجة الجديد).

في النهاية، ستعرف كيفية نشر واجهة أمامية HAProxy (TLS + HTTP/2)، وعقد Nginx+PHP-FPM متعددة، وتخزين الوسائط المشترك، وRedis لتخزين الكائنات مؤقتًا، واستراتيجية "التفريغ" لإزالة عقدة دون قطع الجلسات أو عمليات التحميل الجارية.

من الناحية العملية، يُستخدم هذا النوع من البنية لاستيعاب الارتفاعات المفاجئة في الطلب دون الحاجة إلى تخصيص موارد زائدة لخادم واحد. لكنه لا يتسامح مع التناقضات الطفيفة: عقدة ذات wp-config.php يختلف الأمر، فوجود ساعة غير متزامنة، وذاكرة تخزين مؤقتة محلية عالقة، يؤدي إلى ظهور أخطاء متقطعة يستحيل إعادة إنتاجها محليًا. في تجربتي، إن قابلية التكرار (النشر، والتكوين، والفحوصات) هي التي تُحدث الفرق بين مجموعة ووردبريس مستقرة ومجموعة غير مستقرة.

ملخص سريع

  • HAProxy في الواجهة الأمامية: إنهاء بروتوكول TLS، وتوجيه HTTP، وفحوصات سلامة التطبيق، و لزوجة اختياري، يعتمد على ملفات تعريف الارتباط.
  • عقد ووردبريس عديمة الحالة : رمز متطابق في كل مكان، لا توجد عمليات تحميل محلية "فريدة"، سجلات مركزية.
  • التحميلات المشتركة نظام ملفات الشبكة (NFS): بسيط (سريع الإعداد) أو تخزين كائنات (أكثر موثوقية). سأشرح نظام ملفات الشبكة هنا (مع بعض النقاط التي يجب مراعاتها).
  • قاعدة معطيات : نسخة أساسية + (ربما) نسخ احتياطية. يقوم ووردبريس بكتابة بيانات أكثر بكثير مما قد تتصور (خيارات، بيانات مؤقتة، جلسات إضافات).
  • مخبأ ذاكرة تخزين مؤقتة لـ FastCGI (مع توخي الحذر بشأن ملفات تعريف الارتباط/المصادقة) + ذاكرة تخزين مؤقتة لكائنات Redis. يتم التحكم في إبطال الصلاحية، وليس "تخزين كل شيء مؤقتًا والدعاء".
  • استغلال : تصريف البيانات من الواجهة الخلفية، والنشر الذري (rsync/artifacts)، والتجهيز، وأوامر التحقق (curl، WP-CLI، السجلات).

إذا كنت بحاجة إلى تحديد الأولويات: (1) عمليات التحميل المشتركة، (2) استخدام نفس قيمة التشفير (Salt)، (3) استخدام رؤوس بروكسي صحيحة، (4) استخدام ذاكرة التخزين المؤقت للكائنات في Redis، (5) استخدام التخزين المؤقت لبروتوكول HTTP فقط عند التحكم في الاستثناءات. لقد رأيت العديد من المشاريع تبدأ باستخدام "التخزين المؤقت لـ FastCGI في كل مكان" وتنتهي بحادثة تسريب محتوى.

قبل البدء (المتطلبات الأساسية)

الوصول مطلوب:

  • استخدم SSH كجذر أو sudo على HAProxy وعلى كل عقدة ويب.
  • الوصول إلى MySQL/MariaDB (CLI) على خادم قاعدة البيانات.
  • WP-CLI على عقدة ويب واحدة على الأقل (ويفضل أن تكون جميعها)، إصدار حديث متوافق مع WP 6.9.4.
  • الوصول إلى نظام أسماء النطاقات (DNS)

الحد الأدنى للإصدارات الموصى بها (أبريل 2026، ووردبريس 6.9.4):

  • PHP 8.1+ (8.2/8.3 غالبًا ما تكون أكثر راحة). انظر إصدارات PHP المدعومة.
  • Nginx المستقر، PHP-FPM، HAProxy 2.8+ (أو إصدار توزيعة مكافئ)، Redis 7+.
  • MySQL 8.0+ أو إصدار حديث من MariaDB. (تجنب الإصدارات التي انتهى دعمها.)

النسخ الاحتياطي الإلزامي قبل أي تغيير:

  • تفريغ قاعدة البيانات + نسخة احتياطية wp-content (الإضافات، القوالب، التحميلات).
  • تصدير ملف التكوين (Nginx، PHP-FPM، HAProxy).
# Sur le serveur DB (ou depuis un bastion avec accès)
# Remplacez DB_NAME, DB_USER. Utilisez un compte avec droits de lecture.
mysqldump --single-transaction --routines --triggers --events 
  -u DB_USER -p DB_NAME | gzip > /root/backup-db-$(date +%F).sql.gz

# Sur un nœud WordPress (code + contenus)
# --numeric-ids évite les surprises UID/GID si vous restaurez ailleurs.
tar --numeric-owner --numeric-ids -czf /root/backup-wpcontent-$(date +%F).tar.gz 
  /var/www/example.com/wp-content

بدائل مفيدة حسب قيودك:

  • إذا كانت قاعدة البيانات كبيرة، فمن الأفضل استخدام نسخة مضغوطة منها. pigz (متعددة النوى) وتخزينها على وحدة تخزين منفصلة: mysqldump ... | pigz -p 4 > ....
  • إذا كانت لديك جداول كبيرة جدًا (سجلات، تحليلات)، يمكنك استبعاد بعض الجداول غير الأساسية مؤقتًا لـ تسارع إجراء اختبار تجاوز الفشل، ثم إعادة التكامل بعد ذلك (مع مراعاة اتساق التطبيق).
  • إذا كان مزود خدمة الاستضافة الخاص بك يتطلب استخدام خادم وكيل خارجي، فتأكد من ذلك. certbot وتعمل تحديثات APT من خلال HAProxy.

ملاحظة من واقع الخبرة: كثيراً ما رأيت عمليات نقل البيانات باستخدام "HAProxy + عقد متعددة" تفشل ليس بسبب HAProxy نفسه، بل بسبب عدم القدرة على مشاركة الملفات المرفوعة أو بسبب ذاكرة تخزين مؤقتة قوية تخزن صفحات الإدارة. اجعل النسخة الأولى بسيطة، ثم حسّنها تدريجياً.

الخطوة 1: تحديد بنية الشبكة المستهدفة وتدفق الشبكة (من يتحدث مع من)

بنية مرجعية (بسيطة وفعالة):

  • 1x HAProxy عام (عنوان IP عام، المنافذ 80/443).
  • ضعفين (أو أكثر) عقد الويب خاص: Nginx + PHP-FPM + WordPress (كود متطابق).
  • 1x DB (أساسي)، ويمكن إضافة نسخة واحدة اختيارياً.
  • 1x رديس (ذاكرة التخزين المؤقت للكائنات). يمكن أن تكون موجودة في قاعدة البيانات أو منفصلة.
  • 1x NFS (أو تخزين الكائنات) لـ wp-content/uploads.

موجز HTTP:

  • العميل → HAProxy (TLS) → Nginx (HTTP) → PHP-FPM (socket) → DB/Redis/NFS.

ما الذي سيتغير بالنسبة لـ WordPress؟

  • يجب أن تكون العقد عديمة الحالة : لا تقم بتخزين أي شيء فريد محليًا (الملفات المرفوعة، ذاكرة التخزين المؤقت للقرص "غير الصالحة"، جلسات PHP على القرص إذا تم استخدامها بواسطة مكون إضافي).
  • الملكية الفكرية الحقيقية نحتاج إلى إعادة توجيهها X-Forwarded-For et X-Forwarded-Protoواطلب من ووردبريس تفسيرها بشكل صحيح (وإلا سيتم اكتشاف HTTPS على أنه "مزيف" أو ملفات تعريف ارتباط معطلة أو محتوى مختلط).

الحالات الاستثنائية التي أضعها في الاعتبار منذ مرحلة التصميم:

  • روابط الويب / قائمة عناوين IP المسموح بها إذا كان مكون إضافي للدفع أو جدار حماية تطبيقات الويب (WAF) يقوم بإنشاء قائمة مسموح بها على عنوان IP، فسيتعين عليك الاعتماد على عنوان IP الحقيقي للعميل (وبالتالي التحكم في سلسلة الوكيل).
  • WP-كرون : على عدة عقد، تشغيل wp-cron.php قد يؤدي تدفق بيانات الإنترنت إلى تنفيذ مهام متزامنة. لذا، أفضل استخدام مهمة مجدولة (cron job) على خادم واحد (أو مهمة خارجية) وتعطيل خاصية التشغيل "لكل زيارة".
  • التحميلات المجزأة (بعض الإضافات/أدوات الإنشاء): يمكن تقسيم عملية التحميل إلى عدة طلبات. في حال وجود خاصية التخزين الجزئي، تأكد من أن هذه الطلبات تُرسل إلى نفس الخادم أو أن التخزين المؤقت مشترك.

توافق أدوات إنشاء الصفحات (Divi 5، Elementor، Avada): تُولّد هذه الأدوات العديد من طلبات AJAX/REST وتتعامل مع الوسائط. لذا، يجب أن تكون بنيتك التحتية قادرة على التعامل مع ذلك.

  • طلبات POST كثيفة (تعديل، استيراد/تصدير التخطيطات)،
  • تحميلات متعددة،
  • نقاط نهاية REST (/wp-json/) حساسة للتخزين المؤقت.

الخطوة الثانية: تثبيت HAProxy، وإنهاء TLS، وإجراء فحوصات سلامة موثوقة.

على نظامي Debian/Ubuntu، قم بتثبيت HAProxy وتفعيل الخدمة. قم بتعديل التعليمات بما يتناسب مع توزيعتك.

sudo apt-get update
sudo apt-get install -y haproxy

sudo systemctl enable haproxy
sudo systemctl status haproxy --no-pager

الخيار التقني: أقوم بإنهاء بروتوكول TLS على HAProxy. هذا يُسهّل مركزية الشهادات، والتشفير، وبروتوكول HTTP/2، وHSTS. تبقى خوادمك الخلفية على بروتوكول HTTP الخاص (أو بروتوكول mTLS الداخلي إذا كنتَ تُطبّق معايير صارمة).

بالنسبة للشهادات، استخدم Let's Encrypt على HAProxy (certbot في وضع "deploy hook") أو مديرًا داخليًا. يتوقع HAProxy عادةً ملفًا .pem مُدمجة (الشهادة + المفتاح). مثال بسيط:

# Exemple : concaténer fullchain + privkey pour HAProxy
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem 
  /etc/letsencrypt/live/example.com/privkey.pem 
  | sudo tee /etc/haproxy/certs/example.com.pem > /dev/null

sudo chmod 600 /etc/haproxy/certs/example.com.pem
sudo chown root:root /etc/haproxy/certs/example.com.pem

الحالات الشاذة في بروتوكول TLS التي أتعامل معها بشكل منهجي:

  • تجديد بعد تجديد شهادة Let's Encrypt، لا يُعاد تحميل HAProxy تلقائيًا دائمًا، وذلك بحسب إعداداتك. أضف خطافًا يقوم بـ systemctl reload haproxy (أعد التحميل، وليس إعادة التشغيل) وراقب أخطاء تحليل PEM.
  • شبكة معلومات متعددة المجالات إذا كان لديك عدة مضيفات افتراضية، فيمكن لـ HAProxy تحميل دليل الشهادات. اختبر الشهادة الصحيحة باستخدام openssl s_client -servername ....
  • HTTP / 2 بعض عملاء الشركات/الوكلاء يُظهرون سلوكاً غريباً. احتفظ بـ alpn h2,http/1.1 (كما هو الحال هنا) وتأكد من أنك لا تكسر أي عمليات تكامل.

فحوصات السلامة: تجنب فحوصات TCP البسيطة. قد يقبل الخادم بروتوكول TCP ولكنه يعاني من تعطل PHP-FPM أو قاعدة بيانات غير قابلة للوصول. أفضل استخدام نقطة نهاية للتطبيق تفحص على الأقل PHP وقاعدة البيانات، دون الاعتماد على ذاكرة التخزين المؤقت.

إنشاء نقطة نهاية /lb-health.php يتم نشره على كل عقدة (في جذر الموقع الإلكتروني)، ويقوم بإجراء اختبار اتصال بسيط بقاعدة البيانات. تنبيه: يجب حماية هذا الملف (لا يحتوي على معلومات حساسة)، ومن الأفضل أن يكون الوصول إليه متاحًا فقط من خلال HAProxy (قائمة التحكم بالوصول للشبكة).

<?php
/**
 * Health check simple pour HAProxy.
 * - Vérifie que PHP tourne
 * - Vérifie une connexion DB via WordPress (wpdb)
 *
 * Sécurité :
 * - Ne renvoyez aucune info sensible
 * - Restreignez l'accès par IP (firewall / Nginx allow/deny)
 */

define('SHORTINIT', true); // Démarrage WordPress minimal (plus rapide)
require __DIR__ . '/wp-load.php';

global $wpdb;

try {
    // Requête très légère ; évite les tables lourdes.
    $ok = $wpdb->get_var('SELECT 1');
    if ((string) $ok === '1') {
        header('Content-Type: text/plain; charset=utf-8');
        echo "OKn";
        exit;
    }
} catch (Throwable $e) {
    // Ne logguez pas l'exception ici en clair (risque d'infos).
}

http_response_code(503);
header('Content-Type: text/plain; charset=utf-8');
echo "KOn";

التفاصيل المهمة في هذا الكود (والتي أقوم بتصحيحها غالبًا أثناء التدقيق):

  • SHORTINIT يتجنب تحميل القوالب/الإضافات. هذا يقلل من زمن الاستجابة ويحد من الآثار الجانبية (يمكن للإضافة التي تقوم بطلب خارجي أثناء عملية التهيئة أن "تخدع" عملية التحقق الخاصة بك).
  • Le try/catch يتجنب إرجاع رمز 200 مع صفحة خطأ PHP (اعتمادًا على التكوين)، مما قد يجعل HAProxy يعتقد أن كل شيء على ما يرام.
  • الفحص SELECT 1 يتحقق من اتصال قاعدة البيانات. لا يتحقق من أن ووردبريس يعمل "بشكل وظيفي"، ولكنه يكتشف بالفعل نسبة كبيرة من حالات الفشل (تعطل قاعدة البيانات، خلل في نظام أسماء النطاقات الداخلي، بيانات اعتماد غير صالحة، مجموعة خوادم ممتلئة).

خيار مفيد (حالة استثنائية): إذا كان أكثر أعطالك تكرارًا هو تجمد Redis (ذاكرة التخزين المؤقت للكائنات) مما يتسبب في ارتفاع مفاجئ في زمن الاستجابة، فيمكنك إضافة اختبار اتصال Redis (ping). اختياري إلى فحص السلامة. تحذير: لا تتسبب في تعطل النظام الخلفي بالكامل في حالة تعطل Redis و الموقع يمكن العمل بدونه (وإلا فإنك تحول حادثة Redis إلى انقطاع كامل للخدمة).

<?php
// Variante : check Redis optionnel (ne doit pas fuiter d'infos)
define('SHORTINIT', true);
require __DIR__ . '/wp-load.php';

global $wpdb;

$ok_db = false;
$ok_redis = null; // null = non testé

try {
    $ok_db = ((string) $wpdb->get_var('SELECT 1') === '1');
} catch (Throwable $e) {
    $ok_db = false;
}

// Test Redis brut (si le serveur est joignable) - sans dépendre d'un plugin.
$redis_host = defined('WP_REDIS_HOST') ? WP_REDIS_HOST : null;
$redis_port = defined('WP_REDIS_PORT') ? (int) WP_REDIS_PORT : 6379;

if ($redis_host) {
    $fp = @fsockopen($redis_host, $redis_port, $errno, $errstr, 0.2);
    if ($fp) {
        stream_set_timeout($fp, 0, 200000);
        fwrite($fp, "*1rn$4rnPINGrn");
        $resp = fgets($fp);
        fclose($fp);
        $ok_redis = (is_string($resp) && str_starts_with($resp, "+PONG"));
    } else {
        $ok_redis = false;
    }
}

if ($ok_db) {
    header('Content-Type: text/plain; charset=utf-8');
    echo "OKn";
    // Vous pouvez aussi exposer un état très minimal en header, côté réseau privé uniquement.
    // header('X-Health-Redis: ' . ($ok_redis === null ? 'skip' : ($ok_redis ? 'ok' : 'ko')));
    exit;
}

http_response_code(503);
header('Content-Type: text/plain; charset=utf-8');
echo "KOn";

نعم، SHORTINIT يُعدّ هذا حلاً وسطاً. فهو يُقلّل من حجم بيانات تهيئة ووردبريس، ولكنه لا يزال يُحمّل إعدادات قاعدة البيانات. وهو يُشكّل توازناً جيداً في المواقع الكبيرة جداً. مرجع عام حول تهيئة ووردبريس: wp-load.php (مرجع للمطورين).

الخطوة 3: تقارب الجلسة (الثبات) وحالات استخدام ووردبريس (لوحة تحكم ووردبريس، ملفات تعريف الارتباط، REST)

لا يستخدم ووردبريس "جلسة خادم" بشكل افتراضي: تتم المصادقة عبر ملفات تعريف الارتباط الموقعة، لذلك تعمل الأنظمة متعددة العقد بدون ثبات. si الكود، والملحات، ووقت النظام متسقة. لكن الأمر يصبح معقدًا عندما تستخدم بعض الإضافات (التجارة الإلكترونية، والأمان، والتخزين المؤقت، والمحررات) جلسات PHP، أو ملفات مؤقتة، أو أقفال القرص.

في تجربتي، أفضل حل وسط هو:

  • لا يسبب لزوجة لتحسين حركة المرور العامة (توزيع أفضل)،
  • لزوجة ل /wp-admin et /wp-login.php إذا كنت تشك في وجود إضافات "تحتفظ بالحالة"،
  • أو اللزوجة العامة إذا كنت في مرحلة استقرار وتريد تقليل المجهولات.

حالة استثنائية واقعية: يقوم مستخدم مسجل الدخول (باستخدام Elementor/Divi/Avada builder) بإجراء الكثير من طلبات AJAX عبر /wp-admin/admin-ajax.php et /wp-json/إذا كانت سياسة ملفات تعريف الارتباط في خادم البروكسي/ذاكرة التخزين المؤقت لـ FastCGI لديك غير سليمة، فقد تُقدّم محتوى "عامًا" لمستخدم مسجّل الدخول (أو العكس). وهذا يُعيدنا إلى مشكلة التخزين المؤقت.

مواقف أخرى تؤدي إلى التصاق "غير متوقع":

  • تحميل / استيراد في وضع الإدارة: قد تُؤدي بعض العمليات إلى استعلامات واستطلاعات طويلة الأمد. إذا أعاد HAProxy توزيع البيانات في المنتصف، وقام الملحق بتخزين الحالة في ملف محلي، فسيتعطل سير العمل.
  • راحة Nonce يتعامل ووردبريس مع الأرقام العشوائية على جانب ملفات تعريف الارتباط والوقت، ولكن يمكن أن تؤدي الخوادم الوكيلة التي تعدل الرؤوس (أو الساعات غير المتزامنة) إلى ظهور أخطاء 401/403 متقطعة. /wp-json/.
  • الحد من معدل بخصوص HAProxy: إذا قمت بوضع قيود صارمة للغاية على /wp-json/يمكن أن يتصرف Elementor كبرنامج آلي (يرسل العديد من الطلبات الصغيرة). لذا، يُنصح بالتخطيط لاستخدام قوائم التحكم بالوصول (ACLs) القائمة على المسارات بدلاً من تحديد معدل الطلبات بشكل عام.

الخطوة 4: التخزين المشترك لمجلد wp-content/uploads (NFS أو كائن) وإبطال الصلاحية

بدون مساحة تخزين مشتركة، ستشعر بالتأثير فورًا: صورة تم تحميلها من الخادم "أ" غير موجودة على الخادم "ب"، مما يؤدي إلى ظهور أخطاء 404 عشوائية حسب التوزيع. وتزيد أدوات إنشاء الصفحات من حدة هذه المشكلة (عمليات تحميل وإعادة إنشاء متكررة).

نظام ملفات الشبكة (NFS) (سريع النشر، ولكن احذر من الأقفال والأداء)

مثال: خادم NFS يقوم بالتصدير /srv/nfs/wp-uploadsكل عقدة ويب تقوم بتثبيت هذا الدليل على /var/www/example.com/wp-content/uploads.

# Sur le serveur NFS
sudo apt-get install -y nfs-kernel-server
sudo mkdir -p /srv/nfs/wp-uploads
sudo chown -R www-data:www-data /srv/nfs/wp-uploads
sudo chmod 2775 /srv/nfs/wp-uploads
# /etc/exports (exemple)
# Autorisez uniquement le réseau privé de vos nœuds web
# no_root_squash est pratique mais discutable ; préférez des UID/GID cohérents.
sudo tee /etc/exports > /dev/null <<'EOF'
/srv/nfs/wp-uploads 10.0.10.0/24(rw,sync,no_subtree_check)
EOF

sudo exportfs -ra
sudo systemctl restart nfs-kernel-server

على كل عقدة ويب:

sudo apt-get install -y nfs-common

# Sauvegardez l'ancien uploads local si existant
sudo rsync -a /var/www/example.com/wp-content/uploads/ /root/uploads-local-backup/ || true

# Montez temporairement pour tester
sudo mount -t nfs4 10.0.20.10:/srv/nfs/wp-uploads /var/www/example.com/wp-content/uploads

# Persistant via /etc/fstab
sudo tee -a /etc/fstab > /dev/null <<'EOF'
10.0.20.10:/srv/nfs/wp-uploads /var/www/example.com/wp-content/uploads nfs4 rw,hard,timeo=600,retrans=2,_netdev 0 0
EOF

sudo mount -a

نقطة مهمة: تأكد من أن www-data (أو مستخدم PHP-FPM) لديه نفس معرّف المستخدم/معرّف المجموعة على جميع العُقد. وإلا، فستكون لديك صلاحيات "وهمية" على NFS.

الحالات الشاذة لتقنية NFS التي أراها غالبًا في بيئة الإنتاج:

  • مقبض ملف قديم بعد إعادة نشر/إعادة تشغيل نظام ملفات الشبكة (NFS): تبدأ خوادمك الخلفية بإرجاع أخطاء 500 أثناء عمليات التحميل. تحقق من ذلك. dmesg يتم الوصول إلى البيانات من عقد الويب واسترجاعها بشكل سليم. في الحالات الحساسة، يتجنب تخزين الكائنات (المتوافق مع S3) هذا النوع من المشاكل.
  • هاملت قد يكون نظام ملفات الشبكة (NFS) مناسبًا للقراءة، ولكنه كارثي للعديد من الملفات الصغيرة (مثل الصور المصغرة). إذا كانت مكتبة الوسائط الخاصة بك ضخمة، فاستخدم شبكة توصيل محتوى (CDN) أمامها. /wp-content/uploads/ أو قم بتمرير الكائن + المكون الإضافي المناسب.
  • تأمين تقوم بعض الإضافات بتأمين الملفات. في نظام NFS، يعتمد ذلك على الخيارات والإصدار. اختبر الميزات الأساسية (إضافة النسخ الاحتياطي، تحسين الصور، الاستيراد) قبل التبديل.

إبطال ذاكرة التخزين المؤقت بعد التحميل

إذا قمت بتفعيل التخزين المؤقت لبروتوكول HTTP (FastCGI)، فقد تبقى الصفحات التي تشير إلى الوسائط مخزنة مؤقتًا حتى بعد اكتمال التحميل. وهذا ملحوظ في المواقع الإلكترونية التحريرية. برغيالحل الأمثل هو الحذف المُستهدف (حسب عنوان URL) عبر إضافة تخزين مؤقت متوافقة، أو الحذف باستخدام HAProxy/Varnish إن وُجد. هنا، سأعتمد على Nginx/FastCGI وأوصي باستراتيجية مُتحفظة (عدم التخزين المؤقت للمستخدمين المُسجلين، وفترة صلاحية قصيرة، والحذف اليدوي عبر WP-CLI عند الحاجة).

حالة أخرى استثنائية: يقوم المطورون أحيانًا بإنشاء ملفات CSS/JS في wp-content/uploads/ (أو مجلد فرعي) وتتوقع أن تكون مرئية على الفور. إذا كنت تقدم خدمة uploads عبر Nginx مع رؤوس الرسائل immutable إذا كانت هذه البرامج عدوانية للغاية، يمكنك الاحتفاظ بإصدار أقدم على جانب المتصفح. احتفظ immutable بالنسبة للأصول ذات البصمات الرقمية، وليس للملفات "الديناميكية" التي يتم إعادة إنشائها بدون تجزئة.

الخطوة 5: قاعدة البيانات (الأساسية/النسخة الاحتياطية)، حدود القراءة/الكتابة والحدود الفعلية

لا يتناسب توزيع أحمال المواقع الإلكترونية مع حجم قاعدة البيانات. فغالبًا ما تصبح قاعدة البيانات هي عنق الزجاجة بعد إضافة 2-3 خوادم ويب. يكتب ووردبريس بيانات أكثر مما يدركه الكثيرون: خيارات التحميل التلقائي، والبيانات المؤقتة، وجلسات الإضافات، والسجلات، وغيرها.

استراتيجية عملية:

  • ابدأ بـ مادة صلبة أولية (وحدة المعالجة المركزية/ذاكرة الوصول العشوائي/عمليات الإدخال/الإخراج في الثانية)، فهارس نظيفة، سجل استعلامات بطيء.
  • أضف réplique لأغراض النسخ الاحتياطي، وذكاء الأعمال، وربما القراءة إذا كان لديك طبقة تطبيق تعرف كيفية التوجيه (لا يقوم نظام WordPress الأساسي بتقسيم القراءة/الكتابة بشكل أصلي).

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

مرجع مفيد (واجهة برمجة تطبيقات قاعدة بيانات ووردبريس): فئة wpdb.

حالات استثنائية لقاعدة البيانات في بيئة متعددة العقد:

  • روابط زيادة عدد عُقد الويب تزيد من عدد اتصالات MySQL. اضبط max_connections وخاصة مجموعات PHP-FPM الخاصة بك (وإلا ستتعرض قاعدة بياناتك لهجوم DDoS من خلال الاتصالات المتزامنة).
  • تضخم التحميل التلقائي طاولة wp_options يؤدي تفعيل التحميل التلقائي (autoload=yes) إلى زيادة تكلفة كل طلب. يساعد Redis في ذلك، لكنه لا يخفي كل شيء إذا كانت ذاكرة التخزين المؤقت غير نشطة أو إذا كانت عمليات الإبطال متكررة.
  • المعاملات تُجري بعض الإضافات عمليات مطولة أثناء المعاملات. ومع وجود عدة عُقد، يزداد احتمال حدوث تنازع على الأقفال. لذا، فعّل خاصية تسجيل الأحداث البطيئة وراقب حالات الانتظار/الأقفال.

الخطوة 6: ذاكرة التخزين المؤقت للخادم (FastCGI) + ذاكرة التخزين المؤقت لكائنات Redis، دون إلحاق الضرر بنفسك

طبقتان مختلفتان:

  • ذاكرة التخزين المؤقت HTTP (ذاكرة التخزين المؤقت Nginx FastCGI): تعمل على تسريع الصفحات العامة. فعالة للغاية، ولكنها خطيرة إذا قمت بتخزين الصفحات المخصصة مؤقتًا باستخدام ملفات تعريف الارتباط.
  • ذاكرة التخزين المؤقت للكائن (ريديس): يُسرّع استعلامات قاعدة البيانات المتكررة (الخيارات، الاستعلامات). أكثر أمانًا لـ WordPress، ومفيد حتى للمستخدمين المسجلين.

Redis: قم بتثبيت Redis واستخدم إضافة ذاكرة التخزين المؤقت للكائنات المُدارة. الملف wp-content/object-cache.php يجب أن تكون الملفات متطابقة على جميع العُقد (التي يتم نشرها عبر مسار العمل الخاص بك). توخَّ الحذر عند النشر الجزئي: فقد رأيتُ مجموعة خوادم تحتوي إحدى عُقدها على ملف object-cache.php بينما لا تحتوي الأخرى عليه، مما أدى إلى تفاوت في الأداء وظهور أخطاء متقطعة.

مرجع : بيانات واجهة برمجة التطبيقات العابرة (مفيد لفهم ما يمكن تخزينه).

التخزين المؤقت لحالات HTTP الاستثنائية (FastCGI): في حال تفعيله، يجب تحديد مفهوم "قابلية التخزين المؤقت" بوضوح. بالنسبة لـ WordPress، يبقى وجود ملفات تعريف الارتباط الخاصة بالمصادقة والمعاينة المؤشر الأكثر موثوقية. لا تكتفِ بـ "عدم تخزين /wp-admin/ مؤقتًا": إذ يمكن للمستخدم المسجل الدخول تحميل صفحة عامة، ويجب أن تظل هذه الصفحة غير قابلة للتخزين المؤقت إذا كانت مُخصصة (شريط الإدارة، محتوى مقيد، أسعار خاصة، إلخ).

لن أدرج هنا تكوين FastCGI كاملاً (فهو يعتمد كثيراً على مساراتك)، ولكن إليك الاستثناءات التي أختبرها دائماً كحد أدنى:

  • أي طلب يتضمن ملف تعريف ارتباط wordpress_logged_in_ ou wordpress_sec_
  • wp-postpass_ (محتوى محمي بكلمة مرور)
  • comment_author_ (وفقًا لمنطقك)
  • جميع الطرق /wp-json/, /wp-admin/, /wp-login.php, admin-ajax.php
  • أي طلب يستخدم طريقة أخرى غير GET/HEAD

الخطوة 7: النشر، والتجهيز، والترحيل، والتفريغ الكامل للعقدة

قاعدتان تمنعان 80% من حوادث موازنة الأحمال في ووردبريس:

  • النشر الذري (دليل الإصدار + رابط رمزي)، أو على الأقل مزامنة جميع العقد قبل إعادة تشغيل حركة المرور.
  • بالُوعَة عقدة قبل الصيانة: دع الاتصالات تنتهي، ولا تقبل أي اتصالات جديدة.

مصرف على جانب HAProxy

باستخدام وقت تشغيل مقبس HAProxy، يمكنك وضع الخادم في وضع "التفريغ" دون إعادة التشغيل.

# Exemple : mettre web2 en drain
echo "set server wp_backends/web2 state drain" | sudo socat stdio /run/haproxy/admin.sock

# Vérifier l'état
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | grep wp_backends

ثم تقوم بنشره على web2، وتعيده إلى وضع "الاستعداد":

echo "set server wp_backends/web2 state ready" | sudo socat stdio /run/haproxy/admin.sock

في حالات الطوارئ: إذا كنت تستخدم خاصية إبقاء الاتصال نشطًا عبر HTTP بشكل مكثف، فقد تستغرق عملية "التفريغ" وقتًا أطول من المتوقع (بسبب الاتصالات المستمرة). لذا، اضبط مهلة HAProxy وراقب الاتصالات النشطة. show stat قبل قطع الخدمة.

تصميم واقعي

يجب أن تتضمن بيئة الاختبار لديك ما يلي: HAProxy (أو على الأقل Nginx كخادم وكيل عكسي)، وRedis، ومشاركة الملفات المرفوعة. وإلا، فأنت بذلك تختبر سيناريو غير موجود في بيئة الإنتاج.

ترحيل قاعدة البيانات (مثال): إذا قمت بنسخ بيئة الإنتاج إلى بيئة الاختبار، فتذكر استبدال عناوين URL وتعطيل الفهرسة. WP-CLI:

# Sur staging
wp core version
wp search-replace 'https://www.example.com' 'https://staging.example.com' --all-tables --precise
wp option update blog_public 0

# Si vous avez un cache plugin, purge (commande selon plugin)
# wp cache flush (cache objet) :
wp cache flush

متغير بيئة الاختبار متعددة العقد: إذا كان لديك نطاق ثانٍ (للاختبار) خلف نفس خادم HAProxy، فتأكد من عدم مشاركة نفس ملفات تعريف الارتباط (النطاق/المسار) بين البيئات عن طريق الخطأ. عمليًا، تجنب ذلك. *.example.com بالنسبة لملفات تعريف الارتباط الخاصة بالمصادقة إذا كانت بيئة الاختبار موجودة على نطاق فرعي يمكن الوصول إليه من نفس المتصفحات.

وثائق WP-CLI: wp search-replace.

ملفات التكوين الكاملة

هابروكسي: /etc/haproxy/haproxy.cfg

إعداد كامل (يجب تعديل عناوين IP/الأسماء). يشمل: TLS، HTTP/2، إعادة توجيه HTTP→HTTPS، رؤوس الوكيل، خاصية التثبيت الاختيارية، وفحص الحالة. /lb-health.phpومسؤول المقبس للتصريف.

# /etc/haproxy/haproxy.cfg
# Configuration HAProxy pour WordPress multi-nœuds
# Testez avec : haproxy -c -f /etc/haproxy/haproxy.cfg

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon

    # Socket admin pour opérations runtime (drain, stats)
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s

    # TLS : ajustez selon votre politique
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384

defaults
    log global
    mode http
    option httplog
    option dontlognull
    option http-keep-alive
    option forwardfor header X-Forwarded-For
    timeout connect 5s
    timeout client  60s
    timeout server  60s

    # Retries prudents : évite de multiplier la charge sur backends lents
    retries 2

frontend fe_http
    bind :80
    http-request redirect scheme https code 301 unless { ssl_fc }

frontend fe_https
    bind :443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1

    # Indique à l'app que l'origine est HTTPS
    http-request set-header X-Forwarded-Proto https
    http-request set-header X-Forwarded-Port 443

    # HSTS (activez seulement si vous êtes sûr de votre HTTPS)
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # Limite basique sur les uploads géants (peut être ajustée)
    # Attention : certains builders importent des ZIP lourds
    http-request deny if { req.body_size gt 104857600 }

    default_backend wp_backends

backend wp_backends
    balance roundrobin

    # Cookie de stickiness (optionnel) :
    # Activez si vous avez des plugins qui dépendent d'un état local.
    cookie SRV insert indirect nocache

    # Health check HTTP applicatif
    option httpchk GET /lb-health.php
    http-check expect status 200

    # Transmettez l'IP backend (utile en debug)
    http-response set-header X-Backend %s

    # Backends (IP privées)
    server web1 10.0.10.11:80 check cookie web1 inter 2s fall 3 rise 2
    server web2 10.0.10.12:80 check cookie web2 inter 2s fall 3 rise 2

الحالات الاستثنائية لـ HAProxy التي أراقبها:

  • HTTP→HTTPS إذا قمت بوضع شبكة توصيل محتوى (CDN) أو جدار حماية تطبيقات الويب (WAF) أمامها، فقد يصبح التوجيه على جانب HAProxy زائداً عن الحاجة، أو حتى يدور في حلقة مفرغة إذا كنت تعتمد على رأس غير موثوق به. في هذه الحالة، قم بإجراء التوجيه على مستوى شبكة توصيل المحتوى، واترك HAProxy في وضع "الخمول".
  • حد req.body_size مفيدٌ ضد بعض أنواع التجاوزات، لكنه يُعطّل استيراد القوالب (Divi/Elementor) إذا لم يتم تعديله. أُفضّل تقييده حسب المسار (مثلاً، السماح بمزيد من العناصر في /wp-admin/ (من عناوين IP الداخلية/VPN) إذا كانت لديك سياسة صارمة.
  • فحص الصحة مقابل مخبأ : إذا كان لدى الواجهة الخلفية خادم Nginx يُرجع رمز الاستجابة 200 /lb-health.php ولكن بما أن PHP-FPM قد توقف، يجب أن يمر فحصك عبر PHP (كما هو الحال هنا)، وإلا فلن يحصل HAProxy على العقدة أبدًا.

مرجع HAProxy (الوثائق الرسمية): دليل تكوين HAProxy.

Nginx (عقد الويب): /etc/nginx/sites-available/example.com

مثال على استخدام Nginx لـ WordPress خلف HAProxy. النقاط الرئيسية: رؤوس البروكسي، عنوان IP حقيقي، قواعد FastCGI، وحظر لتقييد الوصول. /lb-health.php إلى HAProxy.

# /etc/nginx/sites-available/example.com
server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/public;
    index index.php;

    # IP réelle depuis HAProxy (adaptez au réseau)
    set_real_ip_from 10.0.0.0/8;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    # Health check : autorisez uniquement HAProxy
    location = /lb-health.php {
        allow 10.0.0.10;  # IP privée HAProxy
        deny all;
        include snippets/fastcgi-php.conf;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    # Fichiers statiques
    location ~* .(css|js|jpg|jpeg|png|gif|ico|webp|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, max-age=2592000, immutable";
        try_files $uri =404;
    }

    # WordPress
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # PHP
    location ~ .php$ {
        include snippets/fastcgi-php.conf;

        # Important : script filename correct
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # Timeouts (builders et imports peuvent être longs)
        fastcgi_read_timeout 120s;

        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    # Sécurité : bloquez l'accès à des fichiers sensibles
    location ~* /(wp-config.php|readme.html|license.txt) {
        deny all;
    }
}

ملاحظة: لقد وضعت php8.3-fpm.sock كمثال (PHP 8.1+). قم بتكييفه مع المقبس الفعلي.

الحالات الشاذة المتكررة في Nginx/PHP-FPM على الأنظمة متعددة العقد:

  • مسارات جذر المستند اختلافات بين العُقد (إحداها تُشير إلى إصدار، والأخرى إلى إصدار مختلف). النتيجة: أخطاء عشوائية من نوع 404/500. يُعدّ فرض بنية دليل موحدة ورابط رمزي ثابت الحل الأمثل من حيث التكلفة.
  • أقصى حجم للجسم : إذا قمت بتقييده من جانب Nginx (client_max_body_sizeأما من جانب HAProxy، فيجب عليك مواءمة كلا الأمرين. وإلا، ستضيع وقتك في تشخيص أخطاء 413 "أحيانًا".
  • IP الحقيقي : إذا قمت بالتفعيل real_ip_recursive دون تقييد صارم set_real_ip_from باستخدامك للخوادم الوكيلة، فإنك تفتح الباب أمام انتحال عناوين IP عبر الرؤوس.

ملف wp-config.php (مستخلصات من عدة عقد)

لا تنسخ مقتطفًا قديمًا من مدونة تعود لعام ٢٠١٨. بالنسبة لـ WordPress 6.9.4، حافظ على إعداداتك نظيفة ومتطابقة في جميع المواقع. بالنسبة للمواقع متعددة العقد: استخدم نفس القيم السرية (Salts)، وخادم وكيل عكسي HTTPS، وRedis، وتعطيل تعديل الملفات (اختياريًا).

<?php
// Extraits pertinents pour un WordPress derrière HAProxy (WP 6.9.4+)

// DB (exemple)
define('DB_NAME', 'wpdb');
define('DB_USER', 'wpdb_user');
define('DB_PASSWORD', 'change-me');
define('DB_HOST', '10.0.30.10');

// Clés/salts : identiques sur tous les nœuds (sinon déconnexions)
define('AUTH_KEY',         '...'); 
define('SECURE_AUTH_KEY',  '...');
define('LOGGED_IN_KEY',    '...');
define('NONCE_KEY',        '...');
define('AUTH_SALT',        '...');
define('SECURE_AUTH_SALT', '...');
define('LOGGED_IN_SALT',   '...');
define('NONCE_SALT',       '...');

// Détection HTTPS derrière reverse proxy
// Sécurité : ne faites confiance qu'à votre réseau interne (HAProxy)
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

// Désactive l'éditeur de fichiers en admin (réduit le risque)
define('DISALLOW_FILE_EDIT', true);

// Redis (si votre plugin/object-cache.php le supporte)
define('WP_REDIS_HOST', '10.0.40.10');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_TIMEOUT', 1);
define('WP_REDIS_READ_TIMEOUT', 1);

// Optionnel : si vous avez un réseau séparé pour Redis
// define('WP_REDIS_PASSWORD', '...');

// Forcer URLs si vous avez des incohérences (à manier avec précaution)
// define('WP_HOME', 'https://example.com');
// define('WP_SITEURL', 'https://example.com');

تفاصيل عملية حول هذه المقتطفات:

  • رسم الخرائط X-Forwarded-Proto$_SERVER['HTTPS'] هو بسيط عن قصد. أرى في كثير من الأحيان مقتطفات برمجية تفرض استخدام HTTPS "بغض النظر عن أي شيء"، مما يؤدي إلى تعطيل عمليات التحقق الداخلية من HTTP، أو واجهات سطر أوامر تقوم بتحميل ووردبريس خارج سياق الوكيل.
  • لا مجال للتفاوض بشأن استخدام نفس الملح. إذا كان لديك نظام تدوير سري، فقم بتنفيذه بطريقة منسقة عبر جميع العقد (نشر متزامن)، وإلا سيتم فصل الجميع "بشكل عشوائي".
  • بالنسبة لـ Redis، احرص على أن تكون مهلة الانتظار قصيرة. فبطء Redis أسوأ من توقفه التام: إذ يتسبب ذلك في "توقف" عمال PHP-FPM.

مرجع حول أمان الملفات وتحريرها: تصلب وورد.

ملف php.ini (أو ملف إعدادات PHP-FPM): حدود مُكيّفة مع أدوات البناء

غالباً ما ينمو Divi/Elementor/Avada memory_limit وأحجام التحميل. اضبطها دون الوقوع في فخ "شبكة الجيل الثاني في كل مكان".

; /etc/php/8.3/fpm/conf.d/99-wordpress.ini
; Réglages raisonnables pour WordPress + page builders
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120
max_input_time = 120
max_input_vars = 5000

; OpCache (à ajuster selon votre codebase)
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=2

حالة استثنائية في PHP-FPM: قم بضبط إعدادات مجموعة التخزين الخاصة بك (pm.max_children, pm.max_requestsمع (1) ذاكرة الوصول العشوائي الفعلية، (2) قاعدة البيانات. pm.max_children قد يؤدي "الاستغلال" إلى نقل المشكلة إلى MySQL (عدد كبير جدًا من الاتصالات) وتفاقم حالات انتهاء المهلة.

التحقق

عليك التحقق من 4 أشياء: TLS، والتوزيع، واتساق WordPress، والتبعيات (DB/Redis/NFS).

1) التحقق من صحة HAProxy

sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl restart haproxy
sudo journalctl -u haproxy -n 200 --no-pager

2) تحقق من التوجيه والخادم الخلفي الذي يتم خدمته

# Doit répondre 200 et afficher un header X-Backend (web1/web2)
curl -I https://example.com | sed -n '1,20p'

# Faites plusieurs fois : vous devez voir alterner X-Backend si pas de stickiness
for i in $(seq 1 10); do curl -sI https://example.com | grep -i '^x-backend'; done

3) تحقق من الفحوصات الصحية

# Depuis HAProxy (ou bastion), tester l'endpoint sur chaque nœud
curl -sS http://10.0.10.11/lb-health.php
curl -sS http://10.0.10.12/lb-health.php

4) تحقق من ووردبريس (WP-CLI)

# Sur chaque nœud web
cd /var/www/example.com/public
wp core version
wp option get home
wp option get siteurl
wp cache flush

5) تحقق من NFS (الملفات المرفوعة مرئية في كل مكان)

# Sur web1 : créer un fichier test
sudo -u www-data bash -lc 'echo test-$(hostname) > /var/www/example.com/wp-content/uploads/lb-test.txt'

# Sur web2 : doit voir le même fichier
sudo -u www-data cat /var/www/example.com/wp-content/uploads/lb-test.txt

6) تحقق من Redis

# Sur le serveur Redis
redis-cli PING

# Sur un nœud WP : selon plugin, vérifiez via WP-CLI ou page de statut.
# À défaut, test réseau :
nc -vz 10.0.40.10 6379

فحوصات إضافية أقوم بها دائمًا تقريبًا بعد عملية التحويل:

  • على مدار الساعة : timedatectl على جميع العُقد. قد يتسبب الانحراف في حدوث قيم عشوائية غير صالحة وانقطاعات في الاتصال.
  • سلسلة IP قارن عنوان IP الذي يظهر في ووردبريس (السجلات، إضافة الأمان) بعنوان IP الحقيقي الخاص بك. إذا كان كل شيء هو عنوان IP الخاص بـ HAProxy، فقد تأتي قواعد الحماية من هجمات القوة الغاشمة وقواعد تحديد معدل الطلبات بنتائج عكسية.
  • عمليات تحميل حقيقية عبر لوحة التحكم (وليس مجرد ملف نصي): اختبر صورة، ثم قم بإعادة إنشاء الصورة المصغرة إذا كان لديك مكون إضافي للتحسين.

إذا لم ينجح الأمر

أتبع هذا الترتيب دائمًا: الشبكة ← HAProxy ← Nginx ← PHP-FPM ← WordPress ← قاعدة البيانات/Redis/NFS. وإلا، ستضيع وقتك في "إصلاح" WordPress بينما المشكلة تكمن فقط في رأس بروكسي مفقود.

قائمة التحقق التشخيصية (الأوامر)

# 1) HAProxy écoute ?
sudo ss -lntp | grep -E ':80|:443'

# 2) HAProxy voit ses backends UP ?
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | grep wp_backends

# 3) Logs HAProxy
sudo journalctl -u haproxy -n 200 --no-pager

# 4) Nginx OK sur un backend ?
curl -I http://10.0.10.11 | sed -n '1,20p'
sudo journalctl -u nginx -n 200 --no-pager

# 5) PHP-FPM vivant ?
sudo systemctl status php8.3-fpm --no-pager
sudo tail -n 200 /var/log/php8.3-fpm.log

# 6) DB accessible depuis backend ?
mysql -h 10.0.30.10 -u wpdb_user -p -e "SELECT 1;"

# 7) NFS monté ?
mount | grep wp-content/uploads
dmesg | tail -n 50

أساليب التشخيص التي توفر الوقت في الحوادث "المتقطعة":

  • اختبر الواجهة الخلفية مباشرةً عن طريق إضافة رأسية. Host: (إذا كان لديك عدة مضيفات افتراضية): curl -sSI -H 'Host: example.com' http://10.0.10.11/.
  • تحقق من تشبع PHP-FPM (إذا كانت صفحة الحالة مفعلة) أو على الأقل سجلات "وصل الخادم إلى pm.max_children".
  • لتحديد سبب خطأ 504 من جانب HAProxy: تحقق مما إذا كان سبب انتهاء المهلة هو HAProxy (مهلة الخادم) أو Nginx (مهلة قراءة fastcgi). يختلف الإجراء التصحيحي المطلوب.

مخطط تشخيصي

عرض السبب المحتمل التحقق الحلول
إعادة توجيه متكررة من HTTP إلى HTTPS X-Forwarded-Proto غائب أو غير مُفسَّر curl -I https://example.com + رؤوس التفتيش إضافة X-Forwarded-Proto على جانب HAProxy وتعيين HTTPS في wp-config.php
404 صورة عشوائية لا تتم مشاركة الملفات المرفوعة بين العقد أنشئ ملفًا على web1، واقرأه على web2 قم بتثبيت NFS / التبديل إلى وحدة تخزين الكائنات، وتحقق من معرف المستخدم/معرف المجموعة.
تسجيل دخول المسؤول الذي "يسقط" أملاح مختلفة، ساعات غير متزامنة، مكون إضافي ذو حالة قارن wp-config.phpتحقق من بروتوكول NTP قم بتوحيد الإعدادات، وفعّل خاصية التثبيت في لوحة التحكم إذا لزم الأمر.
البوابة 504 انتهى الزمن قاعدة بيانات PHP-FPM مثقلة أو بطيئة سجلات Nginx/PHP-FPM، سجل الاستعلامات البطيئة اضبط PHP-FPM (pm.*)، و opcache، وحسّن قاعدة البيانات، وأضف ذاكرة تخزين مؤقتة.
المحتوى "الخاص" يُعرض علنًا تم تكوين ذاكرة التخزين المؤقت لبروتوكول HTTP بشكل خاطئ (تم تجاهل ملفات تعريف الارتباط) اختبر الصفحة في حالة تسجيل الدخول/عدم تسجيل الدخول، وقارن العناوين قم بتعطيل ذاكرة التخزين المؤقت لملفات تعريف الارتباط الخاصة بالمصادقة، ثم امسحها، وراجع قواعد FastCGI.
الأخطاء 401/403 /wp-json/ في مجال التحرير (المطورين) أرقام عشوائية غير صالحة (الساعة)، ذاكرة تخزين مؤقتة/وكيل على REST، قواعد جدار حماية تطبيقات الويب صارمة للغاية قارن الوقت، واختبر REST بدون ذاكرة تخزين مؤقتة، وسجلات جدار حماية تطبيقات الويب (WAF). مزامنة بروتوكول NTP، واستبعاد REST من ذاكرة التخزين المؤقت، وضبط قوائم التحكم بالوصول/تحديد معدل الطلبات.
عمليات التحميل التي تفشل "بشكل عشوائي" حدود غير متناسقة (HAProxy/Nginx/PHP)، وشبكة NFS غير مستقرة أعد إنتاج المشكلة باستخدام ملف كبير، تحقق من الأرقام 413/499/504. dmesg مواءمة الأحجام/المهل الزمنية، أو تثبيت نظام ملفات الشبكة (NFS)، أو تمرير الكائن

الأخطاء الشائعة والمزالق

بعض الأخطاء الشائعة التي أراها تتكرر، حتى بين الفرق ذات الخبرة:

  • نسخ إعدادات Nginx إلى المكان الخطأ (الموقع غير مُفعّل، هذا سيء) server_nameيفحص nginx -t والرابط في sites-enabled.
  • نسيان الفاصلة المنقوطة في wp-config.php وتعطيل عقدة واحدة فقط. النتيجة: أخطاء متقطعة تعتمد على التوزيع. انشر عبر التكامل المستمر وقم بـ php -l على كل عقدة.
  • اختبر مباشرة في بيئة الإنتاج بدون نسخ احتياطييجعل موازن الأحمال عملية التراجع أكثر صعوبة إذا قمت بتغيير مكونات متعددة في وقت واحد.
  • استخدم مقتطفًا قديمًا لفرض استخدام HTTPS، وهو ما لا يحترم إعدادات البروكسي الخاصة بك. مع ووردبريس 6.9.4، احتفظ بربط بسيط يعتمد على X-Forwarded-Proto وحصر الثقة في الشبكة الداخلية.
  • مخبأ عدواني : ذاكرة التخزين المؤقت /wp-admin, /wp-jsonأو تجاهل ملفات تعريف الارتباط wordpress_logged_in_*هذا تسريب محتمل للمحتوى.
  • لم يتم تحديث الروابط الدائمة بعد عملية الترحيل/التجريب، في الأنظمة متعددة العُقد، يظهر هذا الخطأ على شكل "يعمل على web1 ولا يعمل على web2" إذا لم تكن قواعد إعادة الكتابة متطابقة. تحقق من تطابق إعدادات Nginx، وأعد إنشائها عبر WP-CLI إذا لزم الأمر.

الأخطاء المرتبطة (الرسائل المحددة) التي ستراها في السجلات:

  • إنجن إكس : upstream timed out (110: Connection timed out) while reading response header from upstream (غالباً ما يكون PHP-FPM مثقلاً أو تكون قاعدة البيانات بطيئة).
  • PHP-FPM : server reached pm.max_children setting (مجموعة موارد صغيرة أو استعلامات بطيئة للغاية).
  • نواة/نظام الملفات غير المتصل : nfs: server ... not responding / stale file handle (تثبيت غير مستقر، إعادة تشغيل خادم NFS، مشكلة في الشبكة).
  • WordPress : أخطاء REST 401/403 عند تحرير الإجراءات (الساعة/NTP، ذاكرة التخزين المؤقت على REST، أو مكون إضافي أمني مفرط العدوانية).

مرجع مفيد حول REST (غالباً ما يكون مرتبطاً بالمطورين): دليل واجهة برمجة تطبيقات REST الخاصة بـ WordPress.

أمان الخادم

يؤدي موازنة الأحمال إلى زيادة مساحة السطح: المزيد من الأجهزة، والمزيد من المفاتيح، والمزيد من نقاط الفشل. إليك بعض الممارسات الملموسة (والقابلة للتحقق):

  • جدار الحماية قم بعرض المنفذين 80 و443 فقط على HAProxy. يجب ألا تكون عقد الويب عامة.
  • تقييد /lb-health.php إلى عنوان IP الخاص بـ HAProxy (Nginx) allow/deny (مع جدار الحماية). تصبح نقطة نهاية الصحة العامة هدفًا سهلًا للمهاجم.
  • رؤوس الأمان HSTS (إن وُجد)، وتطبيقات جانب الخادم/التطبيق حسب الحاجة. تجنب إضافة CSP عشوائيًا إذا كنت تستخدم Elementor/Divi/Avada دون اختباره مسبقًا.
  • أذونات : wp-config.php 640/600، المفاتيح خارج المستودع، وعدم الكتابة إلى الكود إذا قمت بالنشر في وضع القراءة فقط.
  • تحديثات على الأقل، قم بأتمتة عملية الكشف. ينشر ووردبريس التفاصيل في إصداراته. إصدارات ووردبريس.
  • مراقبة : مقاييس HAProxy (إحصائيات المقبس)، زمن استجابة قاعدة البيانات، معدل الخطأ 5xx، وتشبع PHP-FPM.

إجراءان لتعزيز أمان "الخادم" أضيفهما غالبًا إلى هذا النوع من الإعدادات:

  • عزل الشبكة الصارم لا يمكن الوصول إلى قاعدة البيانات/Redis/NFS إلا من الشبكة الخاصة لعقد الويب (مجموعات الأمان/iptables). في حال اختراق عقدة ويب، يجب الحد من الحركة الجانبية.
  • رمز للقراءة فقط إن أمكن، قم بتثبيت مجلد كود ووردبريس كقراءة فقط (أو على الأقل، قيّد صلاحيات الكتابة لمستخدم PHP). هذا يقلل بشكل كبير من تأثير إضافة RCE.

إذا كنت ترغب في التعمق أكثر، فتابع التغييرات الأساسية عبر Trac ونسخة GitHub: تتبع نظام WordPress الأساسي et wordpress-develop (GitHub).

الموارد

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

هل أحتاج إلى تفعيل خاصية التثبيت في ووردبريس؟

ليس بالضرورة. يعمل نظام ووردبريس الأساسي بشكل جيد بدون خاصية الاحتفاظ بالملفات إذا كانت عقدك عديمة الحالة تمامًا (نفس القيم السرية، نفس الكود، ملفات مشتركة). فعّلها مؤقتًا إذا كنت تشك في أن أحد الإضافات يستخدم جلسات PHP أو ملفات مؤقتة محلية، ثم عالج السبب الجذري.

لماذا ينقطع اتصال المسؤول الخاص بي بشكل عشوائي بعد المرور عبر HAProxy؟

الثلاثي الكلاسيكي: استخدام أنواع مختلفة من الملح بين العقد، أو عدم تزامن ساعات النظام (NTP)، أو عدم اتساق اكتشاف HTTPS (ملفات تعريف الارتباط الآمنة). تحقق wp-config.php متطابقة في كل مكان والتخطيط X-Forwarded-Proto.

هل يمكنني استخدام NFS لجميع محتويات ووردبريس؟

أتجنبه لأن كل wp-content (الإضافات/القوالب) في بيئة الإنتاج، باستثناء حالات محددة. يؤثر نظام ملفات الشبكة البطيء أو غير المستقر بشكل مباشر على وقت استجابة الخادم (TTFB). يُفضل نشر الكود محليًا (باستخدام rsync/artifact) ومشاركته فقط عند الضرورة. uploads (وربما دليل تخزين مؤقت إذا كنت تعرف السبب بالضبط).

هل تعمل ذاكرة التخزين المؤقت FastCGI مع Elementor/Divi/Avada؟

نعم، بالنسبة لحركة المرور العامة، بشرط استبعاد المستخدمين المسجلين ونقاط النهاية الحساسة بشكل صارم. يكمن الخطر في "إخفاء" استجابة مخصصة عبر ملفات تعريف الارتباط. اختبر دائمًا مع المستخدمين المسجلين وغير المسجلين، وعلى /wp-json/ et admin-ajax.php.

كيفية إزالة عقدة من المجموعة دون قطع الاتصال بالمستخدمين؟

استخدم الوضع استنزاف يتم تشغيل HAProxy عبر وقت تشغيل المقبس. لا تقبل العقدة اتصالات جديدة، ولكنها تسمح بإكمال الاتصالات الحالية. ثم تقوم بالنشر والتحقق والاستعادة. استعداد.

ماذا لو أصبحت قاعدة البيانات هي عنق الزجاجة بعد إضافة عقد الويب؟

فعّل تسجيل الاستعلامات البطيئة، ثم قِس الأداء، ثم حسّن: الفهارس، والاستعلامات، وذاكرة التخزين المؤقت للكائنات في Redis، وتقليل خيارات التحميل التلقائي. يساعد النسخ المتماثل في بعض أحمال العمل، لكن ووردبريس لا يفصل حركة مرور القراءة/الكتابة تلقائيًا: اختبر بدقة قبل إرسال عمليات القراءة إلى نسخة متماثلة.

كيف يمكننا التحقق من أن كل عقدة تحتوي على نفس الكود تمامًا؟

الطريقة الأكثر موثوقية هي النشر عبر ملف مضغوط (tar.gz موقّع) أو دليل مُرقّم الإصدارات مع روابط رمزية. ولتحديد المشاكل، قارن قيمة التجزئة التالية:

# Sur chaque nœud : hash d'un ensemble représentatif
cd /var/www/example.com/public
find wp-includes wp-admin -type f -maxdepth 2 -print0 | sort -z | xargs -0 sha256sum | sha256sum

اجتازت فحص الصحة الخاص بي بنجاح على الرغم من أن الموقع يعرض خطأ 500 في بعض الصفحات.

فحصك سطحي للغاية. اطلب فحص PHP وقاعدة البيانات وRedis (اختياري). احرص على عدم التسبب بأي آثار جانبية (مثل عدم وجود عمليات كتابة أو تخزين مؤقت). إذا واجهت أخطاء 500 على مسارات معينة، فراجع سجلات PHP-FPM وقم بتفعيل فحص أكثر تركيزًا على التطبيق مؤقتًا على مسار محدد.

هل هذا متوافق مع شبكة توصيل المحتوى (CDN)؟

نعم. عادةً ما يكون خادم توصيل المحتوى (CDN) أمام HAProxy (أو جدار حماية تطبيقات الويب WAF)، ويظل HAProxy نقطة دخولك الأصلية. تأكد من إدارة سلسلة عناوين IP بشكل صحيح، ولا تثق إلا بعناوين IP الخاصة بخادم توصيل المحتوى. X-Forwarded-For.

ما هو الخطأ الأكثر تكلفة الذي تراه في هذا النوع من الإعداد؟

إنّ وجود خلل في إعدادات ذاكرة التخزين المؤقت لبروتوكول HTTP، والتي تُقدّم المحتوى المُسجّل للزوار (أو العكس)، ليس مجرد خطأ برمجي، بل هو ثغرة أمنية. إذا لم تكن متأكدًا تمامًا من قواعدك، فابدأ بدون ذاكرة تخزين مؤقت لبروتوكول HTTP، ثم أضفها لاحقًا بحذر.