Ubuntu 24.04でLAMPスタックを構築する方法
Ubuntu 24.04にApache, MariaDB, PHP 8.3 (PHP-FPM)をインストールし、CertbotでHTTPS化する手順を解説します。unix_socket認証の設定や、ソースコードがそのままダウンロードされる等のトラブル解決策も詳しく紹介しています。
構築内容
LAMP stackは、1台の Ubuntu 24.04 サーバー上で動作する4つのコンポーネントで構成されます。ベースとなる Linux、HTTPリクエストを処理する Apache、データを保持する MariaDB、そしてコードを実行する PHP 8.3 です。完了時には、アプリケーションディレクトリを配信する名前ベースの virtual host、最小権限を持つ専用ユーザーを備えたデータベース、PHP-FPMを介して Apache に接続された PHP、そして Let's Encrypt による無料の証明書がセットアップされます。
インストール自体は4つの apt コマンドで行います。本ガイドの主な内容は、各コンポーネント間の連携設定です。また、新しい stack で「空白のページが表示される」「ソースコードがダウンロードされる」「インストールしたデータベースにアクセスできない」といった、よくあるトラブルの原因についても解説します。これらの問題にはそれぞれ特有の兆候があります。以下に、実際に表示されるエラーメッセージとともに記載します。
前提条件と注意点
sudo権限を持つユーザーまたはroot、およびパブリックIPv4アドレスを持つ、新規インストール済みのUbuntu 24.04 KVM VPSを想定します。最小構成のスタックは1 GBのRAMで動作しますが、本格的なデータベースを使用するアプリケーションを動かす場合は、2 GB以上のメモリを割り当ててください。MariaDBのデフォルトのバッファと少数のPHP-FPMワーカーだけで、最初の1 GBをすぐに消費するためです。
最後にCertbotを正常に動作させるには、以下の2つの条件を満たす必要があります。あらかじめ準備しておいてください。まず、VPSのパブリックIPを指すAレコードを持つドメイン名が必要です。Let's EncryptはHTTP経由でそのドメインに対して検証を行うため、IPアドレスのみでは証明書を取得できません。次に、インターネットからポート80および443にアクセスできる必要があります。多くのプロバイダーでは、サーバー上のufwでの設定に加え、コントロールパネルのネットワークファイアウォールでもこれらのポートを開放する必要があります。DNSの変更が反映されるまでには最大1時間かかる場合があるため、先にAレコードを設定しておけば、作業時には反映されているはずです。
Step 1 - Apacheのインストールとデフォルトページの確認
sudo apt update
sudo apt install -y apache2apt は、サービスの起動と有効化を自動で行います。以下のコマンドで確認してください:
systemctl status apache2active (running) という行が表示されていれば正常です。次に、ブラウザで http://YOUR_SERVER_IP/ を開いてください。大きな "It works!" というバナーが表示された Apache2 Ubuntu Default Page が表示されれば、正しく動作しています。これはエラーではなく、Apacheが正常にリクエストを処理している証拠です。このページは /var/www/html/index.html にあり、デフォルトの virtual host である 000-default.conf によって提供されています。これらは後で無効化しますが、現時点ではこの状態が正解です。
もしページが読み込めず、かつ systemctl でプロセスが実行中(running)と表示される場合は、firewall が原因です。次のステップで対処します。
Step 2 - HTTPとHTTPSのファイアウォールを開放する
apache2パッケージは、3つのufwアプリケーションプロファイルを使用します。一覧は以下の通りです。
sudo ufw app listApache、Apache Full、Apache Secureが表示されます。Apacheはport 80のみ、Apache Secureはport 443のみ、Apache Fullはその両方です。最終的にTLSを追加するため、Apache Fullを使用します。
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enableufw 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にはLTS(長期サポート)版であるMariaDB 10.11が標準搭載されているため、外部リポジトリを追加する必要はありません。サービスを起動した後、以下の手順でセキュリティを強化します。
sudo mysql_secure_installationプロンプトの内容は、Enterキーを連打せずに必ず確認してください。current root password(現在のrootパスワード)を求められた場合は、まだ設定されていないため、そのままEnterを押してください。"Switch to unix_socket authentication?"(unix_socket認証に切り替えますか?)という質問については、このパッケージでは既に有効になっているため、回答を変える必要はありません。n を選択してください。"Change the root password?"(rootパスワードを変更しますか?)については、次の段落の理由により n を選択してください。それ以外の項目(anonymous usersの削除、remote root loginの禁止、test databaseの削除、privilege tablesの再読み込み)については、すべて Y と回答してください。
ここで多くのユーザーが混乱する点があります。UbuntuのMariaDBでは、root データベースアカウントはパスワードではなく unix_socket 認証を使用します。これは、既に認証済みの operating-system ユーザーをデータベースが信頼することを意味します。そのため、rootシェルからは以下の操作が可能です。
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 インタープリタを埋め込みます。構成は単純ですが、スクリプトを処理する場合でも静的画像を処理する場合でも、すべてのワーカーが PHP のコピーを保持します。また、これらはライフサイクルを共有し、最も効率の低い Apache の prefork MPM でしか動作しません。対照的に PHP-FPM は、Apache がソケットを介して通信する独立したプロセスプールとして PHP を実行します。これにより、Apache は静的ファイルにはスレッドベースの event MPM を使用し、PHP リクエストのみを転送できます。プロセスプールは Web サーバーとは独立してチューニング可能です。また、将来的に 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 ソケットへルーティングするルールが含まれています。このルールの核となる部分は、すべての .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 の間で、ソケットまたはその先のファイルに関する認識が一致していないことが原因です。
Step 5 - アプリケーション用の名前ベースのバーチャルホスト
名前ベースのバーチャルホストを使用すると、1つの 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所有権の設定が重要です。Ubuntu では Apache と PHP-FPM は共に www-data ユーザーとして動作します。そのため、Web サーバーが読み取る必要があるファイルや、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 を設定すると、index ファイルがない場合に Apache がディレクトリ一覧を表示するのを防ぎます。これを設定しないと、訪問者がソースツリーを閲覧できてしまいます。AllowOverride All を設定すると .htaccess ファイルが動作するようになり、多くの PHP アプリケーションが期待する Pretty URL が利用可能になります。アプリケーションに不要な場合は、None を削除することでわずかに高速化できます。このサイトを有効化し、デフォルトのサイトを無効化して、設定を確認し、リロードしてください。
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2apache2ctl configtest を実行すると Syntax OK と表示されるはずです。a2dissite 000-default の行は忘れられがちな設定であり、デフォルトページが表示され続ける原因となります。これについては、トラブルシューティングのセクションで説明します。
Step 6 - PHPの動作確認とファイルの削除
app rootに1行のPHPファイルを作成します:
echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.phphttp://app.example.com/info.php にアクセスしてください。正しく動作していれば、ロードされているモジュールが一覧表示された、紫とグレーの PHP Version 8.3.x のテーブルが表示されます。その際、Server API の行が FPM/FastCGI と表示されていることを確認してください。この表示により、リクエストが mod_php ではなく PHP-FPM を経由していることが証明されます。
確認後、すぐにファイルを削除してください:
sudo rm /var/www/testapp/info.phpphpinfo() は、PHPの正確なバージョン、ロードされているすべての拡張機能、ファイルパス、および環境の詳細を公開します。これは脆弱性を持つバージョンを探索する攻撃者にとって有用な情報となります。これはテスト用の設定であり、機能ではありません。ページを確認したら、直ちに削除してください。もしテーブルが表示されず、ブラウザが info.php のダウンロードを促した場合は、PHPとApacheの連携設定に問題があります。他の作業を行う前に、failures セクションを確認してください。
Step 7 - アプリケーション用データベースと最小権限ユーザーの作成
socket-authenticated rootとしてデータベースを開きます:
sudo mysql次に、そのデータベースのみに限定されたデータベースとユーザーを1つずつ作成します:
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;ここでは3つの意図的な設定を行っています。utf8mb4は本物の4バイトUTF-8です。古いutf8エイリアスは、絵文字や一部のCJK文字をサイレントに切り捨てます。そのため、常にutf8mb4を使用してください。権限(grant)はappdb.*に対して付与されており、*.*ではありません。このユーザーは自身のデータベースのみを操作でき、それ以外の操作はできません。これにより、アプリにSQLインジェクションの脆弱性があっても、他のサイトのテーブルを読み取られることはありません。また、'appuser'@'localhost'により、アカウントの接続元をそのマシン自体に制限しています。
作成したユーザーとしてテストを行います:
mysql -u appuser -p appdbパスワードが要求され、MariaDB [appdb]>プロンプトが表示されます。-hフラグがないことに注意してください。フラグを外すと、クライアントはローカルの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が有効なサーバー(一般的なパフォーマンス調整、および多くのコンテナイメージの標準設定)では、これらは異なるホストとして照合されます。そのため、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には決して設定しないでください。PHPのmysqliおよびPDOは、hostに文字列のlocalhostが指定されると、作成したアカウントと一致するUnix socketへ切り替わります。もしフレームワークが数値によるTCP hostを要求する場合は、実際の接続方法に合わせてユーザーを作成してください。具体的には'appuser'@'127.0.0.1'、または他のマシンからデータベースに到達させる必要がある場合に限り、ファイアウォールルールと組み合わせた@'%'を使用してください。
Step 8 - CertbotによるHTTPSの追加
HTTPでログインフォームを配信すると、パスワードが平文で送信されます。また、最新のブラウザではページが「保護されていない通信」と表示されます。Certbotを使用すれば、コマンド一つでこれを解決できます。Apache用プラグインをインストールしてください:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheここではCertbotが2つのプラグインを使用します。apache authenticatorは、実行中のApacheを通じて一時的にチャレンジファイルを配信することで、ドメインの所有権を証明します。次にapache installerが、virtual hostを書き換えて443ブロックを追加し、新しい証明書を指定します。また、すべてのHTTPトラフィックをデフォルトでHTTPSへリダイレクトします。Certbot 2.0以降、リダイレクトの有無は自動的に設定されます。HTTPの配信を継続する必要がある場合は、--no-redirectを指定してください。Step 5で実際のServerNameを設定しているため、Certbotはドメインを自動的に検出します。証明書の有効期限は90日間です。パッケージには自動更新用のsystemd timerが含まれています。sudo certbot renew --dry-runでtimerの状態を確認してください。末尾はCongratulations, all simulated renewals succeededである必要があります。
チャレンジの詳細、更新timer、DNSおよびfirewallの要件については、Certbotを使用してApacheで無料のLet's Encrypt TLS証明書を発行する方法のガイドを参照してください。
Backups, upgrades, and hardening
状態を保持する2つの要素、すなわち databases と 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 上にスタック全体を再構築できます。
Upgrades は通常の sudo apt update && sudo apt upgrade です。問題となるのは PHP version bump です。将来の Ubuntu でデフォルトが PHP 8.4 に変更されると、apt が 8.3 と並行して php8.4-fpm をインストールする場合があります。このとき socket が /run/php/php8.4-fpm.sock に変わりますが、Apache の設定は依然として 8.3 の socket を指したままになります。新しい conf (sudo a2enconf php8.4-fpm) を有効にし、古いものを無効にしてください。そうしないと、通常の upgrade 後にサイトが Primary script unknown を返し始めます。PHP のリリース速度は LTS distro よりも速いため、パッチバージョンを固定するのではなく、最新の PHP release notes を確認してください。
初日に実施すべき hardening の手順が2つあります。1つ目は、サーバーに Fail2Ban による SSH 監視 を導入することです。公開されている VPS は、数分以内に自動化されたログイン試行を受け取ります。小さな jail を設定することで、数千回の試行を、ban される前の数回に抑えられます。2つ目は、ファイルを直接編集する代わりに、ブラウザから Apache virtual hosts、MariaDB databases、および users を管理したい場合、Webmin ウェブベースのコントロールパネル が有効です。これはまさにこのスタック上で動作し、作成した設定ファイルを制御します。どちらも各コンポーネントの理解に代わるものではありませんが、日常的な作業負荷を軽減します。
発生するエラーと表示される文字列
デフォルトページが消えない。 Virtual hostを編集してreloadしても、ブラウザに "Apache2 Ubuntu Default Page" と "It works!" というバナーが表示され続けます。Apacheは最初に一致したVirtual hostを返します。リクエストに一致する ServerName がない場合、アルファベット順で先に来る設定が優先されます。000-default.conf は testapp.conf より先にソートされます。リクエストのhostnameが ServerName と一致していないか、sudo a2dissite 000-default を実行していません。デフォルト設定を無効化(sudo systemctl reload apache2)し、apache2ctl -S で確認してください。apache2ctl -S はvhostマップを表示し、どの設定がデフォルトを占有しているか示します。ブラウザのキャッシュもクリアしてください。古いページの200 OKがキャッシュされている場合があります。
.phpファイルが実行されずダウンロードされる。 info.php を開くと、ブラウザが <?php のソースコードをそのままダウンロードするか、プレーンテキストとして表示します。PHPハンドラがアタッチされていないため、Apacheがそのファイルを静的アセットとして配信しています。sudo a2enmod proxy_fcgi または sudo a2enconf php8.3-fpm をスキップしたか、その後にApacheを再起動していません。Step 4の3つの手順をすべて実行してreloadしてください。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 に接続しようとしています。接続先のサーバーでhostname解決が無効化されているため(skip-name-resolve)、MariaDBはこれらを異なるホストと判断します。localhost はUnix socketであり、127.0.0.1 はTCPです。アプリケーションの接続先をホスト localhost に設定してsocketを使用させるか、フレームワークがTCP接続しかサポートしていない場合は、2つ目のアカウント '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から指定されたパスにスクリプトを見つけられませんでした。主な原因は2つあります。1つ目は、設定内のFPM socketがインストールされていないPHPバージョンを指している場合(例:アップグレード後に8.3のみが動作している状態で php8.4 socket を指定している)です。2つ目は、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 が発生する。 他のWebサーバーがすでに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 はソケット経由で PHP にアクセスするため、高速なスレッド型 event MPM を利用でき、将来的に nginx へ移行する場合もそのまま利用可能です。現在の標準的な選択肢です。mod_php は、プロセス内での動作に依存するレガシーなアプリケーションにのみ適しています。
なぜブラウザで PHP ファイルが実行されず、ダウンロードされるのですか?
PHP ハンドラが割り当てられていないため、Apache が .php ファイルを静的ファイルのダウンロードとして処理しています。FPM を使用している Ubuntu 24.04 の場合、sudo a2enmod proxy_fcgi、sudo a2enconf php8.3-fpm、またはその後の Apache の再起動のいずれかが不足しています。これら 3 つをすべて実行してリロードし、apache2ctl -M | grep fcgi で proxy_fcgi_module がリストされているか確認してください。修正されるまでサーバーからソースコードが流出するため、至急対応してください。
正しいパスワードを入力しても MariaDB で root アクセスが拒否されるのはなぜですか?
パスワードが設定されていないためです。Ubuntu の MariaDB は root アカウントを unix_socket で認証し、OS の root ユーザーに紐付けています。そのため、通常のシェルから mysql -u root を実行すると、仕様により ERROR 1698 (28000): Access denied for user 'root'@'localhost' が返されます。代わりに sudo mysql で接続してください。また、root を再利用するのではなく、アプリケーションごとにパスワード認証を行う別のユーザーを作成してください。
LAMP サイトに HTTPS を追加するにはどうすればよいですか?
certbot と python3-certbot-apache をインストールし、ドメインの A レコードをサーバーに向け、sudo certbot --apache を実行してください。Apache 認証器が実行中の Apache を通じてドメインの所有権を証明し、インストーラーがポート 443 用に virtual host を書き換え、自動更新を設定します。Certbot と Apache の完全ガイド では、認証の手順、更新タイマー、および一般的な失敗パターンについて説明しています。