Before you start: backups, a pause and a lower TTL
WordPress's migration guide begins with a full backup: the WordPress directory, images, plugins and other files, plus the database. Keep that copy somewhere off both hosts. It is your way back if anything goes wrong.
Decide when the site will stop changing. Anything written to the old database after you export it, such as comments, orders, form entries or new posts, will not be on the new host. For a quiet blog that rarely matters. For a shop, pick a quiet hour, pause publishing, and do the export and the DNS change close together.
A day ahead, lower the TTL on the DNS records that point at the old host. The old TTL decides how long resolvers keep sending visitors to the old address after you change it. Point your domain at website hosting explains the records in detail.
- Full backup of files and database, stored away from both hosts
- Note the WordPress Address and Site Address under Settings, General
- Lower the DNS TTL one day before the move
- Leave the old hosting account running until the move is finished
Export the files and the database
Download every file in the WordPress directory over SFTP, FTP or your old host's file manager: core files, wp-config.php, and above all wp-content, which holds your themes, plugins and uploads. Copying everything is simpler than deciding what can be skipped.
Then export the database. WordPress documents two ways. phpMyAdmin's export produces an .sql file from the browser, which is how most shared hosting accounts do it. On a server with a shell, mysqldump does the same, for example mysqldump --add-drop-table -h host -u user -p database > site.sql.
Open wp-config.php from the download and note the $table_prefix value. Your tables start with it, wp_ by default, and the new wp-config.php must use the same prefix or WordPress will not find them.
- Files: the whole WordPress directory, including wp-content
- Database: an .sql export from phpMyAdmin or mysqldump
- Note $table_prefix from the old wp-config.php
- A migration plugin can bundle files and database into one archive; check its size limits against your site before you rely on it
Import on the new host and update wp-config.php
On the new host, create an empty database and a user for it, or use the one your plan provides. Upload the files into the web root, then import the .sql file into the new database with whatever tool the host supports.
WordPress's guide is short on this step: if the database name or user changed, edit wp-config.php to have the correct values. In practice that is four settings, DB_NAME, DB_USER, DB_PASSWORD and DB_HOST, and DB_HOST is the one people miss. Many hosts run MySQL on a separate server, so the value is a hostname, often with a port after a colon, rather than localhost. WordPress reads a host:port value and connects to that port.
If the new host offers a one-click WordPress install, you do not need it for a move: you are bringing a complete WordPress of your own. Remove any placeholder index page from the web root so it does not sit in front of your site.
- DB_NAME, DB_USER and DB_PASSWORD: the new database's values
- DB_HOST: the endpoint the host gives you, including a port if there is one
- $table_prefix: unchanged from the old site
- Error establishing a database connection: one of those four values is wrong
Test on the new host before you touch DNS
WordPress stores its address in the database, so a moved copy keeps redirecting to the old domain. To test it at a temporary address first, WordPress documents adding two lines to wp-config.php that set the address directly: define( 'WP_HOME', 'https://temporary.example' ); and define( 'WP_SITEURL', 'https://temporary.example' ); with no slash at the end. Remove both once the real domain points at the new host.
Know what the test does not prove. Links and images inside your posts still name the old domain, and while that domain still points at the old host, they load from the old host. A page that looks perfect may be pulling its images from the server you are leaving. Check your browser's network panel, or temporarily rename a file on the old host, to be sure what you are looking at.
If the domain itself is changing, a plain text search and replace across the database will break things. WordPress warns that some themes and widgets store values with the length of the URL recorded, so a different length corrupts them. Use a tool that understands serialized data, such as WP-CLI: wp search-replace 'old.example' 'new.example' --skip-columns=guid. Never change the guid column: WordPress says feed readers rely on it never changing.
- Log in, open several posts, a page with images, and any forms or checkout
- Check that images and scripts load from the new host, not the old one
- Domain changing: a serialized-safe search and replace, never on guid
- Old addresses stuck in cached plugin data: WordPress's guide says transients can be deleted and are rebuilt on demand
Permalinks, nginx and the HTTPS redirect loop
Two problems appear on a new host far more than any others, and both come from the server rather than from WordPress.
The first is permalinks. Apache reads rewrite rules from .htaccess, and WordPress writes its pretty permalink rule there. Nginx has no .htaccess equivalent, so on an nginx host the .htaccess file is ignored and every rule in it, including redirects your plugins added, needs an nginx version. WordPress's nginx page gives the rule permalinks need: try_files $uri $uri/ /index.php?$args. Without it, the home page works and every post returns 404. On Apache, the WordPress guide's fix is to resave the structure under Settings, Permalinks.
The second is the redirect loop. If the new host terminates HTTPS at a reverse proxy and passes requests to WordPress over plain HTTP, WordPress believes it was reached over http, redirects to https, and the browser goes round until it gives up. WordPress's HTTPS documentation gives the fix for wp-config.php: when the X-Forwarded-Proto header contains https, set $_SERVER['HTTPS'] to on. Like any wp-config.php setting, it belongs above the line that loads wp-settings.php.
- 404 on every post but the home page: the rewrite rule is missing
- Too many redirects: add the X-Forwarded-Proto check to wp-config.php
- Redirects that lived in .htaccess: recreate them in the nginx configuration
Switch DNS, then retire the old host
When the site works on the new host, point the domain at it, at whichever provider runs your nameservers. Remove the temporary WP_HOME and WP_SITEURL lines as soon as the domain resolves to the new host, so WordPress goes back to the address stored in its settings.
The certificate for your domain usually comes after the DNS change, because certificate validation checks the live name. A certificate warning straight after the switch is normal, and it lasts until the host has issued a certificate for your hostname.
Keep the old hosting until the old TTL has run out and you have checked the live site a few times, and keep your backup after that. If anything did reach the old site during the move, such as an order or a comment, you can still copy it across while the old host exists.
- Point DNS, then remove the temporary address lines
- Expect the certificate after DNS, not before
- Visit from a phone on mobile data to see what new visitors see
- Cancel the old hosting only after the move has settled
Moving a WordPress site to Puranode
Every Puranode website server is an nginx and PHP container with its own free address under sites.puranode.com, already on HTTPS, which is the temporary address for testing. The web root is /home/container/www: upload your files there over SFTP, not into the folder above it, and delete the default index.php before you test. Leave the one-click WordPress option off, because you are bringing your own install.
The database lives outside the container. The panel gives you its endpoint, username and password, and that endpoint, not localhost, goes in DB_HOST. If you are unsure how you will import your .sql file, email hello@puranode.com before you start. TLS ends at our proxy, so add the X-Forwarded-Proto snippet to wp-config.php before you test, and the container's own nginx configuration, nginx/conf.d/default.conf, is a file you can edit for rewrite rules. When the site works on its sites.puranode.com address, add a CNAME for your domain and email hello@puranode.com with the hostname, and we issue its certificate. How web hosting works covers the rest, and the web hosting page lists the plans.