"Коротко:
<br />
Чистый VPS на Ubuntu 20.04/22.04 превращается в работающий WordPress с Nginx, PHP 8.2, MariaDB и SSL за одну команду.<br />
Скрипт делает всё сам: создаёт базу, настраивает конфиги, выставляет права, разворачивает WordPress через WP-CLI.<br />
На выходе — продакшн-установка с HTTPS, плагинами и ежедневным бэкапом.<br />
<h2>Диагноз: почему ручная установка WordPress — это квест с плохим концом</h2>
<p>Свежий VPS. Ubuntu 22.04, чистый. Задача простая — поднять WordPress. Открываешь три туториала, в каждом разные команды, на шаге с правами <code>wp-content</code> всё ломается, браузер пишет «Ошибка установки соединения с базой данных WordPress» — и ты уже второй час в этом болоте.</p>
<p>Знакомо. Я поднял несколько сотен таких установок. Сейчас покажу, как это делается один раз — правильно.</p>
<p><strong>Что будет в статье:</strong></p>
<ul>
<li>Готовый <a class="wpil_keyword_link" href="https://it-apteka.com/tag/bash/" target="_blank" rel="noopener" title="Bash" data-wpil-keyword-link="linked" data-wpil-monitor-id="1585">bash</a>-скрипт: Nginx + PHP 8.2 + MariaDB + WordPress одной командой</li>
<li><a href="https://it-apteka.com/acme-nginx-nastrojka-let-s-encrypt-vruchnuju-i-cherez-nginx-proxy-manager/" title="ACME + Nginx: настройка Let’s Encrypt вручную и через Nginx Proxy Manager" target="_blank" rel="noopener" data-wpil-monitor-id="1589">SSL через Let’s Encrypt</a> — бесплатно, автоматически</li>
<li><a href="https://it-apteka.com/glpi-ustanovka-i-nastrojka-https-ldap-inventory-zajavki-i-glpi-agent/" title="GLPI установка и настройка: HTTPS, LDAP, inventory, заявки и GLPI Agent" target="_blank" rel="noopener" data-wpil-monitor-id="1599">Установка и настройка</a> WordPress через WP-CLI без браузера</li>
<li>Автоматический бэкап базы и файлов</li>
<li>Troubleshooting: 6 самых частых ошибок и как их лечить</li>
<li>Бонус: локальная установка WordPress через <a class="wpil_keyword_link" href="https://it-apteka.com/tag/docker/" target="_blank" rel="noopener" title="Docker" data-wpil-keyword-link="linked" data-wpil-monitor-id="1588">Docker</a></li>
</ul>
<p><strong>Время:</strong> 10-15 минут от чистого VPS до рабочего сайта с HTTPS.<br />
<strong>Нужно:</strong> SSH-доступ к серверу, домен направленный на IP сервера.</p>
<h2>Архитектура: что поднимаем и зачем</h2>
<p>Перед тем как запускать команды, пойми что происходит. Стек выглядит так:</p>
<pre class="mermaid">
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Браузер"] -->|"HTTPS :443"| B["Nginx\nвеб-сервер"]
B -->|"FastCGI socket"| C["PHP-FPM 8.2\nобработка кода"]
C -->|"TCP localhost:3306"| D["MariaDB\nбаза данных"]
C --> E["WordPress\n/var/www/domain"]
F["Let's Encrypt\nSSL сертификат"] --> B
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style B fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style C fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style D fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style E fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style F fill:#f8fafc,stroke:#64748b,stroke-width:2px,color:#475569
</pre>
<p>Nginx принимает запросы и отдаёт статику сам. Динамику — PHP файлы — передаёт PHP-FPM через сокет. PHP-FPM выполняет код WordPress, который ходит в MariaDB за данными. Let’s Encrypt даёт сертификат, Nginx его использует для HTTPS.</p>
<p><a href="https://it-apteka.com/nginx-protiv-apache-kakoj-veb-server-vybrat-v-2026-godu/" title="Nginx против Apache: какой веб-сервер выбрать в 2026 году" target="_blank" rel="noopener" data-wpil-monitor-id="1590">Nginx выбран вместо Apache</a> потому что потребляет меньше памяти, лучше держит статическую нагрузку и конфигурируется проще под WordPress. На VPS с 1-2 GB RAM это принципиально.</p>
<h2>Системные требования</h2>
<table>
<thead>
<tr>
<th>Параметр</th>
<th>Минимум</th>
<th>Рекомендую</th>
</tr>
</thead>
<tbody>
<tr>
<td>ОС</td>
<td>Ubuntu 20.04 LTS</td>
<td>Ubuntu 22.04 LTS</td>
</tr>
<tr>
<td>RAM</td>
<td>1 GB</td>
<td>2 GB (если плагинов больше 10)</td>
</tr>
<tr>
<td>Диск</td>
<td>20 GB <a class="wpil_keyword_link" href="https://it-apteka.com/tag/ssd/" target="_blank" rel="noopener" title="ssd" data-wpil-keyword-link="linked" data-wpil-monitor-id="1587">SSD</a></td>
<td>40 GB SSD</td>
</tr>
<tr>
<td>CPU</td>
<td>1 vCPU</td>
<td>2 vCPU</td>
</tr>
<tr>
<td>PHP</td>
<td>8.1</td>
<td>8.2 (используем в скрипте)</td>
</tr>
<tr>
<td>MariaDB</td>
<td>10.6</td>
<td>10.11 LTS</td>
</tr>
<tr>
<td>Nginx</td>
<td>1.18</td>
<td>последний стабильный</td>
</tr>
<tr>
<td>WordPress</td>
<td>6.x</td>
<td>последний (проверяй перед установкой)</td>
</tr>
</tbody>
</table>
<p>На момент публикации актуальна WordPress 6.7 и PHP 8.2. Перед установкой проверь свежие релизы на wordpress.org.</p>
<h2>Порты: что должно быть открыто</h2>
<table>
<thead>
<tr>
<th>Порт</th>
<th>Протокол</th>
<th>Сервис</th>
<th>Направление</th>
</tr>
</thead>
<tbody>
<tr>
<td>22</td>
<td>TCP</td>
<td>SSH</td>
<td>входящий</td>
</tr>
<tr>
<td>80</td>
<td>TCP</td>
<td>HTTP (нужен для Certbot)</td>
<td>входящий</td>
</tr>
<tr>
<td>443</td>
<td>TCP</td>
<td>HTTPS</td>
<td>входящий</td>
</tr>
<tr>
<td>3306</td>
<td>TCP</td>
<td>MariaDB</td>
<td>только localhost, не наружу</td>
</tr>
</tbody>
</table>
<p>Порт 3306 наружу не открывай. База данных должна быть доступна только локально.</p>
<h2>Причины: почему ручная установка всегда что-то ломает</h2>
<p>Разберём конкретно — что идёт не так и почему.</p>
<h3>Несоответствие версий PHP</h3>
<p>Ubuntu 22.04 из коробки даёт PHP 8.1 из стандартного репозитория. Проблема не в этом. Проблема в том, что некоторые плагины и темы ещё пишут под 7.4, а часть уже требует 8.2. Скрипт явно указывает версию и ставит её через репозиторий Ondrej — это стандарт для production-установок на <a href="https://it-apteka.com/zapusk-bash-skriptov-v-linux-cherez-terminal-cron-python-windows-i-raspberry-pi/" title="Запуск bash скрипта: chmod, cron, Python, Windows и Raspberry Pi" target="_blank" rel="noopener" data-wpil-monitor-id="2351">Ubuntu</a>.</p>
<h3>Кривые права на файлы</h3>
<p>Классика жанра — <code>chmod 777</code> на <code>wp-content</code> «чтобы работало». Работает. До первого взлома. Правильная схема: директории 755, файлы 644, владелец <code>www-data</code>. Именно так настраивает <a class="wpil_keyword_link" href="https://it-apteka.com/category/scripts/" target="_blank" rel="noopener" title="Скрипты" data-wpil-keyword-link="linked" data-wpil-monitor-id="1586">скрипт</a>.</p>
<h3>Конфиг Nginx скопирован с Apache-туториала</h3>
<p>Nginx и Apache — разные существа. У Apache есть <code>.htaccess</code>, у Nginx его нет. WordPress генерирует <code>.htaccess</code> для Apache — Nginx его игнорирует. Поэтому без правильного блока <code>try_files</code> в конфиге Nginx постоянные ссылки (ЧПУ) не работают и все URL кроме главной страницы возвращают 404.</p>
<h3>База данных без кодировки utf8mb4</h3>
<p>Если создать базу без явного указания <code>CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci</code>, эмодзи и часть Unicode-символов будут битыми. Мелочь, пока не мелочь.</p>
<h3>wp-config.php без ключей безопасности</h3>
<p>В sample-конфиге стоят заглушки вместо реальных ключей. Если не заменить их уникальными значениями — WordPress работает с дефолтными ключами, что делает cookie сессий предсказуемыми. Скрипт автоматически дёргает свежие ключи с api.wordpress.org.</p>
<h3>SSL настроен, но WordPress не знает об HTTPS</h3>
<p>Certbot перенастроил Nginx на HTTPS. Сайт открывается по HTTPS. Но WordPress в <code>wp-config.php</code> всё ещё думает, что работает по HTTP — и генерирует ссылки без <code>https://</code>. Итог: смешанный контент, бесконечный редирект или слетевшие ресурсы. Лечится двумя строками в <code>wp-config.php</code>, которые скрипт уже добавляет.</p>
<h2>Рецепт: автоматическая установка WordPress на Ubuntu VPS</h2>
<h3>Подготовка</h3>
<p>Три условия перед стартом:</p>
<ul>
<li>Чистый VPS на Ubuntu 20.04 или 22.04 LTS. На других версиях — меняй менеджер пакетов, логика та же.</li>
<li>Root-доступ по SSH. Sudo-пользователь тоже подойдёт — добавь <code>sudo</code> перед командами.</li>
<li>Домен, направленный на IP сервера. A-запись должна резолвиться. Проверь: <code>dig +short yourdomain.com</code> должен вернуть IP сервера. Без этого Let’s Encrypt откажет.</li>
</ul>
<h3>Шаг 1: Подключись и обнови систему</h3>
<p>Старые пакеты — первый источник боли. Обновляй всегда, даже на свежем VPS.</p>
<pre><code class="language-bash">
ssh root@YOUR_SERVER_IP
apt update && apt upgrade -y
</code></pre>
<p>Результат: система обновлена, все пакеты актуальны.</p>
<h3>Шаг 2: Создай скрипт установки</h3>
<p>Скопируй блок целиком. Поменяй переменные в секции «МЕНЯЙ ЗДЕСЬ» на свои значения — домен, пароли, email. Всё остальное трогать не нужно.</p>
<pre><code class="language-bash">
nano /root/install_wordpress.sh
</code></pre>
<p>Содержимое скрипта:</p>
<pre><code class="language-bash">
#!/bin/bash
# ============================================================
# Скрипт установки WordPress на Ubuntu VPS
# Стек: Nginx + PHP-FPM + MariaDB + Let's Encrypt
# Совместимость: Ubuntu 20.04 / 22.04 LTS
# ============================================================
set -e # Остановить при любой ошибке
# ========== МЕНЯЙ ЗДЕСЬ ==========
DOMAIN="yourdomain.com"
DB_NAME="wordpress_db"
DB_USER="wp_user"
DB_PASS="Ch4ng3Me_S3cur3P@ss!"
WP_ADMIN_USER="admin"
WP_ADMIN_PASS="Ch4ng3Me_WpAdm1n!"
WP_ADMIN_EMAIL="your@email.com"
WP_SITE_TITLE="Мой сайт"
PHP_VERSION="8.2"
# =================================
WP_DIR="/var/www/${DOMAIN}"
echo "====== Установка WordPress: старт ======"
echo "Домен: $DOMAIN"
echo "PHP: $PHP_VERSION"
echo "Директория: $WP_DIR"
echo ""
# ----- 1. Обновление -----
echo "[1/9] Обновление системы..."
apt-get update -qq && apt-get upgrade -y -qq
# ----- 2. Nginx -----
echo "[2/9] Установка Nginx..."
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
# ----- 3. PHP -----
echo "[3/9] Установка PHP ${PHP_VERSION}..."
apt-get install -y software-properties-common
add-apt-repository -y ppa:ondrej/php
apt-get update -qq
apt-get install -y \
php${PHP_VERSION}-fpm \
php${PHP_VERSION}-mysql \
php${PHP_VERSION}-curl \
php${PHP_VERSION}-gd \
php${PHP_VERSION}-mbstring \
php${PHP_VERSION}-xml \
php${PHP_VERSION}-zip \
php${PHP_VERSION}-bcmath \
php${PHP_VERSION}-intl \
php${PHP_VERSION}-imagick \
php${PHP_VERSION}-soap \
php${PHP_VERSION}-opcache
systemctl enable php${PHP_VERSION}-fpm
systemctl start php${PHP_VERSION}-fpm
# ----- 4. Настройка PHP -----
echo "[4/9] Настройка PHP..."
PHP_INI="/etc/php/${PHP_VERSION}/fpm/php.ini"
sed -i "s/upload_max_filesize = .*/upload_max_filesize = 64M/" $PHP_INI
sed -i "s/post_max_size = .*/post_max_size = 64M/" $PHP_INI
sed -i "s/memory_limit = .*/memory_limit = 256M/" $PHP_INI
sed -i "s/max_execution_time = .*/max_execution_time = 300/" $PHP_INI
sed -i "s/;date.timezone.*/date.timezone = Europe\/Moscow/" $PHP_INI
# OPcache
cat >> /etc/php/${PHP_VERSION}/fpm/conf.d/10-opcache.ini <<'OPCACHE'
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
OPCACHE
systemctl restart php${PHP_VERSION}-fpm
# ----- 5. MariaDB -----
echo "[5/9] Установка MariaDB..."
apt-get install -y mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
# ----- 6. База данных -----
echo "[6/9] Создание базы данных..."
mysql -u root <<SQLEOF
CREATE DATABASE IF NOT EXISTS ${DB_NAME}
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost'
IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
SQLEOF
echo "База ${DB_NAME} создана."
# ----- 7. WordPress -----
echo "[7/9] Скачивание WordPress..."
mkdir -p $WP_DIR
cd /tmp
wget -q https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
cp -a wordpress/. $WP_DIR/
rm -rf /tmp/wordpress /tmp/latest.tar.gz
# wp-config.php
cp ${WP_DIR}/wp-config-sample.php ${WP_DIR}/wp-config.php
sed -i "s/database_name_here/${DB_NAME}/" ${WP_DIR}/wp-config.php
sed -i "s/username_here/${DB_USER}/" ${WP_DIR}/wp-config.php
sed -i "s/password_here/${DB_PASS}/" ${WP_DIR}/wp-config.php
# Уникальные ключи безопасности
SALT=$(curl -sL https://api.wordpress.org/secret-key/1.1/salt/)
printf '%s\n' "$SALT" >> ${WP_DIR}/wp-config.php
# Дополнительные константы
cat >> ${WP_DIR}/wp-config.php <<'WPCONSTANTS'
/* HTTPS через reverse proxy */
define('FORCE_SSL_ADMIN', true);
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
&& $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
/* Лимит памяти */
define('WP_MEMORY_LIMIT', '256M');
/* Отключаем редактор файлов в админке */
define('DISALLOW_FILE_EDIT', true);
/* Ограничиваем ревизии */
define('WP_POST_REVISIONS', 5);
WPCONSTANTS
# Права на файлы
chown -R www-data:www-data $WP_DIR
find $WP_DIR -type d -exec chmod 755 {} \;
find $WP_DIR -type f -exec chmod 644 {} \;
# ----- 8. Конфиг Nginx -----
echo "[8/9] Настройка Nginx..."
cat > /etc/nginx/sites-available/${DOMAIN} <<NGINXEOF
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN} www.${DOMAIN};
root ${WP_DIR};
index index.php index.html;
access_log /var/log/nginx/${DOMAIN}_access.log;
error_log /var/log/nginx/${DOMAIN}_error.log;
client_max_body_size 64M;
# WordPress permalinks
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
# PHP-FPM
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
# Блокировка скрытых файлов и .htaccess
location ~ /\. {
deny all;
}
# Блокировка wp-config.php
location ~* wp-config\.php {
deny all;
}
# Блокировка xmlrpc (если не нужен)
location = /xmlrpc.php {
deny all;
}
# Кеширование статики
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|svg|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
log_not_found off;
}
}
NGINXEOF
ln -sf /etc/nginx/sites-available/${DOMAIN} /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
# ----- 9. UFW -----
echo "[9/9] Настройка файрвола..."
apt-get install -y ufw
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
echo ""
echo "====== Установка завершена ======"
echo "Сайт: http://${DOMAIN}"
echo "Следующий шаг: настрой SSL (команды ниже в статье)"
echo "================================="
</code></pre>
<p>Сохрани: <code>Ctrl+X</code>, затем <code>Y</code>, затем <code>Enter</code>.</p>
"Важно:
<br />
DB_PASS и WP_ADMIN_PASS в <a href="https://it-apteka.com/rukovodstvo-po-optimizacii-postgresql-i-mysql-5-realnyh-primerov-s-gotovymi-skriptami/" title="Руководство по оптимизации PostgreSQL и MySQL: 5 реальных примеров с готовыми скриптами" target="_blank" rel="noopener" data-wpil-monitor-id="1600">скрипте — примеры</a>. Замени их на свои уникальные значения. Дефолтные пароли из туториалов сканируются ботами в первые же сутки после запуска сайта.<br />
<h3>Шаг 3: Запусти скрипт</h3>
<pre><code class="language-bash">
chmod +x /root/install_wordpress.sh
bash /root/install_wordpress.sh
</code></pre>
<p>Скрипт работает 3-5 минут. В конце выведет «Установка завершена» с адресом сайта. Пока идёт установка - самое время вспомнить, что ты ещё не пил кофе сегодня.</p>
<h3>Шаг 4: Настройка SSL через Let's Encrypt</h3>
<p>Certbot перепишет конфиг Nginx под HTTPS сам. Тебе только нужно подтвердить домен и email.</p>
<pre><code class="language-bash">
apt install -y certbot python3-certbot-nginx
certbot --nginx \
-d yourdomain.com \
-d www.yourdomain.com \
--non-interactive \
--agree-tos \
--email your@email.com \
--redirect
# Проверь, что автообновление настроено
systemctl status certbot.timer
</code></pre>
<p>После этой команды Nginx автоматически перенастраивается на HTTPS. Сертификат обновляется каждые 60 дней через systemd timer - без твоего участия.</p>
<h3>Шаг 5: Установка и настройка WordPress через WP-CLI</h3>
<p>WP-CLI - командная строка для WordPress. Позволяет завершить установку без браузера, сразу настроить сайт, поставить плагины и темы. Это быстрее мастера установки раза в три.</p>
<pre><code class="language-bash">
# Устанавливаем WP-CLI
curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp
# Проверяем
wp --info --allow-root
</code></pre>
<p>Разворачиваем WordPress - те же данные, что вписывал в скрипт:</p>
<pre><code class="language-bash">
cd /var/www/yourdomain.com
wp core install \
--url="https://yourdomain.com" \
--title="Мой сайт" \
--admin_user="admin" \
--admin_password="Ch4ng3Me_WpAdm1n!" \
--admin_email="your@email.com" \
--allow-root
# Ставим структуру постоянных ссылок
wp rewrite structure '/%postname%/' --allow-root
wp rewrite flush --allow-root
# Часовой пояс
wp option update timezone_string 'Europe/Moscow' --allow-root
echo "WordPress готов."
</code></pre>
<p>Всё. WordPress установлен, ЧПУ настроены, часовой пояс выставлен. Без браузера, без мастера, без «after installation».</p>
<h3>Шаг 6: Базовые плагины через WP-CLI</h3>
<p>Ставлю на каждую новую установку. Минимальный набор для нормального сайта:</p>
<pre><code class="language-bash">
cd /var/www/yourdomain.com
# Безопасность
wp plugin install wordfence --activate --allow-root
# SEO
wp plugin install wordpress-seo --activate --allow-root
# Кеширование
wp plugin install w3-total-cache --activate --allow-root
# Резервные копии
wp plugin install updraftplus --activate --allow-root
# Оптимизация изображений
wp plugin install smush --activate --allow-root
# Проверяем результат
wp plugin list --allow-root
</code></pre>
"Правило
<br />
Каждый плагин — это потенциальная уязвимость и дополнительная нагрузка на PHP. Больше 15-20 плагинов без реальной необходимости — и сайт начинает тормозить, а обновлять становится страшно. Ставь только то, что реально используешь.<br />
<h3>Шаг 7: Тема и финальная настройка</h3>
<pre><code class="language-bash">
cd /var/www/yourdomain.com
# Установка темы Astra (легкая, быстрая, популярная)
wp theme install astra --activate --allow-root
# Отключаем комментарии по умолчанию
wp option update default_comment_status closed --allow-root
# Проверяем целостность файлов WordPress
wp core verify-checksums --allow-root
echo "Готово. Открывай https://yourdomain.com/wp-admin"
</code></pre>
<h2>Проверка: убеждаемся что всё работает</h2>
<pre><code class="language-bash">
# Статус всех сервисов
systemctl status nginx php8.2-fpm mariadb --no-pager
# HTTP -> HTTPS редирект работает
curl -I http://yourdomain.com
# HTTPS отвечает
curl -I https://yourdomain.com
# PHP-FPM сокет существует
ls -la /var/run/php/php8.2-fpm.sock
# Логи на ошибки (должно быть пусто или INFO)
tail -20 /var/log/nginx/yourdomain.com_error.log
# Проверка через WP-CLI
cd /var/www/yourdomain.com
wp core version --allow-root
wp plugin list --allow-root
</code></pre>
<p>Правильный вывод <code>curl -I https://yourdomain.com</code>: первая строка <code>HTTP/2 200</code>, в заголовках есть <code>X-Powered-By: PHP/8.2</code>. Если видишь 301 или 302 - редирект работает, смотри куда ведёт.</p>
<h2>Безопасность: минимум которого нельзя пропускать</h2>
<p>Скрипт уже настроил UFW и закрыл несколько векторов в <a href="https://it-apteka.com/nginx_proxy_manager/" title="Nginx Proxy Manager: настройка reverse proxy для Proxmox, Nextcloud, Vaultwarden и ещё семи сервисов" target="_blank" rel="noopener" data-wpil-monitor-id="2276">Nginx</a>. Добавь это поверх.</p>
<h3>Fail2ban против брутфорса</h3>
<pre><code class="language-bash">
apt install -y fail2ban
# Правило для WordPress wp-login.php
cat > /etc/fail2ban/jail.d/wordpress.conf <<'EOF'
[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/yourdomain.com_access.log
maxretry = 5
bantime = 3600
findtime = 600
EOF
# Фильтр
cat > /etc/fail2ban/filter.d/wordpress.conf <<'EOF'
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php
ignoreregex =
EOF
systemctl restart fail2ban
fail2ban-client status
</code></pre>
<h3>Защита SSH</h3>
<pre><code class="language-bash">
# Смени стандартный порт SSH (опционально, но полезно)
# Сначала убедись что можешь войти по ключу!
# Отключи вход по паролю если используешь ключи
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
# Не перезапускай SSH пока не проверишь вход по ключу в отдельной сессии
sshd -t # Проверка конфига без применения
</code></pre>
"Не
<br />
Перед перезапуском sshd открой вторую SSH-сессию и убедись что вход по ключу работает. Если ошибёшься в конфиге и закроешь единственную сессию — потеряешь доступ к серверу. Это не гипотетический сценарий.<br />
<h2>Резервное копирование: настраиваем автобэкап</h2>
<p>Бэкап который не проверен - не бэкап. Настроим ежедневное копирование базы и файлов с автоудалением старых архивов.</p>
<pre><code class="language-bash">
cat > /root/wp_backup.sh <<'BACKUPEOF'
#!/bin/bash
DOMAIN="yourdomain.com"
WP_DIR="/var/www/${DOMAIN}"
BACKUP_DIR="/root/backups/${DOMAIN}"
DB_NAME="wordpress_db"
DB_USER="wp_user"
DB_PASS="Ch4ng3Me_S3cur3P@ss!"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# База данных
mysqldump -u${DB_USER} -p${DB_PASS} ${DB_NAME} \
| gzip > ${BACKUP_DIR}/db_${DATE}.sql.gz
# Файлы (только wp-content - тема, плагины, загрузки)
tar -czf ${BACKUP_DIR}/files_${DATE}.tar.gz \
${WP_DIR}/wp-content/
# Удаляем бэкапы старше 7 дней
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
echo "[$(date)] Бэкап завершён: ${DATE}"
BACKUPEOF
chmod +x /root/wp_backup.sh
# Добавляем в cron: каждую ночь в 3:00
(crontab -l 2>/dev/null; echo "0 3 * * * /root/wp_backup.sh >> /var/log/wp_backup.log 2>&1") | crontab -
# Проверяем что cron добавился
crontab -l
# Тестовый запуск
/root/wp_backup.sh
ls -lh /root/backups/yourdomain.com/
</code></pre>
<p>Бэкапы лежат в <code>/root/backups/yourdomain.com/</code>. Раз в неделю копируй их куда-то ещё - на другой сервер или в облачное хранилище. Одна точка хранения - это не бэкап.</p>
<h3>Как восстановить из бэкапа</h3>
<pre><code class="language-bash">
# Восстановление базы данных
gunzip < /root/backups/yourdomain.com/db_20240101_030000.sql.gz \
| mysql -u wp_user -p wordpress_db
# Восстановление файлов
tar -xzf /root/backups/yourdomain.com/files_20240101_030000.tar.gz \
-C /
# Восстановление прав
chown -R www-data:www-data /var/www/yourdomain.com
</code></pre>
<h2>Обновление WordPress: как делать безопасно</h2>
<pre><code class="language-bash">
cd /var/www/yourdomain.com
# Шаг 1: сначала бэкап
/root/wp_backup.sh
# Шаг 2: проверяем что есть обновления
wp core check-update --allow-root
wp plugin list --update=available --allow-root
# Шаг 3: обновляем WordPress
wp core update --allow-root
wp core update-db --allow-root
# Шаг 4: обновляем плагины
wp plugin update --all --allow-root
# Шаг 5: обновляем тему
wp theme update --all --allow-root
# Шаг 6: проверяем целостность
wp core verify-checksums --allow-root
</code></pre>
<p>Если после обновления что-то сломалось - откатывайся из бэкапа и разбирайся какой именно плагин виноват. Обновляй плагины по одному если не уверен.</p>
<h2>Бонус: установка WordPress локально через Docker</h2>
<p>Если нужна локальная среда для разработки - не мучайся с <a href="https://it-apteka.com/wamp-server-ustanovka-nastrojka-i-sravnenie-s-xampp-polnyj-gajd/" target="_blank" rel="noopener" data-wpil-monitor-id="1998">XAMPP или WAMP</a>. Docker поднимает WordPress локально за 30 секунд и не засоряет систему.</p>
<pre><code class="language-bash">
# Docker если нет
curl -fsSL https://get.docker.com | sh
mkdir wp-local && cd wp-local
cat > docker-compose.yml <<'EOF'
version: '3.8'
services:
db:
image: mariadb:10.11
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: wp_pass
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:php8.2-apache
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: wp_pass
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wp-content:/var/www/html/wp-content
depends_on:
- db
volumes:
db_data:
EOF
docker compose up -d
echo "WordPress локально: http://localhost:8080"
</code></pre>
<p>Через 30 секунд открывай <code>http://localhost:8080</code>. Файлы темы и плагинов живут в папке <code>./wp-content</code> - редактируй прямо на хосте в любом редакторе.</p>
<h2>Осложнения: 6 ошибок и как их лечить</h2>
<h3>Ошибка 1: «Ошибка установки соединения с базой данных WordPress»</h3>
<p>Самая частая. Лечится по алгоритму.</p>
<pre><code class="language-bash">
# Шаг 1: MariaDB запущена?
systemctl status mariadb
# Если не запущена:
systemctl start mariadb
# Шаг 2: данные в wp-config.php совпадают с реальностью?
grep -E "DB_NAME|DB_USER|DB_PASSWORD|DB_HOST" /var/www/yourdomain.com/wp-config.php
# Шаг 3: база и пользователь существуют?
mysql -u root -e "SHOW DATABASES;"
mysql -u root -e "SELECT User, Host FROM mysql.user;"
# Шаг 4: пересоздай пользователя если что-то не то
mysql -u root <<EOF
DROP USER IF EXISTS 'wp_user'@'localhost';
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'Ch4ng3Me_S3cur3P@ss!';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EOF
</code></pre>
<h3>Ошибка 2: Nginx возвращает 502 Bad Gateway</h3>
<pre><code class="language-bash">
# PHP-FPM жив?
systemctl status php8.2-fpm
# Перезапустить:
systemctl restart php8.2-fpm
# Сокет существует?
ls -la /var/run/php/php8.2-fpm.sock
# Логи Nginx:
tail -20 /var/log/nginx/error.log
tail -20 /var/log/php8.2-fpm.log
</code></pre>
<p>502 почти всегда означает что PHP-FPM не отвечает. Либо упал, либо сокет указан неверно в конфиге Nginx. Проверь что путь к сокету в <code>/etc/nginx/sites-available/yourdomain.com</code> совпадает с реальным путём из <code>ls</code>.</p>
<h3>Ошибка 3: Страница установки появляется снова после установки</h3>
<pre><code class="language-bash">
# wp-config.php существует?
ls -la /var/www/yourdomain.com/wp-config.php
# Если нет - создай из sample
cp /var/www/yourdomain.com/wp-config-sample.php \
/var/www/yourdomain.com/wp-config.php
# Пропиши данные БД
nano /var/www/yourdomain.com/wp-config.php
# Права
chown www-data:www-data /var/www/yourdomain.com/wp-config.php
chmod 644 /var/www/yourdomain.com/wp-config.php
</code></pre>
<h3>Ошибка 4: Certbot не выдаёт сертификат</h3>
<pre><code class="language-bash">
# Домен должен резолвиться в IP сервера
dig +short yourdomain.com
curl -s ifconfig.me
# Оба значения должны совпадать
# Порт 80 открыт?
ufw status
ufw allow 80
# Попробуй снова
certbot --nginx -d yourdomain.com -d www.yourdomain.com
</code></pre>
<p>Certbot делает HTTP-challenge: обращается на твой домен по порту 80 и проверяет специальный файл. Если <a class="wpil_keyword_link" href="https://it-apteka.com/tag/dns/" target="_blank" rel="noopener" title="DNS" data-wpil-keyword-link="linked" data-wpil-monitor-id="1584">DNS</a> не указывает на сервер или порт 80 закрыт - сертификат не выдаётся. Никаких исключений.</p>
<h3>Ошибка 5: URL-ы кроме главной страницы дают 404</h3>
<pre><code class="language-bash">
# Проверь конфиг Nginx - должен быть блок try_files
grep -A3 "location /" /etc/nginx/sites-available/yourdomain.com
# Должно быть:
# location / {
# try_files $uri $uri/ /index.php?$args;
# }
# Если блок есть - сбрось rewrite rules через WP-CLI
cd /var/www/yourdomain.com
wp rewrite flush --allow-root
</code></pre>
<h3>Ошибка 6: Белый экран смерти после установки плагина</h3>
<pre><code class="language-bash">
# Включи режим отладки (только на время!)
cd /var/www/yourdomain.com
wp config set WP_DEBUG true --raw --allow-root
wp config set WP_DEBUG_LOG true --raw --allow-root
wp config set WP_DEBUG_DISPLAY false --raw --allow-root
# Смотри лог ошибок
tail -f /var/www/yourdomain.com/wp-content/debug.log
# Деактивируй последний установленный плагин
wp plugin deactivate имя-плагина --allow-root
# После решения - обязательно отключи debug
wp config set WP_DEBUG false --raw --allow-root
</code></pre>
"Если
<br />
Последовательность диагностики: сначала логи Nginx, потом логи PHP-FPM, потом лог WordPress debug.log. Девять из десяти проблем диагностируются за три минуты чтения логов. Не гадай — читай логи.<br />
<h2>FAQ</h2>
<h3>Почему используем Nginx а не Apache для WordPress?</h3>
<p>Nginx потребляет меньше памяти при той же нагрузке - критично на VPS с 1-2 GB RAM. Статику отдаёт быстрее без дополнительных модулей. Конфигурация для WordPress предсказуемее: один файл вместо цепочки <code>.htaccess</code>. Apache тоже работает, просто требует больше ресурсов и настройки.</p>
<h3>Как проверить что SSL сертификат установлен правильно?</h3>
<pre><code class="language-bash">
# Проверка через curl
curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|certificate|expire"
# Проверка срока действия
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -dates
# Автообновление работает?
certbot renew --dry-run
</code></pre>
<h3>Что если нужно поменять версию PHP после установки?</h3>
<pre><code class="language-bash">
# Устанавливаем новую версию (например 8.3)
apt install -y php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd \
php8.3-mbstring php8.3-xml php8.3-zip php8.3-opcache
# Меняем версию в конфиге Nginx
sed -i 's/php8.2-fpm.sock/php8.3-fpm.sock/' \
/etc/nginx/sites-available/yourdomain.com
# Перезапуск
systemctl restart php8.3-fpm
systemctl reload nginx
# Проверяем
php8.3 -v
curl -sI https://yourdomain.com | grep PHP
</code></pre>
<h3>Как узнать что WordPress взломан?</h3>
<pre><code class="language-bash">
# Проверка целостности файлов WordPress
cd /var/www/yourdomain.com
wp core verify-checksums --allow-root
wp plugin verify-checksums --all --allow-root
# Поиск подозрительных PHP файлов в uploads
find /var/www/yourdomain.com/wp-content/uploads -name "*.php" -type f
# Поиск недавно изменённых файлов (последние 24 часа)
find /var/www/yourdomain.com -name "*.php" -newer /var/www/yourdomain.com/wp-login.php
</code></pre>
<h3>Сколько времени занимает полная установка?</h3>
<p>Скрипт выполняется 3-5 минут в зависимости от скорости интернета на сервере. <a href="https://it-apteka.com/avtomatizacija-ssl-dlja-desjatkov-domenov-cherez-acme-sh-i-dns-api-bez-certbot/" title="Автоматизация SSL для десятков доменов через acme.sh и DNS API (без Certbot)" target="_blank" rel="noopener" data-wpil-monitor-id="1591">SSL через Certbot</a> - 1-2 минуты. Настройка через WP-CLI - 1 минута. Итого от чистого VPS до рабочего WordPress с HTTPS: 10-15 минут.</p>
<h2>Итог: что поднято и что теперь работает</h2>
<p>Если прошёл все шаги - у тебя сейчас продакшн-установка WordPress на VPS. Не «как-нибудь работает», а нормально:</p>
<table>
<thead>
<tr>
<th>Компонент</th>
<th>Статус</th>
<th>Что это даёт</th>
</tr>
</thead>
<tbody>
<tr>
<td>Nginx + PHP 8.2 + MariaDB</td>
<td>работает</td>
<td>быстрый LEMP-стек, настроенный под WordPress</td>
</tr>
<tr>
<td>SSL / HTTPS</td>
<td>работает</td>
<td>бесплатный сертификат, автообновление каждые 60 дней</td>
</tr>
<tr>
<td>WordPress</td>
<td>установлен через WP-CLI</td>
<td>правильный wp-config.php, ЧПУ, ключи безопасности</td>
</tr>
<tr>
<td>OPcache</td>
<td>включён</td>
<td>PHP работает быстрее без лишних дисковых операций</td>
</tr>
<tr>
<td>UFW + Fail2ban</td>
<td>настроены</td>
<td>открыты только нужные порты, брутфорс блокируется</td>
</tr>
<tr>
<td>Автобэкап</td>
<td>каждую ночь в 3:00</td>
<td>база и wp-content, хранится 7 дней</td>
</tr>
<tr>
<td>Базовые плагины</td>
<td>установлены</td>
<td>SEO, кеш, <a class="wpil_keyword_link" href="https://it-apteka.com/category/security/" target="_blank" rel="noopener" title="Безопасность" data-wpil-keyword-link="linked" data-wpil-monitor-id="1583">безопасность</a>, бэкап</td>
</tr>
</tbody>
</table>
<p>Теперь зайди в <code>https://yourdomain.com/wp-admin</code>, убедись что всё открывается, и смени пароль администратора если поставил тестовый.</p>
<p>Добавь сайт в Google Search Console и Yandex.Webmaster - чем раньше, тем быстрее начнётся индексация.</p>
"Не
<br />
Описывай конкретно: что делал, что получил, что в логах. Не «не работает» — а «Nginx возвращает 502, в error.log написано вот это». С конкретикой решается за 5 минут.<br />
Коротко: что получишь
Чистый VPS на Ubuntu 20.04/22.04 превращается в работающий WordPress с Nginx, PHP 8.2, MariaDB и SSL за одну команду.
Скрипт делает всё сам: создаёт базу, настраивает конфиги, выставляет права, разворачивает WordPress через WP-CLI.
На выходе — продакшн-установка с HTTPS, плагинами и ежедневным бэкапом.
Диагноз: почему ручная установка WordPress — это квест с плохим концом
Свежий VPS. Ubuntu 22.04, чистый. Задача простая — поднять WordPress. Открываешь три туториала, в каждом разные команды, на шаге с правами wp-content всё ломается, браузер пишет «Ошибка установки соединения с базой данных WordPress» — и ты уже второй час в этом болоте.
Знакомо. Я поднял несколько сотен таких установок. Сейчас покажу, как это делается один раз — правильно.
Что будет в статье:
- Готовый bash-скрипт: Nginx + PHP 8.2 + MariaDB + WordPress одной командой
- SSL через Let’s Encrypt — бесплатно, автоматически
- Установка и настройка WordPress через WP-CLI без браузера
- Автоматический бэкап базы и файлов
- Troubleshooting: 6 самых частых ошибок и как их лечить
- Бонус: локальная установка WordPress через Docker
Время: 10-15 минут от чистого VPS до рабочего сайта с HTTPS.
Нужно: SSH-доступ к серверу, домен направленный на IP сервера.
Архитектура: что поднимаем и зачем
Перед тем как запускать команды, пойми что происходит. Стек выглядит так:
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Браузер"] -->|"HTTPS :443"| B["Nginx\nвеб-сервер"]
B -->|"FastCGI socket"| C["PHP-FPM 8.2\nобработка кода"]
C -->|"TCP localhost:3306"| D["MariaDB\nбаза данных"]
C --> E["WordPress\n/var/www/domain"]
F["Let's Encrypt\nSSL сертификат"] --> B
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style B fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style C fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style D fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style E fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style F fill:#f8fafc,stroke:#64748b,stroke-width:2px,color:#475569
Nginx принимает запросы и отдаёт статику сам. Динамику — PHP файлы — передаёт PHP-FPM через сокет. PHP-FPM выполняет код WordPress, который ходит в MariaDB за данными. Let’s Encrypt даёт сертификат, Nginx его использует для HTTPS.
Nginx выбран вместо Apache потому что потребляет меньше памяти, лучше держит статическую нагрузку и конфигурируется проще под WordPress. На VPS с 1-2 GB RAM это принципиально.
Системные требования
| Параметр |
Минимум |
Рекомендую |
| ОС |
Ubuntu 20.04 LTS |
Ubuntu 22.04 LTS |
| RAM |
1 GB |
2 GB (если плагинов больше 10) |
| Диск |
20 GB SSD |
40 GB SSD |
| CPU |
1 vCPU |
2 vCPU |
| PHP |
8.1 |
8.2 (используем в скрипте) |
| MariaDB |
10.6 |
10.11 LTS |
| Nginx |
1.18 |
последний стабильный |
| WordPress |
6.x |
последний (проверяй перед установкой) |
На момент публикации актуальна WordPress 6.7 и PHP 8.2. Перед установкой проверь свежие релизы на wordpress.org.
Порты: что должно быть открыто
| Порт |
Протокол |
Сервис |
Направление |
| 22 |
TCP |
SSH |
входящий |
| 80 |
TCP |
HTTP (нужен для Certbot) |
входящий |
| 443 |
TCP |
HTTPS |
входящий |
| 3306 |
TCP |
MariaDB |
только localhost, не наружу |
Порт 3306 наружу не открывай. База данных должна быть доступна только локально.
Причины: почему ручная установка всегда что-то ломает
Разберём конкретно — что идёт не так и почему.
Несоответствие версий PHP
Ubuntu 22.04 из коробки даёт PHP 8.1 из стандартного репозитория. Проблема не в этом. Проблема в том, что некоторые плагины и темы ещё пишут под 7.4, а часть уже требует 8.2. Скрипт явно указывает версию и ставит её через репозиторий Ondrej — это стандарт для production-установок на Ubuntu.
Кривые права на файлы
Классика жанра — chmod 777 на wp-content «чтобы работало». Работает. До первого взлома. Правильная схема: директории 755, файлы 644, владелец www-data. Именно так настраивает скрипт.
Конфиг Nginx скопирован с Apache-туториала
Nginx и Apache — разные существа. У Apache есть .htaccess, у Nginx его нет. WordPress генерирует .htaccess для Apache — Nginx его игнорирует. Поэтому без правильного блока try_files в конфиге Nginx постоянные ссылки (ЧПУ) не работают и все URL кроме главной страницы возвращают 404.
База данных без кодировки utf8mb4
Если создать базу без явного указания CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, эмодзи и часть Unicode-символов будут битыми. Мелочь, пока не мелочь.
wp-config.php без ключей безопасности
В sample-конфиге стоят заглушки вместо реальных ключей. Если не заменить их уникальными значениями — WordPress работает с дефолтными ключами, что делает cookie сессий предсказуемыми. Скрипт автоматически дёргает свежие ключи с api.wordpress.org.
SSL настроен, но WordPress не знает об HTTPS
Certbot перенастроил Nginx на HTTPS. Сайт открывается по HTTPS. Но WordPress в wp-config.php всё ещё думает, что работает по HTTP — и генерирует ссылки без https://. Итог: смешанный контент, бесконечный редирект или слетевшие ресурсы. Лечится двумя строками в wp-config.php, которые скрипт уже добавляет.
Рецепт: автоматическая установка WordPress на Ubuntu VPS
Подготовка
Три условия перед стартом:
- Чистый VPS на Ubuntu 20.04 или 22.04 LTS. На других версиях — меняй менеджер пакетов, логика та же.
- Root-доступ по SSH. Sudo-пользователь тоже подойдёт — добавь
sudo перед командами.
- Домен, направленный на IP сервера. A-запись должна резолвиться. Проверь:
dig +short yourdomain.com должен вернуть IP сервера. Без этого Let’s Encrypt откажет.
Шаг 1: Подключись и обнови систему
Старые пакеты — первый источник боли. Обновляй всегда, даже на свежем VPS.
ssh root@YOUR_SERVER_IP
apt update && apt upgrade -y
Результат: система обновлена, все пакеты актуальны.
Шаг 2: Создай скрипт установки
Скопируй блок целиком. Поменяй переменные в секции «МЕНЯЙ ЗДЕСЬ» на свои значения — домен, пароли, email. Всё остальное трогать не нужно.
nano /root/install_wordpress.sh
Содержимое скрипта:
#!/bin/bash
# ============================================================
# Скрипт установки WordPress на Ubuntu VPS
# Стек: Nginx + PHP-FPM + MariaDB + Let's Encrypt
# Совместимость: Ubuntu 20.04 / 22.04 LTS
# ============================================================
set -e # Остановить при любой ошибке
# ========== МЕНЯЙ ЗДЕСЬ ==========
DOMAIN="yourdomain.com"
DB_NAME="wordpress_db"
DB_USER="wp_user"
DB_PASS="Ch4ng3Me_S3cur3P@ss!"
WP_ADMIN_USER="admin"
WP_ADMIN_PASS="Ch4ng3Me_WpAdm1n!"
WP_ADMIN_EMAIL="your@email.com"
WP_SITE_TITLE="Мой сайт"
PHP_VERSION="8.2"
# =================================
WP_DIR="/var/www/${DOMAIN}"
echo "====== Установка WordPress: старт ======"
echo "Домен: $DOMAIN"
echo "PHP: $PHP_VERSION"
echo "Директория: $WP_DIR"
echo ""
# ----- 1. Обновление -----
echo "[1/9] Обновление системы..."
apt-get update -qq && apt-get upgrade -y -qq
# ----- 2. Nginx -----
echo "[2/9] Установка Nginx..."
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
# ----- 3. PHP -----
echo "[3/9] Установка PHP ${PHP_VERSION}..."
apt-get install -y software-properties-common
add-apt-repository -y ppa:ondrej/php
apt-get update -qq
apt-get install -y \
php${PHP_VERSION}-fpm \
php${PHP_VERSION}-mysql \
php${PHP_VERSION}-curl \
php${PHP_VERSION}-gd \
php${PHP_VERSION}-mbstring \
php${PHP_VERSION}-xml \
php${PHP_VERSION}-zip \
php${PHP_VERSION}-bcmath \
php${PHP_VERSION}-intl \
php${PHP_VERSION}-imagick \
php${PHP_VERSION}-soap \
php${PHP_VERSION}-opcache
systemctl enable php${PHP_VERSION}-fpm
systemctl start php${PHP_VERSION}-fpm
# ----- 4. Настройка PHP -----
echo "[4/9] Настройка PHP..."
PHP_INI="/etc/php/${PHP_VERSION}/fpm/php.ini"
sed -i "s/upload_max_filesize = .*/upload_max_filesize = 64M/" $PHP_INI
sed -i "s/post_max_size = .*/post_max_size = 64M/" $PHP_INI
sed -i "s/memory_limit = .*/memory_limit = 256M/" $PHP_INI
sed -i "s/max_execution_time = .*/max_execution_time = 300/" $PHP_INI
sed -i "s/;date.timezone.*/date.timezone = Europe\/Moscow/" $PHP_INI
# OPcache
cat >> /etc/php/${PHP_VERSION}/fpm/conf.d/10-opcache.ini <<'OPCACHE'
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
OPCACHE
systemctl restart php${PHP_VERSION}-fpm
# ----- 5. MariaDB -----
echo "[5/9] Установка MariaDB..."
apt-get install -y mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
# ----- 6. База данных -----
echo "[6/9] Создание базы данных..."
mysql -u root <> ${WP_DIR}/wp-config.php
# Дополнительные константы
cat >> ${WP_DIR}/wp-config.php < /etc/nginx/sites-available/${DOMAIN} <<NGINXEOF
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN} www.${DOMAIN};
root ${WP_DIR};
index index.php index.html;
access_log /var/log/nginx/${DOMAIN}_access.log;
error_log /var/log/nginx/${DOMAIN}_error.log;
client_max_body_size 64M;
# WordPress permalinks
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
# PHP-FPM
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
# Блокировка скрытых файлов и .htaccess
location ~ /\. {
deny all;
}
# Блокировка wp-config.php
location ~* wp-config\.php {
deny all;
}
# Блокировка xmlrpc (если не нужен)
location = /xmlrpc.php {
deny all;
}
# Кеширование статики
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|svg|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
log_not_found off;
}
}
NGINXEOF
ln -sf /etc/nginx/sites-available/${DOMAIN} /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
# ----- 9. UFW -----
echo "[9/9] Настройка файрвола..."
apt-get install -y ufw
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
echo ""
echo "====== Установка завершена ======"
echo "Сайт: http://${DOMAIN}"
echo "Следующий шаг: настрой SSL (команды ниже в статье)"
echo "================================="
Сохрани: Ctrl+X, затем Y, затем Enter.
Важно: поменяй пароли перед запуском
DB_PASS и WP_ADMIN_PASS в
скрипте — примеры. Замени их на свои уникальные значения. Дефолтные пароли из туториалов сканируются ботами в первые же сутки после запуска сайта.
Шаг 3: Запусти скрипт
chmod +x /root/install_wordpress.sh
bash /root/install_wordpress.sh
Скрипт работает 3-5 минут. В конце выведет «Установка завершена» с адресом сайта. Пока идёт установка - самое время вспомнить, что ты ещё не пил кофе сегодня.
Шаг 4: Настройка SSL через Let's Encrypt
Certbot перепишет конфиг Nginx под HTTPS сам. Тебе только нужно подтвердить домен и email.
apt install -y certbot python3-certbot-nginx
certbot --nginx \
-d yourdomain.com \
-d www.yourdomain.com \
--non-interactive \
--agree-tos \
--email your@email.com \
--redirect
# Проверь, что автообновление настроено
systemctl status certbot.timer
После этой команды Nginx автоматически перенастраивается на HTTPS. Сертификат обновляется каждые 60 дней через systemd timer - без твоего участия.
Шаг 5: Установка и настройка WordPress через WP-CLI
WP-CLI - командная строка для WordPress. Позволяет завершить установку без браузера, сразу настроить сайт, поставить плагины и темы. Это быстрее мастера установки раза в три.
# Устанавливаем WP-CLI
curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp
# Проверяем
wp --info --allow-root
Разворачиваем WordPress - те же данные, что вписывал в скрипт:
cd /var/www/yourdomain.com
wp core install \
--url="https://yourdomain.com" \
--title="Мой сайт" \
--admin_user="admin" \
--admin_password="Ch4ng3Me_WpAdm1n!" \
--admin_email="your@email.com" \
--allow-root
# Ставим структуру постоянных ссылок
wp rewrite structure '/%postname%/' --allow-root
wp rewrite flush --allow-root
# Часовой пояс
wp option update timezone_string 'Europe/Moscow' --allow-root
echo "WordPress готов."
Всё. WordPress установлен, ЧПУ настроены, часовой пояс выставлен. Без браузера, без мастера, без «after installation».
Шаг 6: Базовые плагины через WP-CLI
Ставлю на каждую новую установку. Минимальный набор для нормального сайта:
cd /var/www/yourdomain.com
# Безопасность
wp plugin install wordfence --activate --allow-root
# SEO
wp plugin install wordpress-seo --activate --allow-root
# Кеширование
wp plugin install w3-total-cache --activate --allow-root
# Резервные копии
wp plugin install updraftplus --activate --allow-root
# Оптимизация изображений
wp plugin install smush --activate --allow-root
# Проверяем результат
wp plugin list --allow-root
Правило 15 плагинов
Каждый плагин — это потенциальная уязвимость и дополнительная нагрузка на PHP. Больше 15-20 плагинов без реальной необходимости — и сайт начинает тормозить, а обновлять становится страшно. Ставь только то, что реально используешь.
Шаг 7: Тема и финальная настройка
cd /var/www/yourdomain.com
# Установка темы Astra (легкая, быстрая, популярная)
wp theme install astra --activate --allow-root
# Отключаем комментарии по умолчанию
wp option update default_comment_status closed --allow-root
# Проверяем целостность файлов WordPress
wp core verify-checksums --allow-root
echo "Готово. Открывай https://yourdomain.com/wp-admin"
Проверка: убеждаемся что всё работает
# Статус всех сервисов
systemctl status nginx php8.2-fpm mariadb --no-pager
# HTTP -> HTTPS редирект работает
curl -I http://yourdomain.com
# HTTPS отвечает
curl -I https://yourdomain.com
# PHP-FPM сокет существует
ls -la /var/run/php/php8.2-fpm.sock
# Логи на ошибки (должно быть пусто или INFO)
tail -20 /var/log/nginx/yourdomain.com_error.log
# Проверка через WP-CLI
cd /var/www/yourdomain.com
wp core version --allow-root
wp plugin list --allow-root
Правильный вывод curl -I https://yourdomain.com: первая строка HTTP/2 200, в заголовках есть X-Powered-By: PHP/8.2. Если видишь 301 или 302 - редирект работает, смотри куда ведёт.
Безопасность: минимум которого нельзя пропускать
Скрипт уже настроил UFW и закрыл несколько векторов в Nginx. Добавь это поверх.
Fail2ban против брутфорса
apt install -y fail2ban
# Правило для WordPress wp-login.php
cat > /etc/fail2ban/jail.d/wordpress.conf < /etc/fail2ban/filter.d/wordpress.conf <<'EOF'
[Definition]
failregex = ^ .* "POST /wp-login.php
ignoreregex =
EOF
systemctl restart fail2ban
fail2ban-client status
Защита SSH
# Смени стандартный порт SSH (опционально, но полезно)
# Сначала убедись что можешь войти по ключу!
# Отключи вход по паролю если используешь ключи
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
# Не перезапускай SSH пока не проверишь вход по ключу в отдельной сессии
sshd -t # Проверка конфига без применения
Не закрывай текущую SSH сессию
Перед перезапуском sshd открой вторую SSH-сессию и убедись что вход по ключу работает. Если ошибёшься в конфиге и закроешь единственную сессию — потеряешь доступ к серверу. Это не гипотетический сценарий.
Резервное копирование: настраиваем автобэкап
Бэкап который не проверен - не бэкап. Настроим ежедневное копирование базы и файлов с автоудалением старых архивов.
cat > /root/wp_backup.sh < ${BACKUP_DIR}/db_${DATE}.sql.gz
# Файлы (только wp-content - тема, плагины, загрузки)
tar -czf ${BACKUP_DIR}/files_${DATE}.tar.gz \
${WP_DIR}/wp-content/
# Удаляем бэкапы старше 7 дней
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
echo "[$(date)] Бэкап завершён: ${DATE}"
BACKUPEOF
chmod +x /root/wp_backup.sh
# Добавляем в cron: каждую ночь в 3:00
(crontab -l 2>/dev/null; echo "0 3 * * * /root/wp_backup.sh >> /var/log/wp_backup.log 2>&1") | crontab -
# Проверяем что cron добавился
crontab -l
# Тестовый запуск
/root/wp_backup.sh
ls -lh /root/backups/yourdomain.com/
Бэкапы лежат в /root/backups/yourdomain.com/. Раз в неделю копируй их куда-то ещё - на другой сервер или в облачное хранилище. Одна точка хранения - это не бэкап.
Как восстановить из бэкапа
# Восстановление базы данных
gunzip < /root/backups/yourdomain.com/db_20240101_030000.sql.gz \
| mysql -u wp_user -p wordpress_db
# Восстановление файлов
tar -xzf /root/backups/yourdomain.com/files_20240101_030000.tar.gz \
-C /
# Восстановление прав
chown -R www-data:www-data /var/www/yourdomain.com
Обновление WordPress: как делать безопасно
cd /var/www/yourdomain.com
# Шаг 1: сначала бэкап
/root/wp_backup.sh
# Шаг 2: проверяем что есть обновления
wp core check-update --allow-root
wp plugin list --update=available --allow-root
# Шаг 3: обновляем WordPress
wp core update --allow-root
wp core update-db --allow-root
# Шаг 4: обновляем плагины
wp plugin update --all --allow-root
# Шаг 5: обновляем тему
wp theme update --all --allow-root
# Шаг 6: проверяем целостность
wp core verify-checksums --allow-root
Если после обновления что-то сломалось - откатывайся из бэкапа и разбирайся какой именно плагин виноват. Обновляй плагины по одному если не уверен.
Бонус: установка WordPress локально через Docker
Если нужна локальная среда для разработки - не мучайся с XAMPP или WAMP. Docker поднимает WordPress локально за 30 секунд и не засоряет систему.
# Docker если нет
curl -fsSL https://get.docker.com | sh
mkdir wp-local && cd wp-local
cat > docker-compose.yml <<'EOF'
version: '3.8'
services:
db:
image: mariadb:10.11
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: wp_pass
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:php8.2-apache
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: wp_pass
WORDPRESS_DB_NAME: wordpress
volumes:
- ./wp-content:/var/www/html/wp-content
depends_on:
- db
volumes:
db_data:
EOF
docker compose up -d
echo "WordPress локально: http://localhost:8080"
Через 30 секунд открывай http://localhost:8080. Файлы темы и плагинов живут в папке ./wp-content - редактируй прямо на хосте в любом редакторе.
Осложнения: 6 ошибок и как их лечить
Ошибка 1: «Ошибка установки соединения с базой данных WordPress»
Самая частая. Лечится по алгоритму.
# Шаг 1: MariaDB запущена?
systemctl status mariadb
# Если не запущена:
systemctl start mariadb
# Шаг 2: данные в wp-config.php совпадают с реальностью?
grep -E "DB_NAME|DB_USER|DB_PASSWORD|DB_HOST" /var/www/yourdomain.com/wp-config.php
# Шаг 3: база и пользователь существуют?
mysql -u root -e "SHOW DATABASES;"
mysql -u root -e "SELECT User, Host FROM mysql.user;"
# Шаг 4: пересоздай пользователя если что-то не то
mysql -u root <<EOF
DROP USER IF EXISTS 'wp_user'@'localhost';
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'Ch4ng3Me_S3cur3P@ss!';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EOF
Ошибка 2: Nginx возвращает 502 Bad Gateway
# PHP-FPM жив?
systemctl status php8.2-fpm
# Перезапустить:
systemctl restart php8.2-fpm
# Сокет существует?
ls -la /var/run/php/php8.2-fpm.sock
# Логи Nginx:
tail -20 /var/log/nginx/error.log
tail -20 /var/log/php8.2-fpm.log
502 почти всегда означает что PHP-FPM не отвечает. Либо упал, либо сокет указан неверно в конфиге Nginx. Проверь что путь к сокету в /etc/nginx/sites-available/yourdomain.com совпадает с реальным путём из ls.
Ошибка 3: Страница установки появляется снова после установки
# wp-config.php существует?
ls -la /var/www/yourdomain.com/wp-config.php
# Если нет - создай из sample
cp /var/www/yourdomain.com/wp-config-sample.php \
/var/www/yourdomain.com/wp-config.php
# Пропиши данные БД
nano /var/www/yourdomain.com/wp-config.php
# Права
chown www-data:www-data /var/www/yourdomain.com/wp-config.php
chmod 644 /var/www/yourdomain.com/wp-config.php
Ошибка 4: Certbot не выдаёт сертификат
# Домен должен резолвиться в IP сервера
dig +short yourdomain.com
curl -s ifconfig.me
# Оба значения должны совпадать
# Порт 80 открыт?
ufw status
ufw allow 80
# Попробуй снова
certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot делает HTTP-challenge: обращается на твой домен по порту 80 и проверяет специальный файл. Если DNS не указывает на сервер или порт 80 закрыт - сертификат не выдаётся. Никаких исключений.
Ошибка 5: URL-ы кроме главной страницы дают 404
# Проверь конфиг Nginx - должен быть блок try_files
grep -A3 "location /" /etc/nginx/sites-available/yourdomain.com
# Должно быть:
# location / {
# try_files $uri $uri/ /index.php?$args;
# }
# Если блок есть - сбрось rewrite rules через WP-CLI
cd /var/www/yourdomain.com
wp rewrite flush --allow-root
Ошибка 6: Белый экран смерти после установки плагина
# Включи режим отладки (только на время!)
cd /var/www/yourdomain.com
wp config set WP_DEBUG true --raw --allow-root
wp config set WP_DEBUG_LOG true --raw --allow-root
wp config set WP_DEBUG_DISPLAY false --raw --allow-root
# Смотри лог ошибок
tail -f /var/www/yourdomain.com/wp-content/debug.log
# Деактивируй последний установленный плагин
wp plugin deactivate имя-плагина --allow-root
# После решения - обязательно отключи debug
wp config set WP_DEBUG false --raw --allow-root
Если ничего не помогло - смотри сюда
Последовательность диагностики: сначала логи Nginx, потом логи PHP-FPM, потом лог WordPress debug.log. Девять из десяти проблем диагностируются за три минуты чтения логов. Не гадай — читай логи.
FAQ
Почему используем Nginx а не Apache для WordPress?
Nginx потребляет меньше памяти при той же нагрузке - критично на VPS с 1-2 GB RAM. Статику отдаёт быстрее без дополнительных модулей. Конфигурация для WordPress предсказуемее: один файл вместо цепочки .htaccess. Apache тоже работает, просто требует больше ресурсов и настройки.
Как проверить что SSL сертификат установлен правильно?
# Проверка через curl
curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|certificate|expire"
# Проверка срока действия
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -dates
# Автообновление работает?
certbot renew --dry-run
Что если нужно поменять версию PHP после установки?
# Устанавливаем новую версию (например 8.3)
apt install -y php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd \
php8.3-mbstring php8.3-xml php8.3-zip php8.3-opcache
# Меняем версию в конфиге Nginx
sed -i 's/php8.2-fpm.sock/php8.3-fpm.sock/' \
/etc/nginx/sites-available/yourdomain.com
# Перезапуск
systemctl restart php8.3-fpm
systemctl reload nginx
# Проверяем
php8.3 -v
curl -sI https://yourdomain.com | grep PHP
Как узнать что WordPress взломан?
# Проверка целостности файлов WordPress
cd /var/www/yourdomain.com
wp core verify-checksums --allow-root
wp plugin verify-checksums --all --allow-root
# Поиск подозрительных PHP файлов в uploads
find /var/www/yourdomain.com/wp-content/uploads -name "*.php" -type f
# Поиск недавно изменённых файлов (последние 24 часа)
find /var/www/yourdomain.com -name "*.php" -newer /var/www/yourdomain.com/wp-login.php
Сколько времени занимает полная установка?
Скрипт выполняется 3-5 минут в зависимости от скорости интернета на сервере. SSL через Certbot - 1-2 минуты. Настройка через WP-CLI - 1 минута. Итого от чистого VPS до рабочего WordPress с HTTPS: 10-15 минут.
Итог: что поднято и что теперь работает
Если прошёл все шаги - у тебя сейчас продакшн-установка WordPress на VPS. Не «как-нибудь работает», а нормально:
| Компонент |
Статус |
Что это даёт |
| Nginx + PHP 8.2 + MariaDB |
работает |
быстрый LEMP-стек, настроенный под WordPress |
| SSL / HTTPS |
работает |
бесплатный сертификат, автообновление каждые 60 дней |
| WordPress |
установлен через WP-CLI |
правильный wp-config.php, ЧПУ, ключи безопасности |
| OPcache |
включён |
PHP работает быстрее без лишних дисковых операций |
| UFW + Fail2ban |
настроены |
открыты только нужные порты, брутфорс блокируется |
| Автобэкап |
каждую ночь в 3:00 |
база и wp-content, хранится 7 дней |
| Базовые плагины |
установлены |
SEO, кеш, безопасность, бэкап |
Теперь зайди в https://yourdomain.com/wp-admin, убедись что всё открывается, и смени пароль администратора если поставил тестовый.
Добавь сайт в Google Search Console и Yandex.Webmaster - чем раньше, тем быстрее начнётся индексация.
Не заработало? Пиши.
Описывай конкретно: что делал, что получил, что в логах. Не «не работает» — а «Nginx возвращает 502, в error.log написано вот это». С конкретикой решается за 5 минут.