SYSTEMS OPERATIONAL
WordPress

Fix Broken Images and Links in WordPress After Migration

Getwebup 5 min read

You moved your WordPress site to a new domain or a new server, and now half the images are broken, the CSS looks stripped down, and some links still point at the old address. Nothing crashed - this is almost always a leftover-URL problem, and it has a specific, reliable fix.

Symptom: What You're Actually Seeing

The classic signs, usually right after a migration, domain rename, or an http-to-https switch:

  • Images and uploads 404, or load from the old domain in the browser console
  • Pages look unstyled because theme CSS/JS is still being requested from the old URL
  • Internal links (menus, related posts, page builder content) point back to the old domain
  • Mixed content warnings in the address bar after moving to HTTPS
  • The WordPress admin redirects you back to the old domain and won't let you log in on the new one

Cause: Old URLs Are Baked Into the Database

WordPress doesn't just store your domain in one setting. The old URL is scattered through wp_options (siteurl, home), post content, widget settings, page builder data (Elementor, Divi, WPBakery), custom fields, and sometimes serialized arrays inside plugin settings. A migration copies the database as-is, so every one of those references still points at wherever the site used to live.

Why a Plain Find-and-Replace in phpMyAdmin Breaks Things

This is the part people get burned on. A lot of that data isn't stored as plain text - it's PHP serialized data, and serialized strings carry an explicit length count baked into them, like this:

a:1:{s:6:"domain";s:20:"https://olddomain.com";}

See the s:20? That's telling PHP "the next string is exactly 20 characters." If you run a raw SQL UPDATE ... REPLACE() in phpMyAdmin and your new domain is a different length than the old one, that length count is now wrong. PHP can't unserialize the value anymore, and the setting silently reverts to a default or the field just goes blank. This is why "it worked for some things but broke my theme options" is such a common complaint after a manual migration.

Fix: Use a Serialization-Aware Search-Replace

The tools below rewrite the length prefixes as they go, so serialized data stays valid. Pick whichever fits your access level.

Option 1: WP-CLI (fastest, if you have SSH)

If your Getwebup plan includes SSH access, this is the cleanest route. SSH into the server, cd into the WordPress root, and run a dry run first:

wp search-replace 'https://olddomain.com' 'https://newdomain.com' --dry-run

Check the count of replacements looks sane (not zero, not absurdly high), then run it for real:

wp search-replace 'https://olddomain.com' 'https://newdomain.com' --all-tables --precise

A few flags worth knowing:

  • --all-tables - catches custom tables from plugins, not just WordPress core tables
  • --precise - slower but safer matching for serialized data with unusual characters
  • --skip-columns=guid - use this if you specifically want to preserve original post GUIDs (rare, but some SEO workflows want it)

Run it a second time for the non-www / www variant and the http vs https variant separately - migrations usually need two or three passes, not one.

Option 2: Better Search Replace plugin (no SSH needed)

If you're on shared hosting without terminal access, install the Better Search Replace plugin from the WordPress plugin directory. It runs the same serialization-safe logic through the wp-admin UI:

  1. Go to Tools > Better Search Replace
  2. Enter the old and new URL in the Search/Replace fields
  3. Select all tables
  4. Tick Run as dry run first, review the replacement counts, then untick it and run for real

Always take a database backup before either option - from cPanel, that's Backup Wizard > Download a MySQL Database Backup. If a replacement goes wrong, you want a clean rollback point.

Update wp-config.php and the Site Address Settings

Search-replace handles content, but it's worth locking the core site and home URLs directly so nothing can drift back. Add these two lines near the top of wp-config.php, above the "That's all, stop editing!" comment:

define('WP_HOME', 'https://newdomain.com');
define('WP_SITEURL', 'https://newdomain.com');

This overrides whatever is in the database for those two values, which is also handy if a wrong URL is locking you out of wp-admin entirely - you can fix login access without touching the database at all.

Places URLs Still Hide After the Swap

Even a clean search-replace pass misses a few spots. Check these before calling it done:

LocationWhat to check
Page builder contentElementor and Divi cache generated CSS/HTML separately - regenerate it (Elementor: Tools > Regenerate CSS)
Offload/CDN pluginsWP Offload Media, W3 Total Cache CDN settings, or Cloudflare page rules pointing at the old hostname
.htaccessAny hardcoded RewriteBase or redirect rules referencing the old domain
robots.txt / XML sitemapRegenerate the sitemap so search engines pick up the new URLs
Custom fields (ACF, Pods)Fields storing full URLs instead of relative paths or attachment IDs
Browser and object cacheClear WordPress cache plugin, server-side cache (LiteSpeed/Redis), and hard-refresh your own browser

Prevention: Make the Next Move Painless

  • Store images and internal links as relative paths where your theme/page builder allows it
  • Keep a documented list of every plugin that stores absolute URLs, so you know what to double-check next time
  • Use a staging subdomain for the new environment first, and do the search-replace there before final cutover
  • Always back up the database immediately before running any bulk replace, dry run or not

If replacements ran clean but the front end still shows old URLs, it's almost always a caching layer - object cache (Redis/Memcached), a page cache plugin, or Cloudflare's edge cache all need a manual purge after a database-level change like this. If you're on Getwebup hosting and want a second pair of eyes on it, open a support ticket with your domain and we'll check the cache layers and database in one pass.

Frequently asked questions

Is it safe to just find-and-replace the domain in phpMyAdmin?

No, not with a plain SQL REPLACE(). WordPress stores a lot of settings as PHP serialized data, which includes an exact character-length count for each string. Changing the domain changes the string length, so a raw SQL replace corrupts that data instead of updating it. Use WP-CLI search-replace or the Better Search Replace plugin instead - both rewrite the length counts correctly.

Do I need SSH access to fix this?

No. WP-CLI (via SSH) is the fastest option, but the Better Search Replace plugin does the same serialization-safe replacement entirely through the wp-admin dashboard, which works fine on shared hosting without terminal access.

I updated the database but the site still shows old URLs. Why?

This is almost always a caching layer that hasn't been cleared - an object cache like Redis, a page cache plugin, or an edge cache like Cloudflare. Purge each of those after the search-replace, then hard-refresh your browser.

Why did my site lock me out of wp-admin after changing the domain?

If WordPress' siteurl or home values in the database still point at the old domain, it can redirect wp-admin logins back to a URL that no longer resolves. Add WP_HOME and WP_SITEURL constants to wp-config.php pointing at the correct domain - this overrides the database values and restores access without needing to touch the database.

#wordpress #search-replace #wp-cli #site-migration #serialized-data #wp-config

Keep reading

Chat with Support