EZ Page Sync
Push page edits from a staging WordPress instance to production with cryptographic validation, automatic media sideloading, and database rollback checkpoints.
Visit ezpagesync.comOverview
EZ Page Sync is a database-level, security-hardened WordPress plugin designed to sync content changes from staging environments to production in one click. It maps post assets, remaps links, and migrates layout files without needing complete database exports or manual search-and-replace queries.
HMAC Cryptographic Security
Every synchronization payload is signed using SHA-256 HMAC tokens. Replay attacks are stopped using time-to-live nonces, rate-limiting locks, and whitelisted IP addresses.
Automatic Rollback Snapshots
Before applying updates, the receiver backs up the existing target page. If anything fails (like media sideloading or ACF mismatches), it triggers an automated rollback.
Key Features
Full & Content-Only Sync
Choose between syncing everything (meta, ACF, media attachments, layouts) or push purely textual revisions (slug, title, body markdown) using "Content Only" selectors.
Dry Run Mode
Execute the entire synchronization pipeline—from validation schemas to media processing—without saving to the database. Displays a preview diff summary.
Url Rewriting
Rewrites references to the staging URL found inside your HTML blocks, custom fields, and layout configurations to use the production URL on arrival.
Media Sideloading
Extracts featured images and inline attachments from the post body. They are compressed, sent via secure base64 payloads, and sideloaded into the production library. Run Image Format Converter on the receiver to convert those sideloaded images to WebP/AVIF the moment they land.
ACF & Elementor Remapping
Parses Advanced Custom Fields (ACF) meta arrays and Elementor page configurations (_elementor_data), mapping referenced media IDs to the production media library.
Cache Clearing
Automatically clears cached instances of modified pages for common setups, supporting WP Rocket, LiteSpeed Cache, W3 Total Cache, and WP Super Cache.
Discard Edits
Every successful sync stashes a snapshot on staging. "Discard Edits" restores a page to its last-synced state in one click, and the admin menu badge counts pages with unsynced edits. Included on the Free tier.
Locally Modified Detection
Flags a page as "Locally Modified" when its production copy was edited or restored directly, using a read-only, HMAC-signed divergence check from staging.
How It Works
EZ Page Sync securely transfers data across environments. The staging server compiles and signs the payload, while the production server validates it, creates a backup snapshot, and saves the updates.
Outbound Dispatch Pipeline (Sender)
An editor clicks "Sync" on the Sync Pages dashboard, the Pages/Posts list, or the page metabox. A preview diff opens first; nothing is written until they confirm.
Sender::build_payload() queries the page, custom post meta, taxonomies, ACF fields, and base64 media blocks.
The compiled JSON is signed using SHA-256 HMAC using the API secret key, adding a one-time nonce and timestamp.
The package is sent over HTTPS to the production receiver's registered endpoint via wp_remote_post().
Inbound Processing Pipeline (Receiver)
Receiver::verify_request() performs whitelisting checks, verifies rate limits, and validates the HMAC signature.
If the target page already exists, Rollback::create_snapshot() saves its current state in the custom logs table.
The post data is updated. Base64 media data is written to disk, re-indexed, and local URLs inside the content are remapped.
Page caches are cleared, a success code is returned to the sender, and the sync event is logged in the custom logs table.
Sync Architecture
This flowchart illustrates how the sender and receiver coordinate sync actions using the REST API:
- └─ build_payload()
- └─ sign (HMAC-SHA256)
- └─ wp_remote_post()──►
- └─ handle_sync()
- ├─ verify_request()
- ├─ validate_payload_schema()
- ├─ create_snapshot()
- ├─ find_or_create_production_post()
- ├─ apply_post_update()
- ├─ sync_post_meta()
- ├─ sync_media() ← EzPageSync\Media_Sync
- ├─ sync_acf_fields()
- ├─ sync_taxonomies()
- ├─ sync_yoast_meta()
- └─ log_sync_success()
Technical Component Breakdown
| File/Class Component | Role & Functionality |
|---|---|
| EzPageSync\Sender | Handles outbound sync. Queries metadata, maps dependencies, compiles JSON payloads, calculates HMAC signatures, and manages the admin metabox. |
| EzPageSync\Receiver | Handles incoming payloads. Validates formatting, resolves post matches on production, maps media IDs, and writes changes to the database. |
| EzPageSync\Auth | Handles HMAC validation, checks replay attack nonce tables, validates timestamp bounds, checks whitelisted IPs, and manages rate limiting. |
| EzPageSync\Media_Sync | Handles binary attachments. Converts inline images to base64, uploads them to the media library on the receiver, and rewrites URLs in content. |
| EzPageSync\Rollback | Tracks checkpoints. Serializes posts and meta keys into a custom database table before sync, and handles restoration if errors occur. |
| EzPageSync\Logger | Manages the custom wp_ez_ps_logs table. Logs events and handles automated pruning based on the configured log limits. |
Free vs Premium Feature Matrix
The Free version supports basic content sync for one destination, while the Premium version adds support for custom post types, page builders, and media sideloading.
| Feature | Free Tier | Premium Tier |
|---|---|---|
| Sync Destinations | 1 Site | Unlimited (Multi-destinations & Relay Chains) |
| Plugin Role | Sender OR Receiver (Fixed) | Both (Multi-role simultaneously) |
| Eligible Post Types | page, post | Any registered Custom Post Type (CPT) |
| Metadata & Custom Fields | — | Included (ACF, Yoast, Astra theme options) |
| Page-Builder Support | — | Included (Elementor layout, global widgets & templates) |
| Media Handling | — | Included (Featured image & inline body media) |
| ACF Options Pages | — | Included (Site-wide ACF Options page values) |
| Rollback Manager | — | Included (Full snapshot history and restore controls) |
Configuration Reference
| Setting Key | Role Scope | Default | Description |
|---|---|---|---|
| ez_ps_plugin_role | Both | 'sender' | Role assignment: 'sender' (staging) or 'receiver' (production). |
| ez_ps_api_secret_key | Both | — | Shared API Secret Key for HMAC signatures. Stored encrypted in DB. |
| ez_ps_allowed_sender_ips | Receiver | '' | Comma-separated sender IP addresses or CIDR blocks. Empty matches all. |
| ez_ps_sync_post_types | Both | 'page, post' | Comma-separated list of post types eligible for synchronization. |
| ez_ps_overwrite_media | Receiver | false | If true, overwrites target media library files if duplicate filenames are found. |
| ez_ps_auto_rollback | Receiver | true | Automatically restores post snapshots if error exceptions throw mid-sync. |
| ez_ps_require_snapshot_before_apply | Receiver | true | Aborts sync operations if backup snapshot generation fails. |
| ez_ps_max_log_entries | Both | 500 | Caps rows in custom log tables, pruning oldest entries first. |
| ez_ps_notify_email | Receiver | '' | Recipient email address for automated status alerts. |
Frequently Asked Questions
Does this plugin support WordPress Multisite?
No. Version 1 of the plugin is designed strictly for single-site WordPress installations.
How does the rollback system restore pages?
Before applying any incoming sync payload, the receiver serializes the existing post fields and metadata rows, saving them as an entry in the custom logs table. You can restore this version with one click in the **Rollback Manager** tab on the receiver site.
What happens during a "Dry Run"?
Dry Run runs the entire validation, media parsing, and payload delivery process, but rolls back database changes on the receiver at the end. This allows you to verify that dependencies resolve without saving changes to the live site.
How do I rotate key credentials safely?
Go to **EZ Page Sync → Settings** and click **Regenerate Key**. You must copy the new key to the other environment immediately. Any pings or sync requests sent using the old key will be rejected.
Why are debug messages not written to the Sync Log?
To prevent database bloat. Verbose logs and connection checks are routed directly to PHP's standard error logs (visible in your server logs when `WP_DEBUG_LOG` is enabled). The database log table is reserved for events like successful syncs, failures, and rollbacks.
Does EZ Page Sync replace full-site migration tools like WP Migrate or WPvivid?
No — it solves a different problem. Full-site migration tools replace the entire database, which overwrites WooCommerce orders, form submissions, and new content added to production. EZ Page Sync targets a single page or post, leaving everything else on production untouched. Use a migration tool for initial clones and EZ Page Sync for ongoing page-level deploys.
Is EZ Page Sync available on the WordPress.org plugin repository?
Not currently. The Free version is available as a direct download from ezpagesync.com. Install it by uploading the plugin zip through your WordPress admin panel (Plugins → Add New → Upload). The Premium version is delivered through a Freemius account after purchase.
Download EZ Page Sync
EZ Page Sync is available as a Free tier (single destination, pages and posts) and a Premium tier (unlimited destinations, custom post types, ACF, Elementor, media sideloading, ACF Options sync, and rollback manager). Both install as a standard WordPress plugin zip — no WordPress.org account required.
Free Tier
Single destination. Syncs standard pages and posts. HMAC security, dry run, and connection test included.
Download FreePremium Tier
Unlimited destinations, ACF, Elementor, media sideloading, ACF Options sync, and rollback manager. Delivered via Freemius.
View PricingNeed help deciding which tier fits your setup? Get in touch — happy to talk through it before you test.
Changelog
v1.1.47
October 17, 2026ACF Accuracy, Discard Baselines & Sync Dialog:
- Fixed ACF sync to send ACF's raw storage shape, so
linkfields keep their full title, URL, and target, and unchanged ACF fields are no longer flagged as different on Preview. - Added a first-edit baseline snapshot so Discard Edits is available as soon as an in-sync page is edited, plus an admin menu badge counting pages with unsynced edits.
- Sync status counters (Last Sync, Synced Today, Failed (7d), Total Syncs) now survive clearing or rotating the sync log.
- Renamed the Dry Run button to Sync. It still opens the preview first, and the post-edit metabox now uses the same preview dialog as the dashboard.
v1.1.46
October 12, 2026Freemius SDK Security Update:
- Updated the bundled Freemius SDK from 2.13.4 to 2.13.5: escaped admin-notice output, administrator-only opt-in, and pricing-page settings locked against URL overrides.
- No changes to sync behavior or Free/Premium gating.
v1.1.45
October 8, 2026Parent-Page Safety:
- A child page whose parent can't be matched on production now stops with HTTP 409 (
EZ_PS_PARENT_NOT_FOUND) instead of landing under a parent ID that only exists on staging. Pages already linked keep their production parent. - Preview reports the same case as blocked, and the force dialog is no longer offered for it.
- Rejected parent-slug segments containing
/or%2f. Update both staging and production.
v1.1.44
September 5, 2026Preview Parity & Log Management:
- Dry run previews now show the destination's hierarchical slug path and permalink, and the post-edit metabox matches the dashboard modal layout.
- Added a "Latest per page" sync-log filter and a confirmed Delete Log bulk action.
- The Sync Options tab and ACF toggle are hidden when ACF isn't available, and admin styles no longer leak onto post edit screens.
v1.1.43
September 5, 2026Hierarchical Matching & Media Extraction:
- Added parent-aware matching via
post_parent_slugs, so child pages that share a slug under different parents no longer cross-match. - Added Astra display-condition remapping, ACF slug hints for relationship and post-object fields, and production-side resolution of excluded video attachments.
- Media extraction now picks up root-relative upload URLs in CSS
url(...), classic[gallery]shortcodes, and numeric-string Elementor image IDs.
v1.1.42
September 2, 2026Discard Edits & Media Preflight:
- Added Discard Edits (Free): restores a staging page to its last-synced state in a single all-or-nothing operation.
- Added a media preflight handshake that drops files production already has from the payload, excluded video files by default (
ez_ps_skip_video_media), and added an opt-in referenced-media-only mode. - Every sync now goes through Preview, then Apply. The separate Sync Now and Bulk Sync controls were removed in favor of single-page syncs, and ambiguous slug matches now require force.
v1.1.41
August 22, 2026Silent-Failure Hardening & WordPress 7.0:
- Every apply step (meta, Yoast, media, Astra, Elementor, ACF, taxonomies) now reports its own failure as a warning, or aborts with auto-rollback when Fail On Partial Sync is on.
- Auto Rollback now fires on any
\Throwable, and a failed first-time create deletes the blank page it created. - Raised the minimum WordPress version to 7.0 (tested up to 7.1). The native Pages/Posts list gained per-row sync buttons and the Locally Modified badge.
v1.1.40
August 12, 2026Locally Modified Detection:
- Sync Pages shows an amber Locally Modified badge when the production copy has diverged since the last sync, checked through a read-only, HMAC-signed
/divergence-checkendpoint. - Dashboard Dry Run now opens the same field-level diff report as the metabox.
- Snapshot restores show the animated progress dialog, and the syncable-pages list is paginated with a Last Edit column.
v1.1.39
August 10, 2026ACF Options Sync & Baseline Re-link:
- Added Premium ACF Options page sync from a new Sync Options tab, written through ACF's own field APIs against an allow-list of known options pages.
- Added "Baseline after production refresh" to re-link pages after staging is restored from production, without sending anything to the destination.
- The receiver now caps request size and JSON nesting depth before authentication, and preserves the production author when the staging author no longer exists.
v1.1.38
July 26, 2026Ownership Guards & Partial-Sync Accuracy:
- Closed two ownership-check bypasses on multi-destination installs. A first-time slug match against an unlinked production page now requires force.
- A partial sync (for example, a media failure) is no longer recorded as In Sync, so the deferred work re-syncs.
- Reduced database load with batched child-post lookups and single-pruner log cleanup, and updated the Freemius SDK to 2.13.4.
v1.1.37
July 3, 2026Free-Tier Safety & Reset Locks:
- Fixed role locks on Free tier. The
Admin::has_incoming_sync_history()check now tracks receiver connection pings using theez_ps_receiver_last_contacted_atoption, preventing role changes if a sender has established a connection. - Fixed options cleanup. Resetting a receiver now deletes
ez_ps_receiver_last_contacted_atalongside metadata keys to allow re-purposing the site as a sender. - Expanded PHPUnit coverage with new test assertions for options gating and reset actions.
v1.1.36
July 3, 2026API Key Rotation & Hardened Disconnection:
- Updated `Reset Sync Origin` to regenerate the API key when disconnecting. This prevents stale senders from re-establishing ownership after metadata resets.
- Updated settings interface to display the rotated key alongside confirmation prompts immediately after resets.
- Added validation checks to confirm the reset actions are restricted to administrators (
manage_options).
v1.1.35
July 3, 2026Free-Tier Receiver Locking:
- Locked the plugin role toggle on the Free tier when the site has received incoming sync entries, preventing accidental disruption of active staging senders.
- Added server-side sanitization to reject role modifications if role-change lock criteria are met.
