Search

Find an article

← Back to articles
CMS Migration 8 min read

Fix Internal Links After a Migration Without Making It Worse

Fix internal links after a CMS migration safely by mapping changed URLs, avoiding risky bulk replacements, auditing missed links, and verifying every repair.

After a CMS migration, the links you wrote inside your own posts still point at the addresses they pointed at before the move. Nothing rewrote them, because they are text inside your content rather than a structure the platform manages. Repairing them means finding every place a link was written, working out exactly what the address should now be, and changing it in the content itself. It is a mechanical job, and the part that goes wrong is not the replacing. It is knowing what to replace and knowing when you have finished.

Before you change anything in bulk, the backup and rollback plan has to be verified and restorable. A find-and-replace across every post is the single most destructive operation in this cluster, because it succeeds instantly, reports success, and can be wrong in every post at once. The rollback is a restorable copy taken immediately before the operation, and it is worth taking a fresh one for this specific step even if you took one for the migration itself.

Repairing a link and redirecting it are two different acts

This distinction decides how the rest of the job goes, so it is worth making before anything else.

A repair changes the link. The post now contains the correct address. Anyone following it arrives directly, and the old address is no longer referenced anywhere in your content.

A redirect leaves the link wrong and catches it. The post still contains the old address, and a separate mechanism sends whoever follows it somewhere else.

Both get a reader to the right page, and they are frequently discussed as though they were interchangeable. Operationally they are not, and the reason has nothing to do with search. A redirect layer hides the problem from you. Once it is in place, every wrong link in your archive still works, so you can no longer tell how many wrong links you have. The count stops being visible and therefore stops going down, and the layer that was meant to be temporary becomes permanent because nothing ever forces it to shrink.

The practical rule: repair the content, and treat any redirect layer as a safety net for things you have not repaired yet rather than as the repair. Whether you should have a redirect layer at all, how it should be configured, and what it does to how your pages perform in search are all questions about performance rather than about moving and repairing content, and they belong with an on-page search resource rather than here. This article covers the repair.

Find out what actually changed, before replacing anything

The instinct is to replace the old domain with the new one and move on. That works when the domain is the only thing that changed, which is the uncommon case. A CMS migration usually changes the shape of addresses too, and often not uniformly.

Spend a few minutes establishing the real mapping:

Take a sample of old addresses from across the archive, not from the last month. Include the oldest posts, posts in every category, pages as well as posts, and anything that was ever renamed. Old content is where the exceptions live, for the same reason described in the 12 things that break in every CMS migration: older content was created under older conventions and every convention change is a chance for the mapping to differ.

For each one, find where it lives now. Write the pairs down. You are looking for whether a single rule describes all of them.

Look specifically for the exceptions. Posts with unusual characters in the title. Posts that were renamed after publication, whose address no longer matches their title. Posts that lived under a section that the new platform organizes differently. Attachments and media pages, which many platforms address in their own way. If a single rule does not cover these, one replacement pass will not fix them, and you need to know that before you run it rather than after.

If the sample shows more than one pattern, you have more than one replacement to plan. Running the common one first and dealing with the rest afterward is fine, provided you know the rest exist. What is not fine is running the common one and assuming it was all of them.

The places links hide outside the post body

A content-level replacement changes post bodies. Links live in other places too, and those places will be untouched and will look fine, because nothing about them appears broken until someone clicks.

Navigation menus. Menu items on most platforms are stored separately from content. A menu built with explicit addresses rather than internal references keeps pointing wherever it was pointed. This is the most visible remaining breakage after a migration and one of the last things people check.

Widgets, sidebars and footers. Same category, same problem. Anything with hand-written links in it, including promotional blocks and “start here” lists, holds old addresses.

Category, tag and author descriptions. These are content, but they are not post content, so a replacement scoped to posts will skip them entirely.

Anything inside platform-specific markup. A link written inside a block, a shortcode or an embed may not be visible to a plain text replacement at all, depending on how the source platform stored it and how the destination interpreted it. That whole category is covered in shortcodes, blocks and embeds, and it is worth reading before a bulk operation rather than after, because content the replacement cannot see is content the replacement will silently leave alone.

Image references. Technically the same failure with different symptoms, and worth handling as its own task rather than folding into this one. What happens to your images when you move platforms covers why a picture can be present, visible and still pointing at the old site.

Doing the replacement without making it worse

The operation itself is simple and the safeguards are what matter.

Take a fresh backup immediately before. Not the migration backup. A new one, taken minutes before the operation, so the restore point is precisely the state you want back.

Run it on a small scope first. Pick a handful of posts, apply the change, and read the result. Not the rendered page, the actual content. You are checking that the replacement did what you meant and nothing else.

Be specific about what you match. A broad match is the classic way to damage an archive: a rule loose enough to catch every variation of your old address is also loose enough to catch text that merely resembles it, including addresses that legitimately point at other sites and any post that discusses the old address as a subject. Prefer several narrow rules over one broad one.

Do not match on partial words or fragments. A fragment that appears inside a longer address will be replaced there too, producing addresses that are subtly wrong rather than obviously broken. Those are far harder to find afterward than a plain missing page.

Do one rule at a time and check between them. If three rules run together and the result is wrong, you cannot tell which one did it. If they run separately, you can.

Knowing when you are finished

This is where the job is usually abandoned, because at about eighty percent the site looks correct and the remaining twenty percent is in content nobody visits.

Crawl the site rather than browsing it. Any tool that follows every link on every page and reports what does not resolve will find in minutes what browsing will not find in a week. What matters is that the check is exhaustive and repeatable, not which tool performs it.

Search your own content for the old address. After the replacement, search the archive for the old domain and the old path shape as plain text. The correct result is zero. Anything that comes back is either something the replacement could not see into, or an exception your rule did not cover, and both are worth understanding rather than just fixing individually.

Re-run both checks after a week. Not because links break on their own, but because the first pass through a large archive usually reveals a pattern that was not in your sample, and the second run is where you find out whether your fix for it worked.

Check the menus by hand. They are few, they are the most visible thing on the site, and they are stored where a content check does not look.

Finishing this properly matters more than it appears to, because an unrepaired archive does not degrade gracefully. It stays invisible while the old site is still reachable and then fails all at once when it is not. For where this step belongs in the wider sequence, the migration guide sets out the order.

FAQ

Can I just set up redirects instead of fixing the links?
Redirects will get a reader to the right page, but they leave the wrong address in your content and they make the remaining problem invisible, so it stops shrinking. Treat any redirect layer as a net under the repair rather than a substitute for it. How to configure and evaluate one is a search-performance question rather than a content-repair question.

Why did the domain replacement not fix everything?
Because a migration usually changes the shape of addresses as well as the domain, and often not uniformly. Renamed posts, unusual titles, media pages and content from older conventions are the common exceptions.

Which links get missed most often?
Navigation menus, sidebar and footer widgets, category and author descriptions, and links stored inside platform-specific markup that a text replacement cannot read into. All of them look fine until clicked.

Is it safe to run a find-and-replace across the whole database?
It is safe only with a fresh, verified backup taken immediately before, a narrow match rather than a broad one, and a small-scope trial first. It is the fastest operation in a migration and the one with the largest blast radius.

How do I know I am actually done?
Crawl the site for links that do not resolve, and separately search your own content for the old address as plain text. That second check should return nothing. Run both again a week later.

Written July 2026. Platform behavior and tooling change over time; verify the specifics of your own source and destination platforms before relying on any detail above.

Leave a Reply

Your email address will not be published. Required fields are marked *