Baza WordPressa przenosi się w całości razem z treściami, ustawieniami, kontami i, w sklepie WooCommerce, zamówieniami. Gdy domena się nie zmienia, wystarczy eksport, import i zgodny plik wp-config.php. Gdy zmienia się adres, także z http na https, adresy trzeba zmienić narzędziem, które rozumie dane serializowane. Zwykła zamiana tekstu w pliku SQL uszkadza część ustawień bez żadnego komunikatu.
Eksport i import: limity, które przerywają pracę w połowie
Eksport przez phpMyAdmin wystarcza przy małych bazach. Przy większych import przerywają limity serwera: maksymalny rozmiar przesyłanego pliku, czas wykonania skryptu albo wielkość pojedynczego zapytania. Ten ostatni może objawić się komunikatem MySQL server has gone away (błąd 2006), gdy jedno zapytanie przekracza ustawienie max_allowed_packet. Może to być wielowierszowy INSERT z eksportu, nawet jeśli żaden wiersz nie jest duży, albo pojedyncza duża opcja czy treść strony z buildera. Ten sam komunikat ma też inne przyczyny, opisane w dokumentacji MySQL, m.in. przekroczony czas bezczynności połączenia (wait_timeout) albo restart serwera bazy. Sam komunikat nie przesądza więc o przyczynie.
Import przerwany w połowie zostawia bazę częściowo wypełnioną. Ponowny import do takiej bazy daje błędy zduplikowanych kluczy, więc kolejną próbę zaczyna się od pustej bazy. Przy dużych bazach pewniejszy jest zrzut i import z wiersza poleceń przez SSH, jeśli hosting go udostępnia. Import zawsze do pustej bazy, a plik zrzutu zostaje jako kopia:
mysqldump --single-transaction --default-character-set=utf8mb4 -u UZYTKOWNIK -p STARA_BAZA > zrzut.sql
mysql --default-character-set=utf8mb4 -u UZYTKOWNIK -p NOWA_BAZA < zrzut.sql
Kodowanie znaków i porównywanie: „Ä…” i błąd 1273
Gdy zamiast „ą” widać „Ä…”, a zamiast „ł” „Å‚”, zrzut zapisany w UTF-8 został odczytany jako kodowanie jednobajtowe. Każdy polski znak to w UTF-8 dwa bajty, które po złym odczycie stają się dwoma osobnymi znakami. Przyczyna leży zwykle w ustawieniu kodowania przy eksporcie albo imporcie. Baza WordPressa powinna działać w utf8mb4, które obsługuje też emoji.
Drugi częsty błąd pojawia się przy przenosinach z MySQL 8 na serwer z MariaDB: #1273 - Unknown collation: 'utf8mb4_0900_ai_ci'. To domyślne porównywanie MySQL 8. Własne tabele WordPress zakłada z porównywaniem utf8mb4_unicode_520_ci albo utf8mb4_unicode_ci, ale tabele wtyczek i bazy utworzone z ustawieniami domyślnymi serwera często mają właśnie 0900. MariaDB w wersjach sprzed 11.4.5, w tym w często spotykanej 10.11, go nie zna. Nowsze wydania przyjmują je jako alias własnych porównywań (MDEV-20912). Na starszym serwerze naprawa polega na zamianie porównywania w kopii zrzutu na takie, które serwer zna, np. utf8mb4_unicode_ci, przy zachowaniu kodowania utf8mb4. Zrzut z MySQL 8 może zawierać też inne porównywania z tej rodziny, np. utf8mb4_0900_as_cs, więc przed importem warto wyszukać w nim frazę 0900. Oryginalny zrzut zostaje bez zmian:
sed 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' zrzut.sql > zrzut-mariadb.sql
wp-config.php i prefiks tabel
Komunikat „Błąd łączenia się z bazą danych” (po angielsku Error establishing a database connection) oznacza, że WordPress nie łączy się z bazą: nazwa, użytkownik, hasło albo adres serwera bazy w wp-config.php nie pasują do nowego hostingu. Adres serwera bazy nie zawsze brzmi localhost, więc trzeba go wziąć z panelu nowego hostingu.
Zmienna $table_prefix musi odpowiadać nazwom tabel w zaimportowanej bazie. Jeśli prefiks się zmienia, sama zmiana nazw tabel nie wystarczy. Prefiks jest też częścią nazwy opcji z rolami użytkowników ({prefiks}user_roles) i kluczy uprawnień w metadanych użytkowników ({prefiks}capabilities, {prefiks}user_level). Gdy zostaną ze starym prefiksem, logowanie działa, ale panel odpowiada komunikatem „Brak uprawnień dostępu do wybranej strony.” (po angielsku Sorry, you are not allowed to access this page.). Po zmianie nazw tabel, na bazie z kopią zapasową, trzeba więc zmienić też te klucze, np. dla prefiksów stary_ i nowy_:
UPDATE nowy_options SET option_name = 'nowy_user_roles' WHERE option_name = 'stary_user_roles';
UPDATE nowy_usermeta SET meta_key = CONCAT('nowy_', SUBSTRING(meta_key, 7)) WHERE meta_key LIKE 'stary\_%';
Liczba 7 w drugim zapytaniu to długość starego prefiksu plus jeden. Przy innym prefiksie trzeba ją zmienić.
Zmiana adresu strony i dane serializowane
Adres strony siedzi w opcjach siteurl i home, ale także w treściach stron, metadanych, ustawieniach motywu i wtyczek. Część z tych ustawień WordPress zapisuje jako dane serializowane PHP, w których przy każdym tekście zapisana jest jego długość, np. s:23:"https://stara-domena.pl". Zwykłe „znajdź i zamień” w pliku SQL zmienia adres, ale nie długość. Takiego wpisu PHP nie potrafi już odczytać, a WordPress traktuje go, jakby nie istniał: widżety znikają, a ustawienia motywu i wtyczek wracają do domyślnych.
Adresy zmienia się więc narzędziem, które odczytuje dane serializowane, zmienia je i zapisuje z nowymi długościami, np. poleceniem search-replace w WP-CLI. Najpierw kopia bazy i przebieg próbny, który tylko liczy zmiany, potem właściwa zamiana. Kolumnę guid się pomija: dokumentacja WordPressa zastrzega, że identyfikatorów GUID wpisów nie zmienia się nawet przy zmianie domeny, bo czytniki kanałów RSS uznałyby stare wpisy za nowe.
wp db export przed-zmiana-adresu.sql
wp search-replace 'https://stara-domena.pl' 'https://nowa-domena.pl' --all-tables-with-prefix --skip-columns=guid --dry-run
wp search-replace 'https://stara-domena.pl' 'https://nowa-domena.pl' --all-tables-with-prefix --skip-columns=guid
Page builder Elementor trzyma treść stron w polu _elementor_data jako JSON, w którym ukośniki są poprzedzone znakiem \ (https:\/\/). Zamiana zwykłego zapisu adresu ich nie obejmie, więc potrzebny jest drugi przebieg dla zapisu z ukośnikami poprzedzonymi znakiem \. Elementor ma do tego własne narzędzie zamiany adresu w zakładce narzędzi, a po zmianie trzeba wygenerować jego pliki CSS od nowa.
Zapisy w trakcie przenosin
Zrzut bazy to stan z jednej chwili. Wszystko, co strona zapisze w starej bazie po eksporcie, a przed przełączeniem DNS, nie trafi do nowej: komentarze, wpisy z formularzy, nowe konta, a w sklepie WooCommerce zamówienia i zmiany stanów. Przy stronie wizytówce to zwykle nic. W sklepie to realne zamówienia, dlatego okno między eksportem a przełączeniem musi być krótkie, a zmiany w tym czasie wstrzymane albo przeniesione osobno.
W WooCommerce trzeba też wiedzieć, gdzie są zamówienia. Od wersji 8.2 nowe sklepy zapisują je w osobnych tabelach HPOS (wc_orders, wc_order_addresses, wc_order_operational_data, wc_orders_meta, z prefiksem), starsze w posts i postmeta, a w trybie zgodności w obu zestawach naraz. Który zestaw jest główny, pokazuje WooCommerce → Ustawienia → Zaawansowane → Funkcje. Liczbę zamówień porównuje się właśnie w nim.
Co sprawdzić po imporcie
- liczby wierszy w najważniejszych tabelach (wpisy, metadane, użytkownicy, w sklepie zamówienia w głównym zestawie tabel) na starej i nowej bazie
- polskie znaki w tytułach i treściach oraz w nazwach kategorii
- logowanie administratora i dostęp do wszystkich sekcji panelu
- widżety, menu i ustawienia motywu, czyli miejsca, w których widać uszkodzone dane serializowane
- ponowne zapisanie ustawień bezpośrednich odnośników i wyczyszczenie pamięci podręcznej
Bazę przeniesiesz sam czy z kimś?
Mała baza, ta sama domena, ten sam silnik bazy na starym i nowym serwerze, strona bez sklepu. Wtedy eksport, import i poprawny wp-config.php zwykle wystarczą.
Zmiana domeny albo protokołu, przejście z MySQL 8 na MariaDB, zmiana prefiksu, strona na Elementorze, sklep z zamówieniami albo baza, której import przerywają limity serwera.
W ramach migracji hostingu przenosimy bazy WordPress razem ze stroną: z kontrolą kodowania, zmianą adresów, która nie uszkadza danych serializowanych, i porównaniem liczby wierszy przed i po. Całą kolejność przenosin strony opisuje poradnik o przeniesieniu strony WordPress, a o pracy ze sklepami przeczytasz na stronie sklepy WooCommerce.
Dokumentacja MySQL, MariaDB i WordPressa
- MySQL: MySQL server has gone away: lista przyczyn błędu 2006
- MariaDB MDEV-20912: porównywania
utf8mb4_0900_*jako aliasy w nowszych wersjach - WordPress: Migrating WordPress: ryzyko zamiany adresów w danych serializowanych i zakaz zmiany kolumny GUID
- WP-CLI: search-replace: zamiana z obsługą serializacji i przebieg próbny
- WooCommerce: HPOS: tabele zamówień i tryb zgodności
Polecenia są przykładami dla typowej instalacji. Uruchamiaj je na kopii albo po zrobieniu kopii bazy.