Never update plugins and themes directly on production. A staging site is an exact copy of your site for safe experiments.
Method 1: WP Staging plugin (the simplest)
- Install WP Staging from the WordPress repository
- WP Staging → Create new staging site
- Specify a subdirectory (for example,
staging) - Click Start Cloning
- Staging will be available at
yoursite.com/staging
Method 2: Manual clone via cPanel
# 1. Create a subdomain staging.yoursite.com → folder /staging
# 2. Copy the files:
cp -r public_html/* public_html/staging/
# 3. Create a new database (cPanel → MySQL Databases)
# 4. Export the database:
mysqldump -u user -p production_db > staging.sql
# 5. Import into the new database:
mysql -u user -p staging_db < staging.sql
# 6. In wp-config.php change DB_NAME and SITEURL
Deploying changes from staging to production
After testing on staging:
- Plugins/themes: install the same versions on production
- Code: copy the changed files via FTP
- Database: use WP Migrate DB Pro to transfer it
wp db export and wp search-replace to replace URLs in the database.Common errors when working with staging
Most staging problems are not caused by the cloning itself but by forgetting to change a few key values afterward. Below are the specific error texts that show up most often, and what they actually mean.
| Error text or symptom | Cause | What to do |
|---|---|---|
| Error establishing a database connection right after cloning | wp-config.php still has the production DB credentials, while the new database is named differently | Set DB_NAME, DB_USER and DB_PASSWORD for the new staging database |
| Links and images point to the production domain | The database still has absolute URLs from the original site | Run wp search-replace on staging itself, not on production |
| There has been a critical error on this website after cloning | PHP memory_limit was not enough to clone a large database | Temporarily raise memory_limit in php.ini for the duration of the operation |
| 403 Forbidden when opening /staging/ | The staging directory is password-protected via .htaccess, but the credentials were never handed to the developer | Check Basic Auth in .htaccess or temporarily remove the protection for testing |
For the right order of backup steps before cloning, see the article on WordPress backup.
When a staging copy is not enough
- Testing payment gateways — most acquirers reject requests from a staging subdomain without a separate sandbox; test in the payment provider's own sandbox mode instead
- Checking real load and speed — staging usually shares server resources with production, so the numbers will be inaccurate
- Syncing with external webhooks (a Telegram bot, a CRM) — the webhooks keep hitting the production URL, not staging
- A long-lived staging site used as a "second copy" — over time it drifts from production content and becomes its own source of truth that cannot be trusted
- Checking features that depend on the visitor's real IP (geolocation, anti-fraud) — the staging domain and its IP differ from production, so such features behave differently
A separate trap is assuming that if the site opens fine on staging, a plugin update is safe for production. Staging copies the code and the database, but not the real traffic and concurrent requests — some performance problems only show up under live load.
Automating staging with WP-CLI on a VPS
On a VPS it is more convenient to build staging with a WP-CLI script than through a control panel, especially if the process needs to repeat regularly:
wp db export /tmp/prod.sql --path=/var/www/production
scp /tmp/prod.sql user@server:/var/www/staging/
wp db import /tmp/prod.sql --path=/var/www/staging
wp search-replace 'example.com' 'staging.example.com' --path=/var/www/staging --all-tables
For a detailed breakdown of the WP-CLI commands themselves, see the article on WP-CLI commands.
How to make sure staging is actually isolated
- robots.txt on staging contains a Disallow: / line
- The server response includes an X-Robots-Tag: noindex header
- The database credentials differ from production instead of being a plain copy
- Google Search Console shows no indexed pages with the staging domain
- The .htaccess file or web server configuration for the staging subdomain is physically separate from the production config, not a copy of it with one line changed
curl -sI https://staging.example.com/ | grep -i x-robots-tag
curl -s https://staging.example.com/robots.txt