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

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

Ostatnio zweryfikowano: 22 sierpnia 2026
17 min czytania
Przewodnik
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. 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.


#Architektura danych WordPress i pułapka serializacji PHP

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' => 'biuro@stara-domena.pl'
);

// 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:35:"https://stara-domena.pl/logo.png";s:12:"contact_mail";s:23:"biuro@stara-domena.pl";}

#Anatomia zserializowanego ciągu znaków

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

s:35:"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).
  • 35 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 35. Silnik PHP podczas odczytywania danych z bazy wywołuje funkcję unserialize(). Parser PHP nie szuka zamykającego cudzysłowu na oślep - odlicza dokładnie 35 bajtów od wskazanego miejsca.

#Dlaczego zwykły SQL REPLACE niszczy dane?

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 57 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:35:"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:35 i oczekuje dokładnie 35 znaków.
  3. PHP odcina pierwsze 35 znaków 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: Zgodnie ze standardem WordPressa kolumna guid w opublikowanych postach nigdy 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. Tabela wp_usermeta i wp_comments

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


#Metoda 1: WP-CLI search-replace (rekomendowany standard)

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

Przykładowy wynik w terminalu:

+------------------+------------------+--------------+
| Table            | Column           | Replacements |
+------------------+------------------+--------------+
| wp_commentmeta   | meta_value       | 0            |
| wp_comments      | comment_content  | 4            |
| wp_options       | option_value     | 87           |
| wp_postmeta      | meta_value       | 412          |
| wp_posts         | post_content     | 318          |
| wp_usermeta      | meta_value       | 2            |
+------------------+------------------+--------------+
Success: 823 replacements would be made.

#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 przetwarzanie każdego rekordu przez natywne funkcje deserializacji PHP zamiast agresywnych skrótów regexowych w SQL, co gwarantuje bezbłędną spójność danych.
  • --recurse-objects: zapewnia poprawne przeszukiwanie i modyfikowanie zagnieżdżonych obiektów i tablic wielowymiarowych.
  • --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:

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

#Eksport przetworzonej bazy do nowego 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

#Metoda 2: Wtyczka Better Search Replace (interfejs graficzny)

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.

#Typowe problemy z wtyczką i ich rozwiązania:

  • Błąd 504 Gateway Timeout lub przekroczenie limitu czasu wykonywania skryptu (max_execution_time): Przy bazach danych przekraczających kilkaset megabajtów serwer WWW może przerwać żądanie HTTP. W zakładce Settings wtyczki zmniejsz parametr Max Page Size z domyślnych 20000 do np. 5000 lub 2000 wierszy.

#Metoda 3: Skrypt Search-Replace-DB (Interconnect/it)

W sytuacjach awaryjnych - gdy nie mamy dostępu do SSH, a kokpit WordPressa nie działa z powodu błędu 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!


#Specyfika Page Builderów: Elementor, Divi i Gutenberg

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"]
        B --> E["Gutenberg: Opróżnienie block cache"]
    end
    
    subgraph Edge_Purge["Krok 3: Cache i Krawędź"]
        C & D & E --> F["wp cache flush (Redis/Memcached)"]
        F --> G["Purge CDN (Cloudflare)"]
    end

#1. Elementor i dane escapowane w JSON

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. Edytor blokowy Gutenberg

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.


#Specyfika sklepów WooCommerce i integracji e-commerce

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.

#Zaawansowana architektura: Duże zbiory danych i klastry MySQL

W przypadku serwisów korporacyjnych o rozmiarach bazy danych przekraczających 10-50 GB, standardowe wykonanie wp search-replace może trwać wiele godzin i zablokować tabele silnika InnoDB.

#1. Optymalizacja parametrów transakcyjnych

Domyślnie WP-CLI przetwarza wiersze partiami. Aby zminimalizować obciążenie serwera i uniknąć zapełnienia bufora innodb_buffer_pool_size, można zdefiniować wielkość paczek za pomocą parametru --batch-size:

# Bezpieczna partia dla serwerów z ograniczonym RAM-em
wp search-replace 'https://stara.pl' 'https://nowa.pl' \
  --all-tables \
  --batch-size=5000 \
  --precise \
  --skip-columns=guid

#2. Klastry z replikacją (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 dedykowanej maszynie roboczej i zaimportowanie go w całości z wyłączonymi kluczami obcymi i indeksami na czas ładowania.

#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ą stosowaną przez programistów 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ą w locie wartości siteurl i home w pamięci podręcznej WordPressa, ale nie zmieniają fizycznych wpisów w bazie danych.
  • Opcje edycji adresu w kokpicie (Ustawienia > Ogólne) zostają wyszarzone i zablokowane do edycji.
  • 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.

#Kompletna checklista procedury migracyjnej

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

#Automatyzacja synchronizacji baz danych (Dev, Staging, Prod)

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;
}
'

#Rozwiązywanie problemów (Troubleshooting)

#1. Pętla przekierowań (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, w 99% przypadków przyczyną jest zablokowanie zasobów przez politykę CORS lub mieszaną zawartość (Mixed Content) przy przejściu na HTTPS.
  • 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. Przekroczenie limitu pamięci RAM (Allowed memory size exhausted)

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 --batch-size=2000
  • 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 między różnymi wersjami serwerów bazodanowych (np. z MySQL 8.0 na MariaDB 10.11) może dojść do konfliktu zestawów znaków, na przykład utf8mb4_unicode_520_ci (obsługiwanego w nowszych silnikach MySQL) z utf8mb4_unicode_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
  • Zapewnia to bezproblemowy import i pełną zgodność znaków diakrytycznych w treści wpisów i metadanych.

#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.

Wykorzystanie narzędzia WP-CLI ze sprawdzonymi flagami --all-tables --precise --recurse-objects --skip-columns=guid gwarantuje pełną integralność danych, natychmiastowe wykonanie i bezproblemowe działanie strony pod nowym adresem.

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 w środowisku profesjonalnym jest oficjalne WP-CLI i polecenie wp search-replace. Narzędzie w locie deserializuje struktury PHP, dokonuje zamiany, przelicza długości bajtów i bezpiecznie zapisuje dane z powrotem do bazy.
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?#
W przypadku braku dostępu do powłoki SSH najlepiej skorzystać ze sprawdzonej wtyczki Better Search Replace (od WP Engine) lub zewnętrznego skryptu PHP Search-Replace-DB firmy Interconnect/it, pamiętając o bezwzględnym usunięciu skryptu 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.