Moving a WordPress site from a staging environment to production is one of the most critical operations in web development. Done carelessly, it can result in a broken website, lost data, or significant SEO damage. Done correctly, it's a smooth, low-risk process that keeps your live site stable while you iterate. This guide walks through every step — from pre-deployment preparation through post-launch verification — so you can deploy with confidence.
Understanding Staging vs. Production Environments
Before diving into the steps, it's worth clarifying what each environment is and why both matter.
Staging is a private, isolated copy of your website used for testing. It mirrors your production setup as closely as possible — same PHP version, same server configuration, same plugins and theme. The goal is to safely test new features, plugin updates, or redesigns without affecting real visitors. Mistakes in staging cost nothing; mistakes in production can cost traffic, revenue, and customer trust.
Production is your live website — the one indexed by Google, visited by users, and generating business. Every change deployed here is immediately visible. That's why every change must be tested in staging first.
One critical thing to understand about WordPress: it stores the site URL in its database. The siteurl and home values in wp_options, plus all the internal links embedded in your content, will still reference the staging domain after you copy the database. Updating these URLs correctly is the most important step in any WordPress migration.
Pre-Deployment Checklist — What to Do Before You Start
Skipping preparation is the most common reason migrations fail. Before touching anything on production, complete this checklist:
- Back up existing production data — even if production is empty, establish the habit of backing up before every deploy
- Verify everything works on staging — all forms submit, no PHP errors in the error log, images load, mobile layout is correct
- Confirm plugin compatibility — every plugin is compatible with the PHP version on your production host
- Note your production credentials — database name, database user, password, host, FTP/SSH access details
- Check the production PHP version — it should match staging. If not, update one before migrating
- Clear staging caches — export clean data, not cached junk from caching plugins
Step 1 — Export Everything from Staging
The migration begins with exporting two things from staging: the database and all site files.
Exporting the Database
Use mysqldump via SSH for the most reliable export — no file size limits, no timeouts:
# Recommended: mysqldump via SSH mysqldump -u STAGING_DB_USER -p STAGING_DB_NAME \ --single-transaction \ --routines \ --triggers \ > staging_export_$(date +%Y%m%d).sql # Verify the export completed (should be > 0 bytes) ls -lh staging_export_*.sql
The --single-transaction flag is essential for InnoDB tables — it exports a consistent snapshot without locking tables that could slow down your staging site during export. Alternatively, you can export via phpMyAdmin in DirectAdmin if SSH access is unavailable, though very large databases (over 512 MB) may time out.
Exporting Site Files
The fastest approach is to compress the files on the server and download the archive:
# Compress via SSH — much faster than FTP file-by-file cd /home/staging_user/public_html/ tar -czf ~/staging_files.tar.gz \ --exclude='.git' \ --exclude='*.log' \ --exclude='wp-content/cache' \ --exclude='wp-content/upgrade' \ . # Download the archive to your local machine scp [email protected]:~/staging_files.tar.gz ./
Step 2 — Upload Files and Import the Database to Production
With the export in hand, the next step is getting everything onto your production server.
Uploading Files to Production
# Upload archive and extract on the production server scp staging_files.tar.gz [email protected]:/home/prod_user/ ssh [email protected] cd /home/prod_user/public_html/ tar -xzf ~/staging_files.tar.gz rm ~/staging_files.tar.gz # clean up the temp file
Importing the Database into Production
# Create the production database first (via DirectAdmin if needed) # DirectAdmin: Databases > Create Database # Import via SSH mysql -u PROD_DB_USER -p PROD_DB_NAME < staging_export.sql # Verify the import mysql -u PROD_DB_USER -p -e "SHOW TABLES FROM PROD_DB_NAME;" | wc -l
Step 3 — Update wp-config.php for Production
The wp-config.php you copied from staging contains staging database credentials. You must update every database setting to match production before WordPress will connect to the right database.
// Update these four values for production
define('DB_NAME', 'production_database_name');
define('DB_USER', 'production_db_user');
define('DB_PASSWORD', 'production_db_password');
define('DB_HOST', 'localhost'); // usually localhost on shared hosting
// Table prefix — must match what's in the exported database
$table_prefix = 'wp_';
// Generate fresh security keys for production
// Visit: https://api.wordpress.org/secret-key/1.1/salt/
define('AUTH_KEY', 'paste-fresh-value-here');
define('SECURE_AUTH_KEY', 'paste-fresh-value-here');
define('LOGGED_IN_KEY', 'paste-fresh-value-here');
define('NONCE_KEY', 'paste-fresh-value-here');
define('AUTH_SALT', 'paste-fresh-value-here');
define('SECURE_AUTH_SALT', 'paste-fresh-value-here');
define('LOGGED_IN_SALT', 'paste-fresh-value-here');
define('NONCE_SALT', 'paste-fresh-value-here');
Pro Tip: Always generate new security keys when deploying to production. The staging keys may have been shared with multiple developers or stored in version control. Regenerating them invalidates all existing sessions, forcing everyone to log in again — a small inconvenience that significantly reduces the attack surface of your production site.
Step 4 — Search and Replace URLs in the Database
This is the most technically delicate step in the entire migration. WordPress stores URLs in multiple database tables — not just in plain text, but also inside PHP serialized data structures. A naive find-and-replace will corrupt serialized strings (which encode string lengths), causing WordPress to malfunction. Always use a serialization-aware tool.
Method 1 — WP-CLI (Strongly Recommended)
# Check current URLs first wp option get siteurl wp option get home # Dry run to preview changes before committing wp search-replace 'https://staging.example.com' 'https://example.com' \ --all-tables \ --dry-run # Run the actual replacement wp search-replace 'https://staging.example.com' 'https://example.com' \ --all-tables # Flush everything after replacement wp cache flush wp rewrite flush
Method 2 — Direct SQL for wp_options Only (Minimal Fix)
-- Update the two critical URL options
UPDATE wp_options
SET option_value = 'https://example.com'
WHERE option_name = 'siteurl';
UPDATE wp_options
SET option_value = 'https://example.com'
WHERE option_name = 'home';
-- Verify
SELECT option_name, option_value
FROM wp_options
WHERE option_name IN ('siteurl', 'home');
If you use Method 2, you still need to handle serialized data in wp_posts and wp_postmeta. Install the "Better Search Replace" plugin from WordPress.org, run it with "Replace guids" checked, then remove the plugin once done.
| Method | Handles Serialized Data | Requires SSH | Best For |
|---|---|---|---|
| WP-CLI | Yes — natively | Yes | All migrations (recommended) |
| Better Search Replace plugin | Yes — built in | No | No SSH access |
| Direct SQL UPDATE | No — breaks serialization | No | wp_options siteurl/home only |
Step 5 — Configure DNS and Install SSL
With files uploaded and the database corrected, point your domain to the production server and secure it with SSL. DNS changes can take anywhere from a few minutes to 48 hours to propagate globally, depending on your registrar's TTL settings and how recently the record was last updated.
To install SSL on DirectAdmin hosting, navigate to SSL Certificates in your DirectAdmin control panel. You can install a free Let's Encrypt certificate with one click, or upload a paid certificate if you need OV/EV validation. After the certificate is installed, add the following to your .htaccess to force HTTPS for all visitors:
# Force HTTPS — add before the WordPress block
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
Step 6 — Set File Permissions and Flush All Caches
Incorrect file permissions are a common source of post-migration errors. WordPress needs specific permission levels to function correctly — directories need to be executable, files need to be readable, and the uploads directory needs to be writable by the web server process.
# Standard WordPress permissions
find /home/username/public_html/ -type d -exec chmod 755 {} \;
find /home/username/public_html/ -type f -exec chmod 644 {} \;
# wp-config.php should be more restrictive
chmod 640 /home/username/public_html/wp-config.php
# uploads directory must be writable
chmod 755 /home/username/public_html/wp-content/uploads/
After setting permissions, flush every cache layer:
# Flush WordPress object cache and rewrite rules wp cache flush wp rewrite flush # If using a caching plugin wp rocket clean --post=all # WP Rocket wp w3-total-cache flush all # W3 Total Cache # Flush PHP OPcache if you have SSH access php -r "opcache_reset();"
Step 7 — Post-Deployment Verification
Never declare a deployment done until you've verified the site works correctly end-to-end. Work through this checklist systematically:
- Homepage loads without errors — no PHP warnings, images display correctly
- Browser console is clean — no 404s for JS, CSS, or image assets
- All forms work — contact forms, comments, user registration all submit successfully
- Admin login works — both front-end and wp-admin are accessible
- Permalinks resolve correctly — no 404 errors on internal pages
- No mixed content warnings — all resources load over HTTPS
- Mobile layout is correct — test at 375px, 768px, and 1200px viewport widths
- Analytics tracking fires — open browser dev tools and confirm GA4 or other tracking events send
- robots.txt allows crawling — confirm staging's "Disallow: /" has been removed
- Submit sitemap to Google Search Console — accelerate re-indexing after migration
Critical: Verify robots.txt Indexing Setting
WordPress has a built-in "Discourage search engines from indexing this site" option under Settings > Reading, which is commonly enabled on staging sites to prevent accidental indexing. Leaving it enabled on production will cause your site to disappear from Google within days.
# Check the setting via WP-CLI wp option get blog_public # Should return: 1 (search engines allowed) # If it returns 0, fix it: wp option update blog_public 1
Troubleshooting Common Post-Migration Issues
Even with a careful migration, a few issues come up frequently. Here's how to diagnose and fix them quickly.
White Screen of Death (WSOD)
Usually caused by a PHP memory limit that's too low, or a plugin conflict. Enable debug logging to identify the cause:
// Add to wp-config.php temporarily
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('WP_MEMORY_LIMIT', '256M');
// Then check the log
tail -f /home/username/public_html/wp-content/debug.log
All Pages Return 404 Except Home
This is always a Permalink Rules issue. Go to Admin > Settings > Permalinks and click Save Changes without changing anything. WordPress regenerates the .htaccess rewrite rules automatically.
Images Not Displaying
The URL replacement may not have fully covered all serialized attachment metadata in wp_postmeta. Run the WP-CLI search-replace command again, or check for remaining staging URLs in the database:
SELECT meta_value FROM wp_postmeta WHERE meta_value LIKE '%staging.example.com%' LIMIT 20;
Frequently Asked Questions
Should I back up data before moving from staging to production?
You should always back up data before every migration — both the database using mysqldump and all web files (wp-content, wp-config.php). Store the backups in at least two locations, such as locally and in cloud storage, so you can roll back quickly if something goes wrong after the deploy.
Why do WordPress URLs need to be changed when moving from staging to production?
WordPress stores the site URL in the database — in the wp_options table under siteurl and home — as well as in post content that contains internal links. If URLs are not updated, all links will still point to the staging domain, causing broken resources on production, broken layouts, and SEO damage because canonical tags will point to the wrong domain.
What is the safest method to search and replace URLs in the WordPress database?
Use WP-CLI with the command wp search-replace 'https://staging.example.com' 'https://example.com' --all-tables, or use the Better Search Replace plugin (remove it after use). The safest approach is always to run --dry-run first to see how many rows will be affected before committing to the actual replacement.
After moving WordPress to production, pages return 404 errors — how do I fix this?
404 errors after migration are almost always caused by stale permalink rules carried over from staging. Go to WordPress Admin, navigate to Settings > Permalinks, and click Save Changes without modifying any values. WordPress will automatically flush its rewrite rules and regenerate the .htaccess file.
Full-Featured DirectAdmin Hosting by AsiaGB
AsiaGB Hosting supports DirectAdmin with full PHP 8.3 and MySQL — starting at just 500 THB per year
View Hosting Plans