Documentation

Docs & troubleshooting

Get two sites connected, know what moves and what doesn't, and fix the things that occasionally go wrong — with the exact messages you'll see.

Getting started

PostDeploy moves a post or page from one WordPress site to another. You need the plugin on both sites — PostDeploy Free or PostDeploy Pro, in any combination.

Requirements

  • WordPress 6.4 or newer, and PHP 8.0 or newer, on both sites.
  • The sending site's server must be able to reach the receiving site over the internet (a site behind a password, an IP allow-list, or maintenance mode can't receive).
  • When you push, the receiving site downloads the images and files from the sending site's uploads folder, so those files must be publicly reachable too.

Connect two sites

  1. Install and activate PostDeploy on both sites.
  2. On the site you want to connect to, open PostDeploy → Connections and copy This Site's API Key.
  3. On the other site, open PostDeploy → Connections, enter a Name (any label, like "Production"), the other site's Site URL, and paste the key into Its API Key.
  4. Click Test & Connect. The connection is verified before it is saved.

Push or pull a post

  • Push — on the Dashboard, find the post in the list and click Push. With more than one connection, choose the destination first.
  • Pull — open Remote Content, browse the other site's posts, and pull the one you want.

Keep both sites up to date. Pro checks what the receiving site can handle before it sends, but the best results come from both sites running the latest release of PostDeploy.

Your license (Pro)

  1. Get your license key from the customer portal.
  2. In wp-admin, open PostDeploy Pro → License, paste the key, and click Activate License. It's built into the plugin — there is no separate account screen.
  3. The first time a site connects you may be asked to confirm by email before the license becomes active. Click the link in that email.

If the license later expires, everything you already created keeps working. Only new use of Pro-only features pauses until it is active again — see "Pro features are locked" below.

What gets migrated

Moves with the post

  • Posts, Pages, and other public custom post types — title, content, excerpt, status, and custom fields.
  • Categories, tags, and custom taxonomy terms.
  • The featured image and images used in the content, copied into the receiving site's media library.
  • With Pro: ACF field references (images, galleries, relationships) pointed at the receiving site's own content; Elementor pages including their design data, images, page settings (such as Hide Title and page-level CSS) and the assigned page template; and WPML translations.

Doesn't move

  • Themes, plugins, and site settings. Only the name of a page's template is carried — a theme's own template file must already exist on the receiving site.
  • Elementor Theme Builder layouts and other Elementor library items. They are separate from the page, so set them up on the receiving site.
  • Images that aren't in the media library: files added by FTP outside the uploads folder, theme images, and images hosted on other domains stay exactly as they are.
  • Custom post types that don't have an admin screen or that hold internal data (media attachments and some page-builder storage types are excluded on purpose).

Going from Free to Pro? Pro is a complete plugin with its own connections list, so your Free connections aren't copied over — add them again in Pro. Don't run Free and Pro together; see below.

Troubleshooting

Search by the message you see, or browse by topic. Each entry says what's happening and what to do.

No matches. Try a different word, or contact us.

Connections

"Could not reach the remote site" — or "The destination site returned an unexpected response"
Why it happens

The sending site's server didn't get a proper answer from the other site. Common causes: a wrong address, the site being in maintenance mode or behind a password or IP restriction, an SSL problem, or a firewall / security plugin / host bot-protection blocking the request. The longer message that mentions an "unexpected empty response" or a firewall means the same thing.

What to do
  1. Check the Site URL exactly — https:// or http://, with or without www, no extra path.
  2. In a browser, open https://the-other-site.com/wp-json/postdeploy/v1/status. You should get a short JSON response (usually an authentication error — that's fine, it proves the plugin is reachable). A login page, a firewall block page, or a 404 means the request isn't reaching PostDeploy.
  3. Allow requests to /wp-json/postdeploy/ in your firewall, security plugin, or host protection, and remove any password or IP restriction from the receiving site.

Pushed several languages of the same page at once and only one of them failed this way, not all? That still points to a firewall or security plugin (Wordfence and similar are common) — it doesn't rule it out. These commonly give extra scrutiny to the very first request they've seen from a source in a while and wave through the next ones moments later, so a bulk multi-language push often shows exactly this pattern: the first language in the batch fails, the rest succeed. Re-pushing the failed one on its own usually works once the destination's already "seen" the connection — but the fix is to allow-list the PostDeploy route or the sending site so it doesn't happen again, not to leave the security plugin off.

"The remote site rejected the connection credentials"
Why it happens

The API key is wrong or out of date, it was copied from the wrong site, or the two servers' clocks are more than 5 minutes apart (each request is time-stamped and signed).

What to do
  1. On the other site, open PostDeploy → Connections and copy This Site's API Key again. It must come from the site you're connecting to, not the one you're on.
  2. Paste it with no leading or trailing spaces or line breaks and reconnect.
  3. If it still fails, check that both servers show the correct time — ask your host to sync the server clock.
"PostDeploy is not active on the remote site" / "The remote PostDeploy version is not compatible"
What to do

Install and activate PostDeploy (Free or Pro) on the other site, then update both sites to the latest release and try again.

"Too many requests from this connection. Please try again in a few minutes."
Why it happens

PostDeploy limits how fast one connection can send requests, to protect the receiving site.

What to do

Wait a few minutes and try again. For many posts, use bulk or scheduled deployment (Pro), which queues them instead of sending everything at once.

"PostDeploy Pro was not activated because the free PostDeploy plugin is also active"
Why it happens

Pro already includes everything Free does, and running both would show two PostDeploy menus and two connection lists. Pro won't run while Free is active.

What to do

Deactivate and delete the free PostDeploy plugin, then activate Pro. Pro keeps its own connections, so add them again after switching. Other sites can still run Free — a Pro site and a Free site push to and from each other normally.

Pushing & pulling

"Destination has newer changes … Deploying now will overwrite them"
Why it happens

Pro's conflict detection noticed that the page on the receiving site was edited after PostDeploy last wrote it. It stops so you don't lose those edits by accident.

What to do

Compare the two versions. If the receiving site's changes can be discarded, confirm the prompt to force the deployment; otherwise copy those edits back to the source first. Pro takes a snapshot before overwriting, so you can undo it — see Undoing a push.

"A deployment for this object is already running"
What to do

The same post is already being deployed — usually a double-click, or a scheduled run overlapping a manual one. Wait for it to finish (check Deployments) and try again if needed.

"This post type is not supported for migration"
Why it happens

PostDeploy migrates posts, pages, and custom post types that are public, have an admin screen, and support a title and editor. Internal types — media attachments and some page-builder storage types — are excluded on purpose.

What to do

Check the post type is registered the same way on both sites (same plugin active on each). If it holds internal data rather than content, it isn't meant to be migrated.

"The package exceeds the maximum allowed size"
Why it happens

The content and data for one post can be at most 20 MB. Image and media files aren't counted (they're downloaded separately), so this is almost always huge inline data — for example images embedded directly in the text, or an enormous page-builder layout.

What to do

Upload embedded images to the media library and link to them, or split a very large page into smaller ones.

Elementor pages

The page arrived as one flat block of text, and the design is gone
Why it happens

Elementor falls back to a single text block when it can't read a page's design data. That happens when the receiving site is running a version that can't read what was sent, or when the receiving Pro site's license isn't active.

What to do
  1. Update PostDeploy on both sites to the latest release (Pro 0.9.60 or newer; the free plugin 0.9.64 or newer).
  2. Make sure Elementor is active on the receiving site and, if it runs Pro, that its license is active.
  3. Push the page again — it updates the existing page.
The page title shows on the receiving site but is hidden on the source
Why it happens

"Hide Title" is one of Elementor's page settings. Older versions didn't carry page settings across; they do from Pro 0.9.60. It can also come from the page template or a Theme Builder layout.

What to do
  1. Update both sites and push the page again.
  2. In Elementor, open the page's Settings (gear icon) on both sites and compare Hide Title and Page Layout.
  3. If the source gets its layout from an Elementor Theme Builder template, recreate that template on the receiving site.
The page looks different — wrong width, missing header or footer
Why it happens

The page's template name is carried across, but the template itself isn't. Elementor's own templates (Full Width, Canvas) work anywhere Elementor is active. A template that belongs to your theme only works if the same file exists in the receiving site's active theme — otherwise WordPress quietly uses the default template.

What to do

Use the same theme (or child theme) on both sites, or pick a template that exists on the receiving site. In Elementor: Settings (gear) → Page Layout; in the block editor: the Template field in the sidebar.

A Loop Grid shows empty, or crashes the page/editor, on the receiving site
Why it happens

A Loop Grid is different from every other widget on a page. Ordinary widgets have their content stored right inside the page — text, images, links all travel with it. A Loop Grid instead stores a query ("show me posts from this category," or "show me this custom post type") and runs that query live, against whichever site is currently rendering the page. Two things it depends on don't travel with the page at all:

  • The grid's own Loop Item template — the design used for each item — is a separate Elementor Theme Builder item, not part of the page. It's stored as its own post, and the grid widget just references its ID — so if that ID doesn't exist (or means something else) on the receiving site, Elementor can show the grid empty, or — in some setups — hit a fatal error (Call to undefined method ...::print_content()) trying to render or edit the page at all.
  • If the grid queries dynamically (by post type or category, the common setup) rather than showing specific hand-picked posts, the posts it's looking for also have to actually exist on the receiving site — otherwise the query correctly finds nothing there.
What to do

PostDeploy Pro 0.9.77+: pushing a page with a Loop Grid now offers to bring its Loop Item template along automatically, right in the push confirmation popup (checked by default) — accept it and the reference resolves correctly on its own, no manual export/import needed.

On an older Pro version, or on Free (which doesn't resolve Elementor references at all):

  1. In Elementor on the source site, open the Loop Item template and use Export Template (saves a .json file). On the receiving site, go to Templates → Saved Templates → Import Templates and import it. This is Elementor's own tool for moving Theme Builder templates between sites — several related templates can be moved together with a Template Kit instead, if you have Elementor Pro.
  2. If the grid queries a post type or category, push those underlying posts to the receiving site too (individually, or with Bulk Deployment for a lot of them at once) so there's something on that site for the query to find.
JavaScript from an HTML widget shows up as text on the page, with its symbols garbled
Why it happens

PostDeploy delivers the widget's code unchanged. The damage happens afterwards, when the page is saved in Elementor's editor on a site that has DISALLOW_UNFILTERED_HTML turned on (common on locked-down hosts and multisite). Elementor then strips the <script> tags and encodes the rest of the code, which can also delete part of it.

What to do
  1. On the receiving site, ask your host or developer whether DISALLOW_UNFILTERED_HTML is set in wp-config.php, and remove it (on multisite, use a Super Admin).
  2. Push the page again to restore the original code from the source, and avoid saving that page in Elementor until the setting is off.
  3. If the setting has to stay, keep the JavaScript out of HTML widgets — put it in a code-snippet plugin or your theme instead.

Images & media

Images still load from the original site, or don't appear
Why it happens

PostDeploy copies files that are in the source site's media library (its uploads folder) and points the page at the copies. It can't copy images that live elsewhere: files uploaded by FTP outside the uploads folder, images from your theme, or images hosted on other domains. Web addresses that JavaScript builds while the page runs also can't be detected.

What to do
  • Add the missing images to the media library on the source site, then push again.
  • Update both sites to the latest release — images inside tabs and slides, inside HTML blocks, and addresses with no domain (like /wp-content/uploads/…) are handled from Pro 0.9.57.
"Could not download media from … Forbidden" (or "Unauthorized")
Why it happens

The receiving site fetches each image directly from the sending site. If the sending site blocks direct file requests — hotlink protection, HTTP authentication, an IP allow-list, or bot protection on the uploads folder — the push stops and nothing is half-imported.

What to do

Allow the receiving site's server to fetch /wp-content/uploads/ on the sending site: switch off hotlink protection, remove the password, or allow that server's IP. Then push again.

"This file type is not allowed" / "The file content does not match an allowed media type"
Why it happens

The free plugin migrates JPG, PNG, GIF, WebP, and PDF files. PostDeploy Pro also handles SVG, BMP, TIFF, and ICO images, fonts (WOFF, WOFF2, TTF, OTF, EOT), and video (MP4, M4V, WebM, MOV). SVG files are cleaned of scripts on the way in.

What to do

Use Pro for those file types on the receiving site, or remove the file from the post.

"Media file … exceeds this site's maximum upload size"
What to do

The receiving site's PHP limits (upload_max_filesize and post_max_size) are smaller than the file. Raise them in your host's control panel or php.ini — or ask your host — then push again.

Scheduling & auto-push (Pro)

A scheduled deployment or auto-push runs late, or not at all
Why it happens

WordPress's built-in scheduler only runs when someone visits the site. On a quiet site, or one with caching that hides visits, a scheduled job can wait a long time.

What to do
  1. Check the Queue page — it shows each item's status and any error.
  2. Set up a real server cron so WordPress checks regularly. In wp-config.php:
    define( 'DISABLE_WP_CRON', true );
    and add a cron job on your server, every 5 minutes:
    */5 * * * * curl -s https://your-site.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
Auto-push didn't fire when I published
How it works
  • You pick a destination in the PostDeploy: Auto-Push box on the post editor screen. The default is "Don't auto-push".
  • It fires once, when the post changes to Published — including a normal WordPress scheduled publish. Later edits to an already-published post don't push again; use Push for that.
  • It follows the same queue as scheduled deployments, so the same cron note above applies.
  • Setting up a new auto-push needs an active Pro license; one you already set up keeps working if the license later lapses.

License

Pro features are locked, or the License page says the license isn't active
Why it happens

Either activation wasn't completed, or the license has expired. Without an active license, ACF / Elementor / WPML resolution, new snapshots, new bulk and scheduled deployments, setting up auto-push, and WP-CLI are unavailable.

What to do
  1. Open PostDeploy Pro → License, paste your key, and click Activate License.
  2. If the message says a confirmation email was sent, click the link in that email, then reload the License page.
  3. If the license expired, renew it in the customer portal.

Nothing you already created is lost while a license is inactive: existing snapshots stay restorable, deployments already queued still run, and Posts, Pages, custom post types, and your connections keep working.

Where do I find my license key?

In the customer portal. You can also manage your subscription and download invoices there.

Can I use one license on more than one site?

Each plan covers a set number of sites — 1, 3, 10, or unlimited. Activate the same key on each site, up to your plan's limit. To free up a slot, deactivate the license on a site you no longer use. Staging environments may not count against this limit at all — see the next question.

Does a staging site count against my site limit?
Why it happens

Licensing recognizes common staging/development URL patterns automatically — a hostname that contains localhost or 127.0.0.1, starts with local., dev., test., stage., or staging., or ends with .dev, .test, .staging, .local, .example, or .invalid. For example, staging.yoursite.com matches; a name like yoursite-staging.com does not, and counts as an ordinary site.

Whether a matched staging/dev site is free is a setting on the license itself. When it's enabled, activating on a matching URL doesn't use up a site slot at all, on any plan. When it isn't, a staging URL counts the same as a production one.

What to do

Check your account in the customer portal to confirm whether free staging activation is enabled for your license. Either way, naming your staging environment with one of the patterns above (a staging. subdomain is the simplest) is what makes it eligible.

I cloned or migrated my whole site with another plugin — is my license still valid, and are my connections safe?
Why it happens

Copying an entire WordPress install (files and database) with a backup/migration plugin carries PostDeploy Pro's license data and connections along with everything else, so both the original and the copy can end up looking identical.

What happens to the license

Licensing detects this automatically and pauses ("safe mode") until you tell it which of these is true, from an admin notice: the copy is a duplicate for staging or testing (keeps working, doesn't use a site slot), this is the new home of the original — a real migration, so the license moves with it and the old URL retires — or it's a genuinely new, separate website, which needs its own license activation and does use a site slot.

What happens to your connections

PostDeploy's own site-to-site connections are separate from licensing. If the migration copies everything (including wp-config.php), the copy comes out as an exact working duplicate — same identity, same connections, same stored keys — which also means it can immediately push or pull to every site the original was connected to. If you meant the copy to be a disposable staging site, open Connections on it, remove or repoint anything that shouldn't have live access to production, and click Regenerate Key to give the copy its own identity. If instead the destination got fresh wp-config.php secrets (common when a host provisions a brand-new install), connections will still be listed but won't authenticate until you re-add them — nothing sends silently in that case.

Undoing a push (Pro)

Before a push overwrites a page that already exists, Pro saves a snapshot of it.

  • Undo the last push: on the receiving site, open Deployments, find the deployment, and click Rollback. You'll be asked to confirm.
  • Go back to an earlier version: click History next to the post to open Snapshot History, then Restore This Version on the version you want.

Snapshots are kept for 90 days, then pruned automatically. A snapshot is taken when a push overwrites an existing page, so a page's very first push has nothing earlier to restore.

Still stuck?

Open the customer portal and send us a message. To help us find the cause quickly, include:

  • The exact message you see (a screenshot is ideal).
  • Your WordPress and PHP versions and the PostDeploy version on both sites.
  • Which direction you were going (push or pull) and what kind of content (a post, an Elementor page, and so on).
  • The Diagnostics page in PostDeploy Pro, or the Debug Info panel in the free plugin's Settings, if you can share it.