WP Manifestindependent plugin directory
manifest / updates / wp-migrate-safe

WP Migrate Safe WP Registry grade C-. High-severity findings · 1 findingWP RegistryHigh-severity findings · 1 findingOpen the reportC-

Reliable WordPress backup & restore in .wpress format. Chunked upload, automatic DB prefix rewriting, typed error codes.

★ 3stars
35release downloads
v0.1.4latest · 5mo ago

Install

Download wp-migrate-safe-0.1.4.zip From the v0.1.4 release on GitHub, Apr 27, 2026

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/artemcluster/wp-migrate-safe/releases/download/v0.1.4/wp-migrate-safe-0.1.4.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Reliable WordPress backup & restore — no file-size limits, automatic database-prefix rewriting, honest error messages.

WP Migrate Safe exports and restores complete WordPress sites using the .wpress archive format. It's an independent open-source tool aimed at solving the pain points that plague other migration plugins: silent hangs, upload size limits, opaque failures, and broken imports across different database prefixes.

  • Chunked upload — 5 MB chunks via REST API, bypasses PHP upload_max_filesize completely
  • Automatic DB prefix rewriting — archive with wpsp_ prefix imports cleanly into a site with wp_ (or any other)
  • Serialize-aware search-replace — safely rewrites URLs inside PHP-serialized wp_options/ACF/WooCommerce data
  • Stepped processing — works on shared hosting with max_execution_time=30
  • Heartbeat detection — when a PHP worker dies mid-import, the UI knows within 60 seconds
  • Clear error codes — every failure has a code (e.g. DISK_FULL, DB_IMPORT_SYNTAX), category, hint, and doc link
  • WP-CLI commands — wp migrate-safe export|import|status|backups
  • Global lock — prevents two concurrent imports from destroying the site
  • Metadata embedded — new exports include metadata.json with source URL, prefix, WP version for future imports

Author: Artem Pronin License: GPL-3.0-or-later Version: 0.1.0 — first iteration. Export, import, chunked upload, DB prefix rewriting, serialize-aware search-replace, WP-CLI — all covered by 169 passing tests. Multisite, scheduled backups, and cloud storage land in later iterations (see roadmap).


Why this plugin

All-in-One WP Migration is the most popular migration tool for WordPress, but users run into recurring issues:

  1. File-size limits — the free version caps uploads; the paid "unlimited" extension often breaks silently
  2. Silent failures — a broken import hangs without telling you what went wrong
  3. No rollback information — once restore starts, there's no clarity on site state
  4. Prefix mismatches — importing a wpsp_ archive into a wp_ site produces half-working mess

WP Migrate Safe addresses each of these:

Problem Our approach
Upload size cap Chunked upload over REST (5 MB chunks, SHA-256 verified, resumable)
Silent hang Heartbeat polling — UI shows "⚠ No heartbeat for >60s" within one minute
Vague errors Typed error codes (DISK_FULL, WPRESS_CORRUPTED, DB_IMPORT_SYNTAX…) with hints and doc links
Prefix mismatch PrefixRewriter transparently rewrites backtick-quoted identifiers during SQL import
No pre-flight checks Dry-run validates disk space, MySQL version, archive integrity before touching anything

About the .wpress format

This plugin reads and writes archives in the .wpress format originally developed by ServMask Inc. for their plugin All-in-One WP Migration (GPL v3). We include no code from ServMask — all format handling is an independent, clean-room implementation written from the published specification in their open-source code (see lib/vendor/servmask/archiver/class-ai1wm-archiver.php in ai1wm releases).

Why we chose this format:

  • It's already the de facto standard in the WordPress migration ecosystem
  • Users with existing .wpress backups (from any source) can restore them with this plugin
  • Exports from this plugin can be restored by All-in-One WP Migration, giving users choice
  • The format is simple, streaming-friendly, and well-suited for multi-GB archives

Legal position:

  • File formats are not subject to copyright (see Google LLC v. Oracle America, Inc. — 2021, U.S. Supreme Court, and Lotus Development Corp. v. Borland International, Inc.)
  • Our implementation is located entirely under src/Archive/ (namespace WpMigrateSafe\Archive\*) and shares no code with ServMask's implementation
  • Both projects are licensed under GPL v3, so even if interpreted restrictively, derivative-work permissions align
  • The .wpress extension is not a registered trademark (verified against USPTO and EUIPO as of April 2026)

This plugin is not affiliated with, endorsed by, or connected to ServMask Inc. or All-in-One WP Migration. We gratefully acknowledge their work in establishing the .wpress format — without it, users would be locked into the tool that created each backup.


Installation

Manual

  1. Download the latest release zip
  2. WP Admin → Plugins → Add New → Upload → select the zip
  3. Activate
  4. Go to Tools → Migrate Safe

Composer (for developers embedding in a site)

cd wp-content/plugins
git clone https://github.com/artemcluster/wp-migrate-safe.git
cd wp-migrate-safe
composer install --no-dev --optimize-autoloader

WP-CLI

wp plugin activate wp-migrate-safe

Requirements: PHP 7.4+, WordPress 6.0+, MySQL 5.7+ / MariaDB 10.3+.


Usage

Export the current site

  • UI: Tools → Migrate Safe → Export tab → Start export
  • CLI: wp migrate-safe export [--output=/path/to/backup.wpress]

Upload a .wpress from somewhere else

  • UI: Tools → Migrate Safe → Upload tab → drag & drop the file

Restore a backup

  • UI: Tools → Migrate Safe → Import tab → select backup → Source URL is auto-detected → Start import
  • CLI: wp migrate-safe import backup.wpress [--old-url=http://old.com] [--new-url=https://new.com]

List backups

  • UI: Tools → Migrate Safe → Backups tab
  • CLI: wp migrate-safe backups

Job status

  • CLI: wp migrate-safe status [<job_id>]

Architecture

src/
├── Archive/              .wpress reader/writer (no WordPress dependency)
├── SearchReplace/        serialize-aware URL replacement
├── Plugin/               WordPress bootstrap, admin menu, paths
├── Rest/                 REST API controllers (upload, export, import, backups)
├── Upload/               chunked upload session management
├── Job/                  state machine for long-running operations
├── Export/               export pipeline + step implementations
├── Import/               import pipeline + step implementations
├── Errors/               error code catalog + response builder
├── Concurrency/          global lock (mutex via WP transients)
├── Cron/                 hourly cleanup of stale tmp files
├── Progress/             heartbeat stale detection
└── Cli/                  WP-CLI command registration

tests/                   169 PHPUnit tests (unit + WP integration)
assets/js/               chunked upload, export/import UI clients
views/                   admin page templates
docs/ERROR-CODES.md      user-facing error reference

Each layer has clear boundaries: Archive/ is pure PHP with no WP dependency, SearchReplace/ is independent, the WP-specific pieces (Plugin, Rest, Import, Export) sit on top.


Error codes

Every user-facing error has a stable code so you can search for solutions. See docs/ERROR-CODES.md for the full catalog.

Examples: DISK_FULL, PHP_MEMORY_LOW, WPRESS_CORRUPTED, DB_IMPORT_SYNTAX, JOB_HEARTBEAT_LOST, GLOBAL_LOCK_HELD.


Development

Run tests

composer install
./vendor/bin/phpunit                         # unit tests (fast)
./vendor/bin/phpunit -c phpunit-wp.xml.dist  # WP integration tests (requires WP_TESTS_DIR)

Structure principles

  • Bounded contexts — Archive layer knows nothing about WordPress; Plugin layer knows nothing about chunk byte offsets
  • Stepped processing — every long operation (export, import, chunked upload) splits into small slices that finish in < 20 seconds
  • Test-driven — unit tests for pure logic (Archive reader/writer, SearchReplace, PrefixRewriter); WP-integration tests only where we need a live $wpdb

Roadmap

  • [ ] Multisite support (v1.1)
  • [ ] Scheduled backups via cron (v1.2)
  • [ ] Incremental exports (only changed files since last backup) (v1.2)
  • [ ] Optional cloud storage: S3, SFTP, B2 (v2.0)
  • [ ] Full i18n (only scaffold currently)
  • [ ] Job-token auth for /import/step so users don't have to re-login after import

Contributing

Pull requests welcome. Before submitting:

  1. ./vendor/bin/phpunit must pass
  2. For new functionality, add unit tests (or WP-integration if it requires a live database)
  3. Follow the existing coding style (declare(strict_types=1), typed properties, final classes where practical)
  4. Document user-visible changes in readme.txt changelog

Bug reports: include your PHP version, MySQL version, WordPress version, and if possible the code field from the error you saw.


License

GPL-3.0-or-later. See LICENSE for the full text.

This means you are free to:

  • Use this plugin, commercially or privately, on any site
  • Modify the source code
  • Distribute original or modified versions

Under the condition that:

  • Derivative works remain under GPL-3.0-or-later
  • The full source of derivatives is made available
  • The license notice and copyright are preserved

Acknowledgments

  • ServMask Inc. for originating the .wpress format and the All-in-One WP Migration plugin. The interoperability this plugin provides is only possible because their code is open-source.
  • WordPress contributors for the REST API infrastructure that makes chunked upload and stepped processing possible on shared hosting.
  • wp-cli/wp-cli for the command framework underlying the wp migrate-safe commands.

Releases

5 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
v0.1.4 latest
Apr 27, 2026 5mo ago
wp-migrate-safe-0.1.4.zip +1 asset
23
wp-migrate-safe-0.1.4.zip22
wp-migrate-safe-0.1.4.zip.sha2561
Apr 27, 2026 5mo ago
wp-migrate-safe-0.1.3.zip +1 asset
1
wp-migrate-safe-0.1.3.zip1
wp-migrate-safe-0.1.3.zip.sha2560
Apr 23, 2026 6mo ago
wp-migrate-safe-0.1.2.zip +1 asset
8
wp-migrate-safe-0.1.2.zip7
wp-migrate-safe-0.1.2.zip.sha2561
Apr 23, 2026 6mo ago
wp-migrate-safe-0.1.1.zip.sha256 +1 asset
0
wp-migrate-safe-0.1.1.zip.sha2560
wp-migrate-safe-0.1.1.zip0
Apr 22, 2026 6mo ago
wp-migrate-safe-0.1.0.zip
5
C- grade

Security

WP Migrate Safe 0.1.0 · audited by WP Registry

High-severity findings.

1 high
Audited release
0.1.0
Findings
1
Worst severity
high
Content hash
1092a08c89fcda728421feb3…

Findings

  • high Unauthenticated download of full-site backups stored under a guessable web path

    file_access

    ExportController::generateFilename() writes archives as {sanitize_title(get_bloginfo('name'))}-{Y-m-d-His}.wpress into Paths::backupsDir(), which is trailingslashit(WP_CONTENT_DIR).'backups/wp-migrate-safe' (constant WPMS_BACKUPS_SUBDIR). That directory is inside the web root. The only access control is Paths::ensureDir() dropping a one-line Apache 2.2 .htaccess ('Deny from all\n') and an index.php stub. There is no Apache 2.4 'Require all denied' branch, no web.config, and no nginx deny. nginx, IIS, LiteSpeed/static-file fronts, and Apache with AllowOverride that ignores Limit/AuthConfig will serve the files to anyone who requests the URL. Filenames have no secret: the site title is public and the timestamp is UTC to the second, so brute-forcing a backup window is practical (86400 candidates per day); uploaded archives keep sanitize_file_name() of the original name (e.g. backup.wpress). ensureDir() returns immediately if the directory already exists, so a pre-created folder never gets even that .htaccess. The REST download route is correctly gated on manage_options, but it is irrelevant once the file is directly fetchable. Blast radius is a complete site snapshot: DatabaseDumper dumps every table whose name starts with $wpdb->prefix (users, usermeta, options, posts, comments, WooCommerce/Stripe/SMTP keys, application passwords). DumpDatabase writes the same basename plus .dump.sql next to the archive until AppendDatabaseFile unlinks it; Cron\Cleanup purges stale upload sessions, old jobs, and _extract_* dirs but never leftover *.dump.sql or *.manifest.txt. This is the same class of issue as historically exposed ai1wm-backups / Duplicator / Backup Migration archives, just with this plugin's well-known subdirectory.

    src/Plugin/Paths.php:17-84

    Recommendation

    Store backups outside the document root, or behind a directory whose name contains a high-entropy secret. Add a random token to every archive filename. Emit Apache 2.4 'Require all denied', Apache 2.2 Order/Deny, IIS web.config, and document an nginx location deny; write those files even when the directory already exists. Disable PHP execution in the backups tree. Have cron delete leftover .dump.sql / .manifest.txt / partial .wpress files. Serve downloads only through the existing manage_options REST streamer (or X-Accel-Redirect to an internal location).

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.