WordPress migration process

For small websites

Use the Duplicator plugin in source and destination!

For large websites

First log in to the server console as the root user.

1. Copy files from source to destination

# Files in different servers
rsync -avzP --delete user@host:/path/to/source user@host:/path/to/destination
# Files in same server
rsync -avzP --delete /path/to/source /path/to/destination

The --delete option will remove anything else from the destination directory. Otherwise clean it up first.

2. Fix files permissions in destination

# Fix ownership of all files and folders
chown -R user:user /home/user/public_html/
# Fix the ownership of the root directory
chown user:nobody /home/user/public_html/
# Fix the rights of the root directory
chmod 750 /home/user/public_html/

3. Dump source database

For small databases use phpMyAdmin export function with the gzip compression enabled.

For larger databases:

# Dump the database to a file
mysqldump db_name >> db_name.sql

4. Import source database to destination database

For small databases use phpMyAdmin import.

For larger databases:

First drop all database tables in destination database from PHPMyAdmin.

Then:

# Enter mysql mode
mysql
# Select the database to use
> use db_name; 
# Import the database 
> source /path/to/db_name.sql;
# Exit mysql
> exit;

Note

If this is a new site and its database does not exist then you need to create the database and the database user with their password first. Then assign all privileges of the database to the user and then do the aboveĀ 

5. Rename destination urls

Only necessary when the url changes.

Use WPCLI for this:

# Become the correct user if not already
su user
# Do the url rename
wp search-replace --precise --all-tables https://old.url https://new.url

Note 1

The wp-config.php needs to be edited so that the database name, user and password reflect the correct ones.

Note 2

The WordPress .htaccess rewrite might need some tweaking in the RewriteBase and RewriteRule parts if the source was in a subdirectory and the destination isn't or vice versa.

For instance here is the WordPress core part:

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /subdirectory
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /subdirectory/index.php [L]
</IfModule>
# END WordPress

Also search for all instances of the /subdirectory in it to replace.