Zmiana domeny WordPress: WP-CLI search-replace w bazie danych

Zmiana domeny WordPress: WP-CLI search-replace w bazie danych

Ostatnio zweryfikowano: 14 października 2026
21 min czytania
Poradnik
Architektura serwerowa

Przeniesienie strony WordPress ze środowiska deweloperskiego na domenę produkcyjną, zmiana nazwy marki czy wdrożenie certyfikatu SSL (przejście z HTTP na HTTPS) to rutynowe zadania każdego administratora i programisty. Na pierwszy rzut oka operacja wydaje się banalna: wystarczy wyeksportować bazę danych do pliku .sql, otworzyć go w edytorze kodu, uruchomić funkcję “Znajdź i zamień”, a następnie zaimportować plik z powrotem. Albo wykonać szybkie zapytanie SQL w phpMyAdminie.

Wykonanie takiego zabiegu na produkcyjnej bazie danych WordPressa kończy się natychmiastową awarią witryny. Znikają widgety w stopce i panelach bocznych, resetują się ustawienia kolorów i typografii w motywie, pola zaawansowane (ACF) zwracają puste wartości, a konfiguracje wtyczek e-commerce ulegają wyzerowaniu.

Przyczyną tego stanu rzeczy jest specyficzny sposób, w jaki silnik WordPress oraz wtyczki przechowują złożone struktury danych w relacyjnej bazie MySQL i MariaDB. Dokumentacja WordPressa ostrzega wprost: zwykłe wyszukiwanie i zamiana w całej bazie może uszkodzić serializację, bo część motywów i widgetów zapisuje adres razem z jego długością (Migrating WordPress, developer.wordpress.org). Zrozumienie mechanizmu serializacji PHP oraz opanowanie właściwych narzędzi, przede wszystkim interfejsu wiersza poleceń WP-CLI, to fundament bezpiecznego zarządzania stronami WordPress.


#Czym są dane serializowane w bazie WordPress

Relacyjne bazy danych SQL są zaprojektowane do przechowywania danych skalarnych: liczb, krótkich ciągów znaków czy dat. WordPress często potrzebuje jednak zapisać w pojedynczym wierszu tabeli złożoną, wielowymiarową strukturę danych, na przykład tablicę zawierającą listę aktywnych widgetów, ich kolejność, przypisane ikony oraz parametry konfiguracyjne.

Zamiast tworzyć dziesiątki dodatkowych znormalizowanych tabel w bazie dla każdej wtyczki, twórcy WordPressa zdecydowali się na wykorzystanie natywnej funkcji języka PHP: serialize().

// Przykładowa tablica konfiguracyjna motywu w PHP
$theme_settings = array(
    'site_name'    => 'Mój Sklep WordPress',
    'logo_url'     => 'https://stara-domena.pl/logo.png',
    'contact_mail' => '[email protected]'
);

// Wynik działania funkcji serialize($theme_settings)
// Zapisywany bezpośrednio w kolumnie option_value tabeli wp_options:
a:3:{s:9:"site_name";s:20:"Mój Sklep WordPress";s:8:"logo_url";s:32:"https://stara-domena.pl/logo.png";s:12:"contact_mail";s:21:"[email protected]";}

#Jak odczytać zserializowany ciąg znaków

Przyjrzyjmy się bliżej fragmentowi reprezentującemu adres URL logo:

s:32:"https://stara-domena.pl/logo.png";

Każdy element w zserializowanym formacie PHP posiada ścisłą strukturę:

  • s oznacza typ danych: string (ciąg znaków).
  • 32 to deklarowana długość ciągu znaków (dokładna liczba bajtów).
  • "https://stara-domena.pl/logo.png" to właściwa wartość zamknięta w cudzysłowach.

Kluczowym elementem całej układanki jest liczba 32. Silnik PHP podczas odczytywania danych z bazy wywołuje funkcję unserialize(). Parser PHP nie szuka zamykającego cudzysłowu na oślep, tylko odlicza dokładnie 32 bajty od wskazanego miejsca.

#Dlaczego SQL REPLACE psuje dane serializowane

Wyobraźmy sobie, że migrujemy stronę na nową, dłuższą domenę: https://nowa-znacznie-dluzsza-domena-firmowa.pl. Nowy adres logo to: https://nowa-znacznie-dluzsza-domena-firmowa.pl/logo.png, który ma 56 znaków.

Jeśli wykonamy tradycyjne zapytanie SQL:

-- KRYTYCZNY BŁĄD: To zapytanie zniszczy serializację!
UPDATE wp_options 
SET option_value = REPLACE(option_value, 'https://stara-domena.pl', 'https://nowa-znacznie-dluzsza-domena-firmowa.pl');

W bazie danych powstanie następujący ciąg:

s:32:"https://nowa-znacznie-dluzsza-domena-firmowa.pl/logo.png";

Co dzieje się w momencie uruchomienia strony przez użytkownika?

  1. WordPress pobiera wiersz z bazy danych i przekazuje go do funkcji unserialize().
  2. Funkcja odczytuje nagłówek s:32 i oczekuje dokładnie 32 bajtów.
  3. PHP odcina pierwsze 32 bajty nowego adresu, natrafiając wewnątrz stringa na przypadkowe litery zamiast wymaganego zamykającego cudzysłowu i średnika.
  4. Parser PHP rzuca błąd PHP Notice: unserialize(): Error at offset... i zwraca wartość false.
  5. Motyw lub wtyczka, zamiast spodziewanej tablicy opcji, otrzymuje false.
  6. W efekcie WordPress uznaje, że dane nie istnieją, i przywraca domyślne ustawienia fabryczne motywu lub usuwa widgety.

#Gdzie WordPress przechowuje adresy URL w bazie danych

Adresy URL w instalacji WordPress nie znajdują się w jednym miejscu. Są rozsiane po wielu tabelach systemowych oraz tabelach tworzonych przez zewnętrzne moduły:

graph TD
    A["Baza Danych WordPress"] --> B["wp_options"]
    A --> C["wp_posts"]
    A --> D["wp_postmeta"]
    A --> E["wp_comments / wp_commentmeta"]
    A --> F["wp_usermeta"]
    A --> G["Tabele wtyczek (WooCommerce, Formularze, SEO)"]
    
    B --> B1["siteurl i home (Adresy główne)"]
    B --> B2["sidebars_widgets (Serializowane)"]
    B --> B3["theme_mods (Serializowane)"]
    
    C --> C1["post_content (Linki, obrazki)"]
    C --> C2["post_excerpt"]
    C --> C3["guid (NIE zmieniać w opublikowanych!)"]
    
    D --> D1["_elementor_data (JSON / Escaped)"]
    D --> D2["_wp_attached_file (Ścieżki mediów)"]
    D --> D3["Pola ACF (Serializowane)"]

#1. Tabela wp_options

To najważniejsza tabela konfiguracyjna. Zawiera dwa krytyczne rekordy decydujące o funkcjonowaniu strony:

  • siteurl: wskazuje, gdzie fizycznie znajdują się pliki WordPressa (ścieżka rdzenia).
  • home: wskazuje adres główny witryny wpisywany przez użytkowników w przeglądarce.

Oprócz nich tabela przechowuje w postaci zserializowanej:

  • sidebars_widgets: układ widgetów w panelach bocznych i stopkach.
  • theme_mods_[nazwa_motywu]: kolory, układ nagłówka, typografię i logo z konfiguratora motywów (Customizer).
  • Ustawienia wtyczek SEO (Rank Math, Yoast), wtyczek bezpieczeństwa oraz wtyczek cache.

#2. Tabela wp_posts

Przechowuje treść wpisów, stron, produktów WooCommerce oraz elementów biblioteki mediów.

  • Kolumna post_content: zawiera bezwzględne odnośniki do innych podstron, osadzone tagi <img>, odnośniki do plików PDF oraz komentarze blokowe Gutenberga.
  • Kolumna guid (Globally Unique Identifier): unikalny identyfikator wpisu. Ważna zasada: dokumentacja WordPressa mówi: “Nigdy, przenigdy nie zmieniaj zawartości kolumny GUID, w żadnych okolicznościach” (Migrating WordPress, developer.wordpress.org, tłumaczenie własne). Kolumna guid nie powinna być zmieniana, ponieważ czytniki kanałów RSS używają jej do wykrywania nowych artykułów. Zmiana guid sprawi, że czytniki RSS potraktują wszystkie historyczne posty jako nowe i wyślą powtórne powiadomienia do subskrybentów.

#3. Tabela wp_postmeta

Zawiera dodatkowe atrybuty wpisów i stron. To tutaj lądują zserializowane tablice zaawansowanych pól wtyczki Advanced Custom Fields (ACF), metadane załączników (_wp_attachment_metadata) oraz konfiguracje page builderów, takich jak Elementor czy Divi.

#4. Tabele wp_usermeta i wp_comments

Zawiera adresy URL stron domowych autorów komentarzy oraz metadane profili użytkowników.


#Zmiana domeny WordPress przez WP-CLI search-replace

Interfejs wiersza poleceń WordPressa (WP-CLI) to najszybsze, najbardziej niezawodne i w pełni zautomatyzowane narzędzie do migracji adresów URL. Działa bezpośrednio na serwerze przez połączenie SSH, operując na strukturach obiektowych PHP w pamięci operacyjnej.

#Krok 1: Wykonanie kopii zapasowej (Backup)

Zanim wykonasz jakąkolwiek operację na bazie produkcyjnej, stwórz zrzut bazy do pliku SQL:

# Zrzut bazy z dodaniem instrukcji DROP TABLE
wp db export backup-przed-migracja-$(date +%Y%m%d_%H%M%S).sql --add-drop-table

Jeśli baza ulegnie uszkodzeniu, w kilka sekund przywrócisz stan pierwotny:

wp db import backup-przed-migracja-*.sql

#Krok 2: Uruchomienie przebiegu testowego (—dry-run)

Flaga --dry-run pozwala sprawdzić, ile wierszy i w których tabelach zostanie zmodyfikowanych, bez dokonywania żadnych zmian w bazie:

wp search-replace 'https://stara-domena.pl' 'https://nowa-domena.pl' --all-tables --dry-run

Wynikiem jest tabela z kolumnami Table, Column, Replacements i Type oraz linia podsumowania z łączną liczbą zamian. Kolumna Type pokazuje, czy dana kolumna była przetwarzana w SQL, czy w PHP. Jeśli liczba zamian jest zerowa, najczęściej stary adres różni się od zapisanego w bazie protokołem, prefiksem www albo końcowym ukośnikiem.

#Krok 3: Wykonanie faktycznej zamiany produkcyjnej

Do wykonania ostatecznej zamiany zalecamy zestaw sprawdzonych flag optymalizacyjnych:

wp search-replace 'https://stara-domena.pl' 'https://nowa-domena.pl' \
  --all-tables \
  --precise \
  --recurse-objects \
  --skip-columns=guid \
  --report-changed-only

Szczegółowe wyjaśnienie parametrów:

  • --all-tables: przeszukuje wszystkie tabele znajdujące się w bazie, łącznie z niestandardowymi tabelami wtyczek e-commerce, formularzy i logów.
  • --precise: wymusza użycie PHP zamiast SQL dla wszystkich kolumn. Dokumentacja WP-CLI opisuje to jako wolniejsze, ale pewniejsze przy złożonych danych serializowanych.
  • --recurse-objects: wchodzi rekurencyjnie w zserializowane obiekty. Jest domyślnie włączone, wyłącza się je przez --no-recurse-objects, więc wpisanie go jawnie tylko dokumentuje intencję.
  • --skip-columns=guid: pomija kolumnę guid w tabeli wp_posts, chroniąc czytniki RSS przed duplikacją wpisów.
  • --report-changed-only: wyświetla w podsumowaniu wyłącznie tabele, w których faktycznie dokonano zmian, ukrywając setki pustych wierszy.

#Zmiana domeny w sieci WordPress Multisite

W instalacjach typu Multisite (sieć wielu witryn) tabele systemowe posiadają dedykowane prefiksy (np. wp_2_posts, wp_3_options), a informacje o domenach poszczególnych subwitryn znajdują się w tabelach wp_blogs i wp_site.

W takim środowisku należy użyć flagi --network, która obejmuje wszystkie tabele zarejestrowane w $wpdb dla całej sieci, albo --all-tables-with-prefix, która dodaje tabele z prefiksem niezarejestrowane w $wpdb:

wp search-replace 'https://stara-siec.pl' 'https://nowa-siec.pl' --network --all-tables-with-prefix

Dokumentacja WordPressa przyznaje, że przy zmianie domeny sieci Multisite tego kroku “nie da się jeszcze łatwo zautomatyzować” (Migrating WordPress, developer.wordpress.org, tłumaczenie własne). Po zamianie sprawdź ręcznie:

  • tabele wp_site i wp_blogs, w których zapisane są domena i ścieżka sieci oraz każdej subwitryny,
  • opcje home, siteurl i fileupload_url w tabeli opcji każdej witryny (wp_options, wp_2_options i kolejne),
  • przy przejściu z subdomen na podkatalogi lub odwrotnie: plik .htaccess oraz stałą SUBDOMAIN_INSTALL w wp-config.php.

#Jak wyeksportować bazę z nowymi adresami do pliku SQL

Jeśli przygotowujesz migrację bazy danych dla klienta na lokalnym środowisku i chcesz wygenerować gotowy plik SQL z podmienionymi domenami bez dotykania lokalnej bazy roboczej:

wp search-replace 'https://localhost/projekt' 'https://domena-klienta.pl' \
  --all-tables \
  --export=baza-gotowa-do-importu.sql

#Przejście z www na domenę bez www

Gdy zmienia się tylko prefiks, podmieniaj pełny adres razem z protokołem, żeby nie trafić w cudze ciągi znaków zawierające tę samą nazwę:

wp search-replace 'https://www.twojadomena.pl' 'https://twojadomena.pl' --all-tables --precise --skip-columns=guid --dry-run

#Zmiana adresów URL wtyczką Better Search Replace

Jeżeli zarządzasz stroną na hostingu współdzielonym bez dostępu do SSH lub preferujesz obsługę z poziomu kokpitu WordPressa, najbezpieczniejszym rozwiązaniem jest wtyczka Better Search Replace rozwijana przez zespół WP Engine (dawniej Delicious Brains).

Krok po kroku w kokpicie WordPress:
1. Przejdź do: Wtyczki > Dodaj nową > Wyszukaj "Better Search Replace".
2. Zainstaluj i aktywuj wtyczkę.
3. Przejdź do: Narzędzia > Better Search Replace.
4. W polu "Search for" wprowadź stary adres URL (np. https://stara-domena.pl).
5. W polu "Replace with" wprowadź nowy adres URL (np. https://nowa-domena.pl).
6. Zaznacz wszystkie tabele na liście (użyj skrótu Ctrl+A na Windows lub Cmd+A na macOS).
7. Upewnij się, że opcja "Run as dry run?" (Uruchom na sucho) jest ZAZNACZONA.
8. Kliknij "Run Search/Replace" i przeanalizuj raport.
9. Odznacz opcję "Run as dry run?" i uruchom procedurę ponownie, aby zapisać zmiany.

#Błędy Better Search Replace i jak je naprawić

  • Błąd 504 Gateway Timeout lub przekroczenie limitu czasu wykonywania skryptu (max_execution_time): Przy dużych bazach serwer WWW może przerwać żądanie HTTP. W zakładce Settings wtyczki zmniejsz parametr Max Page Size. Domyślna wartość to 20000 wierszy na porcję, co widać w kodzie źródłowym wtyczki (class-bsr-admin.php); mniejsza porcja oznacza więcej krótszych żądań.

#Jak zmienić siteurl i home w WordPressie

Dwie opcje z tabeli wp_options decydują, czy w ogóle zalogujesz się na stronę pod nowym adresem. Dokumentacja WordPressa (Changing The Site URL) rozróżnia je tak: home to “Adres witryny (URL)”, czyli adres wpisywany przez odwiedzających w przeglądarce, a siteurl to “Adres WordPressa (URL)”, czyli miejsce, w którym leżą pliki rdzenia. Oba pola w Ustawienia > Ogólne powinny zawierać https:// i nie powinny kończyć się ukośnikiem.

Dokumentacja opisuje kilka sposobów zmiany tych wartości. Każdy pasuje do innej sytuacji:

SposóbKiedy go użyćNa co uważać
wp option update w WP-CLIMasz SSH, kokpit nie działaZmienia tylko dwie opcje, resztę adresów poprawia wp search-replace
Stałe WP_HOME i WP_SITEURL w wp-config.phpTrzeba szybko podnieść stronęWartości są wpisane na sztywno, pól w Ustawienia > Ogólne nie da się edytować
update_option() w functions.php motywuMasz tylko FTPKod trzeba usunąć zaraz po pierwszym załadowaniu kokpitu
Stała RELOCATEStrona przeniesiona do innego katalogu lub na inny serwerPoprawia tylko adresy panelu, a pozostawiona w pliku jest niebezpieczna
Edycja wp_options w phpMyAdminMasz tylko panel hostinguNajpierw kopia bazy; prefiks tabel może nie być wp_

#Zmiana siteurl i home przez WP-CLI

Dokumentacja podaje dwa polecenia, a po nich zamianę adresów w treści z pominięciem kolumny guid:

wp option update home 'https://nowa-domena.pl'
wp option update siteurl 'https://nowa-domena.pl'
wp search-replace 'https://stara-domena.pl' 'https://nowa-domena.pl' --skip-columns=guid

#Zmiana adresu przez functions.php

Gdy masz dostęp tylko przez FTP, dokumentacja zaleca dopisanie do pliku functions.php aktywnego motywu, zaraz po otwierającym <?php, dwóch linii:

update_option( 'siteurl', 'https://nowa-domena.pl' );
update_option( 'home', 'https://nowa-domena.pl' );

Po wgraniu pliku otwórz kilka razy ekran logowania lub kokpit, a potem usuń te linie. Dokumentacja pisze wprost: “Nie zostawiaj tego kodu w pliku functions.php” (Migrating WordPress, developer.wordpress.org, tłumaczenie własne). Kod wykonywałby się przy każdym żądaniu i nadpisywał opcje, które później zechcesz zmienić z poziomu kokpitu.

#Stała RELOCATE po przeniesieniu katalogu

Stała define( 'RELOCATE', true ); dopisana w wp-config.php przed komentarzem That's all, stop editing! sprawia, że po zalogowaniu przez wp-login.php na nowym serwerze WordPress przyjmuje bieżący adres. Według dokumentacji poprawia ona tylko adresy panelu, nie resztę konfiguracji, a jej pozostawienie jest niebezpieczne: “Zawsze usuwaj linię RELOCATE z wp-config.php po zakończeniu pracy” (Migrating WordPress, developer.wordpress.org, tłumaczenie własne).

#Transienty w przeniesionej bazie

Dokumentacja zwraca uwagę na transienty, czyli tymczasowe wartości zapisywane w wp_options. Mogą zawierać stare adresy, a ponieważ WordPress odtwarza je sam, w przeniesionej kopii bazy można je bezpiecznie usunąć. Z WP-CLI robi to wp transient delete --all, a dokumentacja podaje też zapytanie dla phpMyAdmina, poprzedzone kopią bazy:

DELETE FROM `wp_options` WHERE `option_name` LIKE '%\_transient\_%';

#Jak zmienić adresy URL, gdy kokpit WordPressa nie działa

W sytuacjach awaryjnych, gdy nie mamy dostępu do SSH, a kokpit WordPressa nie działa z powodu pętli przekierowań lub uszkodzonych plików, sprawdzonym narzędziem od ponad dekady pozostaje skrypt Search-Replace-DB (znany jako SRDB) stworzony przez agencję Interconnect/it.

Procedura użycia skryptu SRDB:
1. Pobierz oficjalne archiwum zip ze strony interconnectit.com.
2. Rozpakuj pliki i zmień nazwę folderu na unikalny, trudny do odgadnięcia ciąg (np. srdb-sec-8f7a9d/).
3. Prześlij folder na serwer przez FTP / SFTP do głównego katalogu instalacji WordPress (tam, gdzie znajduje się plik wp-config.php).
4. Otwórz w przeglądarce adres: https://twoja-domena.pl/srdb-sec-8f7a9d/.
5. Skrypt automatycznie odczyta dane logowania do bazy z pliku wp-config.php.
6. Wypełnij pola "replace" (stara wartość) i "with" (nowa wartość).
7. Kliknij "dry run", a po weryfikacji "live run".
8. KRYTYCZNE: Po zakończeniu kliknij czerwony przycisk "delete me", aby skrypt usunął się z serwera.

[!CAUTION] Pozostawienie skryptu Search-Replace-DB na działającym serwerze stanowi krytyczną lukę bezpieczeństwa. Każdy, kto odgadnie ścieżkę do skryptu, uzyskuje pełny dostęp do odczytu i modyfikacji bazy danych bez konieczności podawania hasła. Zawsze weryfikuj usunięcie folderu po zakończeniu prac!


#Zmiana adresów URL w Elementorze, Divi i Gutenbergu

Nowoczesne page buildery oraz edytor blokowy Gutenberg wprowadzają dodatkowe warstwy kodowania danych, o których należy pamiętać podczas aktualizacji adresów URL.

graph LR
    subgraph WP_CLI_Execution["Krok 1: Baza Danych"]
        A["wp search-replace"] --> B["Tabele wp_posts, wp_postmeta, wp_options"]
    end
    
    subgraph Builder_Sync["Krok 2: Page Buildery"]
        B --> C["Elementor: Narzędzia > Zmień URL"]
        B --> D["Elementor: Regeneruj pliki CSS"]
    end
    
    subgraph Edge_Purge["Krok 3: Cache i Krawędź"]
        C & D --> F["wp cache flush (Redis/Memcached)"]
        F --> G["Purge CDN (Cloudflare)"]
    end

#1. Adresy URL w danych JSON Elementora

Elementor przechowuje układ stron w polu meta _elementor_data w formacie JSON, w którym ukośniki w adresach URL są często poprzedzane znakiem ucieczki (ang. escaped slashes):

{\"url\":\"https:\/\/stara-domena.pl\/wp-content\/uploads\/2026\/08\/baner.jpg\"}

Standardowe wyszukiwanie ciągu https://stara-domena.pl może nie dopasować ciągu z ukośnikami https:\/\/stara-domena.pl. Ponadto Elementor kompiluje reguły stylów do statycznych plików CSS w katalogu wp-content/uploads/elementor/css/.

Właściwa procedura po migracji strony na Elementorze:

  1. Wykonaj standardowe wp search-replace w bazie.
  2. Zaloguj się do kokpitu WordPress i przejdź do: Elementor > Narzędzia > Zastąp adres URL.
  3. Wpisz stary i nowy adres URL i kliknij Zastąp adres URL.
  4. W zakładce Ogólne kliknij przycisk Generuj pliki i dane od nowa (Regenerate Files & Data).

#2. Adresy URL w blokach Gutenberga

Bloki Gutenberga przechowują atrybuty mediów bezpośrednio w komentarzach HTML wewnątrz post_content:

<!-- wp:image {"id":412,"sizeSlug":"full","linkDestination":"custom"} -->
<figure class="wp-block-image size-full"><a href="https://stara-domena.pl/oferta/"><img src="https://stara-domena.pl/wp-content/uploads/2026/08/grafika.png" alt="Oferta"/></a></figure>
<!-- /wp:image -->

Zarówno wp search-replace, jak i Better Search Replace radzą sobie z tym bez problemu, pod warunkiem przeszukania kolumny post_content w tabeli wp_posts.

#3. Adresy wpisane na sztywno w plikach motywu

Część motywów i wtyczek ma adresy zapisane bezpośrednio w plikach PHP, CSS lub JavaScript. Żadna zamiana w bazie ich nie obejmie, ale łatwo je znaleźć na serwerze:

grep -rn "stara-domena.pl" wp-content/themes/twoj-motyw/

#Zmiana domeny w sklepie WooCommerce

Podczas zmiany domeny w sklepie internetowym opartym o WooCommerce należy zwrócić szczególną uwagę na moduły komunikujące się z zewnętrznymi systemami płatności, kurierami oraz systemami ERP:

  1. Bramki płatności (Stripe, PayU, Przelewy24, PayPal):
    • Większość bramek płatności rejestruje w swoich panelach deweloperskich adresy powiadomień zwrotnych (tzw. Webhooks / IPN). Jeśli zmieniasz domenę ze stara.pl na nowa.pl, powiadomienia o opłaconych zamówieniach będą trafiać na stary adres i generować błędy transakcji.
    • Po wykonaniu wp search-replace należy zalogować się do panelu każdej instytucji płatniczej i zaktualizować adres Webhooka (np. https://nowa.pl/?wc-api=WC_Gateway_PayU).
  2. Klucze i Webhooki WooCommerce REST API:
    • W menu WooCommerce > Ustawienia > Zaawansowane > Webhooki sprawdź, czy adresy docelowe (Delivery URL) zostały zaktualizowane. Jeśli webhooki synchronizują stany magazynowe z systemem ERP (np. Subiekt GT, Comarch Optima), zaktualizuj adresy endpointów w oprogramowaniu integrującym.
  3. Pliki sesji i koszyki klientów:
    • Tabela wp_woocommerce_sessions przechowuje tymczasowe dane koszyków. Po zmianie domeny warto wyczyścić wygasłe sesje w menu WooCommerce > Status > Narzędzia > Wyczyść sesje klientów, aby uniknąć błędów tokenów sesyjnych po zmianie ciasteczek domenowych.

#Search-replace na dużej bazie i klastrze MySQL

Przy bardzo dużych bazach jedno przejście wp search-replace po wszystkich tabelach trwa długo i przez cały ten czas zapisuje do tabel, z których korzysta działająca strona. Dokumentacja WP-CLI podaje dwa ograniczenia warte znajomości: tabele bez klucza głównego są pomijane, a opcje --regex i --log wyraźnie spowalniają pracę.

#1. Jak podzielić search-replace na mniejsze części

Polecenie wp search-replace nie ma opcji --batch-size ani --limit; lista opcji w dokumentacji WP-CLI ich nie zawiera, a instrukcje, które je podają, nie zadziałają. Pracę dzieli się inaczej: podając nazwy tabel jako argumenty (dozwolone są symbole wieloznaczne) albo wykluczając tabele przez --skip-tables:

# Najpierw treści, potem metadane, osobnymi przebiegami
wp search-replace 'https://stara.pl' 'https://nowa.pl' wp_posts --precise --skip-columns=guid
wp search-replace 'https://stara.pl' 'https://nowa.pl' wp_postmeta --precise

# Wszystko poza dużą tabelą logów wtyczki
wp search-replace 'https://stara.pl' 'https://nowa.pl' --all-tables --skip-tables='wp_*_log' --precise --skip-columns=guid

Opcja --include-columns zawęża zamianę do wskazanych kolumn, a przy --export liczbę wierszy w jednym poleceniu INSERT ustawia --export_insert_size (domyślnie 50).

#2. Search-replace przy replikacji (Amazon Aurora, MySQL Galera)

Jeśli baza danych działa w architekturze Master-Slave lub klastrze Galera, masowa modyfikacja milionów rekordów wygeneruje potężny strumień danych w dzienniku binarnym (binlog). Może to doprowadzić do znacznego opóźnienia replikacji (Replication Lag) na węzłach odczytu.

Zalecana strategia dla środowisk High-Availability:

  • Wykonanie operacji w oknie serwisowym o najniższym natężeniu ruchu.
  • Tymczasowe odłączenie węzłów replikacyjnych na czas masowego zapisu.
  • Wykorzystanie flagi --export w celu przygotowania przetworzonego pliku SQL na osobnej maszynie roboczej i zaimportowanie go w oknie serwisowym.

#3. Tryb konserwacji na czas migracji

WP-CLI potrafi włączyć tryb konserwacji WordPressa, dzięki czemu odwiedzający nie zobaczą strony w połowie przestawionej na nowy adres:

wp maintenance-mode activate
wp search-replace 'https://stara.pl' 'https://nowa.pl' --all-tables --precise --skip-columns=guid
wp maintenance-mode deactivate

#Przejście z HTTP na HTTPS i konfiguracja serwerów WWW

Jeżeli celem migracji nie jest zmiana nazwy domeny, lecz wdrożenie certyfikatu SSL i przejście z protokołu nieszyfrowanego na szyfrowany:

wp search-replace 'http://twojadomena.pl' 'https://twojadomena.pl' \
  --all-tables \
  --precise \
  --skip-columns=guid

Dzięki temu eliminujesz problem tzw. Mixed Content (mieszana zawartość), w którym przeglądarka blokuje pobieranie skryptów, czcionek lub obrazków serwowanych po starym protokole http://.

#Przekierowania 301 na poziomie serwera Nginx i Apache

Po zmianie domeny w bazie danych krytycznym krokiem z punktu widzenia SEO jest przekierowanie 100% ruchu ze starego adresu na nowy z kodem statusu HTTP 301 (Moved Permanently).

Konfiguracja Nginx (/etc/nginx/sites-available/stara-domena.conf):

server {
    listen 80;
    listen 443 ssl http2;
    server_name stara-domena.pl www.stara-domena.pl;

    ssl_certificate /etc/letsencrypt/live/stara-domena.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/stara-domena.pl/privkey.pem;

    return 301 https://nowa-domena.pl$request_uri;
}

Konfiguracja Apache (.htaccess na starym serwerze):

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^(?:www\.)?stara-domena\.pl$ [NC]
    RewriteRule ^(.*)$ https://nowa-domena.pl/$1 [L,R=301]
</IfModule>

#Zmienne w pliku wp-config.php a wartości w bazie danych

Częstą praktyką na czas awarii jest wymuszenie adresów URL w pliku wp-config.php:

define('WP_HOME', 'https://nowa-domena.pl');
define('WP_SITEURL', 'https://nowa-domena.pl');

Warto wiedzieć, jak te stałe działają w praktyce:

  • Stałe zdefiniowane w wp-config.php nadpisują wartości siteurl i home odczytywane przez WordPressa, ale nie zmieniają wpisów w bazie danych. Dokumentacja nazywa to wprost “wpisaniem wartości na sztywno” i zaznacza, że nie jest to najlepsza poprawka.
  • Pól adresu w Ustawienia > Ogólne nie da się wtedy edytować.
  • Wpisy w treści postów, załącznikach i widgetach nadal będą wskazywać na stary adres.
  • Wymuszenie stałych w wp-config.php traktuj jako rozwiązanie tymczasowe; docelowo baza danych zawsze powinna być zaktualizowana poleceniem wp search-replace.

#Checklista zmiany domeny WordPress

Przed przystąpieniem do prac warto postępować według ustrukturyzowanego planu działania:

EtapZadanieNarzędzie / Komenda
Przed migracjąPełny zrzut bazy danychwp db export backup-$(date +%s).sql --add-drop-table
Przed migracjąKopia plików wp-content/uploads i .htaccessrsync / tar -czf uploads.tar.gz
Przed migracjąWyłączenie wtyczek cache i minifikacjiKokpit WP / wp plugin deactivate
W trakcieTest “na sucho” parametrów zamianywp search-replace 'old' 'new' --all-tables --dry-run
W trakcieFaktyczna zamiana w bazie danychwp search-replace 'old' 'new' --all-tables --precise --skip-columns=guid
W trakciePodmiana domen w builderach (Elementor)Kokpit > Elementor > Narzędzia > Zastąp URL
Po migracjiOpróżnienie Object Cache i Transientswp cache flush oraz wp transient delete --all
Po migracjiPrzebudowa odnośników bezpośrednich (Permalinks)wp rewrite flush --hard

#Skrypt Bash do synchronizacji bazy WordPress między środowiskami

W nowoczesnych procesach wytwórczych opartych na konteneryzacji Docker lub środowiskach deweloperskich (DDEV, Lando, LocalWP), ręczne wpisywanie poleceń bywa czasochłonne i podatne na błędy ludzkie. Standardem w zespołach inżynierskich jest przygotowanie uniwersalnego skryptu powłoki Bash, który automatyzuje pobieranie bazy produkcyjnej, import do środowiska lokalnego, podmianę domen oraz anonimizację wrażliwych danych klientów.

#!/usr/bin/env bash
# Skrypt automatycznej synchronizacji bazy danych: sync-db.sh
set -eo pipefail

PROD_HOST="twoj-serwer-produkcyjny.pl"
PROD_USER="deploy"
PROD_PATH="/var/www/html"
PROD_URL="https://twojafirma.pl"
LOCAL_URL="https://projekt.local"
BACKUP_DIR="./backups"

mkdir -p "${BACKUP_DIR}"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
DUMP_FILE="${BACKUP_DIR}/dump_prod_${TIMESTAMP}.sql"

echo "==> 1/4 Pobieranie bazy produkcyjnej przez SSH..."
ssh "${PROD_USER}@${PROD_HOST}" "cd ${PROD_PATH} && wp db export - --add-drop-table" > "${DUMP_FILE}"

echo "==> 2/4 Import bazy do lokalnego środowiska..."
wp db import "${DUMP_FILE}"

echo "==> 3/4 Wykonywanie bezpiecznej zamiany domen z obsługą serializacji..."
wp search-replace "${PROD_URL}" "${LOCAL_URL}" \
  --all-tables \
  --precise \
  --recurse-objects \
  --skip-columns=guid \
  --report-changed-only

echo "==> 4/4 Czyszczenie pamięci podręcznej i reset odnośników..."
wp cache flush
wp rewrite flush --hard

echo "==> Sukces! Środowisko lokalne jest w pełni zsynchronizowane pod adresem ${LOCAL_URL}."

#Weryfikacja poprawności serializacji po zakończeniu prac

Jeżeli chcesz mieć stuprocentową pewność, że żadne pole w bazie danych nie zostało uszkodzone podczas migracji, możesz uruchomić krótki jednolinijkowy skrypt PHP w WP-CLI, który weryfikuje integralność wszystkich zserializowanych rekordów w tabeli wp_options:

wp eval '
global $wpdb;
$options = $wpdb->get_results("SELECT option_name, option_value FROM {$wpdb->options} WHERE option_value LIKE \"a:%\" OR option_value LIKE \"O:%\"");
$broken = 0;
foreach ($options as $opt) {
    if (is_serialized($opt->option_value) && @unserialize($opt->option_value) === false) {
        echo "Uszkodzona opcja: " . $opt->option_name . PHP_EOL;
        $broken++;
    }
}
if ($broken === 0) {
    echo "Wszystkie zserializowane opcje w tabeli wp_options są w 100% poprawne!" . PHP_EOL;
} else {
    echo "Znaleziono " . $broken . " uszkodzonych rekordów!" . PHP_EOL;
}
'

#Błędy po zmianie domeny WordPress i ich naprawa

#1. Pętla przekierowań po zmianie domeny (ERR_TOO_MANY_REDIRECTS)

Jeśli po zmianie domeny strona wpada w nieskończoną pętlę przekierowań:

  • Sprawdź, czy adresy siteurl i home w tabeli wp_options mają dokładnie taki sam protokół (https://) i prefiks (www vs non-www), jak skonfigurowano na serwerze WWW lub w Cloudflare.
  • Sprawdź konfigurację SSL w Cloudflare (tryb SSL/TLS powinien być ustawiony na Full (Strict), a nie Flexible).
  • Zresetuj plik .htaccess do domyślnej postaci WordPressa poleceniem:
    wp rewrite flush --hard

#2. Zniknięcie stylów CSS i ikon po migracji

  • Jeśli strona ładuje się jako surowy tekst bez stylów, najczęstszą przyczyną są arkusze i skrypty wciąż wskazujące stary adres, mieszana zawartość (Mixed Content) przy przejściu na HTTPS albo blokada zasobów przez politykę CORS.
  • Uruchom konsolę deweloperską w przeglądarce (klawisz F12 > zakładka Console), aby zidentyfikować pliki ładowane ze starego adresu http://.

#3. Uszkodzone widgety lub pola ACF

  • Jeśli zignorowano zasady serializacji i wykonano surowe zapytanie SQL REPLACE, jedynym bezpiecznym wyjściem jest przywrócenie bazy z wykonanego wcześniej zrzutu SQL (wp db import backup.sql) i powtórzenie operacji za pomocą wp search-replace --precise.

#4. Błąd Allowed memory size exhausted w WP-CLI

Podczas operacji search-replace na tabelach zawierających ogromne ilości danych tekstowych (np. rozbudowane tabele logów lub rewizji wpisów), proces PHP w terminalu może wyczerpać przydzielony limit pamięci RAM:

  • Aby tymczasowo zwiększyć limit pamięci dla pojedynczego polecenia WP-CLI, uruchom komendę ze zmienną środowiskową PHP:
    php -d memory_limit=1024M /usr/local/bin/wp search-replace 'https://stara.pl' 'https://nowa.pl' --all-tables
  • Warto również przed migracją usunąć zbędne rewizje wpisów oraz wygasłe transienty poleceniem:
    wp transient delete --all && wp post delete $(wp post list --post_type='revision' --format=ids) --force

#5. Niezgodność kodowania znaków i zestawu znaków (Collation)

Przy przenoszeniu zrzutu bazy danych na starszy lub inny serwer bazodanowy może się okazać, że docelowy serwer nie zna zestawu znaków (collation) użytego w zrzucie, na przykład utf8mb4_unicode_520_ci:

  • Jeśli podczas importu pliku .sql pojawia się błąd #1273 - Unknown collation: 'utf8mb4_unicode_520_ci', zamień ten ciąg w pliku SQL przed importem:
    sed -i 's/utf8mb4_unicode_520_ci/utf8mb4_unicode_ci/g' backup.sql
  • Oba zestawy przechowują utf8mb4, więc polskie znaki zostają zachowane; zmienia się tylko reguła sortowania i porównywania tekstu.

#Podsumowanie

Prawidłowa aktualizacja adresów URL w bazie danych WordPress to proces wymagający świadomości działania mechanizmów języka PHP i relacyjnych baz danych. Stosowanie surowych zapytań SQL lub edycji plików w edytorach tekstowych to najprostsza droga do destabilizacji serwisu i utraty konfiguracji.

Narzędzie WP-CLI z flagami --all-tables --precise --skip-columns=guid, poprzedzone kopią zapasową i przebiegiem --dry-run, zachowuje dane serializowane i kolumnę guid w nienaruszonym stanie.

Planujesz kompleksową modernizację serwisu, zmianę architektury na headless lub migrację na platformy nowej generacji? Sprawdź nasze usługi w zakresie migracji z WordPress do Astro i Next.js oraz dedykowany audyt Core Web Vitals.

Następny krok

Przekuj artykuł w realne wdrożenie

Pod tym wpisem dokładam linki, które domykają intencję użytkownika i prowadzą dalej w strukturze serwisu.

Chcesz wdrożyć ten temat na swojej stronie?

Jeśli chcesz przełożyć wiedzę z artykułu na działającą stronę, sklep albo przebudowę serwisu, przygotuję konkretny zakres prac.

Powiązany klaster

Sprawdź inne usługi WordPress i bazę wiedzy

Wzmocnij swój biznes dzięki profesjonalnemu wsparciu technicznemu w kluczowych obszarach ekosystemu WordPress.

Dlaczego zwykłe zapytanie SQL UPDATE z funkcją REPLACE psuje bazę WordPress#
WordPress przechowuje złożone konfiguracje, widgety, ustawienia motywów i pola ACF w postaci zserializowanych tablic PHP (np. s:16:\"https://stara.pl\"). Funkcja SQL REPLACE podmienia tekst, ale nie aktualizuje licznika znaków (np. 16). Podczas odczytu PHP widzi niezgodność długości i odrzuca całą strukturę jako uszkodzoną.
Jakie jest najbezpieczniejsze narzędzie do zmiany domeny w bazie WordPress#
Najbardziej rekomendowanym narzędziem jest oficjalne WP-CLI i polecenie wp search-replace. Według dokumentacji WP-CLI polecenie poprawnie obsługuje dane serializowane PHP, czyli deserializuje je, podmienia tekst, przelicza długości i zapisuje z powrotem.
Czy należy zmieniać adresy URL w kolumnie guid w tabeli wp_posts#
Nie. Kolumna guid (Globally Unique Identifier) służy czytnikom RSS jako stały, unikalny identyfikator wpisu. Zgodnie z oficjalną dokumentacją WordPressa, kolumna guid nigdy nie powinna być zmieniana dla opublikowanych wcześniej postów, dlatego w WP-CLI stosuje się flagę --skip-columns=guid.
Jak zaktualizować adresy URL na hostingu współdzielonym bez dostępu do SSH#
Bez dostępu do SSH dokumentacja WordPressa wskazuje wtyczkę Better Search Replace (rozwijaną przez WP Engine) albo skrypt Search and Replace for WordPress Databases firmy Interconnect/it. Skrypt trzeba usunąć z serwera zaraz po zakończeniu prac.

Potrzebujesz FAQ dopasowanego do branży i rynku? Przygotujemy wersję pod Twoje cele biznesowe.

Porozmawiajmy

Polecane artykuły

Testy hostingu WordPress 2026

Testy Review Signal Kevina Ohashiego wracają w 2026 po trzech latach przerwy na nowej, otwartej platformie do testów obciążeniowych. Rozkładamy pięć progów cenowych plus WooCommerce, nazywamy zwycięzców i nieobecnych oraz tłumaczymy dane na język europejskich agencji.