Site Cloner
Creates a package (files + database + installer) to migrate WordPress from production to staging. Runs anywhere, no shell required.
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/chidang/site-cloner/archive/refs/heads/master.zipReadme
Site Cloner
A plugin for migrating WordPress from production → staging. It creates a package consisting of one or more archive-*.zip files (files, split automatically) + database.sql + installer.php. It works whether staging is on the same server or a different one, and requires no shell access.
Installation
- Copy the
site-cloner/folder intowp-content/plugins/on the production site. - Go to Plugins → Activate "Site Cloner".
Creating a package (on production)
- Go to Tools → Site Cloner.
- Click Create Package. The plugin will:
- Export the database in chunks (using
mysqldumpif available, otherwise pure PHP). - Compress all files in chunks (to avoid timeouts).
- Export the database in chunks (using
- Download all files in the package:
installer.php, thearchive-*.zipfiles,database.sql, andmanifest.json.
Installing on staging
There are 3 methods; pick one:
Method A — Pull via link (the simplest) ⭐⭐
No manual download/upload needed. Install the plugin on both production and staging:
- On production: after building the package, copy the link shown under "Fastest method".
- On staging: go to Tools → Site Cloner Import, paste the link into the "Pull from production via link" field, check the confirmation box, and click Pull & Migrate.
- Staging automatically downloads the files from production (it supports byte-range downloads, so even very large files work fine), then runs extraction + DB import + search-replace on its own.
The link contains an SHA-256 hashed token: production only stores the hash, while the real token lives in the link. Serving the files goes through PHP with token validation. You should still delete the package once you're done. Note: staging will make an HTTP call to the URL in the link on its own — only paste links you trust.
Method B — Via wp-admin (package already present on staging)
- Install and activate the "Site Cloner" plugin on staging.
- Copy the entire package folder (
wp-content/sd-packages/<id>/, which contains thearchive-*.zipfiles,database.sql,manifest.json, andinstaller.php) from production to staging, into the matchingwp-content/sd-packages/location (via FTP/File Manager). If it's the same server, you can copy directly. - Go to Tools → Site Cloner Import, select the package, check the confirmation box, and click Run Migrate.
- Extracting files: runs in chunks (with progress).
- DB import + search-replace: runs in a single request (don't reload the page in the middle).
- Done. You may get logged out (because the users table now belongs to production) → log back in with a production account.
Why the DB import is bundled into one request: when overwriting the
users/optionstables, splitting it up would cause the next request to lose the login session and hit a 403 partway through. Bundling into one request (with authentication checked up front) avoids this problem entirely.
Very large sites (multi-GB databases) — automatic via runner
When staging allows it, the plugin sets up a standalone runner (runner.php) in the package folder, and the Import page uses it automatically. The runner:
- Does not boot WordPress → it doesn't depend on the login session or
active_plugins, and won't fatal-error while the DB is mid-import. - Imports by byte-offset (~3MB/chunk) and runs search-replace via keyset pagination (500 rows/chunk) → each request stays short, so any DB size works without hitting
max_execution_time. - Authenticates with an SHA-256 hashed token stored on the server (
sd-token.hash); the real token only lives in the admin's browser. - When finished: it deletes
database.sql,sd-token.hash, andsd-state.json, and deletesrunner.phpitself.
If staging can't write runner.php, the plugin automatically falls back to the single-request approach (suitable for small/medium sites).
Prefix: the importer keeps production's prefix and automatically updates
$table_prefixin staging'swp-config.php(if different). Ifwp-config.phpisn't writable, the plugin will notify you to fix it manually.
Method C — Standalone installer (when staging is empty / has no WordPress)
- Create an empty database on staging.
- Upload
installer.php, allarchive-*.zipfiles,database.sql, andmanifest.jsonto the root directory of staging. - Open
https://your-staging.com/installer.php, enter the DB details, and click Start Migrate. - Delete immediately the installer/archive/sql/manifest files once you're done.
Multi-part archives (very large sites)
During the build, files are compressed into multiple parts — archive-1.zip, archive-2.zip, etc. — with each part closed once it exceeds ~200MB. The reason: ZipArchive::close() flushes all data at once, so packaging a multi-tens-of-GB site into a single file would exhaust RAM or time out. Splitting into parts keeps each zip close operation light and stable. All three import paths (standalone installer, wp-admin import, DB runner) automatically extract all the parts.
Why it's "accurate"
The most common source of errors when changing URLs is serialized data (widgets, settings, options). Replacing raw strings with str_replace corrupts the length prefixes inside serialized strings → broken data. The plugin uses a recursive unserialize → replace → re-serialize algorithm, so it preserves the structure intact.
Notes / limitations
- Staging gets completely overwritten (files + DB). Only use it with a staging site you can throw away.
- The new
wp-config.phpis regenerated with random salts; any custom defines from the original config (cache, memory limit, etc.) must be re-added manually. - The
wp-content/sd-packages/folder holds sensitive data (the DB dump). You should delete the package after use. - For very large sites (>a few GB), consider raising
memory_limit/max_execution_timeon staging.site-cloner/ ├── site-cloner.php # bootstrap, menu, AJAX (build + import) ├── includes/ │ ├── class-sd-database.php # export DB in chunks (+ mysqldump fast-path) │ ├── class-sd-archive.php # compress files in chunks │ ├── class-sd-package.php # orchestrates build + manifest │ ├── class-sd-replace.php # serialize-safe search-replace (importer) │ ├── class-sd-importer.php # IMPORT on the staging side (extract + import DB) │ └── class-sd-pull.php # pull-by-link: production serve + staging fetch ├── templates/ │ ├── admin-page.php # package creation UI │ ├── import-page.php # import UI (staging) │ ├── installer.tpl # standalone installer (for empty staging) │ └── runner.tpl # chunked DB runner for very large sites (no WP boot) └── assets/ admin.js, admin.css