ZHBackup – Backup, Restore & Migration
WordPress backup plugin for full-site backup, restore, and migration. Structured database export, chunked imports, scheduled backups, server-side restore, and cleanup tools.
by Zain Hassan · github.com/zain-hassan-0159/zhbackup · website
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/zain-hassan-0159/zhbackup/archive/refs/heads/main.zipReadme
=== ZHBackup – Backup, Restore & Migration === Contributors: zainhassandeveloper Tags: backup, restore, migrate, clone, wordpress-backup Requires at least: 5.8 Tested up to: 7.1 Requires PHP: 7.4 Stable tag: 2.4.3 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html
WordPress backup plugin for full-site backup, restore, and migration. Chunked database export, scheduled backups, and server-side restore.
== Description ==
ZHBackup is a WordPress backup plugin for full-site backup, restore, and migration. Use it to back up a WordPress site, restore a backup onto the same install, clone a site to staging, or migrate WordPress to a new domain — without a license key.
It packages your database, media, plugins, themes, selected wp-content folders, root files, and optional custom root folders, then restores them through a chunked process designed to avoid request timeouts.
The database is stored as a structured package instead of one large SQL replay file, so large page-builder data, post meta, options, and other oversized values can be restored in smaller, traceable steps. Live activity logs show the progress of exports, imports, database rows, large values, file restore, validation, and cleanup.
After a restore, ZHBackup lets you review the site first and run stale-file cleanup separately from the restore process. This keeps restore completion predictable while still giving you a controlled way to remove old plugins, uploads, themes, and selected root-folder files that are not present in the backup.
Key Features:
- Full-Site WordPress Backup — Back up the database, uploads, plugins, themes, mu-plugins, selected
wp-contentfolders, root files, and optional custom root folders. - Structured Database Package — Export database schemas, row chunks, and large values separately for reliable restores of Elementor/page-builder data and other large rows.
- Chunked Export and Restore — Process database and file operations in small AJAX steps to reduce timeout and memory-limit issues.
- Browser Upload or Server Restore — Upload backups through the browser in resumable chunks, or place backups on the server via FTP/SFTP and restore from a table of available files.
- Flexible Archive Formats — Create standard
.zipbackups or optimized.zhbackuparchives. - Live Activity Logs — Follow professional export/import logs with database checkpoints, row counts, large-value restore events, file progress, validation, and cleanup status.
- Post-Restore Cleanup — Save a cleanup plan after restore and run stale-file cleanup manually after reviewing the restored site.
- Scheduled Backups — Automatic daily or weekly full-site backups via WP-Cron, processed in small resumable bursts.
- Serialized-Safe Find & Replace — Update URLs safely across serialized PHP data, JSON-escaped URLs, and Elementor widget data — chunked, resumable, with a dry-run preview mode.
- Secure Backup Storage — Store backups outside the webroot when available. Inside-webroot fallbacks use an unguessable folder and filename, plus
.htaccess,web.config, andindex.phpprotections. - Retention Policies — Automatically clean up older backups by count or age using WP-Cron. Only backups the plugin created are ever deleted; uploaded archives are never touched.
- No Required Account — Use all backup, restore, migration, and cleanup features without a license key or forced signup.
WordPress.org Compliant:
- No obfuscated or encoded PHP
- Proper sanitization for all inputs
- Escaped outputs throughout
- Nonces for all form submissions
- Capability checks on every action
- No hidden external API calls
- No telemetry without explicit consent
== Installation ==
- Upload the
zhbackupfolder towp-content/plugins/. - Activate the plugin through the Plugins menu in WordPress.
- Navigate to ZHBackup in the admin sidebar to start creating backups.
Alternatively:
- Go to Plugins → Add New in your WordPress admin.
- Search for "ZHBackup" or "WordPress backup plugin".
- Click Install Now, then Activate.
== Frequently Asked Questions ==
= Is ZHBackup a WordPress backup plugin? =
Yes. ZHBackup is a free WordPress backup, restore, and migration plugin. It creates a full-site backup (database plus files), restores that backup on the same site or another install, and can clone or migrate WordPress to a new domain.
= Can I clone or migrate a WordPress site with this backup plugin? =
Yes. Create a backup on the source site, restore it on the destination, then use the built-in Find & Replace tool to update URLs. Serialized PHP data, JSON, and Elementor widget data are handled safely.
= Does this plugin work on shared or managed hosting? =
Yes. ZHBackup uses chunked export, upload, import, and restore steps so each request stays small. The database is also restored table-by-table from a structured package instead of replaying one large SQL file.
= How large of a site can this handle? =
There is no fixed size limit in the plugin. Very large sites depend on available disk space, PHP limits, and database performance, but ZHBackup is designed to process large file sets and databases in small resumable steps.
= How does ZHBackup handle large Elementor or page-builder data? =
Large database values are stored separately from normal row chunks and restored in smaller append operations. This helps preserve large _elementor_data, post meta, options, and other oversized rows during restore.
= What is the difference between .zip and .zhbackup? =
Both formats can contain the same backup data. .zip is a standard archive format. .zhbackup is ZHBackup's optimized archive format for this plugin. Both support the structured database package.
= Can I restore a backup uploaded by FTP or File Manager? =
Yes. Upload a .zip or .zhbackup file to the backup storage directory shown on the Import screen. It will appear in the server backup table, where you can restore it directly.
= Where are backups stored? =
The plugin automatically selects the most secure location. It prefers a directory outside the webroot (not accessible via browser), using a site-unique folder name so production and staging installs on the same account do not share backups. If that is not possible it falls back to a randomly named folder inside wp-content (not a guessable zhbackup-backups path). Apache/IIS deny rules are written when storage is inside the site. Existing backups from older folders remain listed, downloadable, and restorable — they are not moved or deleted.
= Can I include custom folders from the WordPress root? =
Yes. The export screen can include optional first-level folders from the WordPress root. WordPress core folders such as wp-admin, wp-includes, and wp-content are handled separately or excluded automatically.
= Can I import a backup from a different domain? =
Yes. After importing, use the built-in Find & Replace tool to update old URLs to your new domain. It handles serialized data safely, including Elementor widget data.
= Does ZHBackup support multisite? =
No. Multisite networks are not currently supported. Restoring a multisite backup into a single site (or the reverse) is detected and refused, because it would leave the installation in an inconsistent state.
= What is post-restore cleanup? =
Restore copies files from the backup first. If older files or folders still exist on the destination site, ZHBackup saves a cleanup plan. After you verify the restored site, go to ZHBackup → Cleanup to remove stale files that are not present in the backup.
= Will cleanup fail the restore if file permissions block deletion? =
No. Cleanup is manual and best-effort. Permission-related deletion failures are logged as warnings so the restore can remain complete and you can review any paths that need manual attention.
= Is the email signup required? =
No. The email signup on the Settings page is completely optional and can be dismissed permanently. The plugin is fully functional without it.
= Is the .zhbackup format free to use? =
Yes. The .zhbackup export format is included for free and does not require any license key or activation page.
= Does this plugin make external API calls? =
Yes, but only for the optional newsletter signup on the Settings page after an administrator explicitly enters an email address and submits the form. No data is sent anywhere during backup, restore, migration, import, export, or find-and-replace operations.
= Will importing overwrite my existing site? =
Yes. Importing a backup restores the backed-up database and files over the current site. Always create a fresh backup of the current site before importing.
== External services ==
ZHBackup includes an optional newsletter signup form on the plugin Settings page.
This feature connects to an external service hosted by Hassan Zain at hassanzain.com. It is used only to add the submitted email address to the ZHBackup newsletter or product updates list.
Data is sent only when an administrator manually submits the optional signup form. The plugin does not contact this service during normal backup, restore, migration, import, export, or find-and-replace usage.
When the form is submitted, the plugin sends:
- Email address entered in the signup form
- Site URL (
home_url()) - Plugin slug (
zhbackup) - Plugin version
Terms of service: https://hassanzain.com/terms-and-conditions Privacy policy: https://hassanzain.com/privacy-policy
== Privacy ==
ZHBackup does not send telemetry or usage analytics during normal backup, restore, migration, import, export, or find-and-replace operations.
The only optional external data transfer is the administrator-initiated newsletter signup described above.
== Screenshots ==
- Export screen for choosing backup components, wp-content folders, optional folders and files, and the backup archive format.
- Existing backups table with backup contents, file size, date, download, restore, and delete actions.
- Import screen with chunked browser upload plus server-side backup restore from uploaded
.zipor.zhbackupfiles. - Find & Replace screen for serialized-safe URL replacement across WordPress database tables.
- Restore Cleanup screen for reviewing and running pending stale-file cleanup after a restore.
- Settings screen showing storage security status, backup engine details, and server health checks.
== Changelog ==
= 2.4.3 =
- Security: backup files stored inside the webroot now use an unguessable folder name and an unguessable filename, so they cannot be fetched by guessing a public path. Apache
.htaccessand IISweb.configdenies are still written for those folders. - Existing backups are not moved or deleted. Older timestamp-named files and any previous
zhbackup-backupsfolder stay listed, downloadable, and restorable. New backups are written only to the current storage path.
= 2.4.2 =
- Existing production/staging installs that still shared one outside-webroot backup folder now write new backups to a site-unique folder. Older archives stay listed, downloadable, and restorable — nothing is moved or deleted — and retention only deletes from this site's own folder.
- Uploaded backups now appear in Existing Backups as well as Import. They stay in the incoming folder so retention still cannot delete a file you uploaded to restore.
- Faster large-database exports without changing the dump format: table column/index metadata is cached for the current table, and each AJAX step uses more of the host's time limit when it is safe to do so.
- WordPress 7.1 compatibility: Tested up to 7.1.
- Fixed cross-site migrations leaving old-site URLs in large page-builder data. Column values over 128 KB (typically Elementor page data) were restored byte-identical, skipping the URL search/replace entirely. They now pass through the same serialized/JSON-safe replacement as every other value.
- Fixed backups being written into a previous site's folder after a migration. The saved storage directory is an absolute server path that travelled with the restored database; it is now rewritten to this installation's own directory and re-validated on every use.
- Fixed a restore ending with "Invalid nonce" on an import that had actually succeeded. When the backup used a different database table prefix, role keys are now renamed as soon as the users table is restored and the session is renewed again at the end of the restore.
- Improved URL migration coverage during restore: besides the exact site URL, the opposite scheme (http vs https), protocol-relative references, and URL-encoded forms of the old address are now replaced too.
- Improved JSON handling in Find & Replace and restores: values that contain no match are no longer re-encoded, and replaced values keep the source's encoding style.
= 2.4.1 =
- Fixed restoring a backup over an existing site failing with "Duplicate foreign key constraint name" when any table used a foreign key. Restoring onto a fresh WordPress install worked, but restoring over a site that already had the same tables aborted partway through the database step: foreign key constraint names must be unique across the whole database, not just per table, so the staged copy of a table clashed with the live one it was about to replace. Foreign keys are now recreated after the tables are swapped into place rather than while they are being staged.
- Fixed foreign keys being left pointing at the temporary swap-aside table after a restore. MySQL follows a table rename, so moving the old table aside silently re-pointed any foreign key that referenced it, and dropping that table afterwards left the constraint referencing a table that no longer existed. Constraints are now rebuilt against the final restored tables.
= 2.4.0 =
- Fixed a critical archive-corruption risk in the
.zhbackupformat: if a file changed size while being read on a l