Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
WordPressDevOpsMySQLTroubleshooting guide

Fix a broken WordPress migration: URLs, redirect loops and 404s

Moved WordPress and something broke? Fix database errors, old-domain URLs, serialized data, HTTPS redirect loops and permalink 404s with WP-CLI commands.

Cloudsoft Zone

Updated 7 min read

Rows of open cardboard moving boxes standing on a tiled floor in a living room
Photograph: MoveON moving / CC BY-SA 4.0, via Wikimedia Commons
In this article

Most broken WordPress migrations come down to four things: the database still contains the old URL, the web server is not routing pretty permalinks, HTTPS is detected wrongly behind a proxy or CDN, or wp-config.php points at the wrong database. Each has a quick, reliable fix, and WP-CLI handles the hardest one, rewriting URLs inside serialized data, safely.

This guide starts with a symptom table so you can jump to your problem, then walks through each fix in the order you should try them. Commands are from the current WP-CLI reference and the WordPress Advanced Administration Handbook.

Find your symptom

Symptom after the moveMost likely causeGo to
"Error establishing a database connection"Wrong database name, user, password or host in wp-config.phpFix 1
Site redirects to the old domain, or admin links point therehome and siteurl still hold the old URLFix 2
Images missing, mixed-content warnings, links to the old domain in postsOld URLs stored in content, meta and optionsFix 3
"Too many redirects" after switching to HTTPSWordPress cannot see HTTPS behind a proxy or CDNFix 4
Home page works, every other page returns 404Rewrite rules or web server config not in placeFix 5
White screen or "There has been a critical error"PHP version, a plugin, or memory limits on the new serverFix 6

Before changing anything, take a fresh database export on the new server, so every fix below can be undone:

Shell
wp db export before-fixes.sql

Fix 1: "Error establishing a database connection"

The handbook's first suspect is wp-config.php. Check DB_NAME, DB_USER, DB_PASSWORD and DB_HOST against what the new host gave you. DB_HOST is the usual culprit: many hosts use a separate database server name rather than localhost, and some need a port or socket.

Shell
# Shows the values WordPress will use, read from wp-config.php
wp config get DB_HOST
wp config get DB_NAME

# Confirms WP-CLI can reach and check the database
wp db check

If the table prefix changed between servers, $table_prefix in wp-config.php must match the imported tables, or WordPress will behave as if it is not installed.

Fix 2: The site keeps sending you to the old domain

WordPress stores its own address in two options: home (the public address) and siteurl (where the core files live). Check them first:

Shell
wp option get home
wp option get siteurl

wp option update home 'https://new-example.com'
wp option update siteurl 'https://new-example.com'

If the values look right but the site still redirects, look for WP_HOME and WP_SITEURL constants in wp-config.php. They override the database, which is useful when you cannot reach wp-admin, but easy to forget.

PHP
define( 'WP_HOME', 'https://new-example.com' );
define( 'WP_SITEURL', 'https://new-example.com' );

The handbook also describes a RELOCATE constant that updates the site URL from the login screen. If you use it, remove it immediately afterwards: leaving it in place lets an attacker change your site URL in some configurations.

Dark terminal window showing several annotated shell prompts with host, branch and status segments
WP-CLI fixes most migration problems from the terminal, even when wp-admin is unreachable.

Fix 3: Old URLs in content, images and settings

Changing home and siteurl does not touch the old address embedded in post content, image URLs, widget settings and plugin options. You need a search and replace across the database, and it must not be done with a text editor or sed on the SQL dump.

Why a plain find-and-replace breaks sites

Many themes, widgets and plugins store settings as PHP serialized data, which records the length of each string:

Text
s:24:"https://old-example.com/";

Replace the domain with one of a different length and the stored length no longer matches. PHP then fails to unserialize the value and the setting silently disappears. WP-CLI's search-replace handles serialized data correctly and never changes primary keys.

Run it safely

Shell
# 1. Preview: counts replacements per table without saving anything
wp search-replace 'https://old-example.com' 'https://new-example.com' \
  --skip-columns=guid --dry-run

# 2. Run it for real
wp search-replace 'https://old-example.com' 'https://new-example.com' \
  --skip-columns=guid

# 3. Catch leftover http:// links from before the site used HTTPS
wp search-replace 'http://old-example.com' 'https://new-example.com' \
  --skip-columns=guid

Notes on the options:

  • --skip-columns=guid: the handbook is emphatic that the guid column in the posts table must never change. Feed readers use it to recognise posts they have already shown.
  • Tables: by default only tables registered with WordPress are searched. Add --all-tables-with-prefix for plugin tables that use your prefix but are not registered.
  • --precise forces PHP processing for every column. It is slower but more reliable for complex serialized structures.
  • --export=fixed.sql writes the transformed data to a file instead of the database, if you prefer to review first.

Search for the bare domain afterwards (for example old-example.com with --dry-run) to find protocol-relative or unusual references, and review those before replacing them.

Fix 4: "Too many redirects" after enabling HTTPS

When a load balancer, reverse proxy or CDN terminates TLS and talks to WordPress over plain HTTP, WordPress thinks every request is insecure and keeps redirecting to HTTPS. The HTTPS handbook page describes the fix: trust the proxy's X-Forwarded-Proto header in wp-config.php, above the "stop editing" line.

PHP
define( 'FORCE_SSL_ADMIN', true );

// Only add this when your proxy or CDN sets the header and clients cannot reach PHP directly.
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && false !== strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) ) {
	$_SERVER['HTTPS'] = 'on';
}

Also check your CDN's TLS mode. If the CDN connects to the origin over HTTP while the origin forces HTTPS, the two will bounce requests between them forever. Use a mode that connects to the origin over HTTPS with a valid certificate.

Fix 5: Every page except the home page is a 404

Pretty permalinks depend on the web server sending unknown paths to index.php. After a move, the rules are often missing.

Shell
# Regenerate rewrite rules; --hard also rewrites .htaccess (Apache, single site only)
wp rewrite flush --hard

On Apache, confirm that mod_rewrite is enabled and that .htaccess contains the standard WordPress block. On Nginx, .htaccess is ignored; the server block needs a location / rule with try_files $uri $uri/ /index.php?$args;. If only custom post types return 404, make sure no page shares a slug with the post type and flush the rules again.

Server rack covered by a tangled mass of blue, white and red network cables
Proxies, CDNs and web server rules each add a layer that can loop redirects or drop permalinks.

Fix 6: White screen or critical error

A new server often means a different PHP version, memory limit or set of extensions. Turn on logging without showing errors to visitors:

PHP
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Reload the page and read wp-content/debug.log. If a plugin is to blame, WP-CLI can still run with plugins skipped, which lets you deactivate the culprit:

Shell
wp plugin list --status=active --skip-plugins
wp plugin deactivate problem-plugin --skip-plugins

Turn WP_DEBUG off again when you are done.

Gotchas

  • Persistent object caches remember old values. If Redis or Memcached was copied or shared, run wp cache flush after any URL change.
  • Page caches and CDNs keep serving old HTML. Purge them after the search and replace, not before.
  • Hard-coded URLs live outside the database too. Check theme files, custom CSS and wp-config.php for the old domain.
  • Multisite is different. URLs are stored in each site's tables and in wp_blogs and wp_site. Use --network with --url to target the network, and follow the multisite moving guide.
  • Uploads must be copied separately. The database only stores paths; missing images after a correct search and replace usually mean wp-content/uploads was not copied or has the wrong ownership.

Post-migration checklist

  1. wp option get home and siteurl show the new HTTPS address.
  2. wp search-replace 'old-example.com' 'new-example.com' --dry-run --skip-columns=guid reports zero matches you did not intend to keep.
  3. Home, a post, a page, a category archive, search and a custom post type all load.
  4. You can log in, save a post and upload an image.
  5. Caches are purged and Site Health shows no new critical issues.
  6. Debug logging is off and any RELOCATE constant is removed.
  7. Redirects from the old domain to the new one return 301 for every path.

Frequently asked questions

  • Can I use sed or a text editor to change URLs in a WordPress SQL dump?

    Not safely. Serialized PHP data stores string lengths, so changing a URL's length corrupts those values. Use wp search-replace, which unserializes, replaces and reserializes the data.

  • Should I replace URLs in the guid column?

    No. The handbook says the guid column must never change, because feed readers use it to identify posts. Add --skip-columns=guid to every search-replace run.

  • What is the difference between home and siteurl?

    home is the address visitors use for your site. siteurl is where the WordPress core files are installed. They are usually the same unless WordPress lives in a subdirectory.

  • Why does wp rewrite flush --hard not fix my 404s on Nginx?

    The --hard option writes .htaccess, which only Apache reads. On Nginx, add try_files $uri $uri/ /index.php?$args; to the location / block of your server configuration.

  • How do I log in if the site redirects to the old domain?

    Use WP-CLI to run wp option update home and wp option update siteurl, or temporarily define WP_HOME and WP_SITEURL in wp-config.php.

  • How do I preview a search and replace before running it?

    Add --dry-run. WP-CLI runs the whole operation and reports the number of replacements per table without saving anything.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Migrating WordPress — WordPress Advanced Administration Handbook
  2. wp search-replace — WP-CLI Command Reference
  3. Common WordPress errors — WordPress Advanced Administration Handbook
  4. HTTPS — WordPress Advanced Administration Handbook
  5. wp rewrite flush — WP-CLI Command Reference
  6. Nginx — WordPress Advanced Administration Handbook

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.