SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor

LAMP на Ubuntu 24.04: Apache, MariaDB и PHP-FPM

Пошагово соберите LAMP на Ubuntu 24.04: Apache, MariaDB с unix_socket, PHP 8.3 через PHP-FPM, виртуальный хост и HTTPS Let's Encrypt через Certbot.

Что вы создаёте

Стек LAMP состоит из четырёх компонентов на одном сервере Ubuntu 24.04: Linux в основе, Apache обрабатывает HTTP-запросы, MariaDB хранит данные, а PHP 8.3 выполняет код. В результате вы получите виртуальный хост на основе имени, обслуживающий каталог реального приложения, базу данных с отдельным пользователем с минимально необходимыми привилегиями, PHP, подключённый к Apache через PHP-FPM, и бесплатный сертификат Let's Encrypt.

Установка выполняется с помощью четырёх команд apt. Почти всё в этом руководстве посвящено настройке взаимодействия компонентов и небольшому набору ошибок, из-за которых новый стек может показывать пустую страницу, отдавать исходный код браузеру для скачивания или не позволять войти в только что установленную базу данных. У каждой такой ошибки есть характерный признак. Ниже для каждой ошибки указано точное сообщение, которое вы увидите.

Предварительные требования и важные ограничения

Предполагается чистый KVM VPS с Ubuntu 24.04, пользователем с правами sudo или учетной записью root и общедоступным IPv4-адресом. Минимальный стек работает при 1 GB оперативной памяти. Перед размещением реального приложения, использующего базу данных, выделите 2 GB, поскольку стандартные буферы MariaDB и несколько рабочих процессов PHP-FPM быстро занимают первый гигабайт.

До запуска Certbot в конце должны выполняться два условия. Подготовьте их заранее. Нужен домен с A-записью, указывающей на общедоступный IP-адрес VPS. 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 регистрирует три профиля приложений 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 и соответствующих правил для v6 должно отображаться значение ALLOW.

Шаг 3 — Установите MariaDB и защитите его

sudo apt install -y mariadb-server
systemctl status mariadb

В Ubuntu 24.04 поставляется MariaDB 10.11 — версия с долгосрочной поддержкой, поэтому внешний репозиторий не требуется. После запуска службы настройте ее безопасность:

sudo mysql_secure_installation

Внимательно читайте запросы, а не нажимайте Enter без разбора. Когда появится запрос текущего пароля root, нажмите Enter: пароль еще не задан. На вопрос «Переключиться на аутентификацию unix_socket?» ответ ничего не изменит, поскольку для этого пакета она уже включена, поэтому нажмите n. Ответьте n на вопрос «Изменить пароль root?» по причине, указанной в следующем абзаце, а на остальные вопросы ответьте Y: удалите анонимных пользователей, запретите удаленный вход root, удалите тестовую базу данных и перезагрузите таблицы привилегий.

Ниже описана часть, которая часто вызывает вопросы. В MariaDB на Ubuntu учетная запись базы данных root использует аутентификацию unix_socket, а не пароль. Это означает, что база данных доверяет пользователю операционной системы, от имени которого уже выполнена аутентификация. Поэтому из оболочки root работает следующая команда:

sudo mysql

...после чего открывается приглашение MariaDB [(none)]> без запроса пароля. Та же команда, выполненная непривилегированным пользователем, отклоняется. В этом и заключается смысл: доступ к root базы данных привязан к sudo на сервере, а пароль невозможно украсть, выманить или подобрать перебором. Это безопаснее пароля, поэтому оставьте эту настройку без изменений. Из этого следует правило: никогда не подключайте приложение к учетной записи root. Создайте отдельного пользователя для каждого приложения (шаг 7), поскольку приложение, подключающееся по TCP с именем пользователя и паролем, все равно не может использовать аутентификацию через сокет. Кроме того, каждое приложение должно иметь доступ только к собственной базе данных.

Шаг 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. Этот устаревший пакет встраивает интерпретатор PHP в каждый процесс Apache. Это простая схема, но каждый рабочий процесс содержит копию PHP независимо от того, обрабатывает ли он скрипт или статическое изображение. Их жизненные циклы связаны, и такая схема работает только с MPM prefork в Apache — наименее эффективным вариантом. PHP-FPM, напротив, запускает PHP в собственном пуле процессов, с которым Apache взаимодействует через сокет. Поэтому Apache может использовать многопоточный MPM event для статических файлов и передавать пулу только PHP-запросы. Пул можно настраивать независимо от веб-сервера. Та же конфигурация FPM будет работать и позже, если перед Apache будет установлен nginx. Это обоснованно считается текущим вариантом по умолчанию.

Apache взаимодействует с FPM через модуль proxy_fcgi. Включите его, включите конфигурацию, установленную пакетом FPM, и перезапустите Apache:

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 скачивается вместо выполнения» и «Основной скрипт неизвестен», которые могут возникнуть позже. Обе ошибки связаны с тем, что 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

Права владельца важны. В Ubuntu Apache и PHP-FPM работают от имени пользователя www-data. Поэтому файлы, которые должен читать веб-сервер, и каталоги, в которые приложение должно выполнять запись, например каталог для загрузок, должны принадлежать 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-приложений для красивых URL. Если приложение не использует эту возможность, замените значение на None, чтобы немного повысить скорость. Включите этот сайт, отключите стандартный, проверьте конфигурацию и перезагрузите Apache:

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 молча обрезает emoji и некоторые символы 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. PHP's mysqli и PDO переключаются на Unix-сокет, если хостом является строка localhost, что соответствует созданной учетной записи. Если фреймворк требует числовой TCP-хост, создайте пользователя в соответствии с фактическим способом подключения: 'appuser'@'127.0.0.1' или @'%' (в сочетании с правилом межсетевого экрана) — только если базе данных необходимо принимать подключения с другого компьютера.

Шаг 8 — Добавление HTTPS с помощью Certbot

Передача формы входа по обычному HTTP приводит к отправке паролей в открытом виде, а все современные браузеры помечают такую страницу как «Не защищено». Certbot решает эту проблему одной командой. Установите его вместе с плагином для Apache:

sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache

Certbot использует здесь два плагина. Apache authenticator подтверждает, что вы контролируете домен, временно размещая проверочный файл через работающий Apache, а apache installer затем перезаписывает ваш виртуальный хост, добавляя блок 443, указывает путь к новому сертификату и по умолчанию перенаправляет весь HTTP-трафик на HTTPS. Certbot не запрашивает подтверждение перенаправления начиная с версии 1.4.0 (май 2020 года), поэтому используйте флаг --no-redirect, если вам необходимо сохранить работу по обычному HTTP. Поскольку на шаге 5 вы настроили корректный ServerName, Certbot автоматически определяет домен. Сертификаты, выданные сегодня, действуют 90 дней (Let's Encrypt сокращает стандартный срок действия до 64 дней 10 февраля 2027 года и до 45 дней 16 февраля 2028 года), а пакет устанавливает таймер systemd, который обновляет их автоматически; проверьте таймер с помощью sudo certbot renew --dry-run, вывод которого должен заканчиваться на Congratulations, all simulated renewals succeeded.

Полное руководство по прохождению проверки, таймеру обновления, а также требованиям к DNS и межсетевому экрану см. в сопутствующем руководстве выпуск бесплатных TLS-сертификатов Let's Encrypt с помощью Certbot на Apache.

Резервное копирование, обновления и защита

Выполняйте резервное копирование двух компонентов, в которых хранится состояние: баз данных и корневого каталога веб-сервера. Ежедневный логический дамп — самый простой и надежный метод, sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', после чего его необходимо скопировать с сервера. Важно обернуть весь конвейер в sudo sh -c: без этого оболочка выполнит перенаправление > /root/... от имени вашего пользователя и завершится с ошибкой Permission denied, так как только mysqldump унаследовал sudo. --single-transaction позволяет получить согласованный снимок таблиц InnoDB без их блокировки. Добавьте к этому tar каталогов /var/www и /etc/apache2/sites-available, и вы сможете восстановить весь стек на новой VPS из этих файлов.

Обновления — это обычный процесс sudo apt update && sudo apt upgrade. Единственная проблема, которая может возникнуть — это обновление версии PHP при переходе на новый релиз дистрибутива. Ubuntu 26.04 LTS поставляется с PHP 8.5 по умолчанию, а пакеты php8.3-*, которые вы установили здесь, отсутствуют в этом релизе. В результате после обновления вы используете php8.5-fpm, сокет становится /run/php/php8.5-fpm.sock, а конфигурация Apache по-прежнему указывает на сокет 8.3. Включите новую конфигурацию (sudo a2enconf php8.5-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 и без пароля. Это сообщение — ожидаемое поведение при корректной работе socket auth, а не признак поломки установки.

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, которая не установлена (например, сокет 8.3 остался после обновления до PHP 8.5), либо файл физически отсутствует, так как DocumentRoot и реальный каталог не совпадают. Проверьте наличие сокета с помощью ls -l /run/php/, убедитесь, что DocumentRoot соответствует расположению файлов, и перезапустите как php8.3-fpm (или ту версию PHP-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 в отдельном пуле с независимой настройкой, к которому Apache обращается через сокет. Он работает с более быстрым многопоточным MPM event и без изменений переносится на nginx в дальнейшем. Это современный вариант по умолчанию. mod_php имеет смысл только для устаревшего приложения, зависящего от поведения 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 из обычной оболочки по этой причине возвращает 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 описаны проверка владения доменом, таймер продления и распространенные причины сбоев.

#lamp#apache#mariadb#php-fpm#ubuntu