WP Manifestindependent plugin directory
manifest / updates / wp-cloner

WP Cloner

WordPress clone/backup utility for cloning cross domain or back-up/restore same domain.

by Oliver Westbrook · github.com/oliverwestbrook/wp-cloner · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/oliverwestbrook/wp-cloner/archive/refs/heads/main.zip

Readme

WP Cloner

Clone a full WordPress site — files and database — into a portable .wpcl archive, then import it onto another site (with automatic, serialization-safe domain rewriting) or back onto the same site as a backup/restore.

  • Author: Oliver Westbrook
  • Version: 1.0.42
  • Requires: WordPress 5.6+, PHP 7.2+
  • License: GPLv2 or later

Export

From the top-level WP Cloner menu, Export opens a review screen:

  • Large files — every file at or above a configurable threshold (default 10 MB), each with a checkbox to exclude it. Everything below the threshold is always included.
  • Database tables — every table grouped by owner (plugin / WordPress / other) with an approximate row count. Exclude a table, or keep its structure but skip its rows.
  • Plugins and themes — active or inactive, each excludable.

Before it runs you choose whether to pause the site (read-only during the database snapshot only) or clone live, and whether to run in the browser or unattended in the background. WordPress core files are excluded by default (the destination keeps its own); an opt-in lets you include core so the destination is restored to this site's exact version (useful when the two sites differ). Export runs in resumable batches with a live progress ring and ETA.

Finished archives are saved under wp-content/uploads/wp-cloner/; the completion screen shows the server path and a download with an optional "delete after download". The WP Cloner screen also lists existing exports to download, delete, resume or cancel.

Import

Import runs from a standalone page (import.php) that boots only the minimum of WordPress needed for database access. The archive is received first — uploaded, or picked from one already on the server for server-to-server moves — then the destructive work runs on-disk in resumable batches with a progress ring.

Pick a mode:

  • Replace — the imported files and database overwrite this site. Nothing extra on this site is removed unless you also tick Full Replace, which makes the site an exact clone: it drops the site's existing tables and deletes leftover wp-content files (plugins, themes, media, and so on) so only the archive's content remains. Destructive and irreversible.
  • Keep newest — a merge: for anything on both sides the newer file (by date) wins, existing database rows are preserved and only new ones are added. Nothing is deleted.

While an import runs the whole site is locked — every request redirects to the import page — until it finishes. The lock survives crashes and self-heals, so returning to the site resumes or safely re-runs the import (the SQL uses DROP TABLE IF EXISTS, so a re-run is idempotent).

Domain rewriting

The archive stores the database with its original domain intact. On import the source host is rewritten to the destination host in a serialization-safe pass — values are unserialized, replaced, and re-serialized — so widgets, menu locations and other serialized data survive a domain change. Both www and bare hosts are handled; cloning to a subdomain of the same root domain maps both source forms onto the exact target host, while a move to a different domain preserves the www prefix.

Access Key

The import page runs outside WordPress and can overwrite the entire site, so the whole page is gated behind a per-site Access Key (shown on the WP Cloner admin screen). Visiting the page directly shows only a key prompt; clicking Import from the admin passes the key automatically. Enter the key from any browser to unlock, resume, or cancel an in-progress import. Keep a copy in case your connection drops.

The .wpcl format

A .wpcl file is a ZIP archive:

manifest.json   metadata (source domain, WP version, timestamp, exclusions)
database.sql    SQL dump of the included tables
files/…         included files, stored relative to the WordPress root

Layout

wp-cloner.php            main plugin file
import.php               standalone import endpoint (SHORTINIT bootstrap)
uninstall.php            option and working-file cleanup
includes/                exporter, importer, admin, inventory, shared constants
assets/                  admin CSS/JS

Building a distributable ZIP

./build.sh

This copies the plugin files into ../output/wp-cloner/ and writes ../output/wp-cloner.zip, ready to install via Plugins → Add New → Upload Plugin.

Notes & limitations

  • wp-config.php is never cloned — the destination keeps its own database credentials and secret keys.
  • Full Replace drops the destination's tables and deletes leftover wp-content files; it cannot be undone. Use it only when you want a true clone. The running plugin, wp-config.php, and the import's own working files are always protected.
  • Import assumes the source and destination use the same database table prefix.
  • During import the destination is locked with a redirect (not a 503) until it completes — fine for a short migration, but worth knowing.
  • The www/subdomain rules treat the last two labels as the root domain, so multi-part TLDs (for example .co.uk) may warrant a manual check.

Changelog

1.0.42 — Excluded plugins are dropped from the exported active_plugins list so the clone doesn't error on missing plugin folders (live site untouched). Core-option description moved to a tooltip.

1.0.41 — Fixed the root cause of the %→placeholder-hash corruption: it comes from the EXPORT (_real_escape() also runs add_placeholder_escape()), so the .wpcl dump itself was corrupted. Escaping now strips the placeholder in export and import. Re-export to get a clean archive.

1.0.40 — Fixed a % in rewritten rows (e.g. "50%", width:50%) being replaced by WordPress's internal placeholder hash (broke text, CSS, and serialized settings); the domain-rewrite write and the Full Replace table scan now avoid prepare()/$wpdb->update().

1.0.38 — Fixed the maintenance/lock page occasionally not lifting after import (release-first, self-heal on import-page visit, and no-cache headers so CDNs can't serve a stale maintenance page).

1.0.37 — Fixed an import locking itself out after replacing the Access Key (it now finishes); the site shows a maintenance page instead of the install screen while the database rebuilds; the uploads/wp-cloner drop-in folder is created on activation; server-side exports get an Import button that opens the importer with the file preselected.

1.0.36 — A failed export can be resumed in one click from the failure message.

1.0.35 — Optional "include WordPress core files" export choice to restore the source's exact version; core excluded by default. Full Replace also removes emptied leftover directories.

1.0.33 — Full Replace option for a true clone (drops the target's tables and deletes leftover wp-content files).

1.0.32 — Serialization-safe domain rewriting so widgets, menu locations and other serialized data migrate correctly; smarter www/subdomain mapping.

1.0.31 — Import page gated behind a per-site Access Key; pass it in one click from the admin, or enter it from any browser to resume or cancel.

1.0.30 — Exports manager on the WP Cloner screen (download, delete, resume, cancel); import from a .wpcl already on the server, with a side-by-side source picker.

1.0.29 — Finished exports kept under wp-content/uploads/wp-cloner/; completion screen shows a copyable server path and a keep-or-delete download.

1.0.28 — Clearer errors when an upload exceeds the server's size limit.

1.0.27 — Filesystem + database export to .wpcl; standalone, resumable import behind a site-wide redirect lock; Replace and Keep-newest modes.

Read the full README on GitHub →