How to Fix WordPress 404 Errors After Changing Hosts
After a host migration, WordPress 404 errors after changing hosts almost always mean your web server can no longer map a requested URL to a PHP script or a static file. The request reaches the server, the server looks for a file or directory that does not exist, and it returns the 404 status code instead of handing the request to WordPress. The fix is rarely about the database or the files themselves, it is about the rewrite rules and the server configuration that translate pretty URLs into index.php calls.
Why the 404 Appears After a Host Move
WordPress uses a front controller pattern. Every request for a post, page, or category is routed through index.php in the site root. The web server must rewrite the incoming URL before WordPress ever runs. On Apache, that rewrite logic lives in .htaccess. On Nginx, it lives in the server block as a try_files directive. When you change hosts, the new server may not have mod_rewrite enabled, the .htaccess file may not have been copied, or the Nginx configuration may use a generic try_files that does not include the WordPress rewrite pattern. The result is that a request for /about/ hits the server, finds no physical directory named about, and returns 404. The request never reaches index.php.
Another cause is a permalink structure mismatch. If the old host stored permalinks as /%postname%/ but the new host has a different default, the database still contains the old structure. WordPress will generate links with the new structure, but the rewrite rules are built from the permalink setting. A mismatch means the server sees a URL that does not match any rule, and again you get a 404. The fix is to flush the rewrite rules, which forces WordPress to regenerate the .htaccess or the Nginx rule set from the current permalink structure.
Flush the Permalink Rules First
Before touching any configuration file, log into the WordPress admin and go to Settings, then Permalinks. Do not change anything. Just click Save Changes. This action triggers a rewrite rule flush. WordPress writes a fresh .htaccess if the file is writable, or it updates the in-memory rules that Nginx uses through the fastcgi_cache and try_files directives. If the 404 persists after this, the problem is almost certainly a server configuration issue, not a WordPress setting.
If you cannot reach the admin dashboard because the login page itself returns 404, you can flush the rules from the command line using WP-CLI. Run this from the WordPress root directory:
wp rewrite flush --hard
The --hard flag forces a rewrite of the .htaccess file. The expected output is a single line confirming the rewrite rules were flushed. If the command returns an error about a missing .htaccess, that file is either absent or not writable. You will need to create it manually or fix permissions.
Inspect and Repair the .htaccess File
On Apache, the .htaccess file in the WordPress root must contain the standard rewrite block. Open it with cat or a text editor. A correct file starts with # BEGIN WordPress and ends with # END WordPress. Inside, the RewriteRule directive sends all non-file, non-directory requests to index.php. If the file is missing, create it with the following content:
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
Save the file and set permissions to 644 so the web server can read it but not write to it accidentally. Then run wp rewrite flush --hard again. If the 404 still shows, check whether Apache has mod_rewrite enabled. On a Debian or Ubuntu host, run sudo a2enmod rewrite and then sudo systemctl restart apache2. On a CentOS or Red Hat host, the module is usually compiled in, but you may need to check httpd -M for the presence of rewrite_module.
Nginx: The try_files Directive
If your new host runs Nginx, the .htaccess file is ignored entirely. The rewrite logic lives in the server block configuration. The critical line is the try_files directive inside the location / block. A correct WordPress configuration looks like this:
location / {
try_files $uri $uri/ /index.php?$args;
}
This directive tells Nginx to first try the exact file path, then the directory path, and finally to pass the request to index.php with the original query string. If your host uses a generic template, you might see a line like try_files $uri $uri/ =404;. That line returns a 404 for any URL that does not map to a physical file, which is exactly the wrong behavior for WordPress. Edit the server block, replace that line with the WordPress version, and reload Nginx with sudo nginx -t to test the configuration, then sudo systemctl reload nginx to apply it.
Some Nginx configurations use a fastcgi_pass directive inside a separate location ~ \.php$ block. That block is fine, but the try_files in the root location must still point to index.php. If you see a 404 only for PHP files, check that the location ~ \.php$ block has the correct fastcgi_param SCRIPT_FILENAME value. A common mistake is a hardcoded path that references the old host's directory structure, such as /home/olduser/public_html instead of the new path.
Check the Database for Serialized Data Corruption
After a migration, the 404 might not be a rewrite issue at all. If you used a search and replace tool to change the domain in the database, serialized data in the wp_options table can become corrupted. Serialized strings store both the value and its length. If the length is not updated after a domain change, WordPress cannot read the option, and some rewrite rules may not load. The symptom is often a 404 on the front page but a working admin, or vice versa. To check, run a query on the wp_options table for any option that contains siteurl or home:
mysql -u username -p -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
Both values should point to the new domain with the correct scheme, either http or https. If they are wrong, update them with UPDATE wp_options SET option_value = 'https://newdomain.com' WHERE option_name = 'siteurl';. Then flush the rewrite rules again. For serialized data elsewhere, use a dedicated search and replace script that accounts for string lengths, not a raw SQL query.
Verify the Host's Document Root and File Permissions
Finally, confirm that the WordPress files are in the correct document root. If the host placed your files in a subdirectory like public_html/wordpress but your domain points to public_html, the server will look for index.php in the wrong place and return a 404. Check the host's control panel or your welcome email for the document root path. Then verify that index.php and .htaccess are in that root. File permissions matter too: directories should be 755 and files should be 644. If .htaccess is owned by a different user, Apache may refuse to read it, which silently disables all rewrite rules.
After you have fixed the rewrite rules, flushed them, and confirmed the document root, test a few URLs with curl -I to see the HTTP status codes. A working site returns 200 OK for the homepage and 200 for a sample post. A persistent 404 means the request is still not reaching index.php, so go back to the server configuration and check the error log at /var/log/nginx/error.log or /var/log/apache2/error.log. The log will tell you exactly which file or directive failed.
When the 404 is gone, do not stop there. Test the login page, a category archive, and a single post. Then run wp rewrite flush --hard one final time and clear any caching plugin’s cache. If you have a reverse proxy or a CDN in front of the site, purge its cache as well, because stale cached 404 responses can linger for hours. Moving forward, keep a copy of your .htaccess or Nginx server block in a version control repository, so the next migration does not start from scratch.
