Redirects Broken After Domain Change: Fix the 404s and Recover SEO

Moved old.com to new.com and now Search Console shows hundreds of 404s. How to build a complete redirect map, deploy it on the old domain, and verify every URL returns a 301.

You migrated from old.com to new.com. You set up a redirect from old.com to new.com, tested the homepage, and it worked. Two weeks later Search Console shows hundreds of 404s on the new domain, traffic dropped, and external backlinks now go nowhere.

Fastest fix: you almost certainly redirected the root instead of every URL path. A root-only redirect sends old.com/blog/article to new.com/ (or to a path that no longer exists), which 404s. You need a path-preserving catch-all plus explicit rules for any URLs whose structure changed, all returning a 301, all deployed on the old domain’s hosting. Run curl -sI "https://old.com/some-deep-url" and check the Location: header: if it points at the bare root or a dead path, that is your bug.

This guide walks through building a complete redirect map and verifying it after deploy.

Which bucket are you in?

Run one curl against a real deep URL from the old site and read the result:

curl -sI https://old.com/blog/real-post showsDiagnosisJump to
301 + Location: https://new.com/ (bare root)Only the root is redirectedCause 1
301 + Location: https://new.com/blog/real-post but that 404s on new siteURL structure changed, map didn’t followCause 2
200 (no redirect at all) on some paths onlyPattern/wildcard rule isn’t matchingCause 3, 4
TLS/cert error on the https:// requestOld domain has no valid certCause 6
Connection refused / timeoutOld hosting is fully decommissionedCause 5

Common causes

Ordered by hit rate, highest first.

1. Only the root domain is redirected, deep URLs unhandled

old.com to new.com redirects the bare root. old.com/blog/article-name follows the redirect and lands at new.com/ (most domain-forwarding rules drop the path), or at new.com/blog/article-name which may not exist. Either way: 404.

How to spot it:

curl -sI "https://old.com/some-deep-url" | head -3

If you see Location: https://new.com/, only the root is redirected. It should read Location: https://new.com/some-deep-url.

2. URL structure changed but the redirect map doesn’t follow

You restructured from /blog/article-name to /articles/article-name during the migration. Even with a full path-preserving redirect, requests land at the old path on the new domain, which 404s.

How to spot it: check the redirect destinations. If they preserve old paths but the old paths don’t exist on the new site, you need a path rewrite as part of the redirect (see Cause 2’s rule in the Vercel/Netlify examples below).

3. Wildcard / pattern redirect not working

You wrote a pattern redirect but the platform doesn’t support that exact syntax, so it silently falls back to no redirect. The three major static hosts use different capture syntax: Vercel uses :slug and :path*, Netlify uses * and :splat, Firebase uses :slug* glob captures. Copy-pasting a rule from one platform into another is a common cause of silent failure.

How to spot it: curl a URL that should match the pattern. A 200 (instead of 301) means the pattern never matched.

4. Hosting platform redirect-count limit (as of June 2026)

Static hosts cap how many redirect rules a config file can hold:

PlatformFileLimit (June 2026)Beyond the limit
Vercelvercel.json redirects1,024 rulesUse Bulk Redirects (up to 1,000,000/project) or Edge Middleware
Verceltotal routes/deploy2,048 routesMove static redirects to Edge Middleware / Bulk Redirects
Netlify_redirects / netlify.tomlNo hard rule count; deploy fails if serialized output is too largeUse wildcards/placeholders; Netlify advises this past ~10,000 rules; Edge Functions for complex cases
Firebasefirebase.json redirectsPractical glob limitUse glob captures instead of one rule per URL

The fix is almost never “add more rules” — it is to collapse hundreds of 1:1 rules into a handful of wildcard captures plus a catch-all.

How to spot it: count the rules in your config. On Vercel, anything past 1,024 in vercel.json is dropped.

5. Old domain’s hosting is fully decommissioned

If you completely shut down old.com’s hosting (not just changed DNS), the redirect can never fire. Old URLs return connection refused or a timeout, and Google can’t follow a redirect that doesn’t respond.

How to spot it: curl -I https://old.com/anything returns a network error. You need to keep a redirect-only deployment running.

6. HTTPS old domain has no certificate

You set up an HTTP redirect, but an HTTPS request to old.com fails with a cert error before the redirect can fire. Modern browsers default to HTTPS, and any domain that ever sent an HSTS header forces HTTPS for the max-age window, so the HTTP version is unreachable.

How to spot it: curl -I https://old.com/ returns a cert error while curl -I http://old.com/ works. The redirect host must serve a valid cert for the old domain (Vercel, Netlify, and Firebase all auto-provision one once DNS points at them).

Shortest path to fix

Step 1: Export ALL old URLs

Sources, roughly in order of completeness:

  • old.com/sitemap.xml (if the old site or an archive still serves it)
  • Search Console (old domain still verified) → Performance → Pages → Export
  • Server access logs from the old hosting (the only source that captures URLs Google never indexed but humans still hit)
  • Wayback Machine for unindexed pages
  • Backlink data (Ahrefs, Majestic, or Search Console Links → Top linked pages) — these are the URLs whose link equity you most need to preserve

Deduplicate into a single list of paths.

Step 2: Build the redirect map

For each old URL, decide its destination:

  • Direct equivalent — map it: old.com/blog/foo to new.com/articles/foo
  • No exact match — redirect to the nearest category: old.com/topic-x/* to new.com/category/x/
  • Truly orphaned — redirect to the homepage only as a last resort (a homepage redirect passes far less link equity than a topical one, and Google may treat a mass of root-redirects as soft-404s)

A CSV keeps the map reviewable and in source control:

old_path,new_path
/blog/article-name,/articles/article-name
/topics/ai,/category/ai
/old-broken-page,/

Step 3: Convert to platform redirect syntax

Specific rules first, catch-all last — both Vercel and Firebase apply the first matching rule, so order matters.

Vercelvercel.json (:slug captures one segment, :path* captures the rest):

{
  "redirects": [
    { "source": "/blog/:slug", "destination": "https://new.com/articles/:slug", "permanent": true },
    { "source": "/topics/:topic", "destination": "https://new.com/category/:topic", "permanent": true },
    { "source": "/:path*", "destination": "https://new.com/:path*", "permanent": true }
  ]
}

"permanent": true emits a 308 (the modern permanent redirect that preserves the request method); Google treats 301 and 308 the same for site moves. Use "permanent": false only for temporary moves.

Netlify_redirects (* matches, :splat is the captured tail; force a 301! if a real file might shadow the rule):

/blog/*    https://new.com/articles/:splat    301!
/topics/*  https://new.com/category/:splat    301!
/*         https://new.com/:splat             301!

Firebasefirebase.json (glob captures use :segment*):

{
  "hosting": {
    "redirects": [
      { "source": "/blog/:slug", "destination": "https://new.com/articles/:slug", "type": 301 },
      { "source": "/:path*", "destination": "https://new.com/:path*", "type": 301 }
    ]
  }
}

Step 4: Deploy the redirects on the OLD domain’s hosting

This is the step most people miss. Keep old.com’s hosting running with only the redirect rules — you don’t need the full app, just a tiny static deployment whose entire job is the redirect config. Vercel, Netlify, and Firebase can all host a “site” that is nothing but a vercel.json / _redirects / firebase.json.

Then confirm two things on the old domain:

  1. DNS for old.com (and www.old.com) still points at this redirect host.
  2. The redirect host has a valid SSL cert for the old domain — without it, Cause 6 bites.

Step 5: Verify with curl

Sample 50+ random old URLs and confirm every one returns a permanent redirect to a live new URL:

while read -r url; do
  # -L follows redirects, -o /dev/null discards the body, then prints the final status + redirect chain
  echo "$url -> $(curl -sIL -o /dev/null -w '%{http_code} via %{num_redirects} hop(s)' "$url")"
done < <(shuf old_urls.txt | head -50)

Every line should end in 200 via 1 hop(s) (one clean 301/308 landing on a live page). Watch for:

  • ... via 2 hop(s) or more — a redirect chain (e.g. http→https→new domain); collapse it so the old URL hits the new URL in a single hop.
  • 404 final status — the destination doesn’t exist; fix the map entry.
  • A redirect to https://new.com/ for a non-root URL — Cause 1 again.

Step 6: Submit a Change of Address in Search Console

In the old domain’s property: Settings → Change of address → Update, then select the new domain. As of June 2026 the tool requires:

  • Both the old and new domains verified in Search Console under the same Google account.
  • Domain-level properties (example.com, not example.com/path).
  • Working 301s already in place — the tool runs pre-move checks and blocks on critical failures.

Google now advises running Change of Address for every variant of the old domain — www and non-www, plus any subdomains — even ones you weren’t actively using. The tool tells Google to migrate ranking signals from the old property to the new one.

Step 7: Keep redirects in place — longer than you think

Google’s own guidance (June 2026) is to keep the redirects live for at least 180 days, and longer if you still see any Search traffic hitting the old URLs. In practice, external backlinks persist for years, so keep the redirect-only deployment running indefinitely. Don’t take it down after the migration “feels done.”

How to confirm it’s fixed

  1. The curl loop in Step 5 shows 200 via 1 hop(s) for every sampled URL.
  2. In Search Console (new domain) → Indexing → Pages, the “Not found (404)” count stops climbing within a few crawl cycles and then declines.
  3. In the old domain’s property, the Change of Address banner reads as an active/completed move rather than “not started.”
  4. Spot-check 5 of your highest-value backlinks in a browser — each should land on the correct new-domain page, not a 404.

Easy to misdiagnose as

People assume Google “figures it out.” Without explicit 301s, link equity is lost. Google does not guess that old.com/foo equals new.com/foo once the URL structure has changed — it needs the redirect to connect the two. A pile of homepage redirects is also a trap: Google can treat path→root redirects as soft-404s and drop them, so map to topical equivalents wherever one exists.

Prevention

  • Build the redirect map before launching the new domain, not as cleanup afterward.
  • Keep redirect rules in source control (vercel.json, _redirects, firebase.json).
  • Maintain the old-domain redirect deployment indefinitely; never take it down.
  • After every deploy, sample-test 50+ redirects with the curl loop.
  • For large sites, design the map around wildcard captures from the start so you stay under platform rule limits (Vercel’s 1,024) and avoid redirect chains.

FAQ

  • How long do I have to keep the redirects? Google says at least 180 days, and longer while old URLs still get Search traffic. Because backlinks outlive that window, keep them indefinitely.
  • Should I use 301 or 302 for a permanent move? Use a permanent redirect (301, or 308 on Vercel’s "permanent": true). A 302/307 is temporary and signals Google to keep indexing the old URL, so ranking signals don’t fully transfer.
  • Do I really need to keep the old domain’s hosting alive? Yes. A redirect only fires if a server responds on the old domain with a valid cert. If you fully decommission the old host, every old URL — and every backlink to it — dies.
  • My site has 50,000 URLs and I hit Vercel’s 1,024 limit. Now what? Collapse 1:1 rules into wildcard captures (/blog/:slug covers every blog post in one rule). For genuinely irregular maps, use Vercel Bulk Redirects (up to 1,000,000/project) or Edge Middleware that looks up the destination from a list.
  • Do query strings and trailing slashes survive the redirect? Not automatically. Vercel and Netlify preserve query strings by default but you must match trailing-slash behavior yourself; test both /blog/foo and /blog/foo/ in your curl sample.

Tags: #Domain #DNS #SSL #Troubleshooting #Redirect