نصب LAMP stack روی Ubuntu 24.04 با PHP-FPM
آموزش کامل نصب Apache، MariaDB و PHP 8.3 روی Ubuntu 24.04. یادگیری تنظیم vhost و HTTPS با Certbot برای جلوگیری از نمایش مستقیم کدهای منبع در مرورگر.
آنچه میسازید
یک LAMP stack شامل چهار بخش در یک سرور Ubuntu 24.04 است: Linux به عنوان پایه، Apache برای پاسخگویی به HTTP، MariaDB برای ذخیره دادهها، و PHP 8.3 برای اجرای کدها. در پایان، شما یک virtual host مبتنی بر نام خواهید داشت که یک دایرکتوری اپلیکیشن واقعی را سرو میکند، یک پایگاه داده با یک کاربر اختصاصی با حداقل دسترسی (least-privilege)، اتصال PHP به Apache از طریق PHP-FPM، و یک گواهی رایگان Let's Encrypt.
فرآیند نصب شامل چهار دستور apt است. تقریباً تمام محتوای این راهنما مربوط به نحوه اتصال این اجزا به یکدیگر است؛ همچنین شامل مجموعهای از خطاهای رایج است که باعث میشود یک stack تازه نصب شده، یک صفحه خالی نمایش دهد، کد منبع را به صورت فایل قابل دانلود به مرورگر تحویل دهد، یا اجازه ورود به پایگاه داده تازه نصب شده را ندهد. هر یک از این خطاها نشانههای مشخصی دارند و هر کدام در ادامه با متن دقیق همان خطا معرفی شدهاند.
پیشنیازها و نکات مهم
فرض کنید یک VPS تازه با سیستمعامل Ubuntu 24.04 و تکنولوژی KVM دارید که یک کاربر sudo یا root دارد و دارای یک IPv4 عمومی است. یک پشته (stack) حداقلی در 1 GB RAM اجرا میشود؛ قبل از نصب یک اپلیکیشن واقعی که به پایگاه داده متصل است، 2 GB رم اختصاص دهید، زیرا بافرهای پیشفرض MariaDB به همراه تعدادی از PHP-FPM workers، سریعاً اولین گیگابایت را مصرف میکنند.
دو مورد زیر باید برقرار باشند تا Certbot در مرحله نهایی کار کند، پس از همین حالا آنها را آماده کنید. شما به یک domain name با یک A record نیاز دارید که به IP عمومی VPS اشاره کند؛ Let's Encrypt از طریق HTTP روی آن نام اعتبارسنجی میکند و یک IP خالی هرگز نمیتواند گواهی دریافت کند. همچنین پورتهای 80 and 443 باید از طریق اینترنت قابل دسترسی باشند؛ در بسیاری از سرویسدهندگان، این به معنای باز کردن آنها در فایروال شبکه در پنل کاربری و همچنین در ufw روی سرور است. تغییرات DNS ممکن است تا 1 ساعت طول بکشد تا منتشر شوند، بنابراین ابتدا A record را تنظیم کنید تا تا زمان مورد نیاز، فعال شده باشد.
Step 1 - Install Apache and confirm the default page
sudo apt update
sudo apt install -y apache2apt سرویس را برای شما اجرا و فعال میکند. وضعیت آن را بررسی کنید:
systemctl status apache2باید خطی شامل active (running) مشاهده کنید. اکنون http://YOUR_SERVER_IP/ را در یک مرورگر باز کنید. مشاهده Apache2 Ubuntu Default Page با بنر بزرگ "It works!" نتیجه صحیح است؛ این موضوع نشاندهنده فعال بودن Apache است و یک خطا نیست. این صفحه در آدرس /var/www/html/index.html قرار دارد و توسط virtual host پیشفرض و همراه با نصب، یعنی 000-default.conf، ارائه میشود. شما بعداً هر دو را غیرفعال خواهید کرد؛ در حال حاضر، مشاهده آنها دقیقاً همان چیزی است که نیاز دارید.
اگر صفحه اصلاً بارگذاری نمیشود اما systemctl نشان میدهد که فرآیند در حال اجرا است، یک فایروال مانع دسترسی شده است. مرحله بعدی مربوط به این مورد است.
Step 2 - باز کردن فایروال برای HTTP و HTTPS
بسته apache2 سه پروفایل اپلیکیشن 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قبل از اجرای ufw enable، اجازه دسترسی به OpenSSH را صادر کنید. حالت پیشفرض ufw مسدود کردن تمام ترافیک ورودی است؛ فعال کردن آن بدون داشتن یک rule برای SSH باعث قطع شدن اتصال خودتان در لحظه فعالسازی میشود — نشست (session) فعلی حفظ میشود اما دیگر نمیتوانید مجدداً متصل شوید. با دستور sudo ufw status وضعیت را تایید کنید؛ باید وضعیت OpenSSH، Apache Full و معادلهای v6 آنها همگی روی ALLOW باشد.
Step 3 - Install MariaDB and secure it
sudo apt install -y mariadb-server
systemctl status mariadbنسخه Ubuntu 24.04 دارای MariaDB 10.11 است که یک نسخه با پشتیبانی طولانیمدت (LTS) میباشد؛ بنابراین نیازی به مخزن (repository) خارجی ندارید. پس از اجرای سرویس، آن را ایمن کنید:
sudo mysql_secure_installationبه جای فشردن مداوم کلید Enter، بهتر است متن پرسشها را بخوانید. وقتی از شما current root password را میخواهد، Enter را فشار دهید؛ زیرا هنوز پسوردی وجود ندارد. وقتی میپرسد "Switch to unix_socket authentication?"، پاسخ شما تغییری ایجاد نمیکند زیرا این قابلیت در این بسته از قبل فعال است، پس n را فشار دهید. برای عبارت "Change the root password?" به دلیل ذکر شده در پاراگراف بعدی، پاسخ n را بدهید، سپس برای سایر موارد پاسخ Y را انتخاب کنید: حذف کاربران ناشناس (anonymous users)، عدم اجازه ورود root از راه دور، حذف پایگاه داده test و بازنشانی جداول دسترسی (privilege tables).
این بخش باعث سردرگمی همه میشود. در MariaDB نسخه Ubuntu، حساب کاربری پایگاه داده root از احراز هویت unix_socket استفاده میکند، نه پسورد. این یعنی پایگاه داده به همان کاربر operating-system که قبلاً با آن احراز هویت کردهاید، اعتماد میکند. بنابراین این دستور از طریق یک shell با دسترسی root اجرا میشود:
sudo mysql...و شما را به یک prompt از نوع MariaDB [(none)]> میبرد بدون اینکه پسوردی از شما خواسته شود. اجرای همین دستور توسط یک کاربر بدون سطح دسترسی (unprivileged) رد میشود که هدف اصلی همین است: دسترسی به root پایگاه داده به sudo در سیستم متصل است و پسوردی برای سرقت، فیشینگ یا حمله brute-force وجود ندارد. این روش نسبت به پسورد امنیت بیشتری دارد، پس آن را تغییر ندهید. قاعده استخراج شده از این موضوع این است: هرگز یک اپلیکیشن را به حساب کاربری root متصل نکنید. برای هر اپلیکیشن یک کاربر اختصاصی بسازید (Step 7)؛ زیرا اپلیکیشنهایی که از طریق TCP با نام کاربری و پسورد متصل میشوند، نمیتوانند از socket auth استفاده کنند و شما میخواهید دسترسی هر اپلیکیشن فقط به پایگاه داده خودش محدود باشد.
Step 4 - Install PHP 8.3 with 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's prefork MPM کار میکند که کمبازدهترین حالت است. در مقابل، PHP-FPM از PHP به عنوان یک pool از فرآیندها استفاده میکند که Apache از طریق یک socket با آنها در ارتباط است. در این حالت، Apache میتواند از threaded event MPM برای فایلهای استاتیک استفاده کند و فقط درخواستهای PHP را به FPM ارجاع دهد. این pool به صورت مستقل از وبسرور تنظیم میشود و اگر بعداً از nginx در مقابل استفاده کنید، دقیقاً همان تنظیمات FPM کار خواهد کرد. این روش به دلایل فنی به حالت پیشفرض تبدیل شده است.
Apache از طریق ماژول proxy_fcgi به FPM متصل میشود. آن را فعال کنید، تنظیمات ارائه شده توسط بسته FPM را فعال کنید و سرویس را restart کنید:
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2a2enconf php8.3-fpm باعث فعالسازی /etc/apache2/conf-available/php8.3-fpm.conf میشود که شامل قانونی برای هدایت فایلهای PHP به socket مربوط به FPM است. هسته اصلی این بخش، هر فایل .php را شناسایی کرده و آن را به socket در مسیر /run/php/php8.3-fpm.sock ارسال میکند:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>شما نیازی به ویرایش آن فایل ندارید؛ تنظیمات پیشفرض صحیح هستند. اما دانستن مسیر socket به شما کمک میکند تا خطاهای "PHP downloads instead of running" و "Primary script unknown" را عیبیابی کنید؛ هر دو خطا ناشی از عدم تطابق Apache و FPM در مورد این socket یا فایل مربوط به آن هستند.
Step 5 - یک Virtual Host مبتنی بر نام برای اپلیکیشن شما
Virtual hosting مبتنی بر نام اجازه میدهد یک IP به چندین سایت سرویس دهد؛ Apache سایت مورد نظر را از طریق header مربوط به 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مالکیت فایلها اهمیت دارد. در Ubuntu، هر دو سرویس Apache و PHP-FPM با کاربر www-data اجرا میشوند؛ بنابراین فایلهایی که وبسرور باید بخواند — و دایرکتوریهایی که اپلیکیشن باید در آنها فایل بنویسد (مانند پوشه uploads) — باید متعلق به www-data باشند. اگر قصد دارید فایلها را با کاربر شخصی خود ویرایش کنید، یک الگوی رایج این است که مالکیت فایلها را بر عهده خودتان بگذارید و کاربر خود را به گروه www-data اضافه کنید؛ برای یک deployment ساده، استفاده از www-data:www-data کمترین پیچیدگی را دارد.
Virtual host را در مسیر /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 در صورت نبود فایل index جلوگیری میکند — در غیر این صورت بازدیدکنندگان میتوانند ساختار پوشههای منبع شما را مرور کنند. تنظیم AllowOverride All اجازه میدهد یک فایل .htaccess کار کند، که اکثر اپلیکیشنهای PHP برای Pretty URLs به آن نیاز دارند؛ اگر اپلیکیشن شما به آن نیاز ندارد، برای افزایش جزئی سرعت، آن را در None قرار دهید. این سایت را فعال کنید، سایت پیشفرض را غیرفعال کنید، تنظیمات را بررسی کنید و سرویس را reload کنید:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2دستور apache2ctl configtest باید خروجی Syntax OK را چاپ کند. خط a2dissite 000-default همان موردی است که کاربران فراموش میکنند و دلیل اینکه صفحه پیشفرض بعداً ثابت میماند (stuck) همین است — این مورد در بخش failures توضیح داده شده است.
Step 6 - Prove PHP runs, then delete the proof
یک فایل PHP تکخطی در root اپلیکیشن ایجاد کنید:
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، تمام افزونههای بارگذاری شده، مسیرهای فایل و جزئیات محیطی شما را فاش میکند؛ این اطلاعات برای هر کسی که در حال جستجوی سرور برای یافتن نسخهای با حفره امنیتی شناخته شده است، یک هدیه محسوب میشود. این یک تست است، نه یک قابلیت. به محض مشاهده صفحه، آن را حذف کنید. اگر به جای جدول، مرورگر شما پیشنهاد download فایل info.php را داد، یعنی PHP به Apache متصل نشده است؛ قبل از هر اقدامی دیگر، به بخش failures مراجعه کنید.
Step 7 - ایجاد پایگاه داده اپلیکیشن و یک کاربر با کمترین سطح دسترسی
پایگاه داده را با کاربر root و از طریق socket-authenticated باز کنید:
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 از فرمت واقعی four-byte UTF-8 استفاده میکند — نام مستعار قدیمی یعنی utf8 باعث حذف بیصدای emoji و برخی کاراکترهای CJK میشود، بنابراین همیشه از utf8mb4 استفاده کنید. دسترسی (grant) روی appdb.* اعمال شده است، نه *.*: این کاربر فقط میتواند به پایگاه داده خودش دسترسی داشته باشد و نه هیچ چیز دیگر؛ بنابراین اگر اپلیکیشن دچار SQL-injection شود، نمیتواند جداول سایر سایتها را بخواند. همچنین 'appuser'@'localhost' دسترسی حساب کاربری را فقط به اتصالات ایجاد شده از خودِ سیستم محدود میکند.
اتصال را با آن کاربر تست کنید:
mysql -u appuser -p appdbسیستم از شما رمز عبور میخواهد و شما را به یک prompt MariaDB [appdb]> میبرد. دقت کنید که پرچم -h وجود ندارد — اگر آن را حذف کنید، کلاینت از طریق local Unix socket متصل میشود، که دقیقاً همان چیزی است که MariaDB به عنوان localhost در نظر میگیرد. یک نکته مهم: برای MySQL و MariaDB، localhost به معنای Unix socket و 127.0.0.1 به معنای اتصال TCP است. در یک نسخه پیشفرض از Ubuntu 24.04 MariaDB، سرور همچنان یک اتصال TCP از 127.0.0.1 را به localhost تبدیل میکند، بنابراین هر دو با حساب کاربری مطابقت دارند — اما در سرورهایی که skip-name-resolve فعال است (یک تنظیم رایج برای بهبود عملکرد و استاندارد در بسیاری از container images)، این دو به عنوان hostهای متفاوت شناخته میشوند و اپلیکیشنی که به 127.0.0.1 متصل شود، با خطای ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) مواجه میشود، حتی اگر رمز عبور درست باشد.
بنابراین اپلیکیشن خود را به host localhost، کاربر appuser و پایگاه داده appdb متصل کنید — هرگز به root متصل نشوید. در PHP، هر دو افزونه mysqli و PDO زمانی که host دقیقاً رشته localhost باشد، به Unix socket سوئیچ میکنند که با حساب کاربری که ساختهاید مطابقت دارد. اگر یک framework بر استفاده از host عددی TCP اصرار داشت، کاربر را مطابق با نحوه اتصال واقعی آن بسازید — یعنی 'appuser'@'127.0.0.1'، یا اگر حتماً باید از ماشین دیگری به پایگاه داده متصل شود، از @'%' (همراه با یک rule در firewall) استفاده کنید.
Step 8 - افزودن HTTPS با Certbot
نمایش فرم ورود از طریق پروتکل HTTP معمولی باعث ارسال رمز عبور به صورت متن ساده میشود و تمام مرورگرهای مدرن، صفحه را با برچسب "Not secure" علامتگذاری میکنند. Certbot این مشکل را با یک دستور حل میکند. آن را با استفاده از افزونه Apache نصب کنید:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheCertbot در اینجا از دو افزونه استفاده میکند. افزونه apache authenticator با ارائه موقت یک فایل چالش از طریق Apache در حال اجرا، مالکیت دامنه را اثبات میکند. سپس افزونه apache installer تنظیمات virtual host شما را بازنویسی میکند تا بلوک 443 را اضافه کند، آن را به گواهی جدید متصل کند و تمام ترافیک HTTP را به صورت پیشفرض به HTTPS هدایت کند. از نسخه Certbot 2.0 به بعد، سوالی در مورد redirect پرسیده نمیشود؛ اگر نیاز دارید سرویس HTTP معمولی را حفظ کنید، از پرچم --no-redirect استفاده کنید. از آنجایی که شما در Step 5 یک ServerName واقعی تنظیم کردهاید، Certbot دامنه را به صورت خودکار شناسایی میکند. اعتبار گواهیها 90 روز است و این بسته یک timer در systemd نصب میکند که آنها را تمدید میکند؛ با استفاده از دستور sudo certbot renew --dry-run زمانبندی را بررسی کنید که باید به Congratulations, all simulated renewals succeeded ختم شود.
برای راهنمای کامل مربوط به چالش، تمدید خودکار، و الزامات DNS و فایروال، به راهنمای مکمل در issuing free Let's Encrypt TLS certificates with Certbot on Apache مراجعه کنید.
Backups, upgrades, and hardening
از دو موردی که وضعیت سیستم شما را نگه میدارند، یعنی پایگاههای داده (databases) و پوشه اصلی وب (web root)، نسخه پشتیبان تهیه کنید. سادهترین روش قابل اعتماد، تهیه یک dump منطقی در هر شب است — ابتدا sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz' و سپس کپی کردن آن به خارج از سرور. استفاده از sudo sh -c برای کل فرآیند ضروری است: بدون آن، shell دستور redirect در > /root/... را با کاربر خودتان اجرا میکند و با خطای Permission denied مواجه میشود، زیرا فقط mysqldump از sudo ارثبری کرده است. --single-transaction یک snapshot منسجم از جداول InnoDB بدون قفل کردن آنها ارائه میدهد. با ترکیب آن با یک tar از /var/www و /etc/apache2/sites-available، میتوانید کل پشته (stack) را از روی آن فایلها در یک VPS تازه بازسازی کنید.
ارتقا (upgrades) یک sudo apt update && sudo apt upgrade معمولی است. مشکل اصلی، ارتقای نسخه PHP است — زمانی که یک نسخه آینده Ubuntu نسخه پیشفرض را به PHP 8.4 تغییر دهد، apt ممکن است php8.4-fpm را در کنار 8.3 نصب کند، socket به /run/php/php8.4-fpm.sock تغییر کند و تنظیمات Apache شما همچنان به socket نسخه 8.3 اشاره کند. فایل conf جدید (sudo a2enconf php8.4-fpm) را فعال و فایل قدیمی را غیرفعال کنید، در غیر این صورت سایت شما پس از یک ارتقای معمول، خطای Primary script unknown برمیگرداند. از آنجایی که نسخههای PHP سریعتر از توزیعهای LTS منتشر میشوند، به جای محدود کردن به یک نسخه patch، یادداشتهای انتشار (release notes) فعلی PHP را بررسی کنید.
انجام دو مرحله برای امنیت (hardening) در روز اول ارزشمند است. اول، استفاده از Fail2Ban برای نظارت بر SSH روی سرور؛ یک VPS عمومی در عرض چند دقیقه با تلاشهای خودکار برای ورود مواجه میشود و یک jail کوچک، هزاران تلاش را پیش از مسدودسازی (ban) به تعداد انگشتشمار کاهش میدهد. دوم، اگر ترجیح میدهید بهجای ویرایش دستی فایلها، virtual hostهای Apache، پایگاههای داده MariaDB و کاربران را از طریق مرورگر مدیریت کنید، پنل مدیریتی وب Webmin دقیقاً روی همین پشته قرار دارد و همان فایلهای تنظیمی را که نوشتید، مدیریت میکند. هیچکدام جایگزین درک اجزای سیستم نمیشوند، اما هر دو اصطکاک کارهای روزمره را کاهش میدهند.
حالتهای شکست و پیامهای مشاهده شده
صفحه پیشفرض از بین نمیرود. شما virtual host خود را ویرایش و reload کردید، اما مرورگر همچنان عبارت "Apache2 Ubuntu Default Page" و بنر "It works!" را نمایش میدهد. Apache اولین virtual host منطبق را سرو میکند؛ اگر هیچ ServerName با درخواست مطابقت نداشته باشد، اولین کانفیگ بر اساس حروف الفبا برنده میشود — 000-default.conf قبل از testapp.conf مرتب میشود. یا نام میزبان (host name) در درخواست با ServerName شما مطابقت ندارد، یا شما هرگز sudo a2dissite 000-default را اجرا نکردهاید. کانفیگ پیشفرض را غیرفعال کنید (sudo systemctl reload apache2) و با دستور apache2ctl -S تایید کنید؛ این دستور نقشه vhost را چاپ میکند و نشان میدهد کدام کانفیگ مالک صفحه پیشفرض است. حافظه کش مرورگر را نیز پاک کنید؛ یک پاسخ 200 کش شده از صفحه قدیمی میتواند همچنان نمایش داده شود.
فایل .php به جای اجرا شدن، دانلود میشود. شما info.php را باز میکنید و مرورگر به جای اجرای آن، فایلی حاوی کد منبع خام <?php را دانلود میکند یا آن را به صورت متن ساده نمایش میدهد. Apache فایل را به عنوان یک دارایی استاتیک سرو میکند زیرا handler مربوط به PHP متصل نشده است — شما از sudo a2enmod proxy_fcgi یا sudo a2enconf php8.3-fpm صرفنظر کردهاید، یا پس از آن Apache را ریاستارت نکردهاید. هر سه مرحله (Step 4) را اجرا و reload کنید. با دستور apache2ctl -M | grep fcgi تایید کنید که ماژول بارگذاری شده است، که باید proxy_fcgi_module را لیست کند. این یک نشت کد منبع (source-code leak) است و نه یک باگ ظاهری؛ پس قبل از قرار دادن هر محتوای واقعی روی سرور، آن را برطرف کنید.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. شما mysql -u root یا mariadb -u root را بدون sudo اجرا کردهاید. حساب root از احراز هویت unix_socket استفاده میکند، بنابراین فقط زمانی شما را میپذیرد که کاربر سیستمعامل شما واقعاً root باشد. راه حل sudo mysql است — بدون -u root و بدون رمز عبور. این پیام رفتار مورد انتظار از احراز هویت socket است که به درستی کار میکند، نه یک نصب خراب.
ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' از اپلیکیشن، با رمز عبور صحیح. حساب کاربری به عنوان 'appuser'@'localhost' وجود دارد، اما اپلیکیشن شما از طریق TCP به 127.0.0.1 متصل میشود؛ در سروری که resolve کردن نام میزبان غیرفعال است (skip-name-resolve)، MariaDB این دو را به عنوان میزبانهای متفاوت تشخیص میدهد — localhost یک Unix socket است و 127.0.0.1 TCP. اپلیکیشن را به host localhost متصل کنید تا از socket استفاده کند و با حساب کاربری مطابقت داشته باشد، یا اگر فریمورک فقط از TCP پشتیبانی میکند، یک حساب کاربری دوم به نام 'appuser'@'127.0.0.1' بسازید.
AH01071: Got error 'Primary script unknown' در /var/log/apache2/testapp-error.log، در حالی که مرورگر File not found. را نشان میدهد. Apache درخواست را به PHP-FPM ارجاع داد، اما FPM نتوانست اسکریپت را در مسیری که Apache اعلام کرده بود پیدا کند. دو علت رایج: socket مربوط به FPM در کانفیگ شما به نسخهای از PHP اشاره دارد که نصب نیست (یک socket از نوع php8.4 پس از ارتقا، در حالی که فقط نسخه 8.3 اجرا میشود)، یا فایل واقعاً وجود ندارد زیرا DocumentRoot و دایرکتوری واقعی با هم اختلاف دارند. با دستور ls -l /run/php/ وجود socket را بررسی کنید، مطمئن شوید 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 قرار میدهد و باعث استفاده از MPM کندِ prefork میشود؛ بنابراین Apache حتی هنگام سرو کردن یک تصویر استاتیک، بار اضافی PHP را نیز حمل میکند. PHP-FPM کد PHP را به عنوان یک pool مجزا و مستقل اجرا میکند که Apache از طریق یک socket به آن دسترسی دارد؛ این روش با MPM سریعترِ event سازگار است و در صورت انتقال به nginx نیز بدون تغییر عمل میکند. این روش، پیشفرض مدرن است؛ استفاده از mod_php تنها برای اپلیکیشنهای قدیمی که به رفتارهای درون-پروسهای (in-process) وابسته هستند، منطقی است.
چرا مرورگر من فایل PHP را دانلود میکند به جای اینکه آن را اجرا کند؟
Apache با فایل .php مانند یک فایل استاتیک برخورد میکند زیرا هیچ PHP handler به آن متصل نشده است. در Ubuntu 24.04 با استفاده از FPM، این یعنی شما یکی از موارد sudo a2enmod proxy_fcgi، sudo a2enconf php8.3-fpm یا ریاستارت کردن Apache را انجام ندادهاید. هر سه مرحله را اجرا و بارگذاری مجدد کنید، سپس با apache2ctl -M | grep fcgi بررسی کنید که آیا proxy_fcgi_module در لیست هست یا خیر. تا زمانی که این مشکل را حل نکنید، سرور در حال لو دادن کد منبع (source code) است، پس آن را یک مورد فوری در نظر بگیرید.
چرا دسترسی 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 در حال اجرا اثبات میکند و نصبکننده، virtual host را برای پورت 443 بازنویسی کرده و تمدید خودکار را تنظیم میکند. راهنمای کامل Certbot و Apache شامل چالشها، زمانبندی تمدید و حالتهای رایج شکست (failure modes) است.