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ę:
soznacza typ danych: string (ciąg znaków).35to 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?
- WordPress pobiera wiersz z bazy danych i przekazuje go do funkcji
unserialize(). - Funkcja odczytuje nagłówek
s:35i oczekuje dokładnie 35 znaków. - PHP odcina pierwsze 35 znaków nowego adresu, natrafiając wewnątrz stringa na przypadkowe litery zamiast wymaganego zamykającego cudzysłowu i średnika.
- Parser PHP rzuca błąd
PHP Notice: unserialize(): Error at offset...i zwraca wartośćfalse. - Motyw lub wtyczka, zamiast spodziewanej tablicy opcji, otrzymuje
false. - 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 kolumnaguidw opublikowanych postach nigdy nie powinna być zmieniana, ponieważ czytniki kanałów RSS używają jej do wykrywania nowych artykułów. Zmianaguidsprawi, ż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ęguidw tabeliwp_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-DBna 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:
- Wykonaj standardowe
wp search-replacew bazie. - Zaloguj się do kokpitu WordPress i przejdź do: Elementor > Narzędzia > Zastąp adres URL.
- Wpisz stary i nowy adres URL i kliknij Zastąp adres URL.
- 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:
- 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.plnanowa.pl, powiadomienia o opłaconych zamówieniach będą trafiać na stary adres i generować błędy transakcji. - Po wykonaniu
wp search-replacenależy zalogować się do panelu każdej instytucji płatniczej i zaktualizować adres Webhooka (np.https://nowa.pl/?wc-api=WC_Gateway_PayU).
- Większość bramek płatności rejestruje w swoich panelach deweloperskich adresy powiadomień zwrotnych (tzw. Webhooks / IPN). Jeśli zmieniasz domenę ze
- 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.
- Pliki sesji i koszyki klientów:
- Tabela
wp_woocommerce_sessionsprzechowuje 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.
- Tabela
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
--exportw 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.phpnadpisują w locie wartościsiteurlihomew 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.phptraktuj jako rozwiązanie tymczasowe; docelowo baza danych zawsze powinna być zaktualizowana poleceniemwp search-replace.
Kompletna checklista procedury migracyjnej
Przed przystąpieniem do prac warto postępować według ustrukturyzowanego planu działania:
| Etap | Zadanie | Narzędzie / Komenda |
|---|---|---|
| Przed migracją | Pełny zrzut bazy danych | wp db export backup-$(date +%s).sql --add-drop-table |
| Przed migracją | Kopia plików wp-content/uploads i .htaccess | rsync / tar -czf uploads.tar.gz |
| Przed migracją | Wyłączenie wtyczek cache i minifikacji | Kokpit WP / wp plugin deactivate |
| W trakcie | Test “na sucho” parametrów zamiany | wp search-replace 'old' 'new' --all-tables --dry-run |
| W trakcie | Faktyczna zamiana w bazie danych | wp search-replace 'old' 'new' --all-tables --precise --skip-columns=guid |
| W trakcie | Podmiana domen w builderach (Elementor) | Kokpit > Elementor > Narzędzia > Zastąp URL |
| Po migracji | Opróżnienie Object Cache i Transients | wp cache flush oraz wp transient delete --all |
| Po migracji | Przebudowa 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
siteurlihomew tabeliwp_optionsmają dokładnie taki sam protokół (https://) i prefiks (wwwvsnon-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
.htaccessdo 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
.sqlpojawia 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.







