{"id":288571,"date":"2026-04-03T06:39:29","date_gmt":"2026-04-03T06:39:29","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/instamigrate\/"},"modified":"2026-07-21T08:09:36","modified_gmt":"2026-07-21T08:09:36","slug":"instamigrate","status":"publish","type":"plugin","link":"https:\/\/sr.wordpress.org\/plugins\/instamigrate\/","author":20510789,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.8.1","stable_tag":"1.8.1","tested":"7.0.2","requires":"5.0","requires_php":"7.4","requires_plugins":null,"header_name":"InstaMigrate","header_author":"InstaWP","header_description":"Migration helper for WordPress site transfers. Provides secure REST API endpoints for database import, file transfer, and search-replace. REMOVE THIS PLUGIN AFTER MIGRATION.","assets_banners_color":"","last_updated":"2026-07-21 08:09:36","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wordpress.org\/plugins\/instamigrate\/","header_author_uri":"https:\/\/instawp.com\/","rating":0,"author_block_rating":0,"active_installs":30,"downloads":1337,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.5.0":{"tag":"1.5.0","author":"instawp","date":"2026-04-03 06:39:17"},"1.7.3":{"tag":"1.7.3","author":"instawp","date":"2026-05-06 06:15:28"},"1.7.4":{"tag":"1.7.4","author":"instawp","date":"2026-06-01 05:51:15"},"1.7.5":{"tag":"1.7.5","author":"instawp","date":"2026-06-01 09:16:45"},"1.7.6":{"tag":"1.7.6","author":"instawp","date":"2026-06-01 10:15:47"},"1.7.7":{"tag":"1.7.7","author":"instawp","date":"2026-06-01 12:28:28"},"1.7.8":{"tag":"1.7.8","author":"instawp","date":"2026-06-04 09:34:56"},"1.7.9":{"tag":"1.7.9","author":"instawp","date":"2026-06-15 04:38:35"},"1.8.0":{"tag":"1.8.0","author":"instawp","date":"2026-06-30 06:35:16"},"1.8.1":{"tag":"1.8.1","author":"instawp","date":"2026-07-21 08:09:36"}},"upgrade_notice":[],"ratings":[],"assets_icons":[],"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.5.0","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","1.7.8","1.7.9","1.8.0","1.8.1"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[151,2814,153,4155,10745],"plugin_category":[59],"plugin_contributors":[212275],"plugin_business_model":[],"class_list":["post-288571","plugin","type-plugin","status-publish","hentry","plugin_tags-backup","plugin_tags-clone","plugin_tags-database","plugin_tags-migration","plugin_tags-transfer","plugin_category-utilities-and-tools","plugin_contributors-instawp","plugin_committers-instawp"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/s.w.org\/plugins\/geopattern-icon\/instamigrate.svg","icon_2x":false,"generated":true},"screenshots":[],"raw_content":"<!--section=description-->\n<p>InstaMigrate provides authenticated REST API endpoints that enable full WordPress site migrations between servers. It handles database export\/import with binary-safe hex encoding, file archiving and transfer, and serialization-aware search-replace for domain changes.<\/p>\n\n<p><strong>Features:<\/strong><\/p>\n\n<ul>\n<li>Database export with binary-safe hex encoding (preserves serialized PHP data)<\/li>\n<li>Database import with streaming line-by-line parsing (handles large dumps)<\/li>\n<li>File upload, download, archiving (tar.gz\/zip), and extraction<\/li>\n<li>Serialization-aware search-replace for domain migrations<\/li>\n<li>Dual authentication: API key header or WordPress Application Passwords<\/li>\n<li>WP-CLI detection (informational, reported in status endpoint)<\/li>\n<li>Admin UI showing API key and endpoint documentation<\/li>\n<\/ul>\n\n<p><strong>Important:<\/strong> This plugin grants full database and filesystem access via its REST API. Deactivate and delete it immediately after your migration is complete.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>insta-migrate<\/code> folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin through the 'Plugins' menu in WordPress<\/li>\n<li>Go to Tools &gt; InstaMigrate to find your API key<\/li>\n<li>Use the API key in the <code>X-Insta-Key<\/code> header for all REST API requests<\/li>\n<li><strong>Remove the plugin after migration is complete<\/strong><\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20do%20i%20find%20my%20api%20key%3F\"><h3>Where do I find my API key?<\/h3><\/dt>\n<dd><p>Go to Tools &gt; InstaMigrate in your WordPress admin. The API key is displayed there and can be regenerated if needed.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20safe%20to%20leave%20active%3F\"><h3>Is this plugin safe to leave active?<\/h3><\/dt>\n<dd><p>No. This plugin exposes powerful migration endpoints. Always deactivate and delete it after your migration is complete.<\/p><\/dd>\n<dt id=\"does%20it%20handle%20serialized%20data%20during%20search-replace%3F\"><h3>Does it handle serialized data during search-replace?<\/h3><\/dt>\n<dd><p>Yes. The search-replace engine correctly unserializes PHP data, performs replacements recursively, then re-serializes with updated byte counts.<\/p><\/dd>\n<dt id=\"what%20authentication%20methods%20are%20supported%3F\"><h3>What authentication methods are supported?<\/h3><\/dt>\n<dd><p>Two methods: (1) <code>X-Insta-Key<\/code> header with the plugin API key, or (2) WordPress Application Passwords via HTTP Basic Auth (requires admin privileges).<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.8.1<\/h4>\n\n<ul>\n<li>New: <code>\/theme\/activate<\/code> switches the active theme, installing it from wordpress.org first if it isn't present. Defaults to Twenty Twenty-One, which supports WordPress 5.3 and newer \u2014 a single theme that works across the whole supported range. This matters before a core change: WordPress ships a theme matched to each release, so the active theme is often newer than the core version being moved to, and it then calls functions that don't exist there and takes the whole site down. The response returns the previous theme so the change can be reversed. A request with no body at all does the whole step \u2014 it activates Twenty Twenty-One and installs it first if the site doesn't have it. On multisite it changes only the current site unless you ask for the whole network.<\/li>\n<li>New: <code>\/core\/update<\/code> updates WordPress core to a requested version. Intended for the case where the site you're copying from runs a newer WordPress than the site you're copying to \u2014 a migration replaces plugins, themes and the database, but not WordPress itself, so without this the destination keeps its older core and a plugin that needs the newer one can fatal on load. Raise-only by design: a request at or below the current version is reported as skipped and never downgrades. Multisite is refused outright \u2014 a network shares one copy of WordPress, so a core change there can't be limited to one site or undone per site. Hosts that require FTP\/SSH credentials for filesystem writes return a distinct <code>fs_credentials_required<\/code> error rather than a generic failure.<\/li>\n<li>New: <code>\/plugins\/deactivate-plugin<\/code> deactivates plugins without deleting them, so the change can be undone \u2014 the response returns the previous active-plugin list as a restore point. Pass <code>all_except_self<\/code> to deactivate everything except InstaMigrate in one call (useful before a core update). Network-activated plugins are included on multisite. InstaMigrate refuses to deactivate itself, so a site is never left without a way back in.<\/li>\n<li>Fix: <code>\/files\/search-replace<\/code> no longer crashes the site when the <code>extensions<\/code> value is sent as plain text (for example <code>txt<\/code>) rather than a list. It previously produced a \"critical error\" page with no usable error message; it now accepts a list, a comma-separated string, or a JSON array, and always returns a proper error if something else goes wrong.<\/li>\n<li>Fix: <code>\/files\/mkdir<\/code> now keeps new directories inside <code>wp-content<\/code>, matching every other file endpoint. Previously it would create a directory anywhere the site's user could write. A path given relative to <code>wp-content<\/code> still works exactly as before \u2014 it is simply anchored there instead of to an undefined location \u2014 so existing callers are unaffected. Only a path pointing outside <code>wp-content<\/code> is now refused.<\/li>\n<li>Fix: the plugin-install, plugin-delete, plugin-deactivate, theme-activate, core-update and file-search-replace endpoints now always return a structured JSON error when something unexpected goes wrong. Previously an error raised outside the guarded section \u2014 including from another plugin's own activation or deactivation code \u2014 produced a bare HTTP 500 with no response body, which is indistinguishable from the site being down.<\/li>\n<\/ul>\n\n<h4>1.8.0<\/h4>\n\n<ul>\n<li>New: skip files and folders during a migration. <code>\/files\/archive<\/code> now honors an <code>exclude<\/code> parameter (a JSON array or CSV of wp-content-relative paths\/globs) \u2014 matched files and whole directories are left out of the archive. Default (no exclude) is byte-identical to before.<\/li>\n<li>New: <code>\/files\/list<\/code> endpoint lists a wp-content directory one level at a time, with file and directory sizes, so a migration UI can show what's on the site and let you choose what to skip. Confined to wp-content (no access outside it).<\/li>\n<li>Fix: archive and database-dump temp files now use a unique name (not just a per-second timestamp), so two exports of the same directory in the same second can't collide on one filename and have a CDN serve a stale copy.<\/li>\n<\/ul>\n\n<h4>1.7.9<\/h4>\n\n<ul>\n<li>New: added a disk-usage endpoint that reports how much space the site uses (files plus database).<\/li>\n<li>New: the status endpoint now also returns the site's admin email address.<\/li>\n<\/ul>\n\n<h4>1.7.8<\/h4>\n\n<ul>\n<li>Fix: <code>\/files\/archive<\/code> (tar.gz) no longer 500s on large <code>uploads\/<\/code>\/<code>plugins\/<\/code> directories. It now streams a POSIX ustar tar straight into a gzip sink one file at a time (constant memory, single pass) instead of building the whole tar in memory and re-reading it to compress (two passes that OOM'd as an uncatchable PHP fatal). Output is unchanged on the wire (still a <code>.tar.gz<\/code>); long paths beyond ustar's 255-char limit are reported in a new <code>long_paths<\/code> field instead of producing an unextractable entry.<\/li>\n<li>Fix: archiving a directory that contains the plugin's own temp dir (e.g. <code>uploads\/<\/code>, which holds <code>uploads\/instamigrate\/<\/code>) no longer self-includes the half-written archive \u2014 the temp dir is skipped during the walk. This removes a runaway-growth timeout and the long-standing silently-ignored <code>exclude<\/code> behaviour for the temp dir.<\/li>\n<\/ul>\n\n<h4>1.7.7<\/h4>\n\n<ul>\n<li>Security: hardened the <code>source_url<\/code> SSRF guard. IP validation now normalizes addresses with inet_pton and tests an explicit CIDR denylist instead of PHP's range filter flags \u2014 closing bypasses via IPv4-mapped IPv6 (e.g. <code>::ffff:169.254.169.254<\/code>), NAT64 (<code>64:ff9b::\/96<\/code>) and CGNAT (<code>100.64\/10<\/code>). The host is resolved once, every returned address is validated, and the validated IP is pinned into the request (<code>CURLOPT_RESOLVE<\/code>) so DNS rebinding between check and connect can't reach an internal address.<\/li>\n<li>Fix: <code>\/db\/import<\/code> now deletes a <code>source_url<\/code>-pulled temp dump on every exit path (no orphaned gz left in <code>uploads\/instamigrate\/<\/code>), and guards the magic-byte read against an unreadable file (clean 500 instead of a fatal).<\/li>\n<li>Fix: <code>\/db\/export<\/code> <code>skip_regenerable<\/code> no longer over-matches \u2014 it uses a Wordfence allowlist (never skips <code>wfconfig<\/code>\/<code>wfls_*<\/code> config + 2FA secrets) and skips only Elementor telemetry (<code>e_events<\/code>), never form submissions. <code>ignore_tables<\/code> now also accepts suffix-only table names.<\/li>\n<li>Improvement: <code>\/files\/fetch-probe<\/code> caps the body it buffers (origins that ignore Range can return a full body), avoiding a memory spike on large artifacts.<\/li>\n<\/ul>\n\n<h4>1.7.6<\/h4>\n\n<ul>\n<li>New: Server-to-server pull. <code>\/db\/import<\/code> and <code>\/files\/extract<\/code> accept a <code>source_url<\/code> param and fetch the artifact directly from the source site (SSRF-guarded: http\/https only, blocks loopback\/private\/reserved\/link-local targets), so a destination can pull large archives straight from the source instead of routing every byte through a migration client. New <code>\/files\/fetch-probe<\/code> endpoint reports whether the destination can reach a given source URL, so callers can fall back to chunked upload when it can't.<\/li>\n<li>New: <code>\/db\/export<\/code> can skip the data of regenerable\/log tables (opt-in via <code>skip_regenerable=1<\/code> and\/or an explicit <code>ignore_tables<\/code> CSV) to shrink huge dumps \u2014 the DROP+CREATE schema is still emitted so dependent plugins don't fatal, only the rows are skipped. Default behavior is unchanged.<\/li>\n<li>New: <code>\/files\/extract<\/code> accepts <code>verify=1<\/code> to count any archive entries missing after extraction and report <code>complete:false<\/code> + <code>missing<\/code> \u2014 surfacing silent ZipArchive\/Phar truncation on very large file trees instead of a false success.<\/li>\n<\/ul>\n\n<h4>1.7.5<\/h4>\n\n<ul>\n<li>Performance: <code>\/db\/export<\/code> now compresses the SQL dump inline in a single pass at gzip level 1, instead of writing the full uncompressed dump to disk and then re-reading and re-compressing it at level 9. This removes a second full pass over the dump and the most CPU-intensive step of the export \u2014 on large databases the level-9 re-compression was the main cause of edge-timeout (HTTP 524) failures. The emitted SQL is byte-for-byte unchanged and the import side auto-detects gzip by magic bytes, so the compression level is fully transparent.<\/li>\n<\/ul>\n\n<h4>1.7.4<\/h4>\n\n<ul>\n<li>New: <code>\/insta-migrate\/v1\/plugins\/install-activate-plugin<\/code> endpoint. Installs a plugin from a URL (or <code>.zip<\/code>) via WordPress's <code>Plugin_Upgrader<\/code> and activates it. Powers App-driven post-migration plugin actions, so an integrating App can request specific plugins be installed on the destination as part of the migration. Authenticated via <code>X-Insta-Key<\/code>.<\/li>\n<li>New: <code>\/insta-migrate\/v1\/plugins\/delete-plugin<\/code> endpoint. Accepts a <code>plugin_files<\/code> array, deactivates each plugin and removes it via <code>delete_plugins()<\/code>. InstaMigrate is filtered out of the request (both <code>instamigrate\/insta-migrate.php<\/code> and <code>insta-migrate\/insta-migrate.php<\/code> directory variants) so the endpoint cannot disable its own access. Authenticated via <code>X-Insta-Key<\/code>.<\/li>\n<\/ul>\n\n<h4>1.7.3<\/h4>\n\n<ul>\n<li>New: Bridge auto-discover. Plugin polls the tracker every 5 minutes (and on every visit to the InstaMigrate admin page) to see if a bridge migration has been assigned to this site, and starts itself automatically. Removes the need for site owners to copy\/paste tracker URL, migration ID, and shared secret. Discover URL is pinned (HTTPS, cert verification on); 60-second outbound rate limit with a manual \"Check now\" button to bypass.<\/li>\n<\/ul>\n\n<h4>1.7.2<\/h4>\n\n<ul>\n<li>New: <code>push_file<\/code> bridge action. Source plugin can now multipart-POST a local file directly to a remote URL \u2014 typically the destination plugin's <code>\/files\/upload<\/code> or <code>\/db\/import<\/code> endpoint. Enables source-bridge inversion for sites where the source is firewalled (Imunify360 IP bans, custom WAFs) but the destination is reachable normally. The source plugin polls outbound, dest receives via its existing endpoints, no tracker upload sink required.<\/li>\n<\/ul>\n\n<h4>1.7.1<\/h4>\n\n<ul>\n<li>Fix: Bridge self-trigger curl timeout raised from 500ms to 5s. On slow PHP-FPM hosts the original 500ms budget could abort before TCP connect + TLS handshake completed, breaking the bridge tick chain. WP-Cron is the safety net but can't fire on dead\/CF-blocked sites.<\/li>\n<\/ul>\n\n<h4>1.7.0<\/h4>\n\n<ul>\n<li>New: Bridge Mode. Plugin can run as an outbound-polling agent for sites where inbound REST is filtered (Cloudflare JS challenge, WAF blocking <code>\/wp-json\/<\/code>). The plugin POSTs to the tracker for the next command, dispatches it via the existing internal handlers, posts the result back, and self-triggers the next tick using <code>CURLOPT_RESOLVE<\/code> to the origin IP for CF bypass. WP-Cron <code>bridge_safety_tick<\/code> resumes the chain on the next admin pageview if a tick dies mid-flight.<\/li>\n<li>Admin UI: \"Bridge Mode\" card in Settings \u2192 InstaMigrate with Start \/ Stop \/ status fields.<\/li>\n<\/ul>\n\n<h4>1.6.0<\/h4>\n\n<ul>\n<li>Performance: DB export now uses multi-row INSERT statements (10-50x faster imports)<\/li>\n<li>Performance: DB import streams gzip directly (no decompress-to-disk step)<\/li>\n<li>Performance: DB import uses transaction batching for legacy single-row dumps<\/li>\n<li>Performance: Added MySQL tuning flags (autocommit=0, unique_checks=0) during import\/export<\/li>\n<li>Backward compatible: v1 single-row dumps still import correctly with auto-batched transactions<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>Internal version alignment (header and constant now both report 1.5.0)<\/li>\n<li>No functional changes from 1.4.0<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>Security: Permission callbacks now require <code>manage_network<\/code> (super admin) on multisite, instead of <code>manage_options<\/code> which subsite admins may have<\/li>\n<li>Security: File concat (<code>\/files\/concat<\/code>) output restricted to plugin temp directory only<\/li>\n<li>Security: File search-replace (<code>\/files\/search-replace<\/code>) restricted to uploads directory only<\/li>\n<li>Security: File upload (<code>\/files\/upload<\/code>) destination restricted to uploads directory only<\/li>\n<li>Security: File search-replace now uses WP_Filesystem for reads and writes instead of direct PHP file functions<\/li>\n<li>Change: Temp directory moved from <code>wp-content\/insta-migrate-tmp\/<\/code> to <code>wp-content\/uploads\/insta-migrate-tmp\/<\/code> per wp.org guidelines<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>Removed all exec()\/shell_exec() calls per wp.org plugin guidelines<\/li>\n<li>Removed <code>\/wp-cli<\/code> REST endpoint (required exec)<\/li>\n<li>Archive creation and extraction now use PharData\/ZipArchive exclusively<\/li>\n<li>Binary detection (has_wp_cli) now uses hardcoded path checks only<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>New: <code>\/db\/fix-prefix<\/code> endpoint \u2014 fixes usermeta meta_key and options option_name values after a migration where the table prefix changed. Prevents users losing roles\/capabilities when the sed prefix-rewrite missed single-quoted values.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: <code>\/files\/concat<\/code> endpoint \u2014 concatenate chunked file uploads on the server (for large archives exceeding PHP upload limit)<\/li>\n<li>New: <code>\/files\/search-replace<\/code> endpoint \u2014 search-replace strings within static files on disk (CSS\/JS\/JSON), used for Elementor CSS domain replacement after DB search-replace<\/li>\n<li>Fix: Numeric columns (INT, BIGINT, DECIMAL, etc.) now exported as plain numbers, not hex-encoded. Prevents MySQL from misinterpreting hex literals as integers.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>Database export\/import with hex encoding<\/li>\n<li>File upload, download, archive, extract<\/li>\n<li>Serialization-aware search-replace<\/li>\n<li>Dual authentication (API key + Application Passwords)<\/li>\n<li>WP-CLI detection in status endpoint<\/li>\n<li>Admin UI with API key management<\/li>\n<\/ul>","raw_excerpt":"Secure REST API endpoints for WordPress site migration \u2014 database export\/import, file transfer, and search-replace.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/288571","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=288571"}],"author":[{"embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/instawp"}],"wp:attachment":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=288571"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=288571"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=288571"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=288571"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=288571"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=288571"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}