Три конфига WordPress: .env, wp-config.php и php.ini под своё окружение

Предыдущая статья разложила конфигурацию сайта на пять слоёв и показала, кто кого переопределяет. Здесь — практика того же: три текстовых конфига, которые администратор WordPress держит в руках каждый день (wp-config.php, .env, php.ini), где у каждого правильный набор директив и почему править их наугад дороже, чем кажется. Разберём пограничную путаницу («адрес сайта — в базу или в wp-config?», «зачем в WordPress .env, если ядро его не читает?», «лимит памяти — в php.ini или в пуле?»), покажем рабочий паттерн разделения dev/staging/prod через переменные окружения и объясним, почему сгенерированный конфиг всегда приходит с плейсхолдерами вместо паролей, а не с боевыми секретами.


Часть I. Три конфига и границы их ответственности

Начнём с главного, что путают: это три разных файла, и настройка попадает «не туда» не из-за ошибки ввода, а из-за неверной модели. Одна таблица:

ФайлГде лежитОтветственностьЧитается кемКогда правится
wp-config.phpв корне WordPressконстанты ядра: креды БД, адрес сайта, соли, префикс таблиц, WP_DEBUG, FS_METHODWordPress-ядро, до подключения к БДпри установке и миграции
.envрядом с проектом (не обязателен)ключ=значение для разделения секретов/параметров между окружениямиобёртка (Docker / mu-plugin / wp-cli env), не ядропри настройке dev/staging/prod
php.ini/etc/php/…/fpm/php.ini и копия в пулелимиты движка: memory_limit, upload_max_filesize, max_execution_time, OpcachePHP-FPM (и отдельно — CLI)при смене нагрузок/лимитов

Мнемоника для границы: wp-config отвечает «как WordPress находит себя», php.ini — «сколько движку можно», .env — «что меняется между окружениями». Лимит загрузки 64 МБ — это php.ini (движок), а не wp-config (там такой константы нет). Название сайта, который виден в админке — это база (слой опций), а не wp-config. Адрес продакшна, который нельзя менять без деплоя — как раз кандидатур в .env. Если настройка «не нашла своего файла» — вернитесь к таблице слоёв в предыдущей статье.

Ещё одна пограничная ловушка: php.ini и пул — это один и тот же слой, но два файла (базовый слой против точечного на сайт). Не путайте их с wp-config: директива memory_limit в php.ini не имеет отношения к константе WP_MEMORY_LIMIT, которая просит у PHP больше памяти для ядра WordPress. У каждой из них свой адресат.


Часть II. wp-config.php: константы, которые нельзя править наугад

Ключевое свойство этого файла: он исполняется до того, как WordPress что-либо прочитал из базы. Значит, всё, что здесь объявлено, доступно ядру в самый ранний момент — и всё, что здесь неверно, ломает загрузку сайта целиком, а не отдельную страницу.

Базовый набор:

PHP
<?php
// подключение к базе — без этих четырёх сайт не стартует вовсе
define( 'DB_NAME',     'wp_database' );
define( 'DB_USER',     'wp_user' );
define( 'DB_PASSWORD', 'REPLACE_ME' );   // см. Часть V: боевой пароль подставляет человек
define( 'DB_HOST',     'localhost' );

// префикс таблиц — у стоковой установки 'wp_'
$table_prefix = 'wp_';

// уникальный «отпечаток» установки: 8 ключей-солей
define( 'AUTH_KEY',         'REPLACE_ME' );
define( 'NONCE_SALT',       'REPLACE_ME' );
// … (остальные шесть)

// поведение ядра
define( 'WP_DEBUG',        false );
define( 'WP_DEBUG_LOG',    true );      // писать ошибки в файл, не на экран гостя
define( 'WP_DEBUG_DISPLAY', false );
define( 'DISALLOW_FILE_EDIT', true );   // запретить правку тем/плагинов из админки
define( 'FS_METHOD',       'direct' );  // ставить обновления напрямую, без FTP

Три правила, нарушение которых стоит часов диагностики:

1. Адрес сайта — в одном месте, а не в двух. Либо home/siteurl в базе (обычный случай), либо константы в wp-config:

PHP
<?php
// ЭТИ строки перекроют поле в базе. Держать и базу, и константы — значит рассинхронить их при миграции.
define( 'WP_HOME',    'https://example.test' );
define( 'WP_SITEURL', 'https://example.test' );

Если решили жить на константах — помните: wp search-replace по базе на них не действует. Разбор замены URL при переезде — в Миграция WordPress между серверами.

2. Соли и ключи — генерируются, не выдумываются. Они уникальны на всю установку; их смена выкидывает из сессий всех залогиненных (и ломает active-nonce у форм). Генерируются официальным WordPress-генератором salts, а не «на глаз». Почему нельзя терять соли в открытом виде и как они работают с паролями — в Шифрование данных и хранение паролей.

3. В wp-config нет «бытовых» настроек. Часовой пояс, «показывать N записей», категория по умолчанию — этого здесь быть не должно; это опции в базе. Критерий из предыдущей статьи: «можно ли это задать в админке?» — если да, дом в базе, а не здесь.

Отдельная, самая дорогая грабля: wp-config.php — shared-файл между релизами, его нельзя перезаписывать кодом из репозитория. Иначе новый выкат затрёт креды. Как выкатывать код, не трогая wp-config — в Деплой без боли.


Часть III. .env в WordPress: где уместен, а где нет

Самый частый вопрос серии: «зачем WordPress нужен .env, если ядро его не читает?» Честный ответ: ядро действительно не читает .env. Файл работает только тогда, когда есть обёртка, которая его грузит и передаёт переменные в PHP. Отсюда и правила применения.

Где .env уместен

Когда у вас несколько окружений (dev на ноутбуке, staging, prod на VPS) и вы хотите один код, но разные секреты/параметры. Тогда:

  1. окружение (Docker, systemd, nginx→php-fpm env[...], wp-cli) загружает .env и делает переменные доступными PHP;
  2. wp-config.php читает их через getenv() с фолбэком — и сам становится окруженчески-нейтральным:
PHP
<?php
// wp-config.php читает переменные окружения; боевые значения приходят из .env, а не хардкодом
function wpc_env( string $name, ?string $default = null ): ?string {
    $v = getenv( $name );
    return ( $v === false || $v === '' ) ? $default : $v;
}

define( 'DB_NAME',     wpc_env( 'DB_NAME',     'wp_database' ) );
define( 'DB_USER',     wpc_env( 'DB_USER',     'wp_user' ) );
define( 'DB_PASSWORD', wpc_env( 'DB_PASSWORD', 'REPLACE_ME' ) );
define( 'DB_HOST',     wpc_env( 'DB_HOST',     'localhost' ) );

define( 'WP_HOME',    wpc_env( 'WP_HOME',    'https://example.test' ) );
define( 'WP_SITEURL', wpc_env( 'WP_SITEURL', 'https://example.test' ) );

define( 'WP_DEBUG', wpc_env( 'WP_DEBUG' ) === '1' );   // 1 в dev, пусто/0 в prod
# .env (для локальной разработки) — НЕ коммитить в git
DB_NAME=wp_database
DB_USER=wp_user
DB_PASSWORD=REPLACE_ME_WITH_REAL_DEV_PASSWORD
DB_HOST=localhost
WP_HOME=https://example.test
WP_DEBUG=1

Практическая польза ровно одна: wp-config.php и код больше не содержат секретов и адресов — те живут в .env, у которого свои права (chmod 600) и который лежит в .gitignore. При переезде между окружениями меняется только .env.

Где .env бесполезен и вреден

На голом shared-хостинге без доступа к переменным окружения и без Docker — .env просто нечем загрузить. Попытка «прикрутить» его ради совместности с модой добавляет лишний слой, который молча не работает: getenv() вернёт пусто, сайт возьмёт фолбэк, а вы будете думать, что сменили пароль, хотя .env не читался. В этом случае wp-config.php с константами — и есть правильный, единственный дом значений.

Минимальный вывод: .env имеет смысл ровно тогда, когда есть загрузчик (Docker/systemd/nginx-env/mu-plugin с Dotenv) и есть ≥2 окружения. Иначе — константы в wp-config, а про переменные окружения забудьте. Генератор форматов это различает: .env-выход — это пары КЛЮЧ=ЗНАЧЕНИЕ верхним регистром, а не PHP-константы.


Часть IV. php.ini: лимиты движка и связь с пулом

php.ini задаёт «сколько разрешено PHP», и это тот слой, где чаще всего «поставил лимит — а он не подействовал». Причин ровно две, обе из предыдущей статьи, здесь — применение к трём типичным директивам.

Что реально задаётся в php.ini (базовый слой для всех пулов):

; /etc/php/8.2/fpm/php.ini
memory_limit        = 256M    ; сколько памяти на один процесс
upload_max_filesize = 64M     ; размер одного загружаемого файла
post_max_size       = 68M     ; всё POST-тело; обязано быть ≥ upload_max_filesize
max_execution_time  = 300     ; сек на обработку запроса
max_input_time      = 300
display_errors      = Off     ; в prod ошибки гостю не показывают
log_errors          = On      ; и пишутся в error_log

Три правила:

  1. post_max_size ≥ upload_max_filesize — иначе большой файл отсекается раньше, чем PHP увидит его как «upload». Дефолтная инверсия (upload 64M при post 8M) — классика «прикрепляю видео, а оно не грузится».
  2. у CLI и FPM разные копии php.ini. Правки в …/cli/php.ini не касаются сайта, работающего через …/fpm/php.ini. «Проверил в терминале — лимит есть, на сайте — нет» это и есть.
  3. пул перебивает php.ini. Если в www.conf есть php_admin_value[memory_limit] — базовый слой над ним не действует, и правка php.ini «не подействовала» ровно потому, что значение вбито выше.

Границы этой статьи: сам разбор пулов, php_admin_value против php_value, Opcache и slowlog — не здесь, а в PHP-FPM: движок под WordPress. Тут php.ini фигурирует как один из трёх форматов конфига и в разрезе «что задаёт кто».


Часть V. Генерировать конфиг, не выдумывая ключи: плейсхолдеры и preflight

Собрать три файла связно — механика, где цена ошибки не «вылет», а тихий переопределяющий слой из Части I. Ручная генерация типично даёт два дефекта: выдуманную директиву/константу, которую ядро проигнорирует, и реальный секрет, случайно вписанный в файл, который уедет в git. Второй — дороже. Отсюда два правила, на которых стоит любой ответственный генератор.

Правило 1: секреты всегда плейсхолдерами. Ни модель, ни шаблон не знают ваших паролей, солей и ключей API. Поэтому на месте секрета обязан быть явный маркер вроде REPLACE_ME / CHANGE_ME_WITH_REAL_VALUE, а не «выглядящее правдоподобно» значение. Значение, похожее на настоящий пароль и не помеченное как плейсхолдер, опаснее пустого: его заливают в прод и не меняют.

Правило 2: безопасные дефолты по окружению. Генератор, знающий, для какого окружения строит конфиг, подставляет разное:

  • production — WP_DEBUG=false, display_errors=Off/log_errors=On, лимиты консервативные;
  • staging — отладку можно включить, но секреты отдельными плейсхолдерами;
  • development — WP_DEBUG=true, мягкие лимиты, заметки-комментарии.

Preflight: чек-лист «что заменить перед запуском». Финальный шаг — приложить к сгенерированному файлу список того, что обязательно надо поправить руками: секреты, пути, домены. Он превращает «получил готовый конфиг» в «получил конфиг с известными дырами, которые надо закрыть до запуска». Это ровно то же мышление, что в Диагностике: система говорит правду о текущем состоянии, а не о том, что вы хотели задать.

Итого, боевой цикл такой: описал окружение → получил три связных конфига только с реальными директивами → получил плейсхолдеры вместо секретов и пометку «где дефолт» → получил preflight → заменил секреты и сохранил .env в .gitignore. Всё, кроме ручной замены секретов, — снимается с человека.


Инструменты под задачу

Описать окружение и получить связный wp-config.php / .env / php.ini — это как раз то, что в codestack.space делает ИИ-генератор конфигураций. Вы описываете окружение словами, выбираете формат (.env, wp-config.php, php.ini) и окружение (development / staging / production), а он отдаёт один конфиг-файл и соблюдает правила из Части V:

  • только реальные директивы и константы — не выдуманные ключи, которые ядро бы проигнорировало;
  • секреты всегда плейсхолдерами (REPLACE_ME) с явным предупреждением «проверьте каждую строку и замените значения перед применением», потому что модель не знает ваших паролей;
  • безопасные значения по умолчанию — для production отладка и verbose выключены, display_errors=Off / log_errors=On;
  • опции «комментарии / группировка по секциям / чек-лист что заменить» — включая preflight из Части V.

Попробуйте собрать конфиг под своё боевое окружение прямо здесь:

ИИ-генератор конфигураций
Гость
Токены: 0 / 50 000
Гостевой лимит — 50 000 токенов. Зарегистрируйтесь, для пользователей лимит + 200 000 токенов.

Он собирает один файл за прогон (.env отдельно от wp-config отдельно от php.ini) — поэтому три конфига из Части I делают тремя прогонами с одним и тем же описанием окружения. Решение, в какой из файлов положить настройку, остаётся за вами (Часть I); генератор снимает ручную часть — не забыть директиву, не выдумать ключ, не оставить реальный пароль в файле.


Чек-лист граблей

  • Положили лимит загрузки в `wp-config` → такой константы нет, ядро игнорирует; дом `upload_max_filesize` — в `php.ini`.
  • Держите адрес сайта и в базе, и в `WP_HOME` → рассинхрон при миграции; выберите один источник.
  • Сменили соли/`AUTH_KEY` на живом сайте → всех выкинуло из сессий, формы с nonce сломались; соли генерируются раз при установке.
  • Прикрутили `.env` на shared-хостинге без загрузчика → `getenv()` пусто, сайт взял фолбэк; `.env` работает только с Docker/systemd/nginx-`env`/mu-plugin.
  • Закоммитили `.env` в git → боевые креды утекли; `.env` обязан быть в `.gitignore`, права `chmod 600`.
  • Поставили `post_max_size` < `upload_max_filesize` → большой файл отсекается до «upload»; первый ≥ второго.
  • Правите `…/cli/php.ini`, а сайт на `…/fpm/php.ini` → «в терминале работает, на сайте нет»; у CLI и FPM разные копии.
  • Ждёте, что правка `php.ini` применится, а в пуле есть `php_admin_value` → базовый слой перебит; правьте пул (разбор — в статье про PHP-FPM).
  • В сгенерированном конфиге значение похоже на реальный пароль и не помечено плейсхолдером → его не меняют и льют в прод; проверяйте, что секрет = явный `REPLACE_ME`.
  • Прогнали генератор один раз и решили, что «конфиг готов» → он собирает один файл; нужно три конфига — три прогона с одним описанием окружения.
  • Перезаписали `wp-config` при выкате из репозитория → затёрли креды; `wp-config` и `.env` — shared-файлы, не часть кода.
  • Не закрыли preflight-чеклист перед запуском → секреты/пути/домены остались плейсхолдерами на проде; прогоните список «что заменить».

👤 Зарегистрируйтесь, чтобы добавить материал в закладки.