RE:NODE

Тестовая копия WordPress: безопасное обновление с бэкапом

Автор Команда RE:NODE
18 мин чтения


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

Почему обновления ломают сайты на WordPress#

Сайт на WordPress - это ядро, тема и куча плагинов от разных авторов, работающих на одной версии PHP с одной базой данных. Обновление меняет один из этих элементов и не знает, что делают остальные. Большинство обновлений проходит без проблем. Те, что причиняют боль, делятся на четыре группы:

  • Плагин вызывает то, что новое ядро удалило или объявило устаревшим, и страница, где это использовалось, показывает фатальную ошибку или пустой экран. Симптомы и строки журнала разобраны в статье белый экран и ошибки 500 в WordPress.
  • Обновление плагина меняет таблицы базы данных при первой загрузке. Это изменение в одну сторону: если вернуть старые файлы плагина, прежняя структура таблиц не вернётся.
  • Обновление темы перезаписывает ваши правки. Всё, что вы изменили внутри папки родительской темы, пропадает, и никто не предупреждает заранее. Дочерняя тема существует именно для того, чтобы этого не случалось.
  • Смена версии PHP - это вообще не обновление WordPress, но она часто идёт вместе с ним. Плагин, который работал на 8.2, на 8.4 может писать предупреждения или падать.

По списку изменений нельзя предсказать, к какой группе относится обновление. Это выясняется только там, где поломка ничего не значит. В этом весь смысл тестовой копии, и поэтому «обновлю тихой ночью» - не план: тихая ночь не делает сломанную страницу оплаты менее сломанной, она лишь означает, что до вас заметит поломку меньше людей.

Порядок из шести шагов#

Вот порядок действий. У каждого шага ниже есть свой раздел.

  1. Посмотрите, что ждёт обновления. Составьте список обновлений ядра, плагинов и тем. Прочитайте список изменений у всего, что перескакивает через основную версию.
  2. Сделайте бэкап боевого сайта: все файлы плюс отдельный дамп базы данных.
  3. Обновите тестовую копию из этого бэкапа. Восстановите файлы, импортируйте базу, перепишите адреса, отключите всё, что копия делать не должна.
  4. Обновите тестовую копию и проверьте её по письменному списку страниц и действий.
  5. Обновите боевой сайт в том же порядке, в тихий час, держа под рукой бэкап из шага 2.
  6. Проверьте боевой сайт и сделайте заметку, что и когда изменилось.
до всеговосстановлениеобновляем сначала туттолько если прошлаБоевой сайтфайлы + базаБэкапфайлы и дамп SQLТестовая копияиз бэкапаПроверкаваш списокОбновить боевойтот же порядок
Порядок обновления сверху вниз

На схеме видна одна важная деталь: тестовая копия строится из бэкапа, а не напрямую с боевого сайта. Так сделано намеренно. Каждый прогон порядка проверяет путь восстановления, и если восстановление не сработает, вы узнаете об этом во вторник, когда ничего не поставлено на карту, а не во время сбоя.

Шаг 1: посмотрите, что ждёт обновления#

Список можно прочитать в wp-admin: Консоль, затем Обновления. Если там, где живёт WordPress, есть оболочка, WP-CLI выдаёт ту же информацию в виде, который удобно вставить в заметку:

bash
$ wp core check-update$ wp plugin list --update=available --fields=name,version,update_version$ wp theme list --update=available --fields=name,version,update_version

Два правила чтения этого списка. Первое: у плагина, который перескакивает через основную версию (например, с 4.x до 5.x), список изменений читают целиком, потому что именно там авторы объявляют об удалённых функциях и миграциях базы. Второе: плагин, который автор не обновлял два года, требует решения сейчас, а не в момент обновления: заменить его или признать, что это риск. В статье защита WordPress объясняется, почему заброшенные плагины - обычный путь взлома сайтов.

Запишите и версию PHP. В wp-admin она находится в Инструменты, затем Здоровье сайта, затем Информация, затем Сервер. На тестовой копии должна стоять та же версия, иначе проверка ничего не доказывает.

Шаг 2: бэкап файлов и базы данных#

Бэкап WordPress состоит из двух частей, и копия только одной из них бэкапом не является. В файлах лежит wp-content (темы, плагины, загрузки) и wp-config.php. В базе данных - каждая запись, страница, комментарий, пользователь и настройка. Если восстановить файлы без базы, получится сайт с темой и без содержимого; если восстановить базу без файлов, получится содержимое со ссылками на отсутствующие картинки.

Сначала сделайте бэкап файлов, сразу после него - дамп базы. На веб-тарифах RE:NODE бэкап - это кнопка или задача на вкладке Schedules, а слоты для бэкапов входят в каждый тариф; бэкап охватывает файлы сервера, поэтому дамп базы - отдельный шаг, который вы делаете сами. Если есть оболочка, кратчайший путь - mysqldump через WP-CLI:

bash
$ wp db export - --single-transaction | gzip > ~/pre-update-$(date +%F).sql.gz$ gzip -t ~/pre-update-$(date +%F).sql.gz && echo "archive readable"

Без WP-CLI то же самое делается через mysqldump с учётными данными из wp-config.php:

bash
$ mysqldump --single-transaction --no-tablespaces \    -h DB_HOST -u DB_USER -p DB_NAME | gzip > pre-update.sql.gz

Флаг --single-transaction делает согласованный снимок таблиц InnoDB без блокировки сайта. Флаг --no-tablespaces убирает ошибку прав на хостингах, где у пользователя базы нет привилегии PROCESS, - это обычный случай для пользователя, который может работать только со своей базой. Флаги подробно разобраны в статье бэкап и восстановление через mysqldump.

Если на тарифе есть страница баз данных в панели, есть второй путь без оболочки: Open in phpMyAdmin входит по одноразовому токену, а Export, затем Custom, затем gzip даёт тот же файл. Ограничения по размеру, на которые натыкаются большие базы, описаны в руководстве по импорту и экспорту в phpMyAdmin.

Затем скачайте оба файла с машины. Бэкап, лежащий рядом с тем, что он защищает, - это удобство, а не защита. Бэкапы из панели можно скачать; дамп тоже можно скачать по SFTP или через файловый менеджер (SFTP и файловый менеджер). Если заблокировать бэкап в панели, он переживёт и ротацию, которая удаляет самый старый слот при создании нового. Сделайте так с бэкапом перед обновлением и снимите блокировку, когда обновление неделю проработает без проблем.

Шаг 3: соберите тестовую копию#

Тестовая копия - это вторая установка WordPress, в которой лежит свежая копия боевого сайта. Это может быть поддомен в том же аккаунте, второй небольшой тариф или папка на машине, которую вы администрируете сами. Требований немного:

  • Та же версия PHP, что и на боевом сайте, и похожий лимит памяти. Тестовая копия на PHP 8.4, проверяющая боевой сайт на 8.2, ничего не доказывает об обновлении.
  • Собственная база данных. Никогда не направляйте тестовую копию на боевую базу, даже «просто посмотреть». Плагин, обновивший таблицы на тестовой копии, тогда изменит и боевой сайт.
  • Собственный адрес, например staging.example.com, с HTTPS. На тарифах приложений и веб-тарифах RE:NODE есть слот прокси: направьте A-запись на показанный адрес, и сертификат будет выпущен и продлеваться автоматически. Самая чистая тестовая копия - второй сервер WordPress, а тарифы WordPress на момент написания начинаются от $5 в месяц; актуальную сетку смотрите на странице веб-хостинга. Общие доводы в пользу двух окружений и то, что в них нужно делать разным намеренно, разобраны в статье staging и production в одном аккаунте.

Теперь восстановите в неё данные. Шаги те же, что при переезде, который подробнее описан в статье перенос WordPress на новый хостинг; вот краткая версия.

  1. Распакуйте бэкап файлов в корень сайта тестовой копии.
  2. Отредактируйте wp-config.php тестовой копии так, чтобы DB_NAME, DB_USER, DB_PASSWORD и DB_HOST содержали значения тестовой базы. Не копируйте значения с боевого сайта.
  3. Импортируйте дамп.
  4. Перепишите адреса.
bash
$ gunzip -c pre-update-2026-10-20.sql.gz | wp db import -$ wp search-replace 'https://example.com' 'https://staging.example.com' \    --skip-columns=guid --all-tables --dry-run$ wp search-replace 'https://example.com' 'https://staging.example.com' \    --skip-columns=guid --all-tables

Всегда сначала запускайте с --dry-run и читайте число замен по таблицам. wp search-replace понимает сериализованные данные PHP, ради этого он и существует: обычный SQL-REPLACE() на сериализованной опции меняет длину строки, но не обновляет префикс длины рядом с ней, и WordPress считает всю опцию повреждённой. --skip-columns=guid не трогает GUID записей, потому что это идентификаторы, а не ссылки, и при переносе сайта они не должны меняться.

Если оболочки нет, ту же работу из wp-admin делает плагин Better Search Replace, и правило о сериализованных данных для него то же. Никогда не используйте поиск с заменой в текстовом редакторе на файле дампа.

Пусть копия ведёт себя как копия

Тестовая копия, которая ведёт себя как оригинал, опасна. Она шлёт письма оригинала, списывает деньги через платёжный шлюз оригинала, публикует в социальные сети оригинала и появляется в поиске как дубликат вашего настоящего сайта. Добавьте это в wp-config.php тестовой копии выше строки с просьбой больше ничего не редактировать:

wp-config.php (staging only)
define( 'WP_ENVIRONMENT_TYPE', 'staging' );define( 'DISABLE_WP_CRON', true );define( 'WP_DEBUG', true );define( 'WP_DEBUG_LOG', true );define( 'WP_DEBUG_DISPLAY', false );define( 'AUTOMATIC_UPDATER_DISABLED', true );

Что делает каждая константа:

КонстантаЗачем она задана
WP_ENVIRONMENT_TYPEПозволяет плагинам и вашему коду определить тестовую копию через wp_get_environment_type()
DISABLE_WP_CRONНе даёт запускаться на копии запланированным задачам (рассылкам, синхронизациям, публикациям)
WP_DEBUG и WP_DEBUG_LOGПишут уведомления PHP в wp-content/debug.log, чтобы прочитать, что сделало обновление
WP_DEBUG_DISPLAYНе выводит эти уведомления на саму страницу
AUTOMATIC_UPDATER_DISABLEDНе даёт тестовой копии обновляться самой между вашими прогонами

Журнал отладки нужен только на тестовой копии. На боевом сайте общедоступный debug.log - это утечка информации. Константа cron объяснена в статье WP-Cron или настоящий cron; на тестовой копии нужно обратное тому, что эта статья настраивает на боевом сайте.

Затем отключите исходящую почту и индексацию поисковиками. Сначала почта - маленьким обязательным плагином, который переживёт смену плагинов. Создайте wp-content/mu-plugins/staging-no-mail.php:

wp-content/mu-plugins/staging-no-mail.php
<?php// Staging only: wp_mail() returns false and nothing is sent.add_filter( 'pre_wp_mail', '__return_false' );

Всё, что отправляет почту другим способом, например плагин со своим SMTP или интеграцией через API, нужно отключить в настройках самого плагина. Особенно проверьте платёжные шлюзы: переведите их в тестовый режим или отключите плагин на тестовой копии.

Теперь поисковики:

bash
$ wp option update blog_public 0

Это включает «Попросить поисковые системы не индексировать сайт», что добавляет сигнал noindex. Это просьба, а не замок, поэтому не размещайте адрес тестовой копии ни в каких публичных ссылках. Если хостинг позволяет защитить поддомен паролем, сделайте это: окно входа - единственный надёжный способ не пустить и роботов, и любопытных клиентов.

Наконец, сделайте внешний вид копии отличимым, чтобы их никто не перепутал. Хватит баннера или другой цветовой схемы админки. Обновить не ту вкладку в конце долгого дня - именно так ломается подобный порядок.

Шаг 4: обновите тестовую копию и проверьте её#

Обновляйте в таком порядке, чтобы причина сбоя была очевидна: по одному элементу за раз, и если позволяет время, проверяйте между ними.

bash
$ wp plugin update --all$ wp theme update --all$ wp core update$ wp core update-db

Порядок выбран намеренно. wp core update-db запускает обновление базы, которое следует за основным релизом ядра, а обновление ядра в конце означает, что сбой плагина не спутают со сбоем ядра. Если плагинов много, обновляйте их пачками по три-четыре и проверяйте между пачками; когда что-то сломается, виновник в последней пачке. Если ломается и с одним плагином из пачки, отключите его и убедитесь:

bash
$ wp plugin deactivate suspect-plugin$ wp plugin list --status=active --fields=name,version

Проверяйте по списку, а не по ощущению

«Я покликал, вроде нормально» проверяет только то, что вы вспомнили нажать. Держите в заметке короткий письменный список и отмечайте его при каждом прогоне. Типичный список для небольшого коммерческого сайта:

ПроверкаКак выглядит сбой
Открываются главная и одна длинная записьБелый экран или пропавшие стили
Страница, собранная в конструктореБлоки пропали, вёрстка сломалась
Отправляется контактная формаНет подтверждения или есть ошибка
Поиск возвращает результатыПустая выдача или фатальная ошибка
Вход и выход под редакторомЦикл перенаправлений
Добавить в корзину, дойти до оформления (магазины)Пустая корзина, нет шлюза
Загрузить картинку в MediaОшибка HTTP при загрузке
wp-content/debug.logНовые фатальные ошибки или повторяющиеся предупреждения об устаревании

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

bash
$ wp cache flush$ tail -n 50 wp-content/debug.log

Запустите и проверку целостности, которая сравнивает файлы ядра с официальными контрольными суммами:

bash
$ wp core verify-checksums$ wp db check

Несовпадение контрольной суммы у файла, который вы не редактировали, означает, что кто-то изменил ядро. Это полезно знать до того, как то же обновление окажется на боевом сайте.

Если проверка не прошла, вы узнали то, зачем приходили. Отметьте, какой плагин или тема стали причиной, решите, ждать ли исправленного релиза, заменить плагин или зафиксировать именно его на текущей версии, и выполните боевое обновление без него. Боевой сайт при этом не тронут.

Шаг 5: обновите боевой сайт#

Только теперь. Сделайте свежий бэкап и дамп базы, потому что бэкап из шага 2 уже устарел на часы или дни, а за это время пришли записи, заказы и комментарии. Затем:

  1. Включите режим обслуживания, если сайт принимает заказы или отправки форм. wp maintenance-mode activate делает это через WP-CLI; пара activate и deactivate - это весь интерфейс.
  2. Выполните ровно те команды обновления, что и на тестовой копии, в том же порядке.
  3. Очистите кэши: плагин кэширования, объектный кэш, если он у вас есть, и любой CDN.
  4. Пройдите тот же список проверок, но уже на боевом адресе.
  5. Выключите режим обслуживания.

Делайте это в самый тихий час по вашей аналитике, но не перед сном. Обновление в одиннадцать вечера и обнаруженная в семь утра сломанная страница оформления стоит целой ночи заказов; обновление в два часа дня в тихий будний день и обнаруженная в 14:20 поломка стоит нескольких минут. «Тихо» важно меньше, чем «я на месте и могу откатить».

Откат, если всё равно пошло не так

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

  1. Отключите виновника. Если сайт показывает белый экран и в wp-admin не войти, переименуйте папку плагина в wp-content/plugins по SFTP или в файловом менеджере либо выполните wp plugin deactivate name. Обычно этого достаточно.
  2. Установите предыдущую версию этого плагина, если обновление не меняло базу. wp plugin install name --version=1.2.3 --force устанавливает конкретный релиз. Если плагин перенёс свои таблицы, старая версия может их не понять.
  3. Восстановите бэкап из шага 5, файлы и базу вместе; при этом теряется всё, что записано с тех пор. Для магазина это настоящая цена, и ради неё существует шаг с режимом обслуживания.

Смысл репетиции на тестовой копии в том, что откат почти никогда не должен доходить до третьего рычага.

Шаг 6: докажите, что восстановление работает#

Бэкап, который никто не восстанавливал, - это гипотеза. Всё выше исходит из того, что при импорте pre-update.sql.gz получается работающий сайт. Выяснить, так ли это, можно только импортом, а обновление тестовой копии в шаге 3 и есть этот тест, если относиться к проблеме там как к находке, а не к помехе.

Сделайте проверку явной, а не подразумеваемой. После каждого восстановления сравните тестовую копию с тем, что вы знаете о боевом сайте:

bash
$ wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_status='publish';"$ wp db query "SELECT COUNT(*) FROM wp_users;"$ wp post list --post_type=page --post_status=publish --format=count

Выполните тот же запрос на боевом сайте: числа должны совпасть с точностью до того, что было опубликовано между бэкапом и проверкой. Префиксы таблиц на разных сайтах различаются; wp db prefix выводит ваш. Затем откройте самую свежую запись и самую свежую загрузку, потому что восстановление с правильными числами и без картинок всё равно провалено.

Три проверки ловят большинство плохих бэкапов:

  • Архив читается: gzip -t file.sql.gz ничего не выводит, если архив цел. Оборванная загрузка или полный диск проявятся здесь.
  • Размер файла правдоподобен. Если дамп в десять раз меньше прошломесячного, что-то молча упало, а задача бэкапа, шесть недель подряд пишущая файлы по 2 КБ, встречается чаще, чем следовало бы.
  • Восстановление завершается без ошибок. Дамп, оборвавшийся посреди CREATE TABLE, «успешно» импортируется, если отправить ошибки в никуда. Читайте вывод.

Раз в квартал сделайте ещё шаг: восстановите в совершенно пустую базу и пустую папку, используя только файлы бэкапа и ваши заметки. Это версия проверки, которая соответствует настоящей катастрофе, когда старой установки, откуда можно взять настройки, у вас нет. Рассуждения подробнее - в статьях проверка восстановления до того, как оно понадобится и бэкапы, которые действительно восстанавливаются.

На RE:NODE восстановление бэкапа из панели - одна кнопка на вкладке Backups, так что файловая половина проверки быстрая; половина с базой - это импорт, который вы делаете сами, поэтому дамп должен быть частью порядка, а не держаться в памяти.

Автоматизация скучных частей#

Порядок выживает, только если он дешёвый. Три вещи стоит автоматизировать.

Плановый бэкап файлов. Вкладка Schedules запускает бэкапы по выражению cron, поэтому ежедневный бэкап в предрассветные часы не требует собственного скрипта. Добавьте второе расписание с задержкой в несколько минут для дампа, если можете запускать его с того же места. Синтаксис описан в статье выражения cron: разбор.

Плановый дамп базы, если есть оболочка:

bash
0 3 * * * cd /var/www/example.com && /usr/local/bin/wp db export - --single-transaction | gzip > /var/backups/wp-$(date +\%F).sql.gz

Обратная косая черта перед % важна в crontab: неэкранированный % воспринимается как перевод строки. Старые дампы удаляйте командой find /var/backups -name 'wp-*.sql.gz' -mtime +14 -delete.

Скрипт обновления тестовой копии, который выполняет шаги из шага 3 одной командой, чтобы обновление было командой, а не двадцатью минутами щелчков. Его форма такова: распаковать файлы, импортировать дамп, выполнить search-replace, записать константы тестовой копии. Храните его в заметке или в закрытом репозитории без учётных данных; учётные данные берите из окружения. Когда вы напишете его впервые, вы найдёте один ручной шаг, о котором забыли, и это лучший довод в пользу скрипта.

Об автоматических фоновых обновлениях тоже нужно принять решение. WordPress по умолчанию применяет малые обновления ядра. Автообновления плагинов и тем можно включать по отдельности в wp-admin. Разумная политика: малые обновления ядра включены, а из плагинов автообновляются те, чью поломку вы можете себе позволить, остальные идут через описанный порядок. Чтобы отключить всё это на сайте, который вы обновляете вручную:

wp-config.php
define( 'WP_AUTO_UPDATE_CORE', 'minor' );add_filter( 'auto_update_plugin', '__return_false' );

Первая строка - это записанное поведение по умолчанию; фильтр (его кладут в маленький обязательный плагин, а не в wp-config.php, который загружается раньше, чем можно добавлять фильтры) отключает автообновления плагинов. Не отключайте исправления безопасности везде, а потом не забывайте о сайте.

Ошибки, из-за которых тестовая копия бесполезна#

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

  • Она разошлась с боевым сайтом. Копию собрали в марте и с тех пор не обновляли. На боевом сайте двенадцать новых плагинов и другая тема. Пересобирайте копию из свежего бэкапа каждый раз - поэтому в порядке это и заложено.
  • У неё другая версия PHP или лимит памяти, поэтому обновление проходит там, где на боевом сайте не прошло бы.
  • В ней нет настоящих данных. Копия с тремя тестовыми записями никогда не покажет плагин, который давится таблицей на 40 000 строк. Восстанавливайте настоящую базу.
  • Она отправляет настоящие письма или принимает настоящие платежи. См. константы выше.
  • Кто-то правит её напрямую, и исправление, которое «сработало на тестовой копии», оказывается вручную изменённым файлом, который не попадёт на боевой сайт. Изменения идут одним и тем же путём в обе стороны.
  • Это боевая база. Самая дешёвая ошибка с самым дорогим результатом. Проверяйте DB_NAME, прежде чем что-либо делать.

Ничего экзотического в них нет. Они случаются потому, что тестовая копия кажется необязательной, а необязательна она только до первого обновления, которое ломает боевой сайт.

FAQ#

Нужна ли тестовая копия для небольшого блога?

Не всегда. Блог с бесплатной темой, четырьмя плагинами и без магазина может вполне разумно ограничиться бэкапом, обновлением на боевом сайте и проверкой, потому что худший реалистичный исход - короткий простой, который откатывается из бэкапа. Тестовая копия окупается, как только сайт принимает деньги, собирает заявки или использует конструктор страниц и собственный код.

Можно ли использовать плагин для staging вместо второй установки?

Такие плагины умеют клонировать сайт в подпапку или поддомен и переносить изменения обратно. Они работают, но у них те же опасности, что и у ручной копии (общие загрузки, случайная почта, конфликт префиксов базы), а перенос обратно может перезаписать контент, созданный на боевом сайте после копирования. Отдельная установка, собранная из бэкапа, проще для понимания и заодно проверяет ваше восстановление.

Как часто обновлять?

Проверяйте раз в неделю и ставьте релизы безопасности в течение дня-двух. Для всего остального проходите полный порядок в фиксированный момент, например в первый рабочий день месяца. Если обновления копятся, каждая пачка становится больше, а виновника найти труднее.

Должна ли тестовая копия использовать тот же сервер базы данных, что и боевой сайт?

Используйте отдельную базу на том же движке и той же основной версии. Тот же сервер допустим, если тариф даёт больше одной базы; важна изоляция в виде отдельных учётных данных и отдельной схемы, чтобы ошибка на тестовой копии не могла задеть боевые данные.

Что делать, если обновление сломало сайт и в wp-admin не войти?

Переименуйте папку wp-content/plugins/name у только что обновлённого плагина или всю папку plugins по SFTP либо в файловом менеджере. WordPress отключит то, что не найдёт, и админка обычно откроется. Прочитайте wp-content/debug.log, чтобы убедиться, какой это был плагин, прежде чем что-то возвращать.

Восстановление из бэкапа - это то же самое, что откат?

Нет. Откат после неудачного обновления должен вернуть только плагин или тему, которые подвели. Восстановление возвращает весь сайт на момент бэкапа, и всё записанное с тех пор теряется. Считайте восстановление крайней мерой, а описанный выше порядок - причиной, по которой оно нужно редко.

Это было полезно?