Skip to content

Всё для WordPress, веб-разработки — и не только

🔄 WordPress в подкаталоге: как перенести установку из корня и обратно

🔄 WordPress в подкаталоге: как перенести установку из корня и обратно

Знакомая ситуация: вы подняли сайт разработки, сверстали тему, подключили контент через WP Migrate DB Pro, и после пуша обнаружили битые стили, потерянные картинки и неработающий wp-admin. Причина почти всегда одна: production стоит в корне, а dev, в подкаталоге (или наоборот), и прямой поиск-замена URL в базе эту разницу не лечит.

Проблема глубже, чем кажется: в корневой установке WordPress все ссылки (и на страницы, и на медиафайлы) используют один и тот же домен. В подкаталоговой установке ссылки на контент идут от адреса сайта, а ссылки на ресурсы (css, js, изображения), от адреса WordPress. Обычный search-replace по базе заменяет все одинаково и ломает половину путей.

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

💡 Быстрый обзор:

  • Определите тип установки: совпадают ли «Адрес WordPress» и «Адрес сайта» в Настройках → Общие
  • Для миграции из подкаталога в корень: захардкодьте WP_SITEURL в wp-config, выполните замену /subdir/ в базе данных, перенесите файлы на уровень выше, обновите корневой index.php
  • Для миграции из корня в подкаталог: замените в БД только пути к /wp-content, обновите адрес WordPress в настройках, создайте подкаталог, скопируйте index.php и.htaccess обратно в корень
  • После любой миграции зайдите в Настройки → Постоянные ссылки и нажмите «Сохранить»: это перестроит структуру URL и сбросит кеш

Как определить, где установлен WordPress

Если вы ставили WordPress вручную, вы наверняка помните, был ли это корень домена или подкаталог вроде /wp либо /blog. Но если сайт унаследован от предыдущего разработчика, развёрнут хостингом в один клик или прошло несколько лет, детали стираются.

Самый быстрый способ: зайдите в админку WordPress, откройте Настройки → Общие и посмотрите на поля «Адрес WordPress (URL)» и «Адрес сайта (URL)». Если значения совпадают, перед вами корневая установка:

Поля адреса WordPress и адреса сайта совпадают

Если поля различаются, WordPress установлен в подкаталог (в примере ниже это /subdir):

Адрес WordPress отличается от адреса сайта

Дополнительный признак подкаталоговой установки: при входе в админку URL содержит подкаталог, например, example.com/wp/wp-admin/ вместо example.com/wp-admin/.

Почему нельзя просто взять и перенести

Корень проблемы, в двойной системе URL, которую WordPress использует при установке в подкаталог. Давайте разложим на конкретных примерах.

Допустим, у вас корневая установка на example.com. Абсолютно все ссылки в базе данных, и на пост /2025/about-page, и на картинку /wp-content/uploads/photo.jpg, начинаются с //example.com. Обычный поиск-замена //example.local//example.com работает идеально.

Теперь возьмём установку в подкаталоге /wp. Ссылка на тот же пост выглядит как //example.com/about-page (через адрес сайта), а ссылка на ту же картинку, //example.com/wp/wp-content/uploads/photo.jpg (через адрес WordPress с подкаталогом). Простая замена //example.local//example.com сломает медиафайлы: система будет искать их без /wp в пути и получит 404.

Таблица ниже показывает, какие группы URL нужно обновлять в каждом направлении миграции:

Направление

URL страниц и постов

URL медиафайлов и ресурсов

Пути к файлам в БД

Подкаталог → корень

Заменить /subdir → `` (убрать подкаталог)

Заменить /subdir/wp-content/wp-content

Заменить /app/public/subdir/app/public

Корень → подкаталог

Оставить как есть

Заменить /wp-content/subdir/wp-content

Заменить /app/public/app/public/subdir

Кроме базы данных, нужно физически переместить файлы и обновить index.php в корне, иначе WordPress не найдёт wp-blog-header.php. Дальше разберём оба маршрута по шагам.

Способ 1: перенос WordPress из подкаталога в корень

Это более простое направление: вы убираете подкаталог из путей, и все URL становятся «плоскими», как в стандартной установке.

Шаг 0: диагностика того, что пойдёт не так

Перед вмешательством полезно увидеть масштаб проблемы своими глазами. На скриншоте ниже, настройки миграции с wp-in-a-subdirectory.local (WordPress в /subdir) на wp-standard-install.local (корневая установка). Настройки WP Migrate DB Pro, стандартные, плюс замена заголовка сайта для демонстрации:

Настройки миграции WP Migrate DB Pro из подкаталога

Результат ожидаемо плачевный: страницы открываются, но без стилей и с битыми картинками:

Результат миграции без стилей и с битыми изображениями

В HTML видны ссылки на ресурсы с мёртвым путём /subdir, которого на целевом сервере уже нет. Попытка зайти в wp-admin вызывает редирект на wp-standard-install.local/subdir/wp-login.php, а такого файла не существует. Теперь починим.

Шаг 1: подготовка

Первым делом защитим доступ к админке на время миграции. Добавьте в wp-config.php константы, которые переопределят настройки из базы данных, так WordPress продолжит пускать вас в админку по старому пути с подкаталогом, даже после того как мы почистим базу:

1define( 'WP_SITEURL', 'http://wp-in-a-subdirectory.local/subdir' );
2define( 'WP_HOME', 'http://wp-in-a-subdirectory.local' );

Затем положите сайт на техобслуживание: отредактируйте index.php в публичном корне, закомментируйте строку require( dirname( __FILE__ )... и после закрывающего тега ?> вставьте html-заглушку с сообщением о кратком простое. Посетители увидят такое:

Страница обслуживания с сообщением о технических работах

Вы при этом продолжаете заходить в админку по адресу http://wp-in-a-subdirectory.local/subdir/wp-admin/, константа WP_SITEURL работает.

Шаг 2: поиск и замена в базе данных

Теперь чистим базу. Запустите поиск-замену с такими парами (показано в интерфейсе WP Migrate DB Pro, но тот же принцип работает с WP-CLI search-replace или SQL-запросами через phpMyAdmin):

  • //wp-in-a-subdirectory.local/subdir//wp-in-a-subdirectory.local
  • /app/public/subdir/app/public (путь к файлам на сервере)
Окно поиска и замены WP Migrate DB Pro для удаления подкаталога

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

Содержимое поста после очистки базы от подкаталога

Шаг 3: физический перенос файлов

Удалите (или закомментируйте) строки с WP_SITEURL и WP_HOME из wp-config.php. Теперь админка отвалится, и сразу переносите файлы из подкаталога на уровень выше.

Через SSH или командную строку на сервере это делается в три команды:

1rm index.php && mv subdir/* . && rm -rf subdir

Через FTP или файловый менеджер хостинга, перетащите всё содержимое подкаталога в публичный корень с заменой index.php:

Перенос файлов WordPress в корневую папку через FTP

Готово. Сайт открывается по корневому URL, картинки и стили на месте:

Сайт успешно работает после переноса в корень

Последний штрих: зайдите в админку (теперь это http://wp-in-a-subdirectory.local/wp-admin без подкаталога), откройте Настройки → Постоянные ссылки и нажмите «Сохранить изменения», даже если ничего не меняли. WordPress перестроит структуру URL и сбросит кеш.

Способ 2: перенос WordPress из корня в подкаталог

Многие разработчики считают установку WordPress в подкаталог хорошей практикой: файлы ядра не захламляют корень, упрощается управление через Git/Composer, а сам домен можно использовать для других приложений. Но миграция существующего сайта в подкаталог объективно сложнее обратной, потому что теперь часть ссылок ДОЛЖНА сохранить подкаталог, а часть, нет.

Шаг 0: диагностика

Та же исходная точка: пробуем стандартную миграцию с корневой установки wp-standard-install.local на подкаталоговую wp-in-a-subdirectory.local (WordPress в /subdir):

Настройки миграции WP Migrate DB Pro из корня в подкаталог

Результат полностью ожидаем: страницы открываются, но стили и изображения бьются, к ним не дописан /subdir в пути:

Битые стили и картинки после миграции в подкаталог

В отличие от первого сценария, ссылки на посты и страницы работают корректно, они и не должны содержать подкаталог. Ломаются именно ресурсы (css, js, медиа), пути к которым теперь обязаны включать /subdir.

Шаг 1: подготовка

Константы WP_SITEURL и WP_HOME на этом этапе НЕ прописываем, наша поиск-замена не коснётся этих значений, и мы обновим адрес WordPress вручную чуть позже.

Страницу обслуживания ставим тем же способом: комментируем require(...) в index.php и добавляем html-заглушку. Посетители видят сообщение о техобслуживании, а вы продолжаете заходить в админку по http://wp-standard-install.local/wp-admin/.

Шаг 2: выборочный поиск-замена в базе

Ключевое отличие от первого способа: мы заменяем ТОЛЬКО пути к файлам и ресурсам, НЕ трогая URL страниц. Для этого таргетируем замену по маске /wp-content:

  • //wp-standard-install.local/wp-content//wp-standard-install.local/subdir/wp-content
  • /app/public/app/public/subdir (путь на сервере)
Настройка выборочной замены для добавления подкаталога

После миграции проверяем содержимое постов: ссылки на другие страницы сайта НЕ содержат подкаталог (правильно), а встроенные изображения, содержат (тоже правильно):

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

Шаг 3: обновление адреса WordPress и перенос файлов

Теперь зайдите в Настройки → Общие и допишите подкаталог в конец «Адрес WordPress (URL)», например, http://wp-standard-install.local/subdir. Сразу после сохранения админка отвалится, потому что WordPress попытается найти файлы по новому пути, а их там ещё нет:

Обновление адреса WordPress с добавлением подкаталога

Создайте подкаталог subdir в публичном корне и переместите в него ВСЕ файлы WordPress. Затем скопируйте index.php и .htaccess ОБРАТНО в корень, чтобы страница обслуживания продолжала висеть, пока мы заканчиваем:

Файлы WordPress перенесены в подкаталог subdir

Восстановите index.php ВНУТРИ подкаталога до заводского состояния, уберите html-заглушку и раскомментируйте строку с require:

1<?php
2define( 'WP_USE_THEMES', true );
3require( dirname( __FILE__ ) . '/wp-blog-header.php' );

Теперь админка снова доступна по адресу http://wp-standard-install.local/subdir/wp-admin/:

Админка WordPress работает после переноса в подкаталог

Проверьте контент, картинки на месте, стили подгружаются:

Контент поста проверен: все изображения на месте

Финальный штрих: корневой index.php

Осталось обновить index.php в публичном корне. Уберите страницу обслуживания и пропишите актуальный путь к wp-blog-header.php с учётом подкаталога:

1<?php
2define( 'WP_USE_THEMES', true );
3require( dirname( __FILE__ ) . '/subdir/wp-blog-header.php' );

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

Сайт успешно работает после переноса в подкаталог

Снова зайдите в Настройки → Постоянные ссылки и сохраните без изменений, чтобы WordPress освежил структуру URL.

Альтернативные инструменты и официальный метод

Описанный выше подход с WP Migrate DB Pro удобен, но не единственен. Вот с чем ещё можно работать:

  • WP-CLI search-replace. Команда wp search-replace '//oldsite.local/subdir' '//newsite.com' с флагом --dry-run сначала покажет, сколько вхождений будет заменено. Для выборочной замены (корень → подкаталог) сузьте маску: wp search-replace '//newsite.com/wp-content' '//newsite.com/subdir/wp-content'.

  • Официальный метод WordPress. Документация developer.wordpress.org описывает процедуру «Giving WordPress Its Own Directory», с детальными конфигурациями для Apache (.htaccess), nginx (server block) и IIS (web.config). Метод не требует плагинов и работает на любом хостинге.

  • Ручной SQL. Если объём правок невелик, можно выполнить UPDATE wp_posts SET post_content = REPLACE(post_content, '/subdir/', '/') напрямую в phpMyAdmin, но обязательно с предварительным бэкапом, сериализованные данные в wp_options и wp_postmeta такая замена разрушит.

Какой бы инструмент вы ни выбрали, правило одно: при миграции КОРЕНЬ → ПОДКАТАЛОГ заменяйте только /wp-content и файловые пути; при миграции ПОДКАТАЛОГ → КОРЕНЬ заменяйте всё, что ссылается на подкаталог.

⁉️🤔 Частые вопросы

Обязательно ли использовать WP Migrate DB Pro для такой миграции?

Нет. WP Migrate DB Pro просто даёт удобный интерфейс для поиска-замены с пониманием сериализованных данных PHP. Технически вы можете выполнить те же замены через WP-CLI (команда wp search-replace тоже корректно обрабатывает serialized strings) или использовать официальный метод WordPress с ручным переносом файлов и правкой index.php. Плагин экономит время на крупных и средних проектах, где много вхождений.

Что делать, если после миграции часть картинок всё равно не грузится?

Самая частая причина: в базе остались жёстко прописанные URL с абсолютными путями старого сервера, которые не попали под маску замены. Проверьте контент постов через phpMyAdmin: SELECT ID, post_content FROM wp_posts WHERE post_content LIKE '%/subdir/%' (или старый домен). Второй кандидат, кеш браузера и CDN: сбросьте и проверьте в режиме инкогнито.

Нужно ли обновлять.htaccess после переноса?

Если вы используете красивые постоянные ссылки, да, но WordPress делает это сам, когда вы нажимаете «Сохранить» на странице Настройки → Постоянные ссылки. Если прав на запись у сервера нет, WordPress покажет готовое содержимое.htaccess, скопируйте его вручную. При миграции в подкаталог убедитесь, что корневой.htaccess (не тот, что внутри подкаталога) не содержит правил, конфликтующих с новой структурой.

Можно ли выполнить миграцию вообще без даунтайма?

Технически да, если использовать метод с.htaccess-редиректами (Method I из официальной документации WordPress, «Без смены URL»). При таком подходе файлы переносятся в подкаталог, а корневой.htaccess бесшовно направляет все запросы в новое расположение. Посетители не замечают переезда. Минус: вы остаётесь на том же домене, и URL сайта формально не меняется (подкаталог не виден в адресной строке).

Почему WP Migrate DB Pro не поддерживает миграцию между разнотипными установками из коробки?

Разработчики Delicious Brains обсуждали это на GitHub почти три года. Корень проблемы: плагин применяет ОДНУ пару поиск-замена ко ВСЕЙ базе, а для миграции между root и subdirectory нужны РАЗНЫЕ замены для разных групп URL (страницы vs ресурсы). Автоматическое определение того, какой именно URL к какой группе относится, потребовало бы парсинга структуры контента, что выходит за рамки простого search-replace. Поэтому текущая рекомендация: приведите сайты к единой схеме установки ДО миграции.

Обсуждение поддержки миграции на GitHub у Delicious Brains

Что в итоге: корень или подкаталог?

Выбор между корневой и подкаталоговой установкой WordPress, по сути, сводится к одному компромиссу. Корневая установка проще: меньше движущихся частей, прямая совместимость между dev и production, никаких сюрпризов с двойными URL. Подкаталоговая, чище архитектурно: файлы ядра изолированы, в корне лежит только index.php, легче обновлять WordPress через Git/Composer и безопаснее держать несколько приложений на одном домене.

Если у вас один production-сайт и один dev-сайт, приведите оба к единой схеме (любой) и забудьте о проблеме. Если вы работаете в команде, где часть проектов исторически в корне, а часть, в подкаталоге, теперь вы знаете точные маски поиска-замены для каждого направления.

Главное правило, которое стоит положить в закладки: при миграции КОРЕНЬ → ПОДКАТАЛОГ трогайте только /wp-content и файловые пути; при миграции ПОДКАТАЛОГ → КОРЕНЬ, заменяйте всё, что содержит подкаталог. И всегда, всегда нажимайте «Сохранить» в Постоянных ссылках после переезда.