Куда что ложится: слои конфигурации WordPress-сайта и почему настройка «не сработала»

У WordPress-сайта нет одного «файла настроек». Поведение сайта складывается из пяти слоёв, которые лежат в разных местах, читаются в разном порядке и переопределяют друг друга. Отсюда самая частая жалоба администратора — «я поставил лимит / включил кэш / сменил адрес, а сайт не изменился»: значение задали не в том слое, и более высокий слой перебил его молча. Разберём карту слоёв целиком — веб-сервер, PHP-движок, константы WordPress, опции в базе, тема и плагины, — научимся для любой настройки отвечать «где её правильный дом» и почему правило «один источник правды на настройку» спасает больше времени, чем сама настройка.


Часть I. Пять слоёв: карта того, где вообще «настраивается сайт»

Начнём с полного перечня. Запрос к странице WordPress проходит эти слои сверху вниз, и на каждом есть своя «кнопка настройки».

запрос браузера
   │
   ▼
[1] Веб-сервер            nginx.conf / .htaccess   — маршрутизация, лимит тела запроса, кэш статики
   │
   ▼
[2] PHP-движок            php.ini + пул www.conf    — память, время исполнения, лимиты загрузки
   │
   ▼
[3] Константы WordPress   wp-config.php             — кредиты БД, адрес сайта, соли, отладка (читаются ДО БД)
   │
   ▼
[4] Опции в базе          таблица wp_options        — название, часовой пояс, ЧПУ, «показывать N записей»
   │
   ▼
[5] Тема и плагины        functions.php, настройки  — внешний вид и поведение поверх ядра
   │
   ▼
отдача страницы

Что лежит в каждом слое (по порядку значимости для администратора):

СлойФайл / местоТипичные настройкиКто читает
[1] Веб-серверnginx.conf, .htaccessмаршрутизация (permalinks), лимит тела запроса, gzip, кэш статикиnginx/Apache
[2] PHPphp.ini, пул www.confmemory_limit, upload_max_filesize, max_execution_time, OpcachePHP-FPM
[3] wp-configwp-config.phpDB_* креды, WP_HOME/WP_SITEURL, соли, WP_DEBUGWordPress-ядро
[4] Базаwp_options (через админку)siteurl/home, gmt_offset, permalink_structure, posts_per_pageWordPress-ядро
[5] Тема/плагиныfunctions.php, экраны настроекцвета, шрифты, поведение плагинов, кэш-плагиныкод темы/плагина

Практический вывод уже здесь: когда кто-то говорит «настроить лимит загрузки файла», это не одна кнопка. Лимит живёт в слое [2] (PHP не примет тело больше upload_max_filesize), но его же частично диктует слой [1] (nginx не пропустит тело больше client_max_body_size). Поставить только в одном месте — и половина лимита не применится. Про «правильный дом» — Часть III.

Отдельный слой, который сюда не вошёл, но всегда рядом — кэш (операторный Opcache, объект-кэш, страничный кэш плагина). Это не настройка, а копия результата настроек: классическая причина «поменял, а не видно» — кэш отдал старый HTML/старые опции. Его учитываем в Части V.


Часть II. Порядок переопределения: кто кого бьёт

Слои читаются не «все вместе», а с приоритетом. Три правила переопределения, которые закрывают большинство «почему не работает».

1. Внутри PHP: пул бьёт php.ini, а admin_value не перебить кодом

У настроек PHP есть скоуп (где законно менять). Практично запомнить три уровня:

; /etc/php/8.2/fpm/php.ini          — базовый слой, для всех пулов
memory_limit = 256M

; /etc/php/8.2/fpm/pool.d/www.conf  — слой пула, точечный на сайт
php_admin_value[memory_limit] = 512M   ; вбито наглухо: ни ini_set(), ни .htaccess не перебьют
php_value[upload_max_filesize] = 32M    ; мягкий: плагин может переопределить через ini_set()
php_flag[display_errors] = off           ; то же для булевых (on/off, не 1/0)

Приоритет: php_admin_value (пул) > php_value/.htaccess/.user.ini (мягкие) > ini_set() в рантайме > php.ini. Разница между admin и обычным — единственное, что важно помнить: admin_value админ ставит финально, плагин его не сдвинет. Углублённый разбор скоупов и Opcache — в PHP-FPM: движок под WordPress.

Ещё грабля: у PHP разные копии php.ini для CLI и для FPM. Настройки, заданные в /etc/php/8.2/cli/php.ini, не действуют на сайт, который крутится через …/fpm/php.ini. «Проверил в терминале — работает, на сайте — нет» — почти всегда это.

2. WordPress: константа из wp-config бьёт опцию из базы

WordPress читает адрес сайта так: если в wp-config.php объявлена WP_HOME/WP_SITEURL — она используется как есть и поле home/siteurl в базе игнорируется. Если константы нет — берётся значение из wp_options.

PHP
<?php
// wp-config.php — константы перекроют одноимённые опции в базе
define( 'WP_HOME', 'https://example.test' );
define( 'WP_SITEURL', 'https://example.test' );

Отсюда ловушка миграции: поменяли адрес через wp search-replace в базе, а сайт всё равно отдаёт старый URL — потому что константа в wp-config перебила базу. Об обратном (база есть, а констант нет) и про замену URL при переезде — Миграция WordPress между серверами.

3. Плагины бьют тему, а админка бьёт код темы

Настройки плагина обычно живут в wp_options под своим ключом и переопределяют поведение темы, заданное в functions.php/Customizer. Практически: «сменил шрифт в теме, а он не поменялся» — потому что кэш-плагин или плагин-оптимизатор подставил свой CSS-ссылкой позже. Правило простое: чем «позже» слой в приоритете, тем громче его настройка — а кэш вообще ставит приоритеты на паузу.


Часть III. Правильный дом настройки: три частых случая целиком

Самая дорогая ошибка — положить настройку в один слой, когда она требует двух. Разберём три типовые.

Лимит загрузки медиафайла (требует трёх мест)

Чтобы прикрепить видео в 256 МБ, значение должно совпасть по всей цепочке прохождения запроса:

# [1] nginx.conf — сервер не пропустит тело запроса больше этого
client_max_body_size 256M;
; [2] php.ini (или php_value в пуле) — PHP не примет файл больше upload,
;     и POST целиком больше post_max_size (post_max_size ≥ upload_max_filesize)
upload_max_filesize = 256M
post_max_size       = 260M
max_execution_time  = 300    ; большой файл дольше обрабатывается
max_input_time      = 300
memory_limit        = 512M   ; на время обработки файл живёт в памяти

Если задать только PHP-лимит, а nginx оставить на дефолтных 1M — получите 413 Request Entity Too Large ещё до того, как PHP увидит запрос. Зададите nginx и PHP, но забудете memory_limit — упадёт на середине обработки. Это ровно тот случай, где «настроить лимит» — не одна кнопка. (Про то, где физически лежит конфиг nginx для WordPress — Nginx: веб-сервер и reverse proxy.)

Адрес сайта (база ИЛИ константа, не оба сразу)

Канонический дом адреса — поле home/siteurl в wp_options (админка → Настройки → Адрес WordPress). Константа в wp-config — это переопределение для особых случаев (жёстко зафиксировать адрес, не давая базе его сменить). Правила:

  • держите адрес в одном месте: либо база (обычный случай), либо константы (когда нужен контроль); не дублируйте — при миграции/search-replace дубль даёт рассинхрон;
  • если решили жить в константах — помните, что search-replace по базе на них не действует (база есть, но её игнорируют).

ЧПУ / маршрутизация пермалинков (слой [1] + слой [4])

Структура постоянных ссылок (/%postname%/) задаётся в админке (слой [4], permalink_structure), но реально работает только если слой [1] настроен переписать URL: для nginx — директива try_files, для Apache — правила .htaccess. Настроили админку, но не тронули веб-сервер → 404 на внутренних страницах. Это не баг WordPress, а провал между слоями [1] и [4].


Часть IV. Что реально принадлежит wp-config.php

Миф о «главном файле настроек» рождает больше всего случайных правок. Правда: wp-config.php читается ядром до подключения к базе и предназначен для ограниченного набора — того, без чего WordPress ещё не может ничего прочитать из базы. Всё остальное туда класть не надо.

Что законно живёт в wp-config.php:

PHP
<?php
// подключение к базе — без этого WP не стартует вообще
define( 'DB_NAME',     'wpdatabase' );
define( 'DB_USER',     'wpuser' );
define( 'DB_PASSWORD', 'СЮДА-НЕ-РЕАЛЬНЫЙ-ПАРОЛЬ' );
define( 'DB_HOST',     'localhost' );

// ключи и соли (уникальные «секреты» установки) — генерируются, не выдумываются
define( 'AUTH_KEY',         '…' );
define( 'NONCE_SALT',       '…' );

// адрес сайта как переопределение (см. Часть III)
define( 'WP_HOME',  'https://example.test' );

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

Чего в wp-config.php не должно быть: часового пояса, «сколько записей на странице», названия сайта, структуры пермалинков, категорий, меню, плагинов. Это всё опции в базе (слой 4) — в wp-config им не место, и выдуманные там константы просто игнорируются ядром. Практический критерий: «можно ли это задать в админке?» — если да, дом этому в базе, а не в wp-config.

Отдельно — креды и соли. Креды БД — это секреты: в wp-config они лежат в открытом виде, и это компромисс WordPress-модели, о котором нужно знать (права файла, бэкапы, доступ в админку). Про то, как их защищают и что такое соли в широком смысле — Шифрование данных и хранение паролей. Здесь важнее другое: генератор конфига (см. Инструменты) ставит на место секретов плейсхолдеры — потому что никакой инструмент не знает ваши реальные пароли, и «безопасно сгенерированный wp-config» означает «проверь и подставь сам», а не «заливай как есть».


Часть V. Один источник правды — и роль кэша

Сводный принцип, из которого выросла вся статья: у каждой настройки должен быть ровно один «истинный» дом. Как только значение продублировано в двух слоях, они расходятся при первом же изменении одного из них — и вы получаете «баг без бага».

Типовые способы нарушить «один источник правды»:

  • адрес сайта и в базе, и в wp-config (Часть II, п.2) — при переезде поменяли базу, константа держит старое;
  • лимит PHP задан и в php.ini, и в .user.ini, и в пуле — кто из них победил, никто не помнит (победил php_admin_value, если он есть);
  • настройки кэш-плагина дублируют настройки темы (Часть II, п.3).

Правило применения: настройте значение там, где ему место, и убедитесь, что выше по приоритету нет переопределяющего слоя. Для этого существует трюкацкий, но надёжный инструмент — прочитать фактическое значение, а не то, которое вы «задали»:

# что реально действует у PHP-FPM (а не что в php.ini)
php-fpm8.2 -tt    # или через админку WordPress → Инструменты → Информация о сайте

# адрес сайта: есть ли переопределяющая константа
grep -E "WP_HOME|WP_SITEURL" /var/www/example.test/wp-config.php

Кэш — отдельный нарушитель «один источник правды». Он не слой настройки, а копия результата. Поменяли опцию в базе → кэш-плагин отдаёт старый HTML, и кажется, что «не применилось». Порядок всегда: изменил настройку → сбросил соответствующий кэш → проверил. Без последнего шага вы диагностируете не конфиг, а кэш. (Где кэш живёт и как чистится — Кэширование; как понять, что «не дышит» именно слой — Диагностика: когда сервер «не дышит».)

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


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

Помнить, какой слой переопределяет какой, какие директивы реальные, а какие ядро проигнорирует, и где поставить безопасный дефолт вместо выдуманного пароля — это механика с высокой ценой ошибки (один незакрытый переопределяющий слой = час «отладки конфига»). Чтобы снять её, в codestack.space есть ИИ-генератор конфигураций. Вы описываете окружение словами («WordPress, PHP 8.2, MySQL на том же хосте, OPcache включён, стаджинг для отладки, лимит загрузки 64M»), выбираете формат (.env, wp-config.php, php.ini) и окружение (development / staging / production), — а он отдаёт связный конфиг-файл:

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

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

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

Он не решает, в какой слой положить настройку (это Части I–IV) — он снимает ручную часть: не забыть директиву, не выдумать несуществующую константу, не оставить реальный пароль в файле, который уедет в git. Само решение «где правильный дом значения» остаётся за вами — см. Часть III и Часть V.


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

  • Лимит загрузки задан только в PHP → nginx (`client_max_body_size`) режет раньше с `413`; лимит живёт в трёх местах сразу.
  • `post_max_size` < `upload_max_filesize` → большой файл не пройдёт как POST целиком; первый обязан быть ≥ второго.
  • Правки в `…/cli/php.ini`, а сайт на `…/fpm/php.ini` → «в терминале работает, на сайте нет»; у CLI и FPM разные копии конфига.
  • Ждёте, что плагин не переопределит `php_value` → мягкий слой перебивается `ini_set()`; для финального слова админа нужен `php_admin_value`.
  • Поменяли адрес через `search-replace`, а сайт отдаёт старый → константа `WP_HOME`/`WP_SITEURL` в `wp-config` перебила базу.
  • Продублировали адрес в базе и в константах → рассинхрон при первой миграции; держите один источник.
  • Задали структуру пермалинков в админке, не тронув веб-сервер → 404 внутри сайта; слой [4] без слоя [1] не работает.
  • Написали в `wp-config` часовой пояс / «показывать N» / название сайта → ядро игнорирует; это опции в базе, а не константы.
  • Вставили реальный пароль БД и закоммитили `wp-config` в git → креды утекли; генератор ставит плейсхолдер — подставляйте сами и не тащите в репозиторий.
  • Поменяли настройку и не сбросили кэш → «не применилось»; проверили не конфиг, а кэш.
  • Перезаписали `wp-config` при выкате из репозитория → затёрли креды; `wp-config` — shared-файл, не часть кода.
  • Читаете php.ini, а не фактическое значение → диагноз «по исходнику» вместо «по факту»; спросите у системы, что реально действует.

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