← Back to blog

How to duplicate a WordPress site you intend to keep

The SandyWP team 7 min read

Every guide to duplicating a WordPress site tells you how to make the copy. Almost none of them tell you what to do with it afterwards, and that gap matters most in the case people actually mean: duplicating a finished site to start the next one.

A copy you make to test something gets deleted in a week, so its inherited baggage never has time to hurt you. A copy you hand to a new client runs for years, with the previous client's users, tokens and license keys still inside it.

We fetched the guides ranking for this phrase before writing (GoDaddy, TeamUpdraft, Learn WordPress, read 2026-09-07). GoDaddy's own framing is "using an existing, preconfigured site as a basis for new sites", and TeamUpdraft runs 3,500 to 4,000 words on the mechanics.

Between the three of them, not one sentence covers what to remove from the duplicate. This post is that part.

Are you duplicating or cloning? The answer changes everything

These get used interchangeably and they are different jobs with different endings.

A clone is a throwaway copy of production, made to answer a question, then deleted. Its risk is that it stays wired to the original while it lives, which we covered separately in cloning a WordPress site without the copy touching production.

A duplicate is a new site that keeps running. Its risk is the opposite: nothing about it stays connected to the original, so nothing ever prompts you to clean it up. It just quietly carries the source site's contents into production under a different domain.

If the copy will still exist in six months, you are duplicating, and the checklist below is the job.

What comes across that a search-replace will never touch

Duplicating is files plus database, and a serialization-aware search-replace rewrites the hostname. Everything below survives that untouched, because none of it is a URL.

User accounts. Every account on the source site is in the duplicate, including the previous client's staff, the contractor who left, and whatever admin account the original host created. They all have working passwords.

Application passwords. This is the one people miss. Application passwords are per-user credentials for the REST API, generated from a user's profile screen, and they are rows in the database like anything else. The duplicate inherits them, they are still valid, and they authenticate against the new domain. WP-CLI can list them per user: wp user application-password list <user>, with a matching delete subcommand.

Anything in wp_options that is not a URL. SMTP credentials, analytics property IDs, mailing list API keys, reCAPTCHA site keys, license keys. All opaque strings that a URL rewrite has no reason to look at.

Content you did not mean to ship. Draft posts, private posts, trashed posts, media attachments no longer referenced by any page, form submissions stored by the contact form plugin. The new client's site now holds the old client's leads.

Scheduled events, which come across in full and start firing on the first pageview. That is covered in detail in the clone post above, so the short version here: run wp cron event list on the duplicate before anyone visits it.

Why the users table needs a different treatment here

We have written about scrubbing a copy of a live site for testing, and the instinct from that job is to anonymize. That is the wrong instinct for a duplicate.

Anonymized users are still users. On a test copy that is fine, because nobody logs into it. On a site that will run for years, a fake account with a working password hash is an account someone can eventually get into.

For a duplicate, delete rather than scramble. wp user list --field=ID gives you the set, and wp user delete <id> --reassign=<your-id> removes an account without orphaning its posts.

Keep exactly the accounts the new site needs, create them fresh, and treat "I will tidy the users later" as the thing that never happens.

Licenses count domains, not sites

Commercial plugins and themes are almost always licensed per domain or per activation, and the activation record lives on the vendor's server, not in your database.

The duplicate arrives holding a license key that is registered to the source site's domain. Two outcomes follow, and which one you get depends on the vendor.

Either the key silently fails to validate on the new domain, so the plugin stops receiving updates and nobody notices until a security release goes out. Or the duplicate's first update check re-activates the key against the new domain, and the original site loses its activation.

The second is worse and it is not rare. Deactivate licenses on the duplicate before its first update check, and buy the new site its own keys.

The cleanup pass, as commands

Run these on the duplicate, before it has a domain anyone knows and before anyone logs in.

# What is actually installed and active
wp plugin list --status=active
wp theme list

# Accounts that came across
wp user list --fields=ID,user_login,user_email,roles

# Credentials that still work
wp user application-password list <user-id>

# Content you may not have meant to copy
wp post list --post_status=draft,private,trash --format=count
wp comment list --format=count

# Scheduled work, before the first pageview
wp cron event list

The point of running these as a list is that each one produces a number you either expected or did not. The ones that surprise you are the cleanup.

Then the removals: delete the users you are not keeping, delete the application passwords, rotate every key in wp_options rather than reusing it, and empty the trash with wp post delete $(wp post list --post_status=trash --format=ids) if the count was not zero.

The shape that avoids most of this

The reason duplicating a client site is painful is that you are starting from a finished site and subtracting. Everything above is subtraction, and subtraction is where things get missed.

The alternative is to build the starting point deliberately once: a site with your standard plugin set, your base theme, your page structure and no client data in it at all, saved as the thing you copy from. Then every new project is addition, and there is nothing to strip.

SandyWP's Templates do this directly. Save a sandbox once, with its WordPress and PHP versions, plugins, theme, files and database, and every launch from it is a fresh copy of that snapshot rather than a copy of somebody's live site. The template is the starter, so the previous client never enters the picture.

If you want to get a real site into a disposable environment first, to see what is inside it before deciding what the starter should contain, the Cloner plugin copies a live install into a sandbox and only reads the original. Every sandbox has SSH and WP-CLI, so the command list above runs on it directly.

For the agency version of this workflow, one starter per client type rather than a chain of copies, we wrote up the pattern here.

Where a duplicate is genuinely the right answer

Be fair to the method: sometimes the finished site is the starting point, and rebuilding it would be silly.

Moving a site to a new host or a new domain is a duplicate and nothing else. Handing a project over to another agency is a duplicate. Spinning up a second regional site that should genuinely start with the same content is a duplicate.

In those cases the copy is meant to inherit everything, and the only items on the list above that still apply are the credentials: rotate the keys, delete the application passwords, sort out the licenses.

What none of this covers

Two honest limits.

A duplicate is not a staging site. It has no path back to the original, so anything you fix on it has to be redone on the source by hand, and that divergence starts on day one.

And an environment does not check your work here. It can stop the copy from sending mail to real people, which is what Email Log does by intercepting wp_mail(), and it can keep the copy off production's server. It cannot tell you that the API key in wp_options belongs to a client who stopped paying you in March. That part is still a person reading a list.

The short version

Duplicating a WordPress site is easy and well documented. Deciding what the duplicate should not contain is neither, and it is the entire difference between a fast start and a slow leak.

Before the duplicate goes anywhere: delete the accounts, delete the application passwords, rotate every credential in wp_options, deactivate the licenses, clear the schedule, and empty the drafts and trash.

Then, if you are going to do this more than once, stop duplicating client sites and build a starter you copy from instead.