Migravo WXR Media Migration
Import an export into a new WordPress site and two things break that nothing in the box will fix for you.
ACF Relationship, Post Object and Gallery fields stop working. They store post IDs, and the new site assigns different IDs, so every one of those fields now points at the wrong post or at nothing at all. Nothing reports it. The fields simply look empty, or worse, quietly reference unrelated content — and on a site built around related posts, professionals, case studies or products, that is most of the site.
Every image still lives on the old server. The core importer copies the references, not the files. The content looks fine right up to the day the source site is switched off.
Migravo fixes both, on a site that has already been imported or as part of importing it.
Repairing relationships
Upload the export from the source site on the ID Map tab. Migravo matches each old post to its new one by slug, builds a map of old ID to new ID, then rewrites every relationship, post-object and gallery field on the site in one pass.
It sweeps the whole database rather than one post type, so both sides of a two-way relationship are repaired as long as both are present — the field on the Artist and the field on the Song are just two more fields to fix, and it does not matter which order you imported them in. Import one post type now and the other later, then run the repair again: the map keeps what it already knows and grows as you import.
It only rewrites values it can account for. A flat list of numeric IDs is remapped; an ACF Link field, a repeater row, a phone number that merely starts with digits, and any ID with no entry in the map are all left exactly as they were. An ID whose target has since been deleted is reported rather than written as a dead reference.
And it tells you what it could not fix, as a verdict rather than a guess. An ID it has no mapping for is left alone — correct in itself, but the field still points somewhere wrong and the number still looks like a valid ID, so nothing errors. Migravo checks each one against the field’s own definition: ACF records which post types a Relationship or Post Object field may reference, so a practice_areas field declared as accepting service while holding an ID that is a trade_report here is broken as a matter of fact. Those are reported as broken and listed first, with how many fields hold each ID and what it actually is. Values that match the type the field accepts are marked as almost certainly already repaired, so re-running the tool does not raise false alarms. Import the missing post type and run it again; the map grows as you import, so order does not matter.
Re-hosting media
During import, Migravo finds every media URL in the post content and the featured image, downloads each file from the source site into the Media Library, and rewrites the content to point at the local copy. Files are deduplicated by source URL and filename, so importing the same export twice does not produce a second copy of every image.
The rest of a real migration
- Resumable, non-blocking import — preview what the file contains, then import with a live progress bar and 1–10 concurrent requests. Close the tab or press Stop and the import offers to continue where it left off. Posts lost to a server hiccup are retried automatically, and any that still fail are listed with a button to retry just those.
- Safe re-imports — choose to skip, update in place, or delete and replace posts that already exist. Matching accounts for the fact that WordPress rewrites slugs on insert, so re-running an import does not silently duplicate posts.
- Taxonomy mapping — send a taxonomy from the export into a different taxonomy on this site, or skip it.
- Author mapping — map each author in the export to an existing user, or create one.
- Field renaming — map meta/ACF field keys when the source and destination names differ.
- Featured image from a custom field — for source sites that store it outside the standard thumbnail meta.
- Multisite aware — choose which site in the network to import into, or to build the ID map against.
- Built for large exports — tested against a 52 MB export of 5,179 posts, which it parses in about 44 seconds using around 4 MB of memory.
Security
- Every AJAX action requires a valid nonce and the
importcapability. - All database queries are parameterized via
$wpdb->prepare(). - Media downloads are guarded against SSRF — loopback, private-network and link-local addresses (including the cloud metadata endpoint at 169.254.169.254) are rejected. Redirects are followed, and every hop is checked by the same rule as the first, so a public URL that redirects to an internal address is refused at the redirect rather than fetched. A source site on a private network can be allowed deliberately, per host, using the
wxmi_allow_private_media_hostfilter — see the FAQ. - Restoring serialized meta values blocks PHP object injection (unserialize is called with
allowed_classes => false). - The temporary directory used during import is given a random, unguessable name, so the export and the per-post cache written inside it cannot be requested by anyone who does not already know the path. An .htaccess and an index.php are written alongside as a second layer — they help on Apache, but they are not relied on, because nginx, LiteSpeed and IIS do not read .htaccess at all.
Why not just use the core WordPress Importer?
Use it — and then use this. The core importer creates the posts, which is the part that already works. What it has no concept of is what became of everything that referenced those posts by ID, or of the files they pointed at:
- It does not know that ACF Relationship, Post Object and Gallery fields hold post IDs, so it leaves every one of them pointing at whatever now happens to own that ID on the new site.
- It copies image references, not images. The content depends on the old server staying online forever.
- Re-running it creates another copy of everything, so a partial or interrupted import cannot simply be repeated.
Is this a full-site migration tool?
No, and deliberately not. If you can copy an entire site — database, uploads and all — a tool built for that is the right choice and this is not it.
Migravo is for the cases where you cannot: you have a WXR export and a destination that already exists, you are pulling a few post types across rather than cloning a whole site, or you are on the other side of an import that already happened and left broken relationships behind. The ID Map tab works on a site that was imported months ago by something else entirely.
