Guides

How to Migrate a WordPress Site: A Practical Step-by-Step Guide

Move a WordPress site from one host to another without downtime or data loss. This guide covers the full process from planning to DNS cutover.

Before You Start: The Migration Checklist

WordPress migrations fail in predictable ways. This checklist prevents most of them:

  • [ ] Current site backed up (files + database)
  • [ ] New hosting account provisioned and tested
  • [ ] WordPress version, PHP version, and required extensions noted
  • [ ] Domain registrar access available for DNS changes
  • [ ] Current DNS TTL checked (lower it 24 hours before migration)
  • [ ] SSH/SFTP access confirmed on new host
  • [ ] New host PHP version matches or exceeds old host

Choose Your Migration Method

There are three main approaches:

MethodBest ForRisk LevelDowntime
Manual (SSH)Experienced users, full controlLowMinimal
Duplicator pluginNon-technical usersLowMinimal
All-in-One WP MigrationQuick moves, small-medium sitesMediumMinimal
Managed migrationComplex sites, enterpriseLowNone

This guide covers the manual SSH method (most reliable) and the Duplicator method (most accessible).

Method 1: Manual Migration (SSH)

This is the most reliable method. You control every step.

Step 1: Backup the Current Site

SSH into your current server:

# Create backup directory
mkdir -p ~/wordpress-backup

# Export database (replace with your credentials) mysqldump -u DB_USER -p DB_NAME > ~/wordpress-backup/database.sql

# Archive the WordPress files tar -czvf ~/wordpress-backup/wordpress-files.tar.gz /path/to/wordpress/

Keep the database backup separate from the file archive.

Step 2: Upload to New Server

# On your local machine, download the backups
scp user@oldserver:~/wordpress-backup/database.sql .
scp user@oldserver:~/wordpress-backup/wordpress-files.tar.gz .

# Upload to new server scp database.sql user@newserver:~/wordpress-backup/ scp wordpress-files.tar.gz user@newserver:~/wordpress-backup/

Or use rsync for efficiency:

rsync -avz --progress ~/wordpress-backup/ user@newserver:~/wordpress-backup/

Step 3: Prepare the New Server

# SSH into new server
ssh user@newserver

# Create the web directory (adjust for your host's structure) mkdir -p /var/www/yoursite.com cd /var/www/yoursite.com

# Extract WordPress files tar -xzvf ~/wordpress-backup/wordpress-files.tar.gz # Move contents up one level if they extracted into a subdirectory mv wordpress/* .

# Set permissions chown -R www-data:www-data /var/www/yoursite.com find /var/www/yoursite.com -type d -exec chmod 755 {} \; find /var/www/yoursite.com -type f -exec chmod 644 {} \;

Step 4: Create the Database

# Login to MySQL
mysql -u root -p

# Create the database CREATE DATABASE wp_yoursite CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

# Create the user CREATE USER 'yoursite_user'@'localhost' IDENTIFIED BY 'STRONG_PASSWORD_HERE';

# Grant permissions GRANT ALL PRIVILEGES ON wp_yoursite.* TO 'yoursite_user'@'localhost';

# Apply and exit FLUSH PRIVILEGES; EXIT;

Step 5: Import the Database

mysql -u yoursite_user -p wp_yoursite < ~/wordpress-backup/database.sql

Step 6: Update WordPress Configuration

Edit wp-config.php:

// Database
define( 'DB_NAME', 'wp_yoursite' );
define( 'DB_USER', 'yoursite_user' );
define( 'DB_PASSWORD', 'STRONG_PASSWORD_HERE' );
define( 'DB_HOST', 'localhost' );

// If moving to HTTPS (add these) define( 'WP_HOME', 'https://yoursite.com' ); define( 'WP_SITEURL', 'https://yoursite.com' );

// Recommended additions for migrations define( 'FS_METHOD', 'direct' );

Step 7: Update URLs in Database

The database contains hardcoded URLs that must be updated:

mysql -u yoursite_user -p wp_yoursite

USE wp_yoursite;

# Update posts and pages (GUID field must NOT be changed for posts) UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://oldserver.com', 'https://yoursite.com');

# Update options UPDATE wp_options SET option_value = REPLACE(option_value, 'http://oldserver.com', 'https://yoursite.com') WHERE option_name IN ('home', 'siteurl');

# Update post meta UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'http://oldserver.com', 'https://yoursite.com');

# Update comments UPDATE wp_comments SET comment_content = REPLACE(comment_content, 'http://oldserver.com', 'https://yoursite.com');

EXIT;

Important: If you're using a plugin that stores full URLs in serialised data (some cache plugins, page builders), you'll need to use WP-CLI for proper serialisation-safe replacements:

# Install WP-CLI if not present
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

# Run search-replace with serialisation support wp search-replace 'http://oldserver.com' 'https://yoursite.com' --allow-root

Step 8: Configure the Web Server

For nginx:

server {
    listen 80;
    server_name yoursite.com www.yoursite.com;
    root /var/www/yoursite.com;
    index index.php;

access_log /var/log/nginx/yoursite-access.log; error_log /var/log/nginx/yoursite-error.log;

client_max_body_size 100M;

location / { try_files $uri $uri/ /index.php?$args; }

location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 30d; add_header Cache-Control "public, immutable"; }

location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } }

Step 9: Test Before DNS Cutover

Add a temporary entry to your local hosts file to test the new server:

# macOS/Linux
sudo nano /etc/hosts

# Add this line (use new server's IP address) 123.45.67.89 yoursite.com www.yoursite.com

Visit yoursite.com in your browser — it will now load from the new server. Test thoroughly:

  • All pages load correctly
  • Images and media display
  • Forms submit correctly
  • Admin login works
  • Permalinks work

Method 2: Duplicator Plugin

For non-technical users, the Duplicator plugin handles most of this automatically.

Step 1: Install Duplicator

On your current site: 1. Install Duplicator by Life Response from the WordPress plugin directory 2. Activate the plugin 3. Go to Duplicator > Packages > Create New

Step 2: Configure and Scan

1. Name your package 2. Click Scan to check for issues 3. If archive is too large, exclude wp-content/uploads/ or cache directories 4. Store the archive and installer.php somewhere safe

Step 3: Upload to New Server

1. Download the archive.zip and installer.php files 2. Upload both to the new server's web directory 3. Visit yoursite.com/installer.php in your browser

Step 4: Run the Duplicator Installer

1. Enter the new database credentials 2. Enter the new site URL 3. Run the installation 4. Duplicator handles the URL replacements automatically

DNS Cutover: The Final Step

Lower TTL in Advance

Do this 24-48 hours before the migration:

# Check current TTL
dig +nocmd +noall +answer yoursite.com

# If TTL is high (e.g., 86400 = 24 hours), lower it # Update your DNS record to TTL: 300 (5 minutes)

Most registrars have DNS settings in their control panel.

Update DNS

Change the A record for yoursite.com (and www) to point to your new server's IP address. Set TTL back to a reasonable value (3600-86400) after the migration is confirmed working.

Monitor After Cutover

  • DNS propagation takes 15 minutes to 48 hours
  • Check whatsmydns.net to see propagation status
  • Monitor your new server's access logs for incoming traffic
  • Keep the old server accessible for 48 hours as a fallback

Troubleshooting Common Problems

"Too Many Redirects" Error

This usually means WP_HOME and WP_SITEURL don't match what's in the database.

// In wp-config.php, force the correct URLs
define( 'WP_HOME', 'https://yoursite.com' );
define( 'WP_SITEURL', 'https://yoursite.com' );

Images Not Loading

URLs in the database weren't updated properly. Run the search-replace again:

wp search-replace 'http://yoursite.com' 'https://yoursite.com' --dry-run --allow-root
# Review the output, then remove --dry-run to execute

Database Connection Error

Wrong credentials in wp-config.php. Double-check:

  • Database name
  • Database username
  • Database password
  • Database host (usually localhost, sometimes a remote hostname)

White Screen of Death

Usually a PHP error. Enable debugging:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Check wp-content/debug.log for the actual error.

Post-Migration Checklist

  • [ ] All pages load correctly
  • [ ] Images and media display
  • [ ] Internal links work (no mixed content warnings)
  • [ ] Admin area accessible
  • [ ] Forms functional
  • [ ] SSL certificate working (green padlock)
  • [ ] CDN cache purged (if using one)
  • [ ] Old host account cancelled or retained for rollback

Bottom Line

The manual SSH method is more reliable than plugins because you control every step and there are no size limits. The investment in learning the process pays off — you'll be able to migrate in 30 minutes once you've done it once.

Start migrations on a Tuesday or Wednesday. DNS propagation is fastest during business hours, and if something goes wrong, you have Thursday/Friday to fix it rather than scrambling on a weekend.

Rather than DIY? Let OpsHelp handle everything.

Managed hosting with support, security, backups, and monitoring — from £50/mo.

Get Managed Hosting →

Need help with your server setup?

OpsHelp provides professional server management, setup, and hardening services.

Get Help from OpsHelp →