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 і файлові шляхи; під час міграції ПІДКАТАЛОГ → КОРІНЬ заміняйте все, що містить підкаталог. І завжди, завжди натискайте «Зберегти» в Постійних посиланнях після переїзду.