حزمة LAMP على Ubuntu 24.04 مع PHP-FPM
أنشئ حزمة LAMP كاملة على Ubuntu 24.04: Apache وMariaDB بمصادقة unix_socket، وPHP 8.3 مع PHP-FPM، ومضيف افتراضي قائم على الاسم، وHTTPS مجاني عبر Certbot.
ما الذي ستبنيه
حزمة LAMP هي أربعة مكوّنات مترابطة تعمل على خادم واحد يشغّل Ubuntu 24.04: Linux في الأساس، وApache الذي يستجيب لطلبات HTTP، وMariaDB الذي يخزّن البيانات، وPHP 8.3 الذي ينفّذ الشيفرة. بنهاية هذا الدليل سيكون لديك مضيف افتراضي (virtual host) قائم على الاسم يخدم دليل تطبيق حقيقي، وقاعدة بيانات بمستخدم مخصص محدود الصلاحيات، وPHP موصول بـ Apache عبر PHP-FPM، وشهادة Let's Encrypt مجانية فوق كل ذلك.
التثبيت نفسه لا يتجاوز أربعة أوامر apt. أما معظم محتوى هذا الدليل فهو الربط بين هذه الأجزاء، والمجموعة الصغيرة من الأخطاء التي تجعل حزمة جديدة كليًّا تعرض صفحة فارغة، أو تسلّم شيفرتك المصدرية إلى المتصفح كملف للتنزيل، أو ترفض السماح لك بالدخول إلى قاعدة البيانات التي ثبّتّها للتو. لكل واحد من هذه الأخطاء توقيع يمكنك التعرّف عليه، وكل واحد منها مذكور أدناه بالنص الدقيق الذي ستراه.
المتطلبات المسبقة والمزالق الصريحة
افترض أن لديك خادم VPS جديدًا كليًّا من نوع KVM يعمل بنظام Ubuntu 24.04، مع مستخدم يملك صلاحيات sudo أو حساب root، وعنوان IPv4 عام. الحزمة الدنيا تعمل ضمن 1 غيغابايت من ذاكرة RAM؛ لكن خصّص لها 2 غيغابايت قبل أن تضع عليها تطبيقًا حقيقيًا متصلًا بقاعدة بيانات، لأن الذاكرة المؤقتة الافتراضية لـ MariaDB مع حفنة من عمليات PHP-FPM العاملة تستهلك الغيغابايت الأول بسرعة.
ثمة أمران يجب أن يتحققا قبل أن يعمل Certbot في نهاية الدليل، فجهّزهما الآن. تحتاج إلى اسم نطاق (domain) بسجل A يشير إلى عنوان IP العام للخادم — إذ يتحقق Let's Encrypt عبر HTTP من ذلك الاسم، ومجرد عنوان IP لا يمكنه أبدًا الحصول على شهادة. كما يجب أن يكون المنفذان 80 و443 قابلين للوصول من الإنترنت، وهو ما يعني لدى كثير من مزوّدي الخدمة فتحهما في جدار حماية الشبكة الخاص بلوحة التحكم بالإضافة إلى فتحهما في ufw على الجهاز نفسه. قد تستغرق تغييرات DNS ما يصل إلى ساعة لتنتشر، لذا اضبط سجل A أولًا كي يكون جاهزًا وقت الحاجة إليه.
الخطوة 1 - ثبّت Apache وتأكد من الصفحة الافتراضية
sudo apt update
sudo apt install -y apache2يقوم apt بتشغيل الخدمة وتفعيلها نيابةً عنك. تحقّق من ذلك:
systemctl status apache2ما تريد رؤيته سطر يقول active (running). افتح الآن http://YOUR_SERVER_IP/ في متصفح. ظهور Apache2 Ubuntu Default Page بلافتتها الكبيرة "It works!" هو النتيجة الصحيحة — فهذا دليل على أن Apache يخدم الصفحات فعلًا، لا خطأ. تقع هذه الصفحة في /var/www/html/index.html وتُخدَّم عبر المضيف الافتراضي المرفق مع الحزمة 000-default.conf. ستعطّل الاثنين لاحقًا؛ أما الآن فوجودهما هو بالضبط ما تريد رؤيته.
إن لم تُحمَّل الصفحة إطلاقًا بينما يفيد systemctl بأن العملية تعمل، فإن جدار حماية يقف في الطريق. وهذا موضوع الخطوة التالية.
الخطوة 2 - افتح جدار الحماية لـ HTTP وHTTPS
تسجّل حزمة apache2 ثلاثة ملفات تعريف تطبيقات (profiles) في ufw. اعرضها:
sudo ufw app listسترى Apache وApache Full وApache Secure. يغطي Apache المنفذ 80 فقط، وApache Secure المنفذ 443 فقط، أما Apache Full فيغطي الاثنين معًا — وهذا ما تحتاج إليه، لأنك ستضيف TLS في نهاية الدليل.
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enableاسمح بـ OpenSSH قبل تنفيذ ufw enable. يرفض ufw افتراضيًا كل حركة المرور الواردة، وتفعيله بلا قاعدة لـ SSH يقطع اتصالك أنت بمجرد أن يُفعَّل — تحتفظ بالجلسة الحالية لكن لن تتمكن من إعادة الاتصال أبدًا. تحقق بالأمر sudo ufw status؛ وتأكد من أن OpenSSH وApache Full ونظيريهما لـ IPv6 جميعها تظهر بحالة ALLOW.
الخطوة 3 - ثبّت MariaDB وأمّنها
sudo apt install -y mariadb-server
systemctl status mariadbيأتي Ubuntu 24.04 بالإصدار MariaDB 10.11، وهو إصدار طويل الدعم (LTS)، فلا تحتاج إلى مستودع خارجي. وبعد أن تعمل الخدمة، عزّز أمانها:
sudo mysql_secure_installationيستحق الأمر قراءة الأسئلة لا الضغط على Enter مرارًا بلا تفكير. حين يطلب البرنامج كلمة مرور root الحالية، اضغط Enter — فلا توجد كلمة مرور بعد. وحين يسأل "Switch to unix_socket authentication?"، فالإجابة لا تُغيّر شيئًا لأن هذا مفعّل أصلًا في هذه الحزمة، فاضغط n. أجب بـ n عن "Change the root password?" للسبب الموضح في الفقرة التالية، ثم أجب بـ Y عن البقية: حذف المستخدمين المجهولين، ومنع تسجيل دخول root عن بُعد، وحذف قاعدة بيانات الاختبار، وإعادة تحميل جداول الصلاحيات.
وهنا الجزء الذي يربك الجميع. في MariaDB الخاص بـ Ubuntu، يستخدم حساب قاعدة البيانات root مصادقة unix_socket، لا كلمة مرور. ويعني ذلك أن قاعدة البيانات تثق بمستخدم نظام التشغيل الذي تحقّق النظام من هويتك عبره أصلًا. لذا ينجح هذا الأمر عند تنفيذه من shell بصلاحيات root:
sudo mysql...لينقلك مباشرة إلى محث MariaDB [(none)]> دون أن يُطلب منك أي كلمة مرور. أما التنفيذ نفسه من مستخدم غير مميّز فيُرفض، وهذا بيت القصيد كله: الوصول إلى root في قاعدة البيانات مربوط بـ sudo على الجهاز، ولا توجد كلمة مرور يمكن سرقتها أو صيدها بالتصيّد (phishing) أو كسرها بالقوة الغاشمة. هذا أكثر أمانًا من كلمة المرور، فلا تعبث به. والقاعدة المترتبة على ذلك: لا تُوجّه تطبيقًا أبدًا نحو حساب root. أنشئ مستخدمًا مخصصًا لكل تطبيق (الخطوة 7)، لأن أي تطبيق يتصل عبر TCP باسم مستخدم وكلمة مرور لا يمكنه أصلًا استخدام المصادقة عبر المقبس (socket)، ولأنك تريد أن يكون كل تطبيق مقتصرًا على قاعدة بياناته الخاصة.
الخطوة 4 - ثبّت PHP 8.3 مع PHP-FPM
إصدار PHP الافتراضي في Ubuntu 24.04 هو 8.3. ثبّت مدير العمليات FPM والامتدادات التي يحتاجها تطبيق نموذجي:
sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
php8.3-curl php8.3-xml php8.3-mbstring php8.3-zipلاحظ ما ليس في هذه القائمة: libapache2-mod-php. هذه الحزمة الأقدم تدمج مفسّر PHP داخل كل عملية Apache. الأمر بسيط، لكن كل عامل (worker) يحمل نسخة من PHP سواء كان يخدم سكربتًا أم صورة ثابتة، ويتشارك الاثنان دورة حياة واحدة، ولا يعمل هذا الأسلوب إلا مع وحدة معالجة Apache المتعددة (MPM) من نوع prefork — وهي الأقل كفاءة. أما PHP-FPM فيشغّل PHP بدلًا من ذلك بوصفه تجمّعًا (pool) مستقلًا من العمليات يتواصل معه Apache عبر المقبس. عندها يمكن لـ Apache استخدام وحدة event ذات الخيوط للملفات الثابتة وتمرير طلبات PHP فقط عبر المقبس، ويُضبط التجمّع بمعزل عن خادم الويب، ويعمل الإعداد نفسه تمامًا لـ FPM لاحقًا إن وضعت nginx في الواجهة بدلًا منه. هذا هو الخيار الافتراضي الحالي لسبب وجيه.
يصل Apache إلى FPM عبر وحدة proxy_fcgi. فعّلها، وفعّل الإعداد الذي أضافته حزمة FPM، ثم أعد التشغيل:
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2يُفعّل الأمر a2enconf php8.3-fpm الملف /etc/apache2/conf-available/php8.3-fpm.conf، الذي يحتوي على القاعدة التي توجّه ملفات PHP إلى مقبس FPM. وجوهر هذه القاعدة أنه يطابق أي ملف .php ويحيله إلى المقبس عند /run/php/php8.3-fpm.sock:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>لا تُعدّل هذا الملف؛ فهو صحيح بصيغته الافتراضية. لكن معرفة مسار المقبس هي ما يتيح لك لاحقًا تشخيص عطل "تنزيل PHP بدلًا من تنفيذه" وعطل "Primary script unknown" — فكلاهما يعود إلى اختلاف بين Apache وFPM حول هذا المقبس أو الملف الذي خلفه.
الخطوة 5 - مضيف افتراضي قائم على الاسم لتطبيقك
تتيح الاستضافة الافتراضية القائمة على الاسم لعنوان IP واحد خدمة مواقع متعددة؛ إذ يختار Apache الموقع بحسب ترويسة Host: في الطلب. أنشئ دليلًا للتطبيق، بعيدًا تمامًا عن /var/www/html الافتراضي:
sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testappملكية الملفات تهم هنا. يعمل كل من Apache وPHP-FPM بمستخدم www-data على Ubuntu، لذا يجب أن تكون الملفات التي يحتاج خادم الويب إلى قراءتها — والأدلة التي يحتاج التطبيق إلى الكتابة فيها، مثل مجلد الرفع (uploads) — مملوكة لـ www-data. وإذا كنت ستُعدّل الملفات أيضًا بمستخدم تسجيل دخولك، فالنمط الشائع أن تمتلك الملفات أنت وتضيف مستخدمك إلى مجموعة www-data؛ أما في نشر بسيط فإن www-data:www-data هو الخيار الأقل مفاجأة.
أنشئ المضيف الافتراضي في /etc/apache2/sites-available/testapp.conf:
<VirtualHost *:80>
ServerName app.example.com
DocumentRoot /var/www/testapp
<Directory /var/www/testapp>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/testapp-error.log
CustomLog ${APACHE_LOG_DIR}/testapp-access.log combined
</VirtualHost>اضبط ServerName على نطاقك الحقيقي. يمنع Options -Indexes قيام Apache بعرض محتويات الدليل حين لا يوجد ملف فهرس — وإلا تصفّح الزوّار شجرة شيفرتك المصدرية. ويتيح AllowOverride All عمل ملف .htaccess، وهو ما تتوقعه معظم تطبيقات PHP من أجل روابط أنيقة؛ اجعلها None لمكسب طفيف في السرعة إن كان تطبيقك لا يحتاجها. فعّل هذا الموقع، وعطّل الموقع الافتراضي، وتحقق من الإعداد، ثم أعد التحميل:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2يجب أن يطبع apache2ctl configtest العبارة Syntax OK. وسطر a2dissite 000-default هو ما ينساه أغلب الناس، وهو سبب ظهور الصفحة الافتراضية لاحقًا وكأنها عالقة — وهذا مشروح في قسم أنماط الفشل.
الخطوة 6 - أثبت أن PHP يعمل، ثم احذف الدليل
ضع ملف PHP من سطر واحد في جذر التطبيق:
echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.phpزر http://app.example.com/info.php. النتيجة الصحيحة هي الجدول الطويل باللونين البنفسجي والرمادي PHP Version 8.3.x الذي يسرد الوحدات المحمَّلة لديك، مع سطر Server API الذي تكون قيمته FPM/FastCGI. يؤكد هذا السطر الأخير أن الطلبات تمر عبر PHP-FPM، لا mod_php.
احذفه الآن على الفور:
sudo rm /var/www/testapp/info.phpتكشف الدالة phpinfo() إصدار PHP الدقيق لديك، وكل امتداد محمَّل، ومسارات الملفات، وتفاصيل البيئة — وهي هدية لأي شخص يتحسس الخادم بحثًا عن إصدار به ثغرة معروفة. هذه أداة اختبار، لا ميزة. احذفها فور رؤيتك الصفحة. وإذا عرض المتصفح تنزيل info.php بدلًا من الجدول، فهذا يعني أن PHP غير موصول بـ Apache؛ انتقل إلى قسم أنماط الفشل قبل أن تفعل أي شيء آخر.
الخطوة 7 - أنشئ قاعدة بيانات التطبيق ومستخدمًا محدود الصلاحيات
افتح قاعدة البيانات بحساب root الموثّق عبر المقبس:
sudo mysqlثم أنشئ قاعدة بيانات واحدة ومستخدمًا واحدًا مقصورًا على هذه القاعدة بالذات:
CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;ثمة ثلاثة خيارات متعمّدة هنا. utf8mb4 هو ترميز UTF-8 حقيقي بأربعة بايتات — أما الاسم المستعار القديم utf8 فيبتر بصمت رموز الإيموجي وبعض أحرف الصينية واليابانية والكورية (CJK)، لذا استخدم دائمًا utf8mb4. والمنح هنا على appdb.*، لا على *.*: أي أن هذا المستخدم يستطيع لمس قاعدة بياناته فقط ولا شيء غيرها، فثغرة حقن SQL في التطبيق لا تستطيع قراءة جداول كل موقع آخر. أما 'appuser'@'localhost' فيقصر الحساب على الاتصالات الصادرة من الجهاز نفسه.
اختبره بهذا المستخدم:
mysql -u appuser -p appdbيطلب الأمر كلمة المرور وينقلك إلى محث MariaDB [appdb]>. لاحظ غياب الخيار -h — فاتركه من دون ذكر، ويتصل العميل عندها عبر مقبس Unix المحلي، وهو بالضبط ما تعدّه MariaDB مطابقًا لـ localhost. مزلق يستحق معرفته: بالنسبة إلى MySQL وMariaDB، يعني localhost مقبس Unix، بينما يعني 127.0.0.1 اتصال TCP. في MariaDB قياسية على Ubuntu 24.04 لا يزال الخادم يحلّ اتصال TCP القادم من 127.0.0.1 إلى localhost، فيتطابق الاثنان مع الحساب — لكن على الخوادم التي يُفعَّل فيها skip-name-resolve (وهو تعديل شائع لتحسين الأداء، والمعيار في كثير من صور الحاويات)، يُعامَل الاثنان بوصفهما مضيفين مختلفين، ويُرفض التطبيق الذي يتصل بـ 127.0.0.1 برسالة ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) حتى لو كانت كلمة المرور صحيحة.
لذا وجّه تطبيقك إلى المضيف localhost، والمستخدم appuser، وقاعدة البيانات appdb — لا إلى root أبدًا. يتحول كل من mysqli وPDO في PHP إلى مقبس Unix حين يكون المضيف السلسلة الحرفية localhost، فيتطابقان مع الحساب الذي أنشأته للتو. وإذا أصرّ إطار عمل (framework) على مضيف TCP رقمي، فأنشئ المستخدم بما يطابق طريقة اتصاله الفعلية — 'appuser'@'127.0.0.1'، أو @'%' (مقترنًا بقاعدة جدار حماية) فقط إذا كان عليه الوصول إلى قاعدة البيانات من جهاز آخر.
الخطوة 8 - أضف HTTPS باستخدام Certbot
خدمة نموذج تسجيل دخول عبر HTTP العادي ترسل كلمات المرور بنص واضح، وكل متصفح حديث يضع على الصفحة علامة "Not secure". يصلح Certbot ذلك بأمر واحد. ثبّته مع إضافة Apache:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheيستخدم Certbot هنا إضافتين (plugins). يثبت مصادِق apache (apache authenticator) أنك تتحكم بالنطاق بخدمة ملف تحدٍّ (challenge) لفترة وجيزة عبر Apache العامل لديك، ثم يعيد مثبِّت apache (apache installer) كتابة مضيفك الافتراضي لإضافة كتلة 443، ويوجّهها إلى الشهادة الجديدة، ويعيد توجيه كل حركة مرور HTTP إلى HTTPS افتراضيًا — فمنذ Certbot 2.0 لم يعد هناك سؤال عن إعادة التوجيه؛ مرّر --no-redirect إن احتجت إلى إبقاء الخدمة على HTTP العادي. ولأنك ضبطت ServerName حقيقيًا في الخطوة 5، يكتشف Certbot النطاق تلقائيًا. تدوم الشهادات 90 يومًا، وتُثبّت الحزمة مؤقّت (timer) في systemd يجدّدها؛ تحقق من المؤقّت بالأمر sudo certbot renew --dry-run، الذي ينبغي أن ينتهي بالعبارة Congratulations, all simulated renewals succeeded.
للاطلاع على الشرح الكامل للتحدي، ومؤقّت التجديد، ومتطلبات DNS وجدار الحماية، راجع الدليل المرافق حول إصدار شهادات Let's Encrypt TLS مجانية باستخدام Certbot على Apache.
النسخ الاحتياطي، والترقيات، والتحصين
خذ نسخة احتياطية من الشيئين اللذين يحملان حالة نظامك: قواعد البيانات وجذر الويب. أبسط طريقة موثوقة هي تفريغ منطقي (dump) ليلي — sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz'، ثم يُنسخ خارج الجهاز. لفّ خط الأنابيب كله داخل sudo sh -c أمر مهم: فمن دونه ينفّذ shell إعادة التوجيه > /root/... بصلاحيات مستخدمك أنت وتفشل برسالة Permission denied، لأن mysqldump وحده هو من ورث صلاحيات sudo. يمنح --single-transaction لقطة (snapshot) متسقة من جداول InnoDB دون قفلها. اقرن ذلك بأمر tar لـ /var/www و/etc/apache2/sites-available، وستتمكن من إعادة بناء الحزمة بأكملها على خادم VPS جديد اعتمادًا على هذه الملفات.
الترقيات هي مجرد sudo apt update && sudo apt upgrade عادي. أما الذي يستحق الحذر فهو قفزة في إصدار PHP — فحين ينقل إصدار مستقبلي من Ubuntu الافتراضي إلى PHP 8.4، قد يثبّت apt الحزمة php8.4-fpm جنبًا إلى جنب مع 8.3، ويصبح المقبس /run/php/php8.4-fpm.sock، بينما يظل إعداد Apache لديك مشيرًا إلى مقبس 8.3. فعّل الإعداد الجديد (sudo a2enconf php8.4-fpm) وعطّل القديم، وإلا بدأ موقعك بإرجاع Primary script unknown بعد ترقية كانت روتينية في ظاهرها. ولأن إصدارات PHP تتغير أسرع من توزيعة LTS، راجع ملاحظات إصدار PHP الحالية بدلًا من تثبيت رقم إصدار فرعي معيّن.
ثمة خطوتا تحصين تستحقان التنفيذ منذ اليوم الأول. أولًا، ضع Fail2ban مراقبًا لـ SSH على الجهاز — فخادم VPS العام يتلقى محاولات تسجيل دخول آلية خلال دقائق، والسجن (jail) الصغير يحوّل آلاف المحاولات إلى حفنة قبل وقوع الحظر. ثانيًا، إذا كنت تفضّل إدارة مضيفات Apache الافتراضية، وقواعد بيانات MariaDB، والمستخدمين عبر متصفح بدلًا من تعديل الملفات يدويًا، فإن لوحة تحكم Webmin القائمة على الويب تعمل فوق هذه الحزمة بعينها وتدير ملفات الإعداد ذاتها التي كتبتها للتو. لا يغني أي منهما عن فهم الأجزاء، لكن كليهما يقلّل الاحتكاك اليومي.
أنماط الفشل، مع النصوص التي ستراها
الصفحة الافتراضية لا تختفي. عدّلت مضيفك الافتراضي، وأعدت التحميل، ولا يزال المتصفح يعرض "Apache2 Ubuntu Default Page" ولافتتها "It works!". يخدم Apache أول مضيف افتراضي مطابق، وحين لا يطابق أي ServerName الطلب، يفوز الإعداد الأول أبجديًا — إذ يسبق 000-default.conf ترتيبيًا testapp.conf. فإما أن اسم المضيف في الطلب لا يطابق ServerName لديك، أو أنك لم تنفّذ قط sudo a2dissite 000-default. عطّل الموقع الافتراضي، ونفّذ sudo systemctl reload apache2، وتأكد بالأمر apache2ctl -S، الذي يطبع خريطة المضيفات الافتراضية ويوضّح أي إعداد يملك الموقع الافتراضي. امسح ذاكرة تخزين المتصفح المؤقتة أيضًا؛ فاستجابة 200 محفوظة من الصفحة القديمة قد تستمر في الظهور لفترة طويلة.
ملف .php يُنزَّل بدلًا من أن يُنفَّذ. تفتح info.php فيُنزّل المتصفح ملفًا يحوي شيفرة <?php الخام، أو يعرضها كنص عادي، بدلًا من تنفيذها. يخدم Apache الملف كملف ثابت لأن معالج PHP غير مرتبط به — إما أنك تجاوزت sudo a2enmod proxy_fcgi، أو sudo a2enconf php8.3-fpm، أو لم تُعد تشغيل Apache بعدهما. نفّذ الأوامر الثلاثة (الخطوة 4) وأعد التحميل. تأكد من تحميل الوحدة بالأمر apache2ctl -M | grep fcgi، الذي يجب أن يسرد proxy_fcgi_module. هذا تسريب للشيفرة المصدرية، لا عيب شكلي، فأصلحه قبل وضع أي شيء حقيقي على الخادم.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. نفّذت mysql -u root أو mariadb -u root من دون sudo. يستخدم حساب root مصادقة unix_socket، فلا يقبلك إلا حين يكون مستخدم نظام التشغيل لديك هو root فعلًا. الحل هو sudo mysql — من دون -u root، ومن دون كلمة مرور. هذه الرسالة هي السلوك المتوقع لمصادقة المقبس حين تعمل بشكل صحيح، لا تثبيتًا معطوبًا.
ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' من التطبيق، مع أن كلمة المرور صحيحة. الحساب موجود بصيغة 'appuser'@'localhost'، لكن تطبيقك يتصل عبر TCP بـ 127.0.0.1 على خادم معطَّل فيه تحليل أسماء المضيفين (skip-name-resolve)، فتعامل MariaDB الاثنين على أنهما مضيفان مختلفان — localhost هو مقبس Unix، و127.0.0.1 هو TCP. وجّه التطبيق إلى المضيف localhost كي يستخدم المقبس ويطابق الحساب، أو أنشئ حسابًا ثانيًا 'appuser'@'127.0.0.1' إن كان إطار العمل لا يتحدث إلا TCP.
AH01071: Got error 'Primary script unknown' في /var/log/apache2/testapp-error.log، مع عرض المتصفح File not found.. سلّم Apache الطلب إلى PHP-FPM، لكن FPM لم يجد السكربت في المسار الذي أعطاه إياه Apache. السببان المعتادان: إما أن مقبس FPM في إعدادك يشير إلى إصدار PHP غير مثبَّت (مقبس php8.4 بعد ترقية بينما لا يعمل سوى 8.3)، أو أن الملف غير موجود فعلًا لأن DocumentRoot والدليل الحقيقي لا يتطابقان. تحقق من وجود المقبس بالأمر ls -l /run/php/، وتأكد من أن DocumentRoot يطابق مكان الملف، وأعد تشغيل كل من php8.3-fpm وapache2.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name عند كل إعادة تشغيل. هذا تحذير غير ضار، لا خطأ — إذ يخبرك Apache بأنه لا يوجد ServerName عام مضبوط. أسكِته بكتابة ServerName your.domain في /etc/apache2/conf-available/servername.conf وتنفيذ sudo a2enconf servername.
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 عند بدء تشغيل Apache. خادم ويب آخر يحتل المنفذ 80 بالفعل — وغالبًا ما يكون nginx تائهًا من تجربة سابقة. اعثر عليه بالأمر sudo ss -ltnp | grep :80، ثم أوقف الخدمة الأخرى وعطّلها قبل تشغيل Apache.
FAQ
mod_php أم PHP-FPM: أيّهما يجب أن أستخدم؟
استخدم PHP-FPM. يدمج mod_php مفسّرًا داخل كل عملية Apache ويفرض وحدة prefork البطيئة، فيتحمّل Apache عبء PHP حتى عند خدمة صورة ثابتة. أما PHP-FPM فيشغّل PHP بوصفه تجمّعًا منفصلًا يُضبط بمعزل عن الخادم ويصل إليه Apache عبر مقبس، ويعمل مع وحدة event الأسرع ذات الخيوط، وينتقل دون تغيير إلى nginx لاحقًا. هذا هو الخيار الافتراضي الحديث؛ ولا معنى لاستخدام mod_php إلا لتطبيق قديم يعتمد على سلوك ما داخل العملية نفسها.
لماذا يُنزّل متصفحي ملف PHP بدلًا من تنفيذه؟
يعامل Apache ملف .php كتنزيل ثابت لأنه لا يوجد معالج PHP مرتبط به. على Ubuntu 24.04 مع FPM يعني هذا أنك فوّت واحدًا من sudo a2enmod proxy_fcgi، أو sudo a2enconf php8.3-fpm، أو إعادة تشغيل Apache بعدهما. نفّذ الأوامر الثلاثة وأعد التحميل، ثم تحقق بالأمر apache2ctl -M | grep fcgi من إدراج proxy_fcgi_module. وإلى أن تصلح ذلك يظل الخادم يسرّب الشيفرة المصدرية، فعامله بوصفه أمرًا عاجلًا.
لماذا يُرفض وصول root في MariaDB حتى مع كلمة المرور الصحيحة؟
لأنه لا توجد كلمة مرور أصلًا — تصادق MariaDB الخاصة بـ Ubuntu حساب root عبر unix_socket، رابطةً إياه بمستخدم root في نظام التشغيل. تُرجع mysql -u root من shell عادي رسالة ERROR 1698 (28000): Access denied for user 'root'@'localhost' بحكم التصميم. اتصل بدلًا من ذلك بالأمر sudo mysql، وأنشئ مستخدمًا منفصلًا موثّقًا بكلمة مرور لأي تطبيق بدلًا من إعادة استخدام root.
كيف أضيف HTTPS إلى موقع LAMP الخاص بي؟
ثبّت certbot وpython3-certbot-apache، ووجّه سجل A لنطاقك نحو الخادم، ثم نفّذ sudo certbot --apache. يثبت مصادِق apache التحكم بالنطاق عبر Apache العامل لديك، ويعيد المثبِّت كتابة المضيف الافتراضي للمنفذ 443 ويهيّئ التجديد التلقائي. ويغطي الشرح الكامل لـ Certbot وApache التحدي، ومؤقّت التجديد، وأنماط الفشل الشائعة.