WordPress Migrate to New Server

How to migrate WordPress from one hosting provider to another.

  • Assumes hosting provider provides cPanel and shell access to hosting server

Some takeaways:

  • cPanel is a powerful administrative control panel that hosting providers provide to their customers, providing excellent tools for individuals, businesses, and organizations to administer and maintain an online presence.
  • cPanel is kind of like David to the Goliath of Big (and evil) Tech
  • the regime isn’t happy when you use these tools. They want the Internet to essentially be a big TV network that they have full control over
  • fuck the traitorous system pigs (I just had to say that 😀)
  • wp-cli is an excellent, powerful tool that can help every WordPress administrator
  • So is acme.sh
  1. Set up DNS records with your DNS provider

temp domain, e.g. d1.example.org
real domain, e.g. example.org

  • Create A record pointing to temp domain at new server’s IP address
  • Should be A record pointing to real domain at old server’s IP address
  • Both records must exist in order for the media transfer to complete when importing the site.

When this process is finished, to go *live* with the new site, switch the A record for the real domain to point to the new server (see last step)

  1. If necessary remove existing WP installation of the site via Softalicious WP manager on the new server (through cPanel)

  2. Set up domains in cPanel on new server

need temp domain and real domain, both point to same directory
cPanel > Domains
should look like this:

d1.example.org    /example.org
example.org       /example.org
  1. Create database and database user

cPanel > MySQL Database Wizard > set up new databases

Grant all privileges to the user

Be sure to save the info

  1. Set database character set and collation
  • If this is not set then certain characters and emojis will not display
  • This is done *before* the WordPress tables are created
mysql -u <user> -p'<password>' <database>
ALTER DATABASE <database> CHARACTER SET = 'utf8mb4';
ALTER DATABASE <database> COLLATE = 'utf8mb4_unicode_ci';
  1. SSL
  • need to create a cert for the real domain and one for the temp domain
  • If server doesn’t support Let’s Encrypt certificates use acme.sh
  • acme.sh is a powerful tool to provide SSL certs for your domains
  • can do similar things to certbot command but doesn’t require root privileges on server
  • https://github.com/acmesh-official/acme.sh/wiki/Simple-guide-to-add-TLS-cert-to-cpanel
  • Save the command you run, such as the example below. If you need to append or remove domains edit the command and re-run it.
  • don’t forget to --deploy after appending domains
    acme.sh --issue -d example.org -w ~/example.org -d www.example.org -w ~/example.org -d mail.example.org -w ~/example.org -d d1.example.org -w ~/example.org
    acme.sh --deploy --deploy-hook cpanel_uapi --domain example.org
  1. Install WP

Make sure to create an alias for the site in the wp-cli config.yml

  path: /home/user/example.org

Install and setup Worepress to the temp domain:

wp-cli.phar @example core download
wp-cli.phar @example config create --force --dbname=<database> --dbuser=<db user> --dbpass='<password>' --dbcharset=utf8mb4 --dbcollate=utf8mb4_unicode_ci --locale=en_US
wp-cli.phar @example core install --url='https://d1.example.org' --title='Example site' --admin_user='<admin user>' --admin_password='<admin password>' --admin_email='<admin email>'
  1. Change tables to InnoDB

Get the database table prefix:

wp-cli.phar @example db prefix

List info including engine type for WordPress tables (insert the prefix near the end of this command):

SELECT TABLE_NAME, TABLE_TYPE, ENGINE, CCSA.character_set_name, COLLATION_NAME  FROM information_schema.`TABLES` T,information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA WHERE CCSA.collation_name = T.table_collation AND T.table_name LIKE '<db prefix>%';

This is my own script to convert the tables to InnoDB:

db-set-engine-innodb @site1

Here is the script:

#!/bin/bash -e

if [[ $# -ne 1 ]]; then
  echo "USAGE: $(basename "$0") <host alias>)"
  exit 0


if ! wp-cli.phar cli alias list | grep -q ${hostAlias}; then
    echo "$hostAlias is not a valid host alias.  Exiting."
    exit 1

tables=($(wp-cli.phar $hostAlias db query "SHOW TABLE STATUS WHERE Engine = 'MyISAM'" --silent --skip-column-names | awk '{ print $1}'))

if [[ ${#tables[@]} -eq 0 ]]; then
    echo "No MyISAM tables found. Exiting."
    exit 0

for t in "${tables[@]}"; do
    echo "Converting: ${t}"
    wp-cli.phar $hostAlias db query "ALTER TABLE ${t} ENGINE=InnoDB"
    echo "Done converting ${t}"
  1. Setup WordPress plugins

    wp-cli.phar @site1 plugin install akismet enlighter health-check iframe ninjascanner ...
    wp-cli.phar @site1 plugin deactivate akismet litespeed-cache w3-total-cache
    wp-cli.phar @site1 plugin activate enlighter health-check iframe ninjascanner
  2. Enable MIME types

  • use WP Add Mime Types plugin

Only necessary if you want to upload different files like shell scripts or less documents. Example:

less = text/css
sh = text/sh

  1. Import

Increase the PHP max file upload size if necessary:
cPanel > Software section > Select PHP Version (opens PHP Selector) > Options tab > upload_max_filesize = 640M

In wp-config.php enable import debugging:

define( 'IMPORT_DEBUG', true );
  • Import can safely be run multiple times.
  • Review the import log to see if any imports failed.
  • Common reasons for failure are characters not supported by the database character set or attachment MIME types that are not allowed.
  • The hosting provider’s virus scanner may flag the update .xml as a virus thus blocking import via the WordPress gui importer tool.

If you run the GUI importer in WordPress take note of the user mapping suggestions. If you decide to use wp-cli later to re-do the import these mappings will be useful.

WP > Tools > Import

Or use wp-cli. Read the wp-cli documentation about creating mapping.csv.

Sample mapping.csv:


This maps any posts created by ‘alex’ on the old site to ‘alex’ on the new site. Posts created by ‘admin’ on the old site will also be mapped to ‘alex’ on the new site.

‘alex’ would have to have been created as the admin user in the wp-cli.phar @example core install step above, or else manually created via the WordPress admin panel prior to running the import.

wp-cli.phar @example import mysite.WordPress.xml --authors=mapping.csv 2>&1 | tee wp-cli-import.log
  1. Sanitize and review the import log

Check the log to see if any imports failed and why

# convert <br /> in the log to newlines
perl -pi -e 's#<br />#\n#g' wp-cli-import.log
# don't need to see stuff that has already been imported
grep -v 'already exists.' wp-cli-import.log | less
  1. Update Site and Home URL

    wp-cli.phar @example option update siteurl 'https://example.org'
    wp-cli.phar @example option get siteurl
    wp-cli.phar @example option update home 'https://example.org'
    wp-cli.phar @example option get home
  2. Go live

Change the DNS A record for the real domain to point to the new server’s IP address. At this point you can also remove the temp domain DNS record and remove it from cPanel > Domains. You can also remove the cert for the temp domain with the acme.sh command.

  1. Going further

There is more that needs to be done. For setting up DNS records tools like cloudflare-utils can be useful to quickly delete records from zone files on Cloudflare so that new records can be bulk-imported from cPanel. This is a fast way to get important records like MX, SPF, and DKIM set up.