WordPress naar een nieuwe server verhuizen zonder downtime
De volgorde bepaalt alles: nieuwe server voorbereiden, gegevens kopiëren, via het hosts-bestand onder het echte domein testen, het certificaat vooraf uitgeven en pas daarna DNS omzetten. Met de weg terug en de echte foutmeldingen.
Een WordPress-verhuizing mislukt zelden door één enkel commando. Ze mislukt door de verkeerde volgorde. Wie eerst DNS omzet en daarna pas kopieert, krijgt gegarandeerd een venster waarin bezoekers ofwel een foutpagina ofwel een half gevulde installatie te zien krijgen. Andersom kunt u de nieuwe server rustig afmaken, hem onder het echte domein testen en het DNS-record pas wijzigen op het moment dat alles aantoonbaar werkt.
Dit artikel beschrijft precies die volgorde, plus de plekken waar het in de praktijk misgaat: geserialiseerde gegevens in de database, collations bij de overstap van MySQL naar MariaDB, certificaten zonder een DNS-record dat al naar de nieuwe machine wijst, en de weg terug voor het geval er toch iets over het hoofd is gezien.
De volgorde die geen downtime oplevert
De oude site blijft tot het allerlaatste moment online. Er wordt niets uitgeschakeld en niets verwijderd. De nieuwe server draait parallel, wordt getest en neemt het pas over zodra het DNS-record is omgezet.
- Verlaag de TTL van het A- en het AAAA-record naar 300 seconden, minstens 24 tot 48 uur van tevoren.
- Zet de nieuwe server op: webserver, PHP, database, gebruikers, mappen.
- Kopieer de bestanden, kopieer de database.
- Geef het certificaat voor het domein uit, ook al wijst DNS nog naar de oude server.
- Pas op uw eigen computer het hosts-bestand aan en klik de site onder het echte domein helemaal door.
- Draai vlak vóór de omschakeling een delta-sync, zodat de laatste wijzigingen meekomen.
- Zet DNS om. Laat de oude server daarna nog enkele dagen draaien.
Het enige punt waarop theoretisch een gat kan ontstaan, is stap 6. Hoe klein dat gat blijft, bepaalt stap 1.
TTL verlagen voordat er iets anders gebeurt
De TTL vertelt resolvers wereldwijd hoe lang ze een antwoord mogen bewaren. Staat die op 86400, dan kan een resolver uw oude IP-adres nog 24 uur lang blijven uitleveren nadat u hebt omgezet. En dit wordt graag over het hoofd gezien: een verlaagde TTL werkt pas nadat de oude TTL is verlopen. Gaat u van 86400 naar 300, dan kan het een dag duren voordat alle resolvers de korte waarde kennen.
Daarom is dit de eerste handeling en niet de laatste. dig hoort op geen enkele distributie bij de basisinstallatie en is in deze handleiding voortdurend nodig, installeer het dus als eerste:
apt install -y bind9-dnsutils
Op Debian 11, Ubuntu 22.04 en Ubuntu 24.04 heet het pakket nog dnsutils. Vanaf Debian 12 is dnsutils alleen nog een overgangspakket dat naar bind9-dnsutils verwijst, op Debian 13 zelfs een puur virtueel pakket zonder eigen versie. Allebei werken ze, want apt lost de enige aanbieder zelf op, maar bind9-dnsutils is de naam die blijft. Controleer daarna de huidige waarde:
dig +noall +answer example.com A
dig +noall +answer example.com SOA
Het getal in de tweede kolom van het A-antwoord is de resterende TTL. Vraag het rechtstreeks na bij de autoritatieve nameserver, anders ziet u alleen de restwaarde uit de cache van uw eigen resolver:
dig @a.ns14.net example.com A +noall +answer
Zet de TTL na de verhuizing weer omhoog, naar 3600 of meer. Permanent korte TTL's zorgen voor onnodige belasting en maken storingen bij uw DNS-provider langer voelbaar.
De nieuwe server passend inrichten
Hier ontstaan de meeste vervelende verrassingen, want de distributie op de nieuwe server levert andere versies dan de oude. Alle pakketcommando's in deze handleiding gaan uit van Debian of Ubuntu. Op AlmaLinux, Rocky Linux en Oracle Linux bestaat apt niet en wijken de pakketnamen af. Stand juli 2026 ziet het er zo uit:
| Systeem | PHP | Database | nginx |
|---|---|---|---|
| Debian 13 | 8.4 | MariaDB 11.8 (geen mysql-server) | 1.26.3 |
| Debian 12 | 8.2 | MariaDB 10.11 | 1.22.1 |
| Ubuntu 24.04 | 8.3 | MySQL 8.0.46 of MariaDB 10.11 | 1.24.0 |
| Ubuntu 22.04 | 8.1 | MySQL 8.0.46 of MariaDB 10.6 | 1.18.0 |
Daaruit volgen twee dingen. Ten eerste: op Debian krijgt u geen mysql-server, daar draait altijd MariaDB. Draaide de oude site op MySQL 8, dan is de verhuizing naar Debian tegelijk een databasewissel, met een heel concrete valkuil verderop. Ten tweede: de sprong van PHP 8.1 naar 8.4 gaat niet vanzelf. Oudere thema's en plugins reageren op verwijderde functies met een Fatal error: Uncaught Error: Call to undefined function of met een witte pagina. Draaide de oude installatie op PHP 8.1 en wilt u de verhuizing niet aan een PHP-upgrade koppelen, dan is Ubuntu 22.04 of een PHP-versie uit een externe pakketbron de rustigere keuze. Wat bewust in de tabel ontbreekt, is Debian 11: daar levert php-cli nog PHP 7.4.33, dus een versie zonder beveiligingsonderhoud en onder wat WordPress aanraadt. Als doel van een verhuizing valt Debian 11 daarmee af, tenzij u vooraf de Sury-pakketbron toevoegt.
De basispakketten voor een typische installatie met nginx en PHP-FPM:
apt update
apt install -y nginx php-fpm php-mysql php-xml php-curl php-mbstring php-zip php-gd php-intl
apt install -y mariadb-server mariadb-client rsync curl
Details over de webserver staan in de nginx-handleiding, de basisbeveiliging van de database in MariaDB en MySQL beveiligen. Is de server gloednieuw, dan loont vooraf een blik in de checklist voor nieuwe rootservers.
Maak de database en de gebruiker aan, met dezelfde tekenset als op de bron:
CREATE DATABASE wp_nieuw CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_nieuw'@'localhost' IDENTIFIED BY 'een-lang-wachtwoord';
GRANT ALL PRIVILEGES ON wp_nieuw.* TO 'wp_nieuw'@'localhost';
FLUSH PRIVILEGES;
Bestanden overzetten zonder de rechten te slopen
Er wordt rechtstreeks van server naar server gekopieerd, niet via uw eigen aansluiting thuis. Het eenvoudigst gaat dat met rsync van de oude naar de nieuwe server, met een SSH-sleutel in plaats van een wachtwoord:
rsync -az --delete --exclude 'wp-content/cache/' --exclude 'wp-content/uploads/backup*' \
-e 'ssh -p 22' /var/www/html/ root@nieuwe.ip:/var/www/html/
De eerste doorloop mag lang duren. Precies daarom draait u die dagen vóór de omschakeling, en vlak ervoor nog één keer hetzelfde commando, dat dan alleen de verschillen overzet.
Na het kopiëren moeten eigenaar en rechten worden rechtgezet, want UID's verschillen per systeem en rsync zet getallen over, geen namen:
chown -R www-data:www-data /var/www/html
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;
chmod 640 /var/www/html/wp-config.php
Twee bestanden gaan er vóór de eerste start uit, als ze aanwezig zijn: wp-content/object-cache.php en wp-content/advanced-cache.php. Beide zijn drop-ins van cacheplugins die naar een Redis- of Memcached-socket wijzen die op de nieuwe server nog niet bestaat. Het resultaat zou een witte pagina zijn, zonder bruikbare melding.
De database overzetten en de valkuil met de collation
De dump maakt u met WP-CLI of op de klassieke manier. WP-CLI heeft het voordeel dat het tekenset en prefix uit wp-config.php haalt:
cd /var/www/html
wp db export /root/wp-dump.sql --add-drop-table
Zonder WP-CLI: op een actueel MariaDB-systeem heet het gereedschap mariadb-dump. De oude naam mysqldump is vanaf MariaDB 11.0 nog slechts een verouderde symlink en meldt zich met Deprecated program name. It will be removed in a future release:
mariadb-dump --single-transaction --default-character-set=utf8mb4 wp_oud > /root/wp-dump.sql
Dan nu de plek waar een verhuizing van Ubuntu met MySQL 8 naar Debian met MariaDB regelmatig onderuitgaat. MySQL 8 gebruikt standaard de collation utf8mb4_0900_ai_ci, die in MariaDB helemaal niet bestaat. De import breekt af met:
ERROR 1273 (HY000) at line 42: Unknown collation: 'utf8mb4_0900_ai_ci'
Repareer dat vóór de import, rechtstreeks in de dump:
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g; s/utf8mb4_0900_as_cs/utf8mb4_unicode_ci/g' /root/wp-dump.sql
grep -c utf8mb4_unicode_ci /root/wp-dump.sql
Daarna importeren:
mariadb --default-character-set=utf8mb4 wp_nieuw < /root/wp-dump.sql
Staat er na de import overal geïnstalleerd in plaats van geïnstalleerd, dan is de tekenset bij de dump of bij de import verloren gegaan. Dat repareert u niet met zoeken en vervangen, maar door dump en import over te doen met --default-character-set=utf8mb4. Precies daarom staat de oude database op dit moment nog onaangeroerd klaar.
Pas daarna op de nieuwe server de inloggegevens in wp-config.php aan: DB_NAME, DB_USER, DB_PASSWORD, en heel belangrijk DB_HOST, mocht de oude installatie naar een externe databaseserver hebben gewezen. Blijft daar het oude adres staan, dan krijgt u Error establishing a database connection te zien, terwijl lokaal alles draait. Klemt de verbinding ondanks correcte gegevens, dan helpt Access denied for user oplossen.
Het domein vervangen, en waarom een SQL-replace de site sloopt
Eerst het goede nieuws: verandert alleen de server en blijft het domein gelijk, dan hoeft u in de database helemaal niets te vervangen. Dat is precies een van de redenen waarom de test via het hosts-bestand beter is dan een test via een tijdelijk domein. Vervangen is alleen nodig als het domein werkelijk wisselt, als u de verhuizing combineert met de stap van http naar https, of als u toch met een testdomein werkt en achteraf moet terugvervangen.
En nu de reden waarom u dat nooit met een eenvoudig SQL-commando doet. WordPress slaat opties, widgetinstellingen en themaconfiguraties op als geserialiseerde PHP-arrays. Een URL staat daarin niet kaal, maar met de bytelengte ervoor:
s:19:"https://example.com"
Vervangt u example.com via UPDATE ... REPLACE() door nieuw-domein.nl, dan wordt de string langer terwijl het getal 19 blijft staan. PHP kan de array daarna niet meer uitpakken, unserialize() geeft false terug en de betreffende instelling is weg. Typische symptomen: widgets verdwenen, thema-opties teruggezet, lege menu-items, en in de logbestanden Notice: unserialize(): Error at offset.
WP-CLI lost dat wel goed op, want het pakt de gegevens uit, vervangt ze en schrijft ze met een gecorrigeerde lengte terug. Altijd eerst met --dry-run:
wp search-replace 'https://oud-domein.nl' 'https://nieuw-domein.nl' --all-tables --precise --dry-run --report-changed-only
Wat de losse opties doen: --all-tables pakt ook tabellen aan die de WordPress-prefix niet volgen, bijvoorbeeld van webshop- of formulierplugins. --precise dwingt verwerking in PHP af in plaats van in SQL, is trager, maar gaat betrouwbaar om met geserialiseerde gegevens. --report-changed-only kort de uitvoer in tot de tabellen met echte treffers.
Ziet de voorvertoning er plausibel uit, laat dan hetzelfde commando zonder --dry-run lopen. Daarna volgt de doorloop die bijna alle handleidingen overslaan: page builders zoals Elementor bewaren hun inhoud als JSON in de database, en daar zijn de schuine strepen gemaskeerd. Een vervanging van https://oud-domein.nl vindt https:\/\/oud-domein.nl eenvoudigweg niet. Vandaar een tweede doorloop:
wp search-replace 'https:\/\/oud-domein.nl' 'https:\/\/nieuw-domein.nl' --all-tables --precise --report-changed-only
Bij Elementor hoort daarna in de backend onder Gereedschap het opnieuw genereren van de CSS-bestanden erbij, anders blijven de gegenereerde stylesheets naar het oude domein wijzen.
Twee dingen bereikt search-replace niet: constanten in wp-config.php en multisite. Staat daar define('WP_HOME', 'https://oud-domein.nl');, dan overschrijft dat elke waarde uit de database en zoekt u urenlang op de verkeerde plek. Bij multisite hebt u bovendien --network nodig en moet u de domeinen in wp_blogs en wp_site aanpassen.
En een waarschuwing over de tabelprefix: verander die niet tijdens de verhuizing. De prefix zit ook in de optienaam wp_user_roles en in de gebruikersmetavelden wp_capabilities en wp_user_level. Wie alleen de tabellen hernoemt, kan weliswaar nog inloggen, maar krijgt daarna Sorry, you are not allowed to access this page. en heeft geen beheerder meer.
Certificaat uitgeven voordat DNS ernaartoe wijst
Hier zit een kip-en-eiprobleem. De gebruikelijke HTTP-validatie van Let's Encrypt eist dat de domeinnaam al naar de validerende server wijst. Dat doet hij op dit moment nog niet, want hij moet de oude site nog uitleveren. Wie toch certbot --nginx start, krijgt:
Certbot failed to authenticate some domains (authenticator: nginx).
Invalid response from http://example.com/.well-known/acme-challenge/...: 404
De nette uitweg is de DNS-validatie. Die controleert een TXT-record _acme-challenge.example.com en trekt zich niets aan van de vraag waar het A-record naartoe wijst:
certbot certonly --manual --preferred-challenges dns -d example.com -d www.example.com
Certbot toont een waarde die u als TXT-record in uw DNS-zone vastlegt. Kijk vóór het bevestigen beslist zelf na of de zone het record al uitlevert, anders verspilt u een mislukte poging:
dig +short TXT _acme-challenge.example.com
Bij wildcarddomeinen of als u dit wilt automatiseren, staat de weg via de DNS-API in het artikel over het wildcardcertificaat. De handmatige variant vernieuwt zichzelf niet, dus schakelt u na de DNS-omschakeling over op de gewone HTTP-validatie. Vanaf dat moment werkt die wel, want het domein wijst nu naar de nieuwe server.
Testen met het hosts-bestand, als een echte bezoeker
Nu volgt het deel dat het verschil maakt tussen "zou moeten werken" en "werkt". U stuurt alleen uw eigen computer naar de nieuwe server, terwijl de rest van de wereld nog de oude site ziet.
Onder Linux en macOS bewerkt u /etc/hosts als root, onder Windows C:\Windows\System32\drivers\etc\hosts in een als beheerder gestarte editor. Voeg een regel toe met het IP-adres van de nieuwe server:
203.0.113.10 example.com www.example.com
Leeg daarna de DNS-cache, anders werkt de wijziging niet meteen door:
resolvectl flush-caches
Onder Windows is dat ipconfig /flushdns, onder macOS sudo dscacheutil -flushcache gevolgd door sudo killall -HUP mDNSResponder. Controleer of het effect heeft gehad:
getent hosts example.com
Een valkuil die veel uren kost: Firefox met DNS over HTTPS ingeschakeld negeert het hosts-bestand. Mozilla heeft dat uitdrukkelijk als "wontfix" gemarkeerd. U blijft dan de oude site zien en houdt de test voor mislukt, terwijl de nieuwe server allang correct antwoordt. Schakel daarom in de testbrowser de versleutelde DNS-resolutie uit, of beter nog: controleer los van de browser. curl kan de resolutie per aanroep overschrijven, helemaal zonder hosts-bestand:
curl -sI --resolve example.com:443:203.0.113.10 https://example.com/
Wat u in deze toestand zou moeten nalopen: de startpagina, minstens twee onderliggende pagina's, een bericht met afbeeldingen, de login op /wp-login.php, de backend, een contactformulier en bij een webshop een testartikel tot aan de kassa. Afbeeldingen verdienen extra aandacht, want ontbrekende uploads vallen in de backend niet op.
Wat de hosts-test uitdrukkelijk niet afdekt: alles wat van buitenaf de server benadert. Webhooks van betaaldiensten, externe cronjobs, crawlers van zoekmachines en uw e-mailverzending komen nog steeds bij de oude bestemming uit. Dat is prima, u moet het alleen weten.
De omschakeling: delta-sync en DNS
Tussen de eerste kopieerslag en de omschakeling zijn er reacties, bestellingen of berichten bij gekomen. De volgorde voor een schone omschakeling:
- Zet op de oude server de onderhoudsmodus aan, of stop op zijn minst even de bestellingen en reacties.
- Synchroniseer de bestanden opnieuw, deze keer duurt het seconden.
- Exporteer de database opnieuw en importeer die op de nieuwe server.
- Leeg de caches:
wp cache flushenwp rewrite flush --hard. - Controleer nogmaals met curl tegen het nieuwe IP-adres.
- Zet het A- en het AAAA-record om.
Vergeet het AAAA-record niet. Blijft dat op het oude IPv6-adres staan, dan komen alle bezoekers met IPv6 nog steeds op de oude server terecht, terwijl bezoekers via IPv4 de nieuwe site zien. Dat levert precies het soort foutbeeld op waarbij twee mensen naast elkaar zitten en verschillende websites voor zich hebben.
De oude server laat u minstens een week draaien, zonder wijzigingen. Hij is uw weg terug en uw archief.
Waaraan u ziet dat het echt gelukt is
Niet "de site laadt", maar meetbare bewijzen:
dig +short A example.com
dig +short AAAA example.com
curl -sI https://example.com/ | head -n 12
In de header mag geen 301 naar een oud domein of naar http:// staan. Vraag daarna de database welk adres WordPress zelf voor het juiste houdt:
wp option get siteurl
wp option get home
Een betrouwbare test op vergeten restanten is een zoekopdracht in de proefmodus. Meldt die nul vervangingen, dan staat het oude adres werkelijk nergens meer vastgelegd:
wp search-replace 'oud-domein.nl' 'oud-domein.nl' --all-tables --dry-run --report-changed-only
Controleer daarbij ook het certificaat, inclusief looptijd en de namen die het dekt:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject
En tot slot het foutlogbestand van de webserver, terwijl u vijf minuten door de site klikt. Duikt daar niets nieuws op, dan bent u klaar. Hapert er toch iets, dan is 502 Bad Gateway oplossen het meest voor de hand liggende aanknopingspunt, meestal omdat de PHP-FPM-socket in het nginx-blok nog het pad van de oude PHP-versie bevat.
Als het misgaat: de weg terug
De weg terug bestaat uit één enkele handeling, en daarom was stap 1 zo belangrijk: zet het A- en het AAAA-record terug op het oude IP-adres. Bij een TTL van 300 seconden zitten de meeste bezoekers binnen vijf minuten weer op de oude server, die ongewijzigd is blijven draaien.
Er is precies één geval waarin dat niet schoon verloopt: wanneer op de nieuwe server al gegevens zijn ontstaan die op de oude ontbreken. Nieuwe bestellingen, reacties, registraties. Na een rollback bestaan die gegevens alleen nog in het nieuwe systeem. Daarom beslist u binnen de eerste minuten of helemaal niet. En daarom die onderhoudsmodus tijdens de omschakeling: die houdt het tijdvenster klein waarin beide gegevensverzamelingen uit elkaar kunnen gaan lopen.
Moet u toch later terug, exporteer dan van de nieuwe server alleen de betrokken tabellen en speel die op de oude in, in plaats van de complete database terug te schuiven. Een kale rollback via een oude volledige back-up wist alles wat er in de tussentijd is gebeurd.
Foutmeldingen letterlijk
Error establishing a database connection
De inloggegevens of DB_HOST in wp-config.php passen niet bij de nieuwe server. Vaak staat daar nog het IP-adres van een externe databaseserver in plaats van localhost.
ERROR 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci'
Een dump uit MySQL 8 wordt in MariaDB geïmporteerd. Zet de collation in de dump met sed op utf8mb4_unicode_ci.
Error: This does not seem to be a WordPress installation.
WP-CLI is in de verkeerde map gestart, of de bestanden staan niet waar u ze vermoedt. Werk met --path=/var/www/html.
ERR_TOO_MANY_REDIRECTS
Bijna altijd een tegenstrijdigheid tussen siteurl in de database, een constante in wp-config.php en de doorverwijzing in de webserver. Daarnaast treft het installaties achter een proxy die de HTTPS-status niet meekrijgen en daardoor eindeloos van http naar https en terug sturen.
De startpagina laadt, alle onderliggende pagina's geven 404
De klassieker bij de overstap van Apache naar nginx. De .htaccess met de permalinkregels wordt door nginx genegeerd, daar is try_files $uri $uri/ /index.php?$args; in het location-blok nodig.
Het geüploade bestand overschrijdt de upload_max_filesize-instructie in php.ini.
De PHP-configuratie is niet mee verhuisd. Zet upload_max_filesize, post_max_size en memory_limit op de waarden van de oude server.
Witte pagina, geen regel in het logbestand
Meestal een drop-in die naar het niets wijst: wp-content/object-cache.php of advanced-cache.php. Hernoemen en opnieuw laden.
Een verhuizing die zo verloopt, kent geen tijdvenster zonder website. Het enige merkbare moment is de DNS-wissel, en de duur daarvan hebt u twee dagen eerder zelf bepaald.
Veelgestelde vragen
Hoe lang duurt het voordat de DNS-omzetting overal doorwerkt?
Waarom mag ik het domein niet gewoon met een SQL-commando in de database vervangen?
Moet ik het domein wel vervangen als alleen de server wisselt?
Hoe kom ik aan een Let's Encrypt-certificaat als het domein nog naar de oude server wijst?
Ik heb het hosts-bestand aangepast, maar zie nog steeds de oude site. Hoe kan dat?
Wat is de snelste weg terug als er na de omzetting iets niet werkt?
2026 KernelHost GmbH. Alle rechten voorbehouden. Deze handleiding is auteursrechtelijk beschermd. Publicatie op andere websites, geheel, gedeeltelijk of in bewerkte vorm, is zonder onze schriftelijke toestemming niet toegestaan. Citeren met bronvermelding en link is uitdrukkelijk welkom.

