Docs/Security

Security

How sites authenticate, how credentials are stored, who can read your exported JSON, and the limits that contain a misconfigured sync.
Updated July 2026·WPChangeSync 2.1

What the security model protects

WPChangeSync moves real content between real sites and stores credentials to do it, so there are four things worth understanding: how sites authenticate to each other, how credentials are stored, who can read the exported files, and how much damage a misconfigured sync can do.

This page covers the parts you may need to act on. The wider security overview covers the model and the questions people ask before buying.

Transport and credentials

Remote connections are authenticated server to server. They are not browser requests, so you do not need permissive CORS settings anywhere.

  • HTTPS is required on both sites. Insecure connections are refused rather than downgraded.
  • Authentication uses an admin-capable Application Password on the receiving site, created under Users, then Profile. It is not the account login password, and it can be revoked on its own.
  • Stored credentials are encrypted at rest. After a clean-slate reset you will need to enter them again, because they are genuinely gone.
  • Redirects are validated against SSRF on every hop, not just the first request.
  • Credentials are dropped, not sent, if a remote redirects HTTPS to plain HTTP. This applies to push, pull, and the editor's Pull action. It can look like an authentication failure when it is really a redirect problem.
  • Import errors are sanitised before being returned to the calling site, so a failure does not leak internals. The detail lives in Activity on the receiving site.

The REST API is also rate limited to 60 requests per minute. In a development environment you can lift that with define('WPCHANGESYNC_DISABLE_RATE_LIMIT', true);.

Protecting the exported JSON files

Your exports are readable files on disk, and depending on what you sync they can contain configuration you would rather not publish. Whether they are reachable over HTTP depends on your web server.

  • Apache: handled for you. WPChangeSync writes a .htaccess into the JSON folder that denies all direct access. No action needed.
  • Nginx: manual. Nginx ignores .htaccess, so you must add a location rule to your server config and reload.
  • lighttpd: manual. Same situation, with its own syntax.

Adjust the path in the examples below to match your actual storage location, relative to the web root. If you chose child-theme storage, the path is inside your theme rather than uploads.

Blocking direct access to the JSON folder
# Apache: WPChangeSync writes this .htaccess into the JSON folder for you.
Order allow,deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>

# Nginx: no .htaccess support, so add this to your server config and reload.
location ^~ /wp-content/uploads/wpchangesync/ {
    deny all;
    return 403;
}

# lighttpd: no .htaccess support either.
$HTTP["url"] =~ "^/wp-content/uploads/wpchangesync/" {
    url.access-deny = ( "" )
}

Keeping secrets out of exports

Licence keys, API tokens and passwords should never travel between sites, and two mechanisms keep them out.

First, an excluded-options list on the Storage screen, which you can add your own keys to. Second, automatic suffix filtering: option and table values whose names end in patterns such as _key, _token, _secret or _password are skipped when building a custom data source, and WordPress core option prefixes cannot be selected at all.

If you are writing your own integration, list your secrets explicitly in its exclude array. The suffix filter is a safety net, not a substitute.

Limits that contain mistakes

Several limits exist specifically so a misconfiguration cannot become a disaster:

  • Deletion propagation is capped, so a badly scoped migration profile cannot empty a small site. When the cap blocks a run, you are told rather than left guessing.
  • Storage paths are validated against traversal, so a crafted path cannot write outside the configured folder.
  • Media downloads are size-capped when sideloading from a source site.
  • Network batch REST endpoints require the network-admin capability on multisite, so a subsite admin cannot manage network-wide jobs.
  • Audit CSV exports are sanitised against spreadsheet formula injection, because logged values can contain anything.
  • Bundled ACF field groups cannot overwrite an existing group through a mismatched payload.

What this does not do

Two honest limits. The pre-overwrite snapshots described in Conflicts, Backups & Rollback are a convenience, not a backup system. Take a real database backup before a bulk import, a licence change, or switching production to automatic sync.

And an admin-capable Application Password is genuinely powerful. Anyone holding it can write content on that site. Treat a remote credential with the same care as an admin account: create it for this purpose, and revoke it when a site leaves your estate. For maximum isolation you can keep the JSON outside the web root entirely and move it with your deploy pipeline instead of serving it.