Skip to main content
Blog|
How-to guides

How to migrate WordPress to another hosting provider

|
Mar 15, 2026|19 min read
HOW-TO GUIDESHow to migrate WordPress toanother hosting providerHOSTNEYhostney.comMarch 15, 2026

Moving a WordPress site from one host to another is one of those tasks that sounds simple but has enough moving parts to go wrong in subtle ways. A WordPress site is not a single thing you can copy – it is a PHP application, a MySQL database, a collection of uploaded files, and a set of configuration values that tie everything together. All of these need to move, and they need to be reconfigured to work at the destination.

This guide covers the full manual migration process step by step, explains where things commonly break, and discusses how modern migration tools eliminate most of the manual work.

What a WordPress site consists of

Before migrating, it helps to understand what you are actually moving. A WordPress installation has four components that need to travel together:

WordPress core files. The PHP files that make up WordPress itself – wp-admin/ , wp-includes/ , and the root files like wp-login.php , wp-cron.php , and index.php . These are identical across all WordPress installations of the same version, so they can be reinstalled fresh at the destination rather than copied. But if you have made any modifications to core files (which you should not have, but some developers do), those changes would be lost with a fresh install.

wp-content directory. This is where your site’s unique content lives:

  • wp-content/themes/  – your active theme and any installed themes
  • wp-content/plugins/  – all installed plugins, active or not
  • wp-content/uploads/  – every image, PDF, video, and file you have ever uploaded through the media library, organized in year/month subdirectories
  • wp-content/mu-plugins/  – must-use plugins, if any
  • Custom directories created by plugins (like cache directories, backup storage, etc.)

The database. WordPress stores all your content in a MySQL (or MariaDB) database – posts, pages, comments, user accounts, plugin settings, theme customizer values, widget configurations, menu structures, and site options. The database also stores your site URL in multiple places, which is why a simple database copy without URL replacement does not work.

wp-config.php. The configuration file that connects everything. It contains database credentials, authentication keys and salts, the table prefix, debug settings, and any custom constants your setup requires. This file needs to be recreated or modified for the destination environment because the database credentials will be different.

Pre-migration checklist

Skipping preparation is the most common cause of failed migrations. Work through this list before touching any files.

Document your current setup

Record these details from your current host:

  • PHP version. Check via your hosting control panel or create a  phpinfo()  file. Your new host needs to support the same PHP version (or newer, if your plugins are compatible). Switching from PHP 8.1 to 7.4 during migration will break things.
  • MySQL/MariaDB version. Some plugins use features specific to MySQL 5.7+ or 8.0+. Note the version.
  • WordPress version. Run  wp core version  via WP-CLI if available, or check the  wp-includes/version.php  file.
  • Active plugins. Note every active plugin and its version. Some plugins store data outside the standard WordPress tables and may need special handling.
  • Custom configurations. Check  wp-config.php  for any custom constants beyond the defaults – custom upload paths, custom content directories, multisite configuration, or plugin-specific constants.

Create a full backup

Before making any changes, create a complete backup of both files and database on the source host. This is your safety net if anything goes wrong.

# Backup all WordPress files
cd /path/to/wordpress
tar -czf ~/wordpress-backup.tar.gz .

# Backup the database
wp db export ~/database-backup.sql

If you do not have WP-CLI, use mysqldump :

mysqldump -u db_username -p db_name > ~/database-backup.sql

Check disk space at the destination

Verify that your new hosting account has enough storage for your files and database. Check your current usage:

# Total file size
du -sh /path/to/wordpress/

# Database size
wp db size --tables

The destination needs at least this much space, plus headroom for the database import process (which temporarily requires space for both the SQL file and the imported data).

Step 1: Export the database

The database export is the most critical step because it contains all your content.

Using WP-CLI

WP-CLI is the cleanest way to export:

wp db export wordpress-db.sql --add-drop-table

The --add-drop-table flag adds DROP TABLE IF EXISTS statements before each CREATE TABLE , which ensures a clean import at the destination if there are any existing tables.

Using mysqldump

If WP-CLI is not available:

mysqldump -u db_username -p \
  --single-transaction \
  --routines \
  --triggers \
  --add-drop-table \
  db_name > wordpress-db.sql

--single-transaction ensures a consistent snapshot without locking the database, so your live site continues working during the export. This is important if your site has traffic during the migration.

Using phpMyAdmin

If you only have cPanel access without SSH:

  1. Open phpMyAdmin from cPanel
  2. Select your WordPress database
  3. Click Export → Custom (not Quick)
  4. Check “Add DROP TABLE” under Object creation options
  5. Set compression to gzip for large databases
  6. Click Go

Large database considerations

Databases over 500 MB need extra attention:

  • Export compression. Pipe the dump through gzip to reduce transfer time: mysqldump -u db_username -p db_name | gzip > wordpress-db.sql.gz
  • Timeout issues. Large exports can hit PHP timeout limits in phpMyAdmin. Always prefer command-line tools for large databases.
  • Binary data. If your database contains serialized PHP data with binary content (some plugins store this), ensure the export uses  --hex-blob  to preserve binary data correctly.

Step 2: Transfer files

You need to move the entire WordPress directory to the new host. The method depends on what access you have.

Using SSH (rsync)

If you have SSH access to both the source and destination, rsync is the best tool. It compresses data during transfer, handles interruptions gracefully (you can resume), and preserves file permissions:

# From the source server, push to destination
rsync -avz --progress \
  /path/to/wordpress/ \
  user@new-host:/path/to/destination/

# Or from the destination, pull from source
rsync -avz --progress \
  user@old-host:/path/to/wordpress/ \
  /path/to/destination/

The trailing slash on the source path matters – it means “copy the contents of this directory,” not “copy the directory itself.”

Using SSH (SCP)

If rsync is not available but SSH is:

# Create a compressed archive first
tar -czf wordpress-files.tar.gz -C /path/to/wordpress .

# Transfer the archive
scp wordpress-files.tar.gz user@new-host:/path/to/destination/

# Extract at the destination
ssh user@new-host "cd /path/to/destination && tar -xzf wordpress-files.tar.gz"

Using SFTP

If you only have SFTP access (common on shared hosting), use an SFTP client like FileZilla:

  1. Connect to the source host, navigate to the WordPress directory
  2. Download everything to your local machine
  3. Connect to the destination host
  4. Upload everything to the new document root

This is the slowest method because everything passes through your local machine. For sites with thousands of media files, this can take hours. If possible, compress the files on the source server first, download the single archive, upload it to the destination, and extract it there.

What to exclude

Some files and directories should not be transferred:

  • Cache directories ( wp-content/cache/ wp-content/w3tc-config/ , etc.) – these will be regenerated
  • Backup archives stored by plugins (UpdraftPlus, All-in-One WP Migration backups can be gigabytes)
  • Debug logs ( wp-content/debug.log )
  • Server-specific files ( .htaccess  may need rewriting for the new server,  php.ini  or  .user.ini  may have different requirements)

Do transfer wp-content/uploads/ in its entirety. Missing media files are the most visible migration failure.

Step 3: Create the database at the destination

On your new host, create an empty MySQL database and a user with full privileges:

CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;

Use utf8mb4 – it is the correct character set for WordPress since version 4.2. Using utf8 (which is actually utf8mb3 in MySQL) will cause issues with emoji and some non-Latin characters.

Most hosting control panels provide a MySQL database wizard that handles this. Use it if available – it ensures the user and database are created with the correct privileges for your hosting environment.

Step 4: Import the database

Using WP-CLI

wp db import wordpress-db.sql

Using the mysql command

mysql -u wp_user -p wordpress_db < wordpress-db.sql

If the SQL file is gzipped:

gunzip < wordpress-db.sql.gz | mysql -u wp_user -p wordpress_db

Using phpMyAdmin

  1. Open phpMyAdmin, select the new database
  2. Click Import
  3. Choose the SQL file (or gzipped SQL)
  4. Click Go

phpMyAdmin has upload size limits (usually 50 MB or 128 MB depending on the server’s PHP configuration). For larger databases, use the command line or ask your host to increase the limit.

Import failures

Common import failures and their causes:

  • “MySQL server has gone away” during import – the SQL file contains a statement larger than  max_allowed_packet . Ask your host to increase this value, or add  SET GLOBAL max_allowed_packet=268435456;  before importing. For a detailed walkthrough of this error, see MySQL server has gone away: what it means and how to fix it.
  • Character set errors – the source database used a character set that the destination MySQL does not support. This is rare with modern MySQL versions but can happen when migrating from very old servers.
  • Foreign key constraint failures – some plugins create tables with foreign key relationships. The import order may violate these constraints. Wrapping the import with  SET FOREIGN_KEY_CHECKS=0;  at the beginning and  SET FOREIGN_KEY_CHECKS=1;  at the end resolves this.

Step 5: Update wp-config.php

The wp-config.php file needs to reflect the new hosting environment. The critical values to update:

// Database credentials - must match what you created in step 3
define('DB_NAME', 'wordpress_db');
define('DB_USER', 'wp_user');
define('DB_PASSWORD', 'strong_password_here');
define('DB_HOST', 'localhost');

// Table prefix - must match the source database
$table_prefix = 'wp_';

Keep the authentication keys and salts from your original wp-config.php . These are used to validate cookies and session tokens. If you change them, all logged-in users will be logged out (which may be acceptable for a migration, but it is worth knowing).

If the new host uses a different MySQL host than localhost (some hosts use a separate database server with a hostname like db.hostingprovider.com ), update DB_HOST accordingly.

Step 6: Search and replace URLs

This is the step that most manual migrations get wrong. WordPress stores the full site URL in the database – not just in the siteurl and home options, but throughout post content, widget settings, theme customizer values, and serialized plugin data.

A naive UPDATE wp_options SET option_value = 'https://new-domain.com' WHERE option_name = 'siteurl'; changes the site URL in the options table, but leaves hundreds or thousands of references to the old URL scattered throughout the database.

The serialized data problem

WordPress plugins store settings as serialized PHP arrays in the database. A serialized string looks like this:

s:49:"https://old-domain.com/wp-content/uploads/logo.png";

The s:49 means “string of 49 characters.” If you do a SQL REPLACE to change the domain name, the string length changes, but the length prefix does not. The serialized data becomes corrupt, and WordPress cannot unserialize it. The plugin’s settings are lost.

This is why you must use a serialization-aware search and replace tool.

Using WP-CLI

WP-CLI’s search-replace command handles serialized data correctly:

wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables

The --all-tables flag includes non-standard tables created by plugins. Without it, only the core WordPress tables are processed.

Run it with --dry-run first to see what would change:

wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables --dry-run

Common replacements needed

You may need multiple search-replace passes:

# HTTPS URL
wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables

# HTTP URL (if old site was HTTP or mixed)
wp search-replace 'http://old-domain.com' 'https://new-domain.com' --all-tables

# Without protocol (some plugins store paths without protocol)
wp search-replace '//old-domain.com' '//new-domain.com' --all-tables

# If the file path changed
wp search-replace '/home/olduser/public_html' '/home/newuser/public_html' --all-tables

Using a PHP script

If WP-CLI is not available, the Search Replace DB script by Interconnect IT handles serialized data. Upload it to your WordPress root, run it from the browser, then delete it immediately – it provides unauthenticated database access.

Step 7: Set file permissions

After transferring files, ensure the permissions are correct for the new server:

# Directories should be 755
find /path/to/wordpress -type d -exec chmod 755 {} \;

# Files should be 644
find /path/to/wordpress -type f -exec chmod 644 {} \;

# wp-config.php should be more restrictive
chmod 640 wp-config.php

The file owner should be the user that PHP runs as on the new host. On shared hosting, this is usually your account user. On a VPS, it may be www-data (Debian/Ubuntu) or nginx / apache (RHEL/CentOS). If the ownership is wrong, WordPress will not be able to write to the uploads directory or update plugins. See The uploaded file could not be moved to wp-content/uploads for a detailed explanation of how file permissions affect WordPress uploads.

Step 8: Test before switching DNS

This is the step that separates clean migrations from stressful ones. Before pointing your domain to the new host, verify that the site works on the new server.

Using a hosts file override

Edit your local machine’s hosts file to temporarily point your domain to the new server’s IP address:

On macOS/Linux: /etc/hosts On Windows: C:\Windows\System32\drivers\etc\hosts

Add a line:

203.0.113.50  yourdomain.com  www.yourdomain.com

Replace 203.0.113.50 with your new server’s IP address. Now when you visit yourdomain.com in your browser, your machine sends the request to the new server instead of the old one. Everyone else still sees the old server.

What to test

  • Homepage loads without errors or missing styles
  • Internal pages and posts display correctly with images loading
  • Admin dashboard ( /wp-admin/ ) loads and you can log in
  • Media library shows images and you can upload a new test file
  • Forms submit correctly (contact forms, WooCommerce checkout if applicable)
  • SSL certificate is valid and HTTPS works
  • Permalinks work (visit a post directly by its URL, not just through navigation)

If permalinks return 404 errors, the server’s rewrite rules need configuration. On Nginx, ensure the try_files $uri $uri/ /index.php?$args; directive is in the server block. On Apache, ensure mod_rewrite is enabled and .htaccess is being read (check AllowOverride All ).

Fixing the “white screen”

If the site shows a blank white page, enable debugging temporarily:

// In wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);

Check wp-content/debug.log for the specific PHP error. Common causes after migration:

  • Wrong PHP version – a plugin requires PHP 8.0+ but the new host runs 7.4
  • Missing PHP extensions – the site needs an extension (like  imagick intl , or  sodium ) that is not installed
  • Database connection error – wrong credentials in  wp-config.php

Step 9: Switch DNS

Once the site is verified on the new server, update your domain’s DNS records:

  1. Log into your domain registrar (or DNS provider)
  2. Update the A record to point to the new server’s IP address
  3. If using  www , update the CNAME or A record for  www  as well
  4. If you have MX records (email), leave them unchanged unless you are also moving email

DNS propagation

DNS changes do not take effect instantly. Different DNS resolvers around the world cache records for the duration of the TTL (Time to Live). If your TTL was set to 86400 (24 hours), some visitors will see the old server for up to 24 hours after the change.

Preparation trick: Lower your DNS TTL to 300 (5 minutes) a day or two before the migration. This means that when you make the actual switch, most DNS caches expire within 5 minutes. After propagation is complete, raise the TTL back to a normal value (3600 or higher).

During propagation

During DNS propagation, some visitors hit the old server and some hit the new one. This is unavoidable. To prevent data loss:

  • Disable comments and registrations on the old site during propagation, so no content is created on the old server that would be lost
  • Put the old site in maintenance mode once you are confident the new site is working
  • If running WooCommerce, consider a brief maintenance window during propagation to avoid orders hitting the old database

Step 10: Post-migration cleanup

After DNS has fully propagated and the new site is confirmed working:

  • Remove the hosts file entry from your local machine
  • Regenerate permalinks – go to Settings → Permalinks and click Save (even without changing anything) to regenerate rewrite rules
  • Clear all caches – page cache, object cache, browser cache, CDN cache
  • Test email delivery – WordPress sends emails for password resets, comment notifications, and WooCommerce order confirmations. Verify these work on the new host
  • Verify cron jobs –  wp-cron.php  handles scheduled tasks (scheduled posts, plugin updates). Verify it is running
  • Check SSL certificate – ensure HTTPS works and the certificate covers your domain
  • Delete the old site after a comfortable period (a week is reasonable) once you are certain everything works

Migration plugins vs manual migration

The manual process described above works and gives you full control. It is also time-consuming, error-prone, and requires SSH access and command-line comfort. For sites with large databases, complex plugin configurations, or WooCommerce with thousands of products, manual migration has significant risk of missing something.

Migration plugins automate most of these steps. The two main approaches are:

Push-based plugins

Plugins like All-in-One WP Migration, Duplicator, and UpdraftPlus package the site (files + database) into an archive on the source server, download it, and re-import it at the destination.

The limitation is that the entire process runs through PHP in your browser. Large sites hit PHP memory limits, execution timeouts, and upload size limits. If your browser tab closes or your connection drops during transfer, you start over. Sites over 1-2 GB often require the premium version of these plugins to handle the size, and even then, the browser-dependent transfer can be unreliable.

Pull-based migration

A more robust approach is pull-based: the destination server connects to the source and pulls data directly, server-to-server, without your browser in the middle.

This eliminates browser dependency entirely. The transfer runs between servers (often on fast data center connections), can handle interruptions and resume from where it stopped, and is not limited by PHP’s upload size or browser timeouts.

Pull-based migration can transfer the database in row-level batches (rather than one massive SQL dump), which means it works reliably even for databases with hundreds of thousands of rows. Files transfer in small chunks, so a single large file or a dropped connection does not require restarting the entire migration.

How migration works on Hostney

Hostney provides a pull-based migration system through a WordPress plugin that you install on your source site. The architecture is designed to handle the specific problems that make WordPress migrations unreliable.

The migration plugin

The Hostney Migration plugin is a WordPress plugin you install on the site you are migrating from. It exposes a secure REST API that Hostney’s infrastructure uses to pull your site data directly.

The process:

  1. Generate a migration token in the Hostney control panel for the destination site
  2. Install the plugin on your source WordPress site (download the latest release from GitHub)
  3. Paste the token in the plugin’s settings page (under Tools → Hostney Migration)
  4. Click connect – the plugin registers with Hostney, sending site metadata (WordPress version, PHP version, database size, file count) so the system can plan the migration
  5. Start the migration from the Hostney control panel

From this point, the migration runs server-to-server without your involvement. You can close the browser tab.

What happens during the migration

Database transfer. The plugin exposes database tables through its REST API. Hostney’s worker pulls rows in batches using primary key pagination – not a single massive dump. This means the migration works reliably regardless of database size, and if the connection drops mid-table, it resumes from the last row transferred rather than starting over.

File transfer. Files transfer in 2 MB chunks. The system scans the source filesystem, skips directories that do not need to migrate (cache directories, backup archives, debug logs, node_modules), and transfers everything else. A single file failure does not stop the migration – it is logged and the process continues.

URL replacement. After the database and files are at the destination, the system performs a serialization-aware search and replace to update all URLs from the source domain to the destination domain. This handles the serialized data problem described earlier automatically.

wp-config.php generation. A fresh configuration file is generated with the correct database credentials for the destination environment, while preserving your authentication keys and salts from the original.

Security of the migration

The migration token is a 96-character cryptographic token that expires after 24 hours. Every request from the worker to the plugin is signed with HMAC-SHA256 – the plugin verifies that each request genuinely comes from Hostney’s infrastructure and has not been tampered with. The plugin requires HTTPS, and it validates that connection requests are not coming from private IP ranges (preventing SSRF attacks).

Once the migration completes (or the token expires), the REST API endpoints stop accepting requests. You should uninstall the migration plugin from the source site after migration.

Migration without the plugin

If you cannot install plugins on your source host (some managed hosts restrict plugin installation), you can migrate via SSH. Provide your source server’s SSH credentials in the Hostney control panel, and the system connects directly to pull the database and files. The same chunked transfer, URL replacement, and configuration generation apply.

What the migration handles automatically

  • Database export and import with foreign key handling
  • Serialization-aware URL replacement across all tables
  • File permissions set correctly for the container environment
  • wp-config.php generated with correct database credentials
  • Binary data (BLOBs) encoded correctly during transfer
  • Large databases handled through paginated row batches
  • WAF and security plugin interference bypassed transparently
  • Real-time progress tracking with detailed logs

What you still need to do

  • DNS switch. Point your domain to Hostney after verifying the migration (the control panel shows the destination IP)
  • SSL certificate. SSL is provisioned automatically, but if you have a custom certificate, configure it through the control panel
  • Email configuration. If your site sends email, verify delivery works on the new host. WordPress email configuration is independent of hosting
  • Test the site. Use the hosts file trick described earlier to verify everything before switching DNS
  • Uninstall the plugin. Remove the Hostney Migration plugin from the source site after migration

Common migration problems and how to avoid them

Mixed content warnings after migration

If your old site used HTTP and the new site uses HTTPS (or vice versa), images and resources loaded over the wrong protocol trigger mixed content warnings. The search-replace step should catch most of these, but hardcoded URLs in theme files, custom CSS, or JavaScript files will not be in the database and will not be replaced.

Fix: search your theme’s PHP, CSS, and JS files for hardcoded URLs and update them. Use protocol-relative URLs ( //domain.com/path ) or HTTPS URLs.

Missing images

If images appear broken after migration, check:

  • The  wp-content/uploads/  directory transferred completely
  • File permissions allow the web server to read the files
  • The URL replacement updated  wp-content/uploads  paths correctly
  • If the old site used a CDN, image URLs may point to the CDN rather than the origin server

Permalink 404 errors

Permalinks work through URL rewriting. If every page except the homepage returns a 404:

  • Nginx: Ensure  try_files $uri $uri/ /index.php?$args;  is in the server configuration
  • Apache: Ensure  mod_rewrite  is enabled and  .htaccess  is being read
  • Go to Settings → Permalinks and click Save to regenerate rewrite rules

Database connection errors

“Error establishing a database connection” means wp-config.php has incorrect database credentials, the database does not exist, or the MySQL service is not running. Verify the DB_NAME , DB_USER , DB_PASSWORD , and DB_HOST values match what you created at the destination.

Scheduled content and cron

WordPress cron jobs (scheduled posts, plugin scheduled tasks) sometimes break after migration because the cron system uses the site URL to trigger itself. If URLs were not updated correctly, or if the old cron entries reference the old domain, scheduled tasks may fail. Run wp cron event list to check pending cron events after migration.

Summary

WordPress migration involves moving four components – core files, wp-content, the database, and wp-config.php – and ensuring they work together at the destination. The critical steps that most migrations get wrong are the serialization-aware URL replacement (never use raw SQL REPLACE on a WordPress database) and testing before switching DNS.

Manual migration via SSH gives you full control and works for any source and destination. It requires command-line comfort and careful attention to each step. Migration plugins automate the process but vary in reliability – browser-dependent push-based plugins struggle with large sites, while server-to-server pull-based approaches handle the transfer more robustly.

Whatever method you use, the checklist is the same: backup first, transfer files and database, update configuration, replace URLs, test thoroughly, then switch DNS. Rushing the testing step is the most common cause of migration-day stress.