WordPress multisite naar single site migreren begint met één kalmerende observatie: je kunt niet zomaar de subsite-tabellen kopiëren. De prefixen kloppen niet, de uploads liggen ergens anders, en gebruikersrechten zitten verstopt in usermeta. Wie dit één keer goed doet, weet daarna precies wat het pad is. Wie het ad-hoc doet, raakt halverwege kwijt waar welke kolom hoort.
Hieronder loop ik door de aanpak die ik standaard inzet. Voornamelijk via wp-cli, omdat dat repeteerbaar en controleerbaar is. Plugin-routes (All-in-One WP Migration, MU-Migration) noem ik waar ze relevant zijn. Voor de meeste subsite-extracties geeft wp-cli meer controle.

Waarom een subsite losweken uit multisite
De meest voorkomende redenen die ik tegenkom:
- Een onderdeel van het netwerk is verkocht of overgedragen aan een andere eigenaar.
- Het multisite-netwerk groeide te zwaar en één subsite verdient eigen hosting met eigen resources.
- Performance-issues: een trage subsite trekt de hele multisite mee, of een plugin-conflict op één subsite raakt de rest.
- De eisen aan SEO of techniek lopen zo uiteen per subsite dat een gedeeld netwerk niet meer logisch is.
Wanneer het niet zinvol is: als de subsite gewoon meedraait zonder issues, het beheer overzichtelijk is, en je het netwerk vooral hebt voor centraal updates en gedeelde plugins. Multisite is een legitieme keuze voor wie meerdere sites met gedeelde infrastructuur beheert. Niet elke subsite hoort eruit gehaald te worden.
WordPress multisite naar single site migreren in vijf fasen
De vijf fasen die ik aanhoud:
- Voorbereiding. Subsite identificeren met
wp site list, plugins en thema’s inventariseren, mediabibliotheek-omvang meten, gebruikerslijst en hun rollen documenteren. - DB-export. Alleen de subsite-tabellen exporteren via
wp db exportmet een tabellen-filter. - Prefix-herschrijven.
wp_2_postswordtwp_posts, en zo voor alle tabellen. Tegelijk de geserialiseerde data corrigeren. - Uploads en bestanden. De
wp-content/uploads/sites/{blog_id}/map overzetten naarwp-content/uploads/op het nieuwe systeem. - Single-site test en cleanup. Een schone WordPress installeren, de aangepaste SQL importeren, uploads kopiëren, gebruikers herstellen, en een complete site-check uitvoeren.
Hieronder per fase wat dat concreet betekent.
Subsite-tabellen exporteren met wp-cli
De eerste echte technische stap. Stel: je subsite heeft blog_id = 2 (zichtbaar via wp site list). De tabellen die daarbij horen hebben de prefix wp_2_. Maar niet alle tabellen: wp_users en wp_usermeta zijn netwerk-breed en bevatten alle gebruikers van álle subsites.
De export-flow:
wp site list --format=table
wp db tables --url=example.com/subsite2/ --format=csv
wp db export --tables=$(wp db tables --url=example.com/subsite2/ --format=csv --skip-plugins --skip-themes) subsite2.sqlWat hier gebeurt:
- Stap 1: bevestigt welke blog_id de subsite heeft.
- Stap 2: lijst alle tabellen voor de subsite. wp-cli is slim genoeg om de juiste prefix te selecteren als je
--urlmeegeeft. - Stap 3: exporteert alleen die tabellen naar een SQL-bestand.
wp_users en wp_usermeta exporteer ik apart, want die zijn niet subsite-specifiek. Daar selecteer ik straks alleen de relevante rijen uit, op basis van wie toegang heeft tot deze subsite.

Prefix-conversie en URL-replacement
Nu het echte handwerk. De geëxporteerde SQL bevat tabelnamen als wp_2_posts, wp_2_options, wp_2_terms. Op de single-site-bestemming moeten dat wp_posts, wp_options, wp_terms zijn.
Eenvoudige sed-rewrite voor de tabelnamen:
sed -i 's/wp_2_/wp_/g' subsite2.sqlOp macOS gebruik je gsed (na brew install gnu-sed) omdat de BSD-sed iets andere syntax heeft.
Pas op: deze rewrite raakt alle voorkomens van wp_2_, niet alleen tabelnamen. Als je toevallig content hebt waar die string letterlijk in voorkomt, krijg je een ongewenste vervanging. In de praktijk is dat zelden een issue, maar controleer het op gevoelige sites.
Daarna importeer je de SQL op de nieuwe single-site database, en draait een search-replace voor URL’s:
wp search-replace 'oudeurl.com/subsite2' 'nieuweurl.com' --all-tables
wp search-replace 'oudeurl.com' 'nieuweurl.com' --all-tablesHier is wp-cli-aanpak veiliger dan een platte SQL-replace. WordPress slaat geserialiseerde data op (in wp_options, in postmeta), en een naïeve string-replace breekt de serialisatie. wp search-replace weet dat te repareren.

De uploads/sites/{blog_id}/ map: het stille deel
Dit is het stukje dat in handleidingen vaak één regel krijgt en in de praktijk twee uur kost. Een multisite slaat de media van elke subsite in wp-content/uploads/sites/{blog_id}/. Voor blog_id 2 is dat dus wp-content/uploads/sites/2/.
Op de single-site bestemming verwacht WordPress alles in wp-content/uploads/ direct. Stappen:
- Map
wp-content/uploads/sites/2/van de multisite kopiëren naarwp-content/uploads/op de single-site. - Attachment-URL’s in de database aanpassen via search-replace. Van
/wp-content/uploads/sites/2/2024/01/image.jpgnaar/wp-content/uploads/2024/01/image.jpg. - Eventueel
_wp_attached_filepostmeta nalopen (de relatieve paden in dat veld bevatten geensites/2/, dus die meestal niet, maar check het).
Concreet commando:
wp search-replace '/wp-content/uploads/sites/2/' '/wp-content/uploads/' --all-tablesDaarna in de mediabibliotheek een steekproef nemen. Zichtbaar in front-end, thumbnails goed, niet stuk.
Gebruikers en rechten herstellen
WordPress multisite slaat gebruikersrechten op een eigenaardige manier op. In wp_usermeta heeft elke gebruiker een veld wp_X_capabilities waar X de blog_id is. Voor blog_id 2 is dat wp_2_capabilities.
Op de single-site is de verwachte sleutel wp_capabilities (zonder nummer). Dus na de import:
wp user meta list {user_id}
wp user meta update {user_id} wp_capabilities '{"administrator":true}' --format=jsonOf in een loop voor alle relevante gebruikers, op basis van een lijst die je vooraf uit wp_usermeta haalt waar meta_key = 'wp_2_capabilities'. Dat is een SQL-query buiten wp-cli om, maar één-malig.
Alternatief: handmatig in de admin per gebruiker de rol opnieuw zetten. Voor een handvol gebruikers prima. Voor honderden is wp-cli of een klein script de manier.
Vragen tijdens een lopende multisite-extractie? Stuur de blog_id, het netwerk-domein en het aantal gebruikers, dan kijk ik mee. Eén ontwikkelaar, één traject.
All-in-One WP Migration en MU-Migration als alternatief
Niet iedereen werkt graag op de command line. Twee plugin-routes die werken voor eenvoudigere scenario’s:
All-in-One WP Migration met de Multisite-extensie kan een subsite exporteren als pakket en op een single-site importeren. Voor kleine subsites zonder veel custom plugins werkt dat aardig. Bij grote uploads-mappen of custom database-structuren loop je tegen limieten op.
MU-Migration is een plugin die specifiek voor multisite-naar-single-site is geschreven. Werkt via wp-cli commando’s onder de motorkap. Voor wie tussen plugin en CLI in wil zitten.
Mijn voorkeur voor complexe scenario’s blijft directe wp-cli, omdat ik dan exact zie wat er gebeurt en kan ingrijpen waar nodig. Voor een kleine, simpele subsite zijn de plugin-routes prima.
De officiële wp-cli-documentatie staat op wp core multisite-convert: WP-CLI Command, nuttig om de gerelateerde commando’s compleet te zien.
Hangt de subsite-extractie samen met een URL-shift, lees dan ook Subdomein naar hoofddomein verhuizen, dat scenario komt vaak in combinatie voor.
Wat kost een multisite-naar-single-site-migratie
Een subsite uit multisite halen reken ik tegen 95 euro per uur. Standaardklus 6 tot 16 uur, complexe scenario’s (custom plugins, veel gebruikers met overlap tussen subsites, custom uploads-paths via Symbolic Links, of een gedeelde mediabibliotheek-aanpak) 16 tot 40 uur.
Verbeteren boven herbouwen geldt ook hier. Een subsite is meestal sneller losgeweekt dan opnieuw gebouwd, ook als de subsite verouderd is. De content, gebruikers en SEO-history zijn waardevol genoeg om mee te nemen. Een complete rebuild kost vaak het dubbele.
Multi-tenant scenario’s in andere stacks (Laravel met eigen schema’s per tenant, Magento met store-views) volgen andere principes. Daar werk je niet met prefixen maar met eigen connections of store_id-filters. Het idee blijft hetzelfde: één tenant losweken zonder de rest te raken. De implementatie is anders.
Source-code is hier letterlijk relevant. Ik werk op de command line met wp-cli, niet via een betaalde plugin als het zonder kan. Eén aanspreekpunt voor het hele traject, geen ticketsysteem, geen migratieteam.



