Ubuntu 24.04 安裝 LAMP 架構教學 (PHP-FPM)
本指南教您在 Ubuntu 24.04 上建置 LAMP 架構,包含 Apache、MariaDB (unix_socket) 與 PHP 8.3 (PHP-FPM)。解決常見的空白頁面、原始碼直接下載或資料庫登入失敗問題,並透過 Certbot 完成 HTTPS 設定。
建立目標
LAMP 架構是在單台 Ubuntu 24.04 伺服器上運行的四個組件:底層的 Linux、負責處理 HTTP 的 Apache、儲存資料的 MariaDB,以及執行程式碼的 PHP 8.3。完成後,您將擁有一個透過域名運作的 Virtual Host 並對應實際的應用程式目錄、一個使用最小權限專用用戶的資料庫、透過 PHP-FPM 與 Apache 連結的 PHP,以及一個免費的 Let's Encrypt 憑證。
安裝過程僅需四個 apt 指令。本指南的大部分內容在於各組件間的配置,以及導致新架構出現問題的常見錯誤,例如:顯示空白頁面、將原始碼作為檔案下載傳送至瀏覽器,或無法登入剛安裝的資料庫。這些錯誤皆有其特徵,下文會列出您會看到的確切錯誤訊息。
前置作業與注意事項
假設您擁有一台全新的 Ubuntu 24.04 KVM VPS,具備 sudo 使用者或 root 權限,以及公用 IPv4 位址。最小化配置在 1 GB RAM 下即可執行;但在部署實際的資料庫應用程式之前,建議配置 2 GB RAM,因為 MariaDB 的預設緩衝區加上少數 PHP-FPM 工作程序會迅速耗盡第一個 GB 的記憶體。
在最後執行 Certbot 之前,必須滿足兩個條件,請先完成以下設定:您需要一個擁有 A 紀錄並指向該 VPS 公用 IP 的 domain name —— Let's Encrypt 會透過該名稱進行 HTTP 驗證,僅有 IP 位址無法取得憑證。此外,必須確保 80 與 443 埠口可從網際網路存取;這通常意味著除了要在 ufw 設定外,還必須在服務商控制台的網路防火牆中開啟這些埠口。DNS 設定的傳播可能需要長達一小時,請先設定好 A 紀錄,待您需要時它便已生效。
Step 1 - 安裝 Apache 並確認預設頁面
sudo apt update
sudo apt install -y apache2apt 會自動啟動並啟用該服務。請執行以下指令進行檢查:
systemctl status apache2輸出結果應包含 active (running)。接著請在瀏覽器中開啟 http://YOUR_SERVER_IP/。若看到帶有大型 "It works!" 橫幅的 Apache2 Ubuntu Default Page,即表示結果正確 — 這證明 Apache 運作正常,並非錯誤。該頁面位於 /var/www/html/index.html,由預設的虛擬主機 000-default.conf 提供服務。稍後您將停用這兩者;目前看到它們顯示正常即可。
若頁面完全無法載入,但 systemctl 顯示程序正在執行,代表防火牆阻擋了連線。下一步將處理此問題。
Step 2 - 開啟 HTTP 與 HTTPS 的防火牆權限
apache2 套件會註冊三個 ufw 應用程式設定檔。列出清單如下:
sudo ufw app list你會看到 Apache、Apache Full 與 Apache Secure。Apache 僅包含 port 80,Apache Secure 僅包含 port 443,而 Apache Full 包含兩者 —— 這才是你需要使用的,因為最後會加入 TLS。
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enable請在執行 ufw enable 之前 先允許 OpenSSH。ufw 預設會拒絕所有傳入流量;若未設定 SSH 規則就啟用防火牆,一旦生效會導致連線中斷 —— 雖然目前連線仍可維持,但將無法重新連線。請使用 sudo ufw status 確認;請確保 OpenSSH、Apache Full 及其 v6 對應項皆顯示為 ALLOW。
Step 3 - 安裝 MariaDB 並進行安全性設定
sudo apt install -y mariadb-server
systemctl status mariadbUbuntu 24.04 內建 MariaDB 10.11(長期支援版本),因此無需使用外部 repository。服務啟動後,請執行安全性強化:
sudo mysql_secure_installation請仔細閱讀提示,而非直接按 Enter。當系統詢問 current root password 時,請按 Enter,因為目前尚未設定密碼。當詢問 "Switch to unix_socket authentication?" 時,由於此套件已預設啟用,選擇何者皆無差異,請按 n。針對 "Change the root password?" 請選擇 n(原因見下段),其餘選項請全部回答 Y:移除匿名使用者、禁止 root 遠端登入、刪除 test 資料庫,並重新載入權限表。
以下是容易產生誤解的部分。在 Ubuntu 的 MariaDB 中,root 資料庫帳號使用 unix_socket 驗證,而非密碼。這代表資料庫會信任您已通過驗證的 operating-system 使用者。因此,在 root shell 下執行以下指令即可成功:
sudo mysql...系統會直接進入 MariaDB [(none)]> 提示字元,且不需輸入密碼。若以非特權使用者執行相同的指令則會被拒絕,這正是此機制的核心:資料庫 root 的存取權限與主機上的 sudo 綁定,因此不存在可被竊取、釣魚或暴力破解的密碼。這比密碼更安全,請保持現狀。由此衍生的原則是:切勿讓應用程式使用 root 帳號。請為每個應用程式建立專用使用者(Step 7),因為透過 TCP 連線並使用使用者名稱與密碼的應用程式無法使用 socket 驗證,且您應限制每個應用程式僅能存取其專屬的資料庫。
Step 4 - 安裝 PHP 8.3 與 PHP-FPM
Ubuntu 24.04 預設的 PHP 版本為 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。該舊版套件會在每個 Apache 處理程序中嵌入 PHP 解譯器。這種方式雖然簡單,但無論是在處理腳本還是靜態圖片,每個 worker 都會攜帶一份 PHP 副本;兩者共用生命週期,且僅支援 Apache 效率最低的 prefork MPM。相比之下,PHP-FPM 將 PHP 運作於獨立的處理程序池中,並透過 socket 與 Apache 通訊。Apache 可以針對靜態檔案使用執行緒化的 event MPM,並僅將 PHP 請求轉交給 FPM。此處理程序池可獨立於 Web Server 進行調優;若日後改用 nginx 作為前端,相同的 FPM 設定依然適用。這是目前的預設方案,且具備充分理由。
Apache 透過 proxy_fcgi 模組連接至 FPM。請啟用該模組、啟用 FPM 套件提供的設定檔,並重新啟動:
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 檔案路由至 FPM socket 的規則。其核心邏輯是比對任何 .php 檔案,並將其轉發至 /run/php/php8.3-fpm.sock 的 socket:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>請勿修改該檔案,其預設設定已正確。但掌握 socket 路徑,能幫助您日後診斷「PHP 變成下載而非執行」以及「Primary script unknown」錯誤——這兩類錯誤皆是因為 Apache 與 FPM 對該 socket 或其後端檔案的認知不一致所致。
Step 5 - A name-based virtual host for your app
Name-based virtual hosting lets one IP serve many sites; Apache picks the site by the Host: header in the request. Make a directory for the app, well away from the default /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/testappOwnership matters. Apache and PHP-FPM both run as the www-data user on Ubuntu, so files the web server must read — and directories an app must write to, such as an uploads folder — should be owned by www-data. If you will also edit files as your login user, a common pattern is to own the files yourself and add your user to the www-data group; for a plain deploy, www-data:www-data is the least surprising.
Create the virtual host at /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>Set ServerName to your real domain. Options -Indexes stops Apache listing the directory when there is no index file — otherwise visitors browse your source tree. AllowOverride All lets a .htaccess file work, which most PHP applications expect for pretty URLs; drop it to None for a small speed gain if your app does not need it. Enable this site, disable the default, check the config, and reload:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2apache2ctl configtest should print Syntax OK. The a2dissite 000-default line is the one people forget, and it is why the default page later appears to be stuck — covered in the failures section.
Step 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.phpphpinfo() 會暴露您的確切 PHP 版本、所有已載入的擴充功能、檔案路徑及環境細節,這會讓試圖尋找已知漏洞的人輕易取得伺服器資訊。這僅是測試用途,並非功能需求。看到頁面後請立即刪除。如果瀏覽器不是顯示表格,而是提示要 下載 info.php,代表 PHP 與 Apache 未正確連接;請在進行其他操作前,直接跳至故障排除章節。
Step 7 - 建立應用程式資料庫與最小權限使用者
使用 socket 驗證的 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 是真正的 4-byte UTF-8 — 舊有的 utf8 別名會靜默截斷 emoji 與部分 CJK 字元,因此請務必使用 utf8mb4。權限僅授予 appdb.*,而非 *.*:此使用者僅能存取其專屬資料庫,無法存取其他資料庫,因此應用程式若發生 SQL injection 漏洞,攻擊者也無法讀取其他網站的資料表。此外,'appuser'@'localhost' 會將帳號限制在僅允許來自本機的連線。
使用該使用者進行測試:
mysql -u appuser -p appdb系統會要求輸入密碼,並進入 MariaDB [appdb]> 提示字元。請注意指令中沒有使用 -h 旗標 — 不使用該旗標時,用戶端會透過 local Unix socket 連線,這正是 MariaDB 將 localhost 視為連線的方式。一個值得注意的細節:對 MySQL 與 MariaDB 而言,localhost 代表 Unix socket,而 127.0.0.1 代表 TCP 連線。 在標準的 Ubuntu 24.04 MariaDB 環境中,伺服器仍會將來自 127.0.0.1 的 TCP 連線解析回 localhost,因此兩者皆符合該帳號;但在啟用了 skip-name-resolve 的伺服器上(這是一種常見的效能優化,也是許多 container images 的標準設定),兩者會被視為不同的主機,若應用程式嘗試透過 127.0.0.1 連線,即使密碼正確,也會被拒絕並回傳 ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES)。
因此,請將應用程式的 host 設定為 localhost,user 設定為 appuser,database 設定為 appdb — 切勿使用 root。當 host 為字串 localhost 時,PHP 的 mysqli 與 PDO 都會切換至 Unix socket,這符合您剛建立的帳號。若框架強制要求使用數值型 TCP host,請根據其實際連線方式建立使用者:使用 'appuser'@'127.0.0.1';只有在必須從其他機器連線至資料庫時,才使用 @'%'(並搭配防火牆規則)。
Step 8 - 使用 Certbot 新增 HTTPS
透過 plain HTTP 傳送登入表單會導致密碼以明文傳輸,且所有現代瀏覽器都會將該頁面標記為「不安全」。Certbot 可透過單一指令解決此問題。請使用 Apache plugin 進行安裝:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheCertbot 在此使用兩個 plugin。apache authenticator 會透過運作中的 Apache 暫時提供 challenge file,藉此證明您擁有該 domain 的控制權;接著 apache installer 會改寫您的 virtual host,新增 443 區塊、指向新憑證,並預設將所有 HTTP 流量重新導向至 HTTPS。自 Certbot 2.0 起,系統不會詢問是否重新導向;若您需要繼續提供 plain HTTP 服務,請傳遞 --no-redirect。由於您在 Step 5 已設定真實的 ServerName,Certbot 會自動偵測該 domain。憑證有效期為 90 天,且該套件會安裝一個 systemd timer 進行自動續期;請使用 sudo certbot renew --dry-run 驗證 timer,其結果應以 Congratulations, all simulated renewals succeeded 結尾。
關於 challenge 流程、續期 timer 以及 DNS 與 firewall 需求的完整說明,請參閱 使用 Certbot 在 Apache 上核發免費 Let's Encrypt TLS 憑證 說明指南。
Backups, upgrades, and hardening
備份維護系統狀態的兩項核心:資料庫與 web root。最簡單且可靠的方法是每晚進行一次 logical dump — 使用 sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz',接著將檔案複製到主機之外。將整個流程封裝在 sudo sh -c 中非常重要:若未執行,shell 會以目前使用者身份執行 > /root/... redirect,並因 mysqldump 未能繼承 sudo 而導致 Permission denied 錯誤。--single-transaction 能在不鎖定 InnoDB tables 的情況下,提供一致的 snapshot。搭配 /var/www 與 /etc/apache2/sites-available 的 tar,您就能從這些檔案在全新的 VPS 上重建整個 stack。
升級是常規的 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 config 仍指向 8.3 的 socket。請啟用新的 conf (sudo a2enconf php8.4-fpm) 並停用舊版,否則在例行升級後,您的網站會開始回傳 Primary script unknown。由於 PHP 發布速度快於 LTS distro,請參考目前的 PHP release notes,而非固定特定的 patch version。
第一天建議執行兩項 hardening 步驟。首先,在主機上安裝 Fail2Ban 監控 SSH — 公開的 VPS 會在幾分鐘內收到自動化登入嘗試,透過設定小型 jail,可以在封鎖前將數千次嘗試縮減至少數幾次。其次,如果您希望透過瀏覽器而非手動編輯檔案來管理 Apache virtual hosts、MariaDB databases 與使用者,Webmin 網頁控制台 可直接運行於此 stack 並驅動您剛寫好的 config files。兩者皆無法取代對各組件的理解,但都能減少日常維運的摩擦。
故障模式與可能出現的字串
預設頁面無法消失。 您編輯了 virtual host 並重新載入,但瀏覽器仍顯示 "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 碼的檔案,或將其顯示為純文字,而非執行該檔案。這是因為 PHP handler 未掛載,導致 Apache 將該檔案視為靜態資源處理 — 您可能跳過了 sudo a2enmod proxy_fcgi 或 sudo a2enconf php8.3-fpm,或者未在之後重新啟動 Apache。請執行所有三個步驟(Step 4)並重新載入。使用 apache2ctl -M | grep fcgi 確認模組已載入,結果應包含 proxy_fcgi_module。這屬於原始碼外洩問題,而非單純的外觀錯誤,請在正式部署內容至伺服器前修復此問題。
ERROR 1698 (28000): Access denied for user 'root'@'localhost'。 您在未執行 sudo 的情況下執行了 mysql -u root 或 mariadb -u root。root 帳號使用 unix_socket 驗證,因此僅在您的 OS 使用者確實為 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,且該伺服器禁用了 host-name 解析 (skip-name-resolve),因此 MariaDB 會將兩者視為不同的主機 — localhost 是 Unix socket,127.0.0.1 是 TCP。請將應用程式指向 host localhost 以使用 socket 並匹配帳號;或者若框架僅支援 TCP,請建立第二個帳號 'appuser'@'127.0.0.1'。
/var/log/apache2/testapp-error.log 中的 AH01071: Got error 'Primary script unknown',瀏覽器顯示 File not found.。Apache 已將請求傳遞給 PHP-FPM,但 FPM 無法在 Apache 指定的路徑下找到該指令碼。常見原因有二:您的設定檔中的 FPM socket 指向了未安裝的 PHP 版本(例如升級後僅運行 8.3,卻仍使用 php8.4 socket),或是檔案確實不存在,因為 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。請在 /etc/apache2/conf-available/servername.conf 中寫入 ServerName your.domain 並執行 sudo a2enconf servername 來消除此警告。
Apache 啟動時出現 (98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80。 另一個網頁伺服器已佔用 port 80 — 通常是先前實驗留下的 nginx 殘留程序。請使用 sudo ss -ltnp | grep :80 找出該程序,並在啟動 Apache 前停止並停用該服務。
FAQ
mod_php 或 PHP-FPM — 我該使用哪一個?
請使用 PHP-FPM。mod_php 會在每個 Apache 程序中嵌入解釋器,並強制使用效能較慢的 prefork MPM,這會導致 Apache 在處理靜態圖片時也承擔 PHP 的額外負擔。PHP-FPM 將 PHP 作為獨立且可個別調優的程序池執行,Apache 透過 socket 與其通訊,並可搭配效能較快的 threaded event MPM 使用,且未來遷移至 nginx 時無需更改設定。這是現代的預設選項;僅在應用程式需依賴某些程序內 (in-process) 行為的舊有系統中,使用 mod_php 才有意義。
為什麼瀏覽器是下載 PHP 檔案而不是執行它?
由於未關聯任何 PHP 處理器 (handler),Apache 會將 .php 檔案視為靜態下載內容。在 Ubuntu 24.04 並使用 FPM 的環境下,這代表您漏掉了 sudo a2enmod proxy_fcgi、sudo a2enconf php8.3-fpm 或之後的 Apache 重啟步驟。請執行這三個步驟並重新載入,接著使用 apache2ctl -M | grep fcgi 確認 proxy_fcgi_module 已列出。在修復問題前,伺服器會外洩原始碼,請將此視為緊急任務。
為什麼即使密碼正確,MariaDB 的 root 權限仍被拒絕?
因為該帳號不需要密碼 — Ubuntu 的 MariaDB 使用 unix_socket 對 root 帳號進行驗證,將其與作業系統的 root 使用者綁定。在一般 shell 中執行 mysql -u root 會依設計回傳 ERROR 1698 (28000): Access denied for user 'root'@'localhost'。請改用 sudo mysql 連線,並為應用程式建立獨立的密碼驗證使用者,而非重複使用 root。
如何為我的 LAMP 網站新增 HTTPS?
請安裝 certbot 與 python3-certbot-apache,將網域的 A record 指向該伺服器,然後執行 sudo certbot --apache。Apache 驗證器會透過運行的 Apache 證明您擁有該網域控制權,安裝程式會重寫虛擬主機 (virtual host) 的 443 埠設定並設定自動續期。完整的 Certbot 與 Apache 指南 涵蓋了驗證挑戰、續期排程以及常見的錯誤模式。