Plugin documentation

Backup & Restore by WpExperts Hub

Complete, checked backups of your database and files, copies on Google Drive, Dropbox or SFTP, optional encryption, and a restore and migration that you can rehearse first and undo afterwards.

1. Overview

Backup & Restore by WpExperts Hub makes complete, checked backups of your WordPress site, keeps copies off your server, and brings the site back (or moves it to a new address) without leaving it half-changed if something goes wrong.

It copies your database and your files into plain, standard archives, reads every archive back to prove it is intact, uploads them to Google Drive, Dropbox or any SFTP server, and restores them through a pre-flight check, an optional dry run and an undo. Everything runs in PHP, in small steps that continue where they stopped, so it works on ordinary shared hosting and survives time limits, crashes and closed browser tabs. No shell access, no backup service and no subscription are needed.

The Site Backup dashboard: backup health 100 out of 100, last backup, storage, next scheduled backup, and Back Up Now
The dashboard: health score, last backup, storage, the next scheduled backup and one-click Back Up Now.

How it works

  1. Back up. A backup is a folder holding a manifest, the database dump and ZIP archives of your plugins, themes, uploads and other files. It is made in small steps (each about 20 seconds), so a PHP time limit or a crash only loses the last step.
  2. Check. A backup is called complete only after every archive was read back, its checksum computed, the database dump's last block found and the manifest written.
  3. Copy off the server. Optionally every backup is uploaded in resumable pieces to Google Drive, Dropbox or SFTP and checked again on the far side. The manifest is uploaded last, so an unfinished upload is never offered for restore.
  4. Restore safely. A restore verifies the checksums, loads the database into temporary tables, extracts files with a rollback journal, then swaps everything in with one atomic step. The replaced tables and files are kept, so Undo can put the previous site back.

At a glance

Backups

Full site, database only, files only or custom. Manual, scheduled (every 6 or 12 hours, daily, weekly, monthly or every N hours) and before-update.

Off-site copies

Google Drive (drive.file permission only), Dropbox (app folder) and SFTP with a confirmed host key. Resumable uploads, verified on arrival.

Encryption

Argon2id passphrase, XChaCha20-Poly1305 per file. The site stores only a public key; the passphrase is never stored.

Restore

Pre-flight checks, dry run, only some tables or files, undo, and a downloadable report.

Migration

Move or clone a site: addresses, folder paths and the table prefix are rewritten safely, including inside serialized data. Portable .tar packages.

Confidence

Health score, automatic re-checks, comparison of two backups, audit trail, job logs, email alerts and WP-CLI.

What it does not do

  • No incremental backups. Every backup is complete and self-contained, so a restore never depends on a chain of earlier backups.
  • No OneDrive, WebDAV or S3 storage in this version. Developers can add more storages through a provider interface (see Developer reference).
  • No multisite. The plugin refuses to load on a multisite network.
  • Updates are never blocked or automatic. Before a plugin, theme or core update it reminds you and lets you back up first or skip. Nothing is forced.
  • It is not a staging-site manager or a malware scanner. It protects the data you already have.
Honest statusGoogle Drive and Dropbox follow the services' public OAuth and upload protocols and were tested against simulated servers; SFTP was tested against a real SSH server. Test your own storage once with the Test button, and restore a backup on a staging copy before you rely on it.

2. Requirements

RequirementNeeded
WordPress6.5 or later (tested up to 7.1). Single site only.
PHP8.1 or later, with the sodium, zlib and mysqli extensions (the Settings system check tests them).
DatabaseMySQL or MariaDB. The database user must be able to create, rename and drop tables (the restore pre-flight tests this).
Disk spaceAbout the size of your site for each backup you keep, plus the same again for a restore (the safety copy). The Dashboard warns when free space is low.
WooCommerceOptional. Its orders (including HPOS tables), products and settings are part of the database and are backed up like everything else.
Scheduled backupsWP-Cron, or a real server cron job on quiet sites (see Schedules).
EmailWorking email delivery if you want notifications.
HostingNothing needs a shell, exec or mysqldump. A very small host may need a smaller archive size (Settings → Compression and archive size); jobs resume by themselves.

3. Installation

  1. Download the plugin zip from your account on wpexpertshub.com.
  2. In WordPress open Plugins → Add New → Upload Plugin, choose the zip and press Install Now.
  3. Activate the plugin. It creates two small tables, prepares a private backup folder and adds a Site Backup menu.
  4. Open Site Backup → Dashboard and press Back Up Now for a first backup.
  5. Activate your licence under Plugins → WpExperts Hub Licences to receive updates.

Licence and updates

Open Plugins → WpExperts Hub Licences, enter your licence key and the email address used for the purchase, and press Activate. The licence is only for updates: every backup, restore and migration feature works without it.

The licence and update check contacts wpexpertshub.com with the licence key, your site address and the plugin version. It never sends backups, database content or file names.

What activation creates

WhatDetails
Two tables{prefix}wphub_backup_backups (one row per backup) and {prefix}wphub_backup_audit (the audit trail). The tables are never part of a backup.
OptionsSettings in wphub_backup_settings, plus wphub_backup_remote (encrypted storage credentials), wphub_backup_encryption (the public key), schedule state and the update log. Options named wphub_backup_* are never exported.
A backup folderOutside the web root when the server allows it, with a random name. It contains backups/, work/, restore/, tmp/, exports/ and import/, plus index.php, .htaccess and web.config that deny web access. The Dashboard tests whether the web server really blocks it.
Scheduled eventswphub_backup_maintenance (hourly housekeeping: cleanup, automatic checks, warnings), wphub_backup_schedule_tick (one event aimed at the next due schedule) and wphub_backup_watchdog (keeps a running job going when the page is closed).

Updating, deactivating and deleting

  • Updating keeps your settings and every backup. The database is upgraded automatically when needed.
  • Deactivating never deletes anything.
  • Deleting the plugin follows Settings → When the plugin is deleted: keep everything (default), remove the settings and tables but keep the backup files, or remove everything including the backup folder. Only a folder the plugin itself created is ever removed.

Multisite

Not supported. On a multisite network the plugin does not load and shows a notice.

4. Quick start

Five minutes to a protected site:

  1. Make a first backup. Site Backup → Dashboard → Back Up Now with Full site. Watch the progress panel; you may close the page and it carries on where WP-Cron works.
  2. Make it automatic. Schedules → Add a schedule: for example Daily at 02:00, Full site, and a second one for the database every 6 hours. Press Save schedules.
  3. Keep a copy off the server. Remote Storage: connect Google Drive, Dropbox or an SFTP server, press Test, then tick where backups should be sent.
  4. Protect the contents. Encryption: choose a passphrase, store it in a password manager, and turn encryption on.
  5. Rehearse a restore. Restore: choose a backup, Check this restore, then Dry run. Nothing changes, and you learn that a restore will work.
  6. Watch the health score. The Dashboard scores your protection from 0 to 100 and tells you what to improve.

5. Features

Making a backup

On the Dashboard choose what to back up:

TypeContainsUse it for
Full siteThe database and all filesBefore big changes; the regular safety net
Database onlyPosts, orders, settings and everything in the databaseFrequent, small snapshots
Files onlyPlugins, themes, uploads and other filesAfter file changes
CustomThe parts you tickExactly what you need

The parts are Database, Plugins, Themes, Uploads (media), Must-use plugins, Other wp-content files, Site-root files (such as .htaccess) and, optionally, WordPress core files (off by default, because core can always be downloaded again and restoring core from an old backup can downgrade a site). wp-config.php is never part of a backup. Give a backup a label such as "Before the WooCommerce update".

Estimate before you back up. Settings → Exclusions → Estimate size and preview exclusions measures what a backup would contain and how big it would be, and shows what the exclusions skip. Exclusions (caches, upgrade folders, log files, OS junk, version-control folders and other backup plugins' archives, plus temporary transients) are on by default and explained one by one in Settings; anything that could not be included is listed in the backup's details.

Keep the page open or notThe browser drives a backup while the page is open. If you close it, WP-Cron continues the job in the background. A site with WP-Cron switched off is flagged on the Dashboard, with the one-line fix.

Checking a backup

  • Verify re-reads every file and checks its size, SHA-256 and the structure of each archive.
  • Deep verify also decompresses every entry and checks its CRC. An encrypted backup needs its passphrase for the deep check.
  • Automatic checks (Settings) re-verify the newest backup weekly or monthly in the background.
  • A verified backup shows a Verified badge, and the health score notices when the newest backup was not checked.

Schedules

Add up to five schedules. Each one has a name, a frequency (every 6 hours, every 12 hours, daily, weekly, monthly or every N hours), a time, a type and its own destinations. The time zone defaults to the site's, and daylight-saving changes are handled.

  • Backups never overlap: if one is still running when the next is due, the new one waits five minutes and tries again.
  • One cron event is aimed at the earliest next run, so scheduling adds almost no load.
  • A schedule that is badly late produces a Dashboard warning and an email.
  • Run now starts a schedule immediately.
The Schedules screen with three automatic backups
Schedules: each with its own frequency, type and destinations.
Quiet sitesWordPress runs scheduled tasks only when somebody visits. For on-time backups add a real cron job on the server, for example */5 * * * * cd /path/to/site && wp cron event run --due-now, or */5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron. The Schedules screen shows a ready-made line for your site.

Keeping and cleaning up backups

Retention is a plan, not a guess: Preview cleanup shows exactly what would be deleted, and the cleanup that runs after every successful backup (and hourly) uses the same plan.

  • Keep the newest N of each kind (full 10, database 10, before-update 5 by default) and/or delete older than N days, with an optional size cap in GB.
  • The newest verified backups are always preserved (3 by default), protected backups are never touched, and the only valid recovery point is never deleted, however old.
  • Failed or cancelled backups never count as recovery points.
  • Protect any backup you want to keep forever.

Before you update

When you start a plugin, theme or core update on the Plugins, Themes or Dashboard → Updates screen, and your last verified backup is older than the number of hours set in Settings (6 by default), a dialog appears:

  • Back up now, then update: a Quick backup (the database and just what is being updated) or a Full one, then the update continues by itself.
  • Skip the backup and update: nothing is blocked and nothing is automatic.
  • Cancel: do nothing.
The dialog that offers a backup before a plugin update
The reminder before an update: back up first, or skip.

Each update is logged: what changed, from which version to which, and which backup was made first. You can switch the reminder off per area (plugins, themes, core) in Settings.

Copies off your server

Open Remote Storage. Your site talks to the storage directly: backups never pass through our servers. Credentials are stored encrypted and are never shown again after saving.

Remote Storage with Google Drive, Dropbox and a connected SFTP server
Remote Storage: Google Drive, Dropbox and SFTP.
Google DriveDropboxSFTP
NeedsA Google account and a free Google Cloud project (OAuth client)A Dropbox account and a free Dropbox appA server with SSH/SFTP and a user that can write to a folder
PermissionOnly drive.file: files the plugin created. Never your password.App folder (recommended) or full access. Never your password.SSH key (recommended) or password; the server key is confirmed once and a changed key is refused.
UploadsResumable, 8 MB piecesUpload sessions, 8 MB pieces2 MB pieces into a .part file renamed when complete
CheckSize and MD5Size and content hashSize and a full SHA-256 read-back (default)

Where backups are sent. Tick the storages that receive every backup; a schedule can override this. Keep a copy on this server as well is on by default; if you untick it, the local copy is deleted only after every chosen storage holds a verified copy. Keep on remote storage sets how many copies (10 by default) and for how long, judged per storage and independently of the local rules. The last valid copy anywhere is never deleted.

Fetch. Backups here lists what a storage holds, and Fetch to this server downloads a backup, checks every file against its manifest and adds it to your list, useful on a fresh install.

Encryption

Encrypted backups are unreadable to anyone without your passphrase: your hosting company, a storage provider, or a thief who copies the files.

  • Your passphrase becomes an X25519 key pair through Argon2id. The site stores only the public half, so scheduled and update backups are encrypted with nobody present and cannot be decrypted by the site.
  • Each backup gets its own random data key, sealed to the public key and stored in the manifest. Each file is encrypted in 1 MiB chunks with XChaCha20-Poly1305; damage, truncation, reordering and swapped chunks are all detected, and encryption can resume after a crash.
  • To restore, deep-verify or open an encrypted backup you type the passphrase. It is used for that one job and never stored: not in the database, a file or a log.
  • A manifest code (HMAC) makes any change to the manifest detectable.
The Encryption screen showing that new backups are encrypted
Encryption: on, with a check that you still know the passphrase.
If you lose the passphraseYour encrypted backups cannot be opened by anyone, including us. There is no reset and no back door. Keep the passphrase in a password manager and on paper in a safe place. The documentation includes a short script that opens an encrypted backup without WordPress (see Getting the site back by hand).

Restoring

Restore → choose a backup → tick the parts → Check this restore. Before anything changes the check proves:

  • the manifest and every checksum are intact and the format is supported;
  • the database user can create, rename and drop tables;
  • there is enough free disk space (including the safety copy and, for an encrypted backup, the decrypted working copy);
  • folders are writable, and how many posts, users and comments the site has now compared with the backup.
Restore pre-flight checks
The restore pre-flight: everything is proven before anything changes.

Dry run does everything except the final switch and gives you a report; it changes nothing. When you are ready, type RESTORE and press Restore now.

How a restore stays safe

  1. Checksums are verified again at the start.
  2. The database is loaded into temporary tables; the live tables are not touched.
  3. Files are extracted with a rollback journal; every file that gets replaced is first copied into a safety folder.
  4. All tables are swapped in with one atomic RENAME TABLE.

If anything fails before step 4, the temporary tables are dropped and the files are put back. After step 4 the replaced tables and files are kept until you delete them, so Undo can reverse the restore. Never extracted: wp-config.php and this plugin's own folder. Unsafe archive entries (path traversal, symbolic links, oversized or bomb-like entries) are refused and counted. Visitors see a short maintenance page (HTTP 503) only while files and tables are being swapped; the admin, login, AJAX, cron and REST stay available so the restore can finish.

After a restoreYou may be signed out, because the restored database brings its own users. Sign in with an account from the backup.

Restoring only part of a backup

  • Only some database tables. Tables are grouped the way people think of them (posts and custom fields, comments, terms, users, settings, and one group per plugin's tables). Restoring half of a group leaves data inconsistent, and the check says so. Tables you leave out keep their current content.
  • Only some files or folders. For each part, one path per line relative to the part (woocommerce, 2026/10), or tick them with Browse the backup.
  • Both work together with Adapt, Dry run and Undo. A restore never deletes files that are not in the backup.
A finished dry run
A dry run: everything except the final switch, then a report.

Moving or cloning a site

  1. Old site. Make a Full site backup, then Migration → Create package and download the .tar file. (An encrypted backup stays encrypted.)
  2. New site. Install WordPress and this plugin. Migration: upload the package (sent in pieces, so PHP's upload limit does not matter) or copy it by FTP into the import folder shown, then Import. The package is checked, the backup appears in your list and is verified automatically.
  3. Restore. Choose the imported backup, tick Adapt the backup to this site (offered whenever the address, table prefix or server folder differs), run the Dry run, then restore.
  4. Sign in with an account from the old site. wp-config.php is never copied, so the new site keeps its own database login and security keys.
The Migration screen
Migration: package a backup, import one, adapt it to the new site.

What "Adapt the backup to this site" changes

Inside the restored data only, never in the live site until the final switch:

  • Addresses. The site and home address become this site's, everywhere: plain text, inside serialized PHP arrays and objects (with string lengths recalculated), in JSON, URL-encoded, protocol-relative, and the other scheme (http ↔ https). siteurl and home are then set outright.
  • Server folder path wherever it was stored.
  • Table prefix. Tables are renamed to this site's prefix; the roles option and user capability keys follow.
  • Extra replacements you type ("old text => new text", at least 4 characters), for a CDN address and so on.

Serialized values are rewritten by a parser that never calls unserialize(), so no object is ever created from backup data. A value that cannot be parsed exactly is left untouched and reported; a host that merely starts like yours (example.com.au when replacing example.com) is not touched. Each batch of rows and its resume point are committed in one transaction, so a crash can neither lose nor repeat work. Not changed on purpose: the post guid column, array keys inside serialized data, binary columns, tables without a primary key (all listed in the report) and wp-config.php.

Portable packages

A package is a plain tar file: manifest.json first, then exactly the files the manifest lists, each with the size and SHA-256 the manifest states. Standard tools read it (tar -tf, tar -xf). On import a package is treated as untrusted: only the files the validated manifest lists are unpacked, with exactly the declared size and checksum. An extra file, a duplicate, a path, a link, a wrong size or a changed byte stops the import and nothing is kept. An id that already exists is refused.

Comparing two backups

At the bottom of Backups, pick two backups and press Compare to see what changed: settings, sizes, which database tables were added, removed or changed in rows, and, for one part at a time, which files were added, removed or changed.

Comparison of two backups
Compare two backups: sizes, parts and database rows.

Backup health score

The Dashboard scores 0–100 from seven checks and says why: a recent backup, a recent check of the newest backup, ready to restore (a restore of the newest backup would pass its checks today), a copy off this server, automatic backups on time, no failures this week and free disk space. Open What this is based on for the breakdown. wp wphub-backup status exits with an error when the health is poor, so a monitoring job can alert you.

Email notifications

Failures (on by default), late schedules, restores, low disk space, failed cleanup and failed remote uploads, and optionally successes. Notifications go to the addresses you enter in Settings, or the site admin email. Emails never contain download links.

Downloads and sharing

Every file of a backup can be downloaded from its Details dialog through a signed link that is valid for five minutes and works only for a logged-in administrator. The files hold your whole site, including its database, so keep them private.

Logs and audit trail

Logs has an Audit trail (who did what and when: backups, restores, deletions, downloads, settings changes, scheduled runs and notifications) and Job logs (the step-by-step log of each backup, restore, verification and estimate, with a search box). Passwords, keys and tokens are removed from every log line.

Diagnostics for support

Settings → System check tests PHP version and extensions, memory and time limits, the backup folder, free disk space, whether the web server really blocks the backup folder, and WP-Cron. Download a diagnostic report gives support everything useful with no passwords, keys, tokens or email addresses in it.

6. Admin screens

Every screen is under the Site Backup menu in wp-admin.

ScreenWhat it is for
DashboardHealth score, last backup, storage, next scheduled backup and Back Up Now
BackupsThe history: details, verify, protect, download, delete and compare
RestorePre-flight, dry run, selective restore, migration options and undo
SchedulesAutomatic backups
Remote StorageGoogle Drive, Dropbox and SFTP
MigrationPackage a backup for another site, import a package
EncryptionProtect backups with a passphrase
SettingsBackup folder, exclusions, retention, checks, notifications, update reminders
LogsThe audit trail and the log of every job

Dashboard

Seven cards: Backup health (click What this is based on to open the breakdown across the whole row), Last completed backup, Backups (count and size on this server), Storage (free space, whether the backup folder is private, connected remote storages), Next scheduled backup, Retention and Last result. Below them, Back up now offers the four types, an optional label, an Encrypt this backup box (when encryption is on) and shortcuts to Restore, All Backups, Remote Storage and Settings. Recent backups lists the latest backups with Details and Restore. Warnings (no completed backup, an old backup, low disk space, a late schedule, a folder reachable from the web, WP-Cron switched off) appear at the top.

Backups

The Backups screen
Backups: filter by status, type, trigger, location and date.
  • Filters by label or id, status, type, trigger (manual, scheduled, before update, migration), location (this server or a storage) and dates.
  • Badges show where each copy is (Local: complete, SFTP: verified), whether it is verified, and whether it is encrypted.
  • Details shows sizes, parts, what was skipped, the copies on remote storage and the job log, with Verify, Deep verify, Protect, Download, Delete and Restore.
  • Delete asks for confirmation; deleting your only completed backup needs an extra confirmation. You can delete just the local copy and keep the remote ones.
  • Compare two backups sits at the bottom.

Restore

Choose a backup, tick the parts, optionally open Only some database tables or Only some files or folders, press Check this restore, then Dry run or Restore now. Previous restores lists every restore and dry run with its report and an Undo for the live ones. If a restore is interrupted, reload the page: it continues from where it stopped, or you can cancel and every file is put back.

Schedules

One card per schedule with Enabled, name, how often, time, weekday or day of the month, what to back up and where to send it, the next run and Run now. Below are the time zone and a ready-made server cron line for quiet sites.

Remote Storage

A card per storage with its status (Connected / Not connected), the one-time setup steps for Google Drive and Dropbox, the SFTP form, and the buttons Save, Connect, Test, Backups here and Disconnect. Below the cards are Where backups are sent, the local-copy and remote-retention options, the SFTP read-back option and a comparison table of the three storages.

Migration

Three numbered steps (old site, new site, restore), then Package a backup for download and Import a package, with the folder to use for very large packages and the free disk space an import needs.

Encryption

The status (on or off, the key id and when it was created, how many backups are encrypted), the button to turn encryption off or on again for new backups, Check that you still know the passphrase, and the form to choose a new passphrase.

Settings

The Settings screen
Settings: where backups are kept, what they contain and what they leave out.

All options are described in Settings below.

Logs

The audit trail
Logs: the audit trail and the log of every job.

Two tabs: Audit trail (searchable and filterable by event and date) and Job logs (pick a job and press Read log).

7. Settings

Open Site Backup → Settings. Defaults are chosen so that a site works without changes.

Where backups are kept

The plugin picks a folder outside the web root when the server allows it, with a random name, and shows it with a test of whether the web server really blocks it. To use your own folder enter a full path that PHP can write to (leave empty for the automatic folder). Existing backups stay where they are. The filter wphub_backup_store_dir can move it from code.

What a full backup contains

  • Default parts for custom backups: Database, Plugins, Themes, Uploads, Must-use plugins, Other wp-content files and Site-root files (WordPress core files are unticked).
  • WordPress core files: include core in Full and Files-only backups (off by default).
  • Temporary data: leave WordPress transients out of the database backup (on by default; they rebuild themselves).

Exclusions

Each category states what it skips and what that costs. On by default: Cache folders, WordPress upgrade leftovers, Log files (*.log, error_log, WooCommerce logs), Operating-system junk (.DS_Store, Thumbs.db), Version-control folders (.git, .svn, .hg) and Archives of other backup plugins. You can also add paths, name patterns, database tables to skip and extra tables to include, and press Estimate size and preview exclusions to see the effect. The filter wphub_backup_exclusions adds exclusions from code.

Compression and archive size

Compression level 1–9 (6 by default) and archive part size (256 MB by default, at least 16 MB). A smaller part size helps small hosts; a part also rolls over after 60,000 entries.

Keeping and cleaning up backups

SettingDefault
Cleanup after each backup and hourlyOn
Keep the newest full backups / databases / before-update backups10 / 10 / 5
Delete backups older than (before-update backups)Never (7 days)
Always keep the newest verified backups3
Size capNone

Preview cleanup shows exactly what would go before you run it.

Checking backups automatically

Re-verify the newest backup weekly or monthly in the background (off by default). A failed check sends an email.

Email notifications

Addresses (the site admin email when empty) and which events to send: failures (on), late schedules (on), restores (on), low disk space (on), cleanup problems (on), remote storage problems (on) and successes (off). The filters wphub_backup_notification_recipients and wphub_backup_notification change recipients and messages from code.

Reminder before updates

Switch the reminder on or off for plugins, themes and core, set how recent the last verified backup must be (6 hours by default) and choose the default backup: Quick (the database and what is being updated) or Full.

Developer

Developer mode writes detailed lines to the job logs. Passwords and keys are removed from log lines either way.

When the plugin is deleted

Keep everything (default); remove the settings and tables but keep the backup files; or remove everything including every backup file. Deactivating never deletes anything.

System check

PHP version and extensions, memory and time limits, the backup folder, free disk space, whether the web server blocks the backup folder, and WP-Cron, plus Download a diagnostic report for support.

8. Developer reference

WP-CLI

Every command runs the same engine, the same checks and the same audit trail as the admin screens. A job is driven step by step until it ends, with progress lines; a failure prints the reason and exits non-zero. Use wp help wphub-backup <command> for the options of any command.

wp wphub-backup backup    [--type=full|database|files|custom] [--components=<list>] [--label=<text>] [--encrypt|--no-encrypt] [--no-upload]
wp wphub-backup list      [--status=<status>] [--format=table|json|csv|ids]
wp wphub-backup info      <id>
wp wphub-backup verify    <id> [--deep] [--passphrase-file=<file>]
wp wphub-backup restore   <id> [--components=<list>] [--tables=<list>] [--paths=part:path,...] [--migrate] [--extra="old=>new;..."] [--dry-run] [--yes] [--passphrase-file=<file>]
wp wphub-backup undo      <run> [--yes]
wp wphub-backup discard   <run>
wp wphub-backup delete    <id>... [--local-only] [--yes]
wp wphub-backup cleanup   [--dry-run]
wp wphub-backup export    <id> [--output=<file>]
wp wphub-backup import    <file.tar>
wp wphub-backup upload    <id> --to=google|dropbox|sftp
wp wphub-backup fetch     <id> --from=google|dropbox|sftp
wp wphub-backup remote    [<provider>]
wp wphub-backup schedules [<id>]
wp wphub-backup status    [--format=table|json]
wp wphub-backup compare   <older> <newer> [--files=<part>]
wp wphub-backup encryption [status|check] [--passphrase-file=<file>]
wp wphub-backup diagnostics
wp wphub-backup maintenance

Passphrases

A passphrase is never accepted as a command-line argument (it would appear in the process list and the shell history). Provide it with the environment variable WPHUB_BACKUP_PASSPHRASE, or --passphrase-file=<file> (keep that file private), or type it when asked (hidden input, only with a terminal).

Exit codes and scripting

  • 0 success, 1 any failure (a failed verify, a refused restore, an unreadable file).
  • status exits 1 when the health is "poor", so a monitoring job can alert: wp wphub-backup status >/dev/null || mail -s "Backup problem" you@example.com < /dev/null.
  • restore without --yes asks for confirmation; anything but "y" changes nothing. --dry-run never asks.
  • backup prints the new id as its last line: ID=$(wp wphub-backup backup --type=database --no-encrypt | tail -1).
# Nightly database backup from a real cron job, then apply the retention rules
0 2 * * * cd /var/www/site && wp wphub-backup backup --type=database --label=nightly && wp wphub-backup cleanup

# Check the newest backup weekly
0 4 * * 0 cd /var/www/site && wp wphub-backup verify $(wp wphub-backup list --status=complete --format=ids | cut -d' ' -f1)

# Rehearse moving a site, then do it
wp wphub-backup import /tmp/site.tar
wp wphub-backup restore 20261009-120000-abc123 --migrate --dry-run
wp wphub-backup restore 20261009-120000-abc123 --migrate --yes

WP-CLI has no PHP time limit, but each step is still short (about 20 seconds), so every job stays resumable. Only one job runs at a time; a command started while another job runs reports it.

Actions

HookArgumentsWhen
wphub_backup_completed$backup_id, $manifestA backup finished and passed its checks
wphub_backup_failed$backup_id, $messageA backup failed (the message has secrets removed)
wphub_backup_verified$backup_id, $ok, $problemsA verification finished
wphub_backup_uploaded$backup_id, $providerA copy was uploaded and checked on remote storage
wphub_backup_restore_finished$resultA restore finished and its data is live (still undoable)
wphub_backup_deleted$backup_id, $contextA backup was deleted everywhere
wphub_backup_schedule_row$schedule, $field_baseInside one schedule card: add your own fields

Filters

HookValuePurpose
wphub_backup_capability'manage_options'Who may use the plugin
wphub_backup_remote_providersarray of id => classRegister another storage provider
wphub_backup_exclusionsarrayAdd or change what backups leave out
wphub_backup_store_dirpathMove the backup folder
wphub_backup_step_budgetseconds (float)How long one step may run
wphub_backup_chunk_bytesintRead size per chunk
wphub_backup_kdf_paramsarray( 'ops' => 3, 'mem' => 67108864 )Argon2id cost for new encryption keys (stored with the key; old backups are unaffected)
wphub_backup_crypto_chunkint (bytes)Plain bytes per encrypted chunk (written into each file's header)
wphub_backup_notification_recipientsarray of emailsWho gets notifications
wphub_backup_notificationarray (subject, body), $event, $ctxChange or cancel an email
wphub_backup_remote_rootfolder nameName of the top-level remote folder
wphub_backup_free_spacebytes or nullOverride the free-space reading

Adding a storage provider

Extend WPHub_Backup_Provider and register it:

add_filter( 'wphub_backup_remote_providers', function ( $providers ) {
    $providers['mystorage'] = 'My_Storage_Provider';   // id: 2-20 characters a-z 0-9 _
    return $providers;
} );

The base class is the whole contract: id(), label(), is_connected(), describe(), test(), ensure_folder(), upload_chunk(), verify_file(), list_backups(), list_files(), download_chunk() and delete_backup(). Throw WPHub_Backup_Remote_Exception for errors: transient ones are retried with back-off, authentication ones disconnect the storage and notify. Save secrets with WPHub_Backup_Remote::save( $id, $conf, $secrets ), which encrypts them. The engine does the rest: queueing, resumable steps, the manifest-last rule, retention, local-copy policy, notifications and the status of each copy.

Backup format (version 1)

One folder per backup, backups/<id>/:

FileContent
manifest.jsonFormat version, plugin version, backup id, label, type, trigger, creation time, duration, the site (address, WordPress and PHP versions, table prefix, charset), the parts, the database dump record (checksum, rows, tables) and each archive's name, size, entry count and SHA-256, totals, skipped items and exclusions. A reader must refuse a format newer than it knows.
database.sql.gzIndependent gzip members (each a whole number of statements) so a restore can resume. Only CREATE TABLE and INSERT INTO statements, each followed by the marker line --wphub:eos.
<part>-NNN.zipStandard ZIP files (stored or deflate, UTF-8 names), rolling over at the configured part size or 60,000 entries. Entry names are relative to the part's folder.
job.logThe job's log.
*.encThe same files, encrypted; the manifest then also carries an encryption block.

Capability, tables and scheduled events

  • Capability: manage_options (filter wphub_backup_capability). Every admin action is also protected by a nonce.
  • Tables: {prefix}wphub_backup_backups, {prefix}wphub_backup_audit.
  • Cron events: wphub_backup_maintenance (hourly), wphub_backup_schedule_tick, wphub_backup_watchdog.
  • Naming: everything is prefixed wphub_backup, WPHub_Backup_, WPHUB_BACKUP_ or wphub-backups.

9. Privacy and security

A backup holds your whole site, including its database: user accounts, orders, private content and API keys. The plugin treats it that way.

Where data goes

  • Backups go only to the storage you connect (Google Drive, Dropbox, your SFTP server), directly from your site. Nothing passes through WpExperts Hub.
  • The licence and update check contacts wpexpertshub.com with the licence key, your site address and the plugin version. It never sends backups, database content or file names, and every backup feature works without a licence.
  • No analytics, no tracking and no advertising.

How backups are protected

  • The backup folder is outside the web root when possible, has a random name and contains files that deny web access; the Dashboard tests that the server really blocks it.
  • Downloads use signed links that expire after five minutes and require a logged-in administrator.
  • Storage credentials are encrypted in the database and never shown again. OAuth state is single-use and bound to the user who started it. The SFTP host key is confirmed once and a changed key is refused.
  • Passwords, keys and tokens are removed from every log line, notification and diagnostic report.
  • Restores and imports treat archives as untrusted: path traversal, symbolic links, oversized or bomb-like entries and undeclared files are refused.
  • No public endpoint starts a backup or a restore. Every privileged action requires the capability and a nonce.
  • Migration never calls unserialize() on backup data, so no object can be injected through a backup.

Personal data

The backup files contain whatever personal data your site holds. Treat them like the site itself: choose storage you are allowed to use, encrypt backups that leave your server, and delete old backups with the retention rules. The audit trail records the administrator's login name against each action.

10. Troubleshooting

First, the quick checks

  • Dashboard warns about the most common problems: an old backup, low disk space, a late schedule, a folder reachable from the web.
  • Settings → System check tests PHP, extensions, memory, time limits, the backup folder, free disk space and WP-Cron.
  • Logs has the log of every job, with a search box.
  • Download a diagnostic report gives support everything useful with no secrets in it.

A backup failed or got stuck

SymptomWhat to do
"The job stopped repeatedly while working on …"PHP ran out of time or memory on that item. Raise max_execution_time or memory_limit, or exclude the item (Settings → Exclusions), then try again. Nothing existing was touched.
The progress bar stoppedKeep the page open: the browser drives the job. If WP-Cron works the job also continues without the page. Cancel removes partial files.
"Not enough free disk space"Delete old backups, move them to remote storage, or use a bigger disk. Switch on "remote only" once two storages hold verified copies.
The backup is much smaller than the siteLook at Details → items not included, and at Settings → Exclusions.
A file "could not be read"Permissions on that file. It is skipped and listed; the rest of the backup is fine.
"A single file over 2 GB"The ZIP format used for parts holds files up to 2 GB. Exclude the file or move it out of the site.
The folder shows "Publicly reachable!"The web server serves the folder. Choose a folder outside the web root in Settings, or block it in the server configuration (Nginx needs a location rule; .htaccess does nothing there).

A restore was interrupted

  • Reload Restore. A running restore continues, or you can Cancel: every file is put back and the temporary tables are removed. The live database is untouched until the very last step.
  • After the last step the previous tables and files are kept. Undo (Restore → Previous restores) puts them back. Replaced tables are named wphub_old<run>_…; replaced files are in the restore/<run>/files folder of the backup folder.
  • If you are locked out after a restore, sign in with an account from the backup. If you cannot, use wp user update <id> --user_pass=….

Remote storage messages

MessageLikely cause
redirect_uri_mismatch (Google)The redirect URI in the Google console differs from the one on the screen, including http versus https.
"access_denied" / "Access blocked"The consent screen is in Testing and your account is not a test user.
Google works for 7 days, then asks againThe app is still in Testing: press Publish app.
"Needs reconnecting"The service revoked access (password change, removed app). Press Connect again; an email is sent.
SFTP "host key changed"The server was rebuilt, or someone is intercepting. Check with the administrator, then look up the key again.
Uploads stall on a small hostLower the archive part size in Settings; the upload resumes by itself.

Scheduled backups are late

WordPress runs scheduled tasks only when somebody visits. Add a real server cron job (see Schedules), or set DISABLE_WP_CRON and let a server cron call wp cron event run --due-now every few minutes.

Getting the site back by hand

A backup is just files; nothing needs this plugin to read it, so you can recover even if WordPress is down or the plugin is gone.

  1. Find the backup folder (the path is shown in Settings or the diagnostic report). It holds manifest.json, database.sql.gz and <part>-001.zip files.
  2. Check the files: unzip -tq uploads-001.zip, and compare SHA-256 values with manifest.json (shasum -a 256).
  3. Load the database. The dump is a series of gzip blocks of CREATE TABLE and INSERT statements with a marker line after each:
    mysql -u USER -p -e "CREATE DATABASE restored CHARACTER SET utf8mb4"
    gunzip -c database.sql.gz | grep -v '^--wphub:eos' | \
      mysql -u USER -p --init-command="SET SESSION sql_mode='NO_AUTO_VALUE_ON_ZERO'; SET SESSION foreign_key_checks=0" restored
    The --init-command matters: WordPress tables use 0000-00-00 dates that a strict default sql_mode refuses. Then point wp-config.php at the new database, or copy the tables you need.
  4. Unpack the files. Each part unpacks into one folder of the site (paths inside the archive are relative to it):
    PartUnpack into
    plugins-*.zipwp-content/plugins/
    themes-*.zipwp-content/themes/
    uploads-*.zipwp-content/uploads/
    mu_plugins-*.zipwp-content/mu-plugins/
    content_other-*.zipwp-content/
    root-*.zipthe folder that holds wp-config.php
    core-*.zipthe WordPress folder (only if you included core)
    Example: unzip -o uploads-001.zip -d /var/www/site/wp-content/uploads
  5. Moved to another address? After loading the database run wp search-replace 'https://old' 'https://new' --all-tables, or use the plugin's Migration once WordPress runs again.

Opening an encrypted backup without WordPress

Encrypted files end in .enc. This script needs only PHP 8.1 with sodium. Run php decrypt.php <backup-folder> <output-folder> and give the passphrase in the environment variable PASSPHRASE. It writes the plain *.zip files and database.sql.gz.

<?php
// decrypt.php - open a Backup & Restore by WpExperts Hub encrypted backup without WordPress.
$dir = $argv[1]; $out = $argv[2]; $pass = getenv('PASSPHRASE');
$m = json_decode(file_get_contents("$dir/manifest.json"), true);
$e = $m['encryption'];
$seed = sodium_crypto_pwhash(SODIUM_CRYPTO_BOX_SEEDBYTES, $pass, base64_decode($e['kdf']['salt']), $e['kdf']['ops'], $e['kdf']['mem'], SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13);
$kp  = sodium_crypto_box_seed_keypair($seed);
$dek = sodium_crypto_box_seal_open(base64_decode($e['sealed']), $kp);
if ($dek === false) { fwrite(STDERR, "Wrong passphrase\n"); exit(1); }
// Optional but recommended: check the manifest code.
$copy = $m; unset($copy['encryption']['mac']);
$sort = function ($v) use (&$sort) { if (!is_array($v)) return $v; $list = array_keys($v) === range(0, count($v) - 1); foreach ($v as $k => $x) $v[$k] = $sort($x); if (!$list) ksort($v); return $v; };
$mac = hash_hmac('sha256', json_encode($sort($copy), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), hash_hmac('sha256', 'wphub-backups-manifest-mac', $dek, true));
if (!hash_equals($mac, $e['mac'])) { fwrite(STDERR, "Manifest was changed\n"); exit(1); }
@mkdir($out, 0700, true);
$files = array_map(fn($f) => $f['file'], $m['files']);
if (!empty($m['database'])) array_unshift($files, $m['database']['file']);
foreach ($files as $enc) {
    $name = substr($enc, 0, -4);                      // strip ".enc"
    $key  = hash_hmac('sha256', "file:$name", $dek, true);
    $in = fopen("$dir/$enc", 'rb'); $o = fopen("$out/$name", 'wb');
    $hdr = fread($in, 12); $cs = unpack('N', substr($hdr, 8, 4))[1];
    $size = filesize("$dir/$enc") - 12; $chunks = (int) ceil($size / ($cs + 16));
    for ($i = 0; $i < $chunks; $i++) {
        $final = $i === $chunks - 1;
        $ct = fread($in, $final ? $size - $i * ($cs + 16) : $cs + 16);
        $ad = 'WPHBENC1' . $name . "\0" . pack('J', $i) . ($final ? 'F' : 'C');
        $pt = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ct, $ad, str_repeat("\0", 16) . pack('J', $i), $key);
        if ($pt === false) { fwrite(STDERR, "$enc: block " . ($i + 1) . " failed authentication\n"); exit(1); }
        fwrite($o, $pt);
    }
    fclose($in); fclose($o); echo "ok $name\n";
}

Reporting a problem

Send the diagnostic report (it contains no passwords, keys, tokens or email addresses) and the job log from Logs to support@wpexpertshub.com.

11. FAQ

Where are my backups?

In a private folder on your server (outside the web root when possible), and on every remote storage you connect. Settings shows the exact folder.

Is a backup on the same server enough?

No. If the server fails or is hacked, the backups go with it. Connect Google Drive, Dropbox or SFTP; the health score only counts a backup as safe when a copy exists off the server.

Can a restore damage my site?

A restore is built so that it cannot half-apply. Checksums are verified first, the database loads into temporary tables, replaced files are copied to a safety folder, and the swap is one atomic step. If anything fails before the swap, everything is put back. After the swap, Undo restores the previous state. Run a Dry run first to see what a restore would do.

What is never restored?

wp-config.php and this plugin's own folder. Files that exist now but are not in the backup are left alone.

How do I move my site to a new domain or host?

Use Migration: make a Full site backup, create a package, import it on the new site, restore it with "Adapt the backup to this site" ticked, run the Dry run first. See Moving or cloning a site.

Does it back up WooCommerce?

Yes. Orders (including HPOS tables), products, customers and settings are part of the database, and product images are in the uploads. It restores them like everything else.

Does it back up the whole site, including WordPress core?

Full site includes the database, plugins, themes, uploads, must-use plugins, other wp-content files and site-root files such as .htaccess. WordPress core files are optional (Settings), because core can always be downloaded again.

What happens if I lose my encryption passphrase?

Encrypted backups cannot be opened by anyone. There is no reset and no back door. Keep the passphrase in a password manager and on paper in a safe place.

Can I use it without a licence?

Yes. The licence is only for updates. Every backup, restore and migration feature works without it.

Does it contact any outside server?

Only the storage you connect (Google, Dropbox or your SFTP server), directly from your site, and wpexpertshub.com for the licence and update check (the licence key, your site address and the plugin version; never your backups, database or content).

Will it slow my site down?

Backups run in short steps in the background of the admin, not on visitor requests. Ordinary page views do no backup work. Lower the compression level or the archive part size on a small host.

Why did my scheduled backup run late?

WordPress runs scheduled tasks when someone visits the site. On a quiet site add a real server cron job (see Schedules).

Can it back up a very large site?

Yes: files are archived in parts (256 MB by default), each step is short, and uploads are resumable. The limits are free disk space and, for a single file, 2 GB (the ZIP format used for parts).

Does it work on multisite?

No. The plugin refuses to load on a multisite network.

Does it do incremental backups?

No. Every backup is complete and self-contained, so a restore never depends on a chain of earlier backups.

Which storages are supported?

Google Drive, Dropbox and SFTP. OneDrive, WebDAV and S3-compatible storage are not included in this version; developers can add providers (see the Developer reference).

How do I recover if WordPress is down?

A backup is standard ZIP files plus a gzip SQL dump, so you can restore by hand with unzip and mysql. The steps, and a script for encrypted backups, are under Getting the site back by hand.

What happens when I delete the plugin?

By default nothing: settings, backup records and backup files are kept. You can choose in Settings to remove the settings and tables, or everything including the backup files. Deactivating never deletes anything.

12. Changelog

0.9.1 · 2026-10-10

  • Licence and update check through WpExperts Hub (Plugins → WpExperts Hub Licences). Updates only: no backup feature depends on it.
  • One consistent look on every screen: the same panels, tables, filter bars, tabs, empty states and headline sizes; the health breakdown opens across the whole row.
  • The Details button on the Dashboard's Recent backups list opens the backup details.
  • SFTP form: the port sits next to the host, and "Look up the server key" sits next to Save.
  • Fixed: a backup could not be resumed after a hard stop at the exact moment an archive part was finished.
  • Fixed: the site address stored in a backup and used for migration is the configured address, not one that depends on how the page was requested.

0.9.0 · 2026-10-10

  • Schedules with retention rules, cleanup preview, email notifications and late-backup detection.
  • Reminder before plugin, theme and core updates with one-click Quick or Full backup, and an update log.
  • Remote storage: Google Drive, Dropbox and SFTP with resumable uploads, verification, retries, remote retention, remote-only mode and fetch.
  • Encryption (Argon2id and XChaCha20-Poly1305), passphrase never stored.
  • Restore: dry run, selective tables and files, file browser, reports, undo.
  • Migration: address, path and table-prefix change with serialized-data-safe replacement; portable .tar packages with strict import.
  • WP-CLI commands, backup health score, comparison of two backups, automatic backup checks, diagnostic report and developer hooks.

0.1.0 · 2026-10-09

  • First release: backups, checks, restore, downloads and the audit trail.

13. Support

Email support@wpexpertshub.com and a person will help. Please include:

  • Your order ID (from your purchase email or My Account on wpexpertshub.com).
  • The plugin version (shown on Plugins), and the WordPress and PHP versions (the diagnostic report lists them).
  • What you expected, what happened and what you already tried. The relevant job log from Logs and the diagnostic report (Settings → System check) help a great deal.
  • Which storage you use (Google Drive, Dropbox or SFTP) and whether the backup is encrypted.

Please do not email passwords or your encryption passphrase. Support never needs them.