Metricum Lab

Technical SEO · Redirects · Site migrations

SEO Redirects: 301 vs 302, Redirect Chains, Canonicals & Migration Validation

Redirects are not a cleanup rule to add after a migration. They are part of the URL-state contract: status semantics, destination equivalence, canonical signals, internal links and release validation must agree before old URLs are retired.

Yurii Pekach Technical SEO & Web Performance ConsultantPublishedUpdated29 min read
SEO redirect migration system showing an old URL moving through a permanent redirect to a final 200 canonical URL with internal-link and sitemap signals aligned.

The short answer

Use 301 or 308 when an old URL has permanently moved to a real equivalent; use 302 or 307 only when the move is temporary. For migrations, build an explicit old→new URL map before writing rules, point each old URL directly to its final destination, update the new URL's self-canonical/internal links/sitemap/hreflang, and block release if the map creates chains, loops, irrelevant many-to-one mappings or non-200 final targets. A Search Console 'Page with redirect' state can be expected; a 'Redirect error' is a different condition that requires diagnosis.

301 / 308

Permanent move

Google treats permanent server-side redirects as a signal that the target should become canonical.

Direct

Preferred migration hop

Metricum Lab release target: every controlled old URL points to the final destination, not to another planned redirect.

≥ 1 year

Migration retention

Google recommends keeping migration redirects as long as possible, generally at least one year.

Status semantics

301 vs 302 is a permanence decision; 307 vs 308 adds request-method semantics

Choose the redirect code from the state of the resource, not from a folklore rule about 'SEO value'.

The useful way to think about SEO redirects is not 'which code passes the most authority?' but 'what statement is the server making about this URL?' Google currently treats 301 and 308 as permanent redirect signals and 302, 303 and 307 as temporary. For a permanent move, Google recommends a server-side permanent redirect when possible. For a genuinely temporary detour, use temporary semantics so the source can remain the long-term search representative.

Redirect status semantics for SEO and HTTP behavior
StatusResource meaningRequest-method behaviorSearch use
301 Moved PermanentlyPermanent new URIRFC 9110 allows some clients to change POST to GETPermanent page/domain/path move
302 FoundTemporary different URIRFC 9110 allows some clients to change POST to GETTemporary outage, experiment or detour where source should remain primary
307 Temporary RedirectTemporary different URIMethod must be preservedTemporary redirect for stateful/non-GET requests
308 Permanent RedirectPermanent new URIMethod must be preservedPermanent move when method preservation matters

Custom diagram

Choose a redirect from URL state, then from method semantics

A decision tree separates permanent versus temporary resource moves first, then distinguishes 301/302 from method-preserving 308/307 where non-GET requests matter.

Choose a redirect from URL state, then from method semanticsA decision tree separates permanent versus temporary resource moves first, then distinguishes 301/302 from method-preserving 308/307 where non-GET requests matter.Has the URL moved?resource state determines semanticsPermanentsource is retiredTemporarysource should remain primary301 / 308permanent canonical signal308 preserves HTTP method302 / 307temporary routing semantics307 preserves HTTP methodPermanence first → method semantics second
For ordinary SEO landing-page GET requests, permanence is the first decision. HTTP method preservation becomes important when the same route also handles POST or other stateful requests.

If your search query was what is 301 redirect in SEO, the precise answer is: an HTTP 301 says the resource has permanently moved and supplies a new Location. Search engines can use that as a canonicalization signal. Search demand also phrases the same task as 301 redirects SEO or seo 301 redirect; the implementation decision is still permanence plus destination equivalence, not the wording of the query.

URL-state model

Decide whether the old URL should redirect, stay accessible with a canonical, or return 404/410

A redirect retires a URL. A canonical keeps a duplicate accessible. A 404/410 says there is no replacement.

A common migration failure starts before the redirect rule: the team has not decided what the old URL means after launch. Google describes permanent redirects and rel=canonical as strong canonicalization signals, while sitemap inclusion is weaker. But those mechanisms solve different product states. If a duplicate must remain accessible—for example, a tracking or alternate presentation URL—a canonical can express preference without removing access. If the old content is retired and has a real successor, redirect it. If there is no equivalent replacement, returning 404 or 410 is more truthful than sending users and crawlers to an unrelated category or home page.

URL-state decision: redirect, canonical, or deletion
Old URL statePreferred controlWhyRelease check
Permanent equivalent exists301 or 308 → equivalent final URLRetire old address and signal new canonical locationFinal destination is relevant, indexable and intended 200
Temporary alternate destination302 or 307Keep source as long-term resource while routing users temporarilyTemporary condition has owner and rollback date
Duplicate must remain accessible200 + rel=canonicalConsolidate preference without removing duplicate URLCanonical target is equivalent; signals do not conflict
Content deleted; no equivalent404 or 410Do not manufacture relevance with an unrelated redirectInternal links/sitemaps stop advertising deleted URL

Migration contract

Build the old→new URL mapping from evidence before you write redirect rules

A migration map is a reviewed data product. Regex is an implementation technique, not the source of truth.

A defensible SEO migration strategy starts with inventory, not with a wildcard. Google's current site-move documentation recommends building the URL mapping before redirects and finding important old URLs from sitemaps, server logs or analytics, Search Console link data and the CMS; it also calls out assets such as images, JavaScript and CSS where relevant. Backlink exports can add another prioritization layer because an old URL with valuable external references deserves explicit review even if it disappeared from the current navigation.

Build a migration mapping that a developer and SEO can both review

  1. 1

    Inventory old URLs from multiple evidence sources

    Merge crawl inventory, XML sitemaps, server logs/analytics, Search Console-linked URLs, CMS exports and high-value backlink targets. Preserve provenance so a missing URL can be explained rather than silently dropped.

    Pass criterionEvery important old URL has at least one source-of-discovery field and a stable source_url key.

  2. 2

    Classify the old URL state before selecting a target

    Mark each row as permanent move, temporary move, duplicate-kept-accessible, consolidated content, or deleted-without-replacement. Do not infer the state from URL similarity alone.

    Pass criterionEvery source URL has one reviewed disposition and a reason that a product/content owner can understand.

  3. 3

    Map to the closest equivalent final destination

    Prefer a page that satisfies the same user task. A many-to-one consolidation is valid when content was genuinely merged, but mass redirects to an irrelevant homepage can be interpreted as soft 404 behavior.

    Pass criterionEach redirect target is explainably equivalent or part of a documented consolidation.

  4. 4

    Separate the mapping contract from the server rule generator

    Generate Apache, NGINX, edge or application rules from the reviewed mapping where possible. That makes the map testable before implementation and prevents a broad regex from becoming undocumented business logic.

    Pass criterionA reviewer can inspect source, target, status and reason without reading server configuration.

A website migration checklist SEO teams can actually use should therefore include a mapping acceptance gate: no duplicate sources, no self-redirects, no target that is itself scheduled to redirect, no empty target for a permanent move, and no unreviewed mass consolidation. The code artifact later in this article makes those checks reproducible.

Custom diagram

Treat redirect rules as compiled output from a reviewed URL-state map

Evidence sources feed an old-URL inventory, each row receives a disposition and final target, then server rules are generated and validated from the approved map.

Treat redirect rules as compiled output from a reviewed URL-state mapEvidence sources feed an old-URL inventory, each row receives a disposition and final target, then server rules are generated and validated from the approved map.Sitemaps / CMSknown URLsLogs / analyticsused URLsGSC / backlinkslinked URLsURL-state mappingsource · disposition · targetstatus · reason · ownerServer rulesApache · NGINX · edgePreflightchain · cycle · schemaObserved source → final pathmust match approved mappingEquivalence lives in the mapping, not in the regex
This separates content/product decisions from implementation syntax. A server regex can be efficient, but it should not decide equivalence by itself.

Path integrity

Redirect chains and loops are usually mapping defects, not crawl-budget trivia

Googlebot can follow long chains, but a migration you control should normally send each source directly to the final destination.

Google says Googlebot can follow up to 10 redirect hops, while its migration guidance advises redirecting directly to the final destination and, when a chain cannot be avoided, keeping it low—ideally no more than three and fewer than five. That is not a target to design toward. For controlled migrations, Metricum Lab's release target is one redirect hop: old URL → final 200 URL. Every extra hop adds latency, makes ownership harder to reason about and creates another place where a temporary status, loop or dead destination can appear.

Audit redirect paths from both the source list and the live site

  1. 1

    Test the old-URL inventory in list mode

    In Screaming Frog SEO Spider, upload the planned/legacy URL list and enable the migration workflow's Always Follow Redirects option so each source can be traced to its final response.

    PathMode → List; Configuration → Spider → Advanced → Always Follow Redirects

    Pass criterionEvery migration source reaches the intended final status and destination; unexpected temporary hops and loops are visible.

  2. 2

    Crawl the live internal graph for redirecting links

    A redirect can be correct for an old external URL while still being unnecessary in current navigation. In the crawl, inspect Response Codes → Redirection (3XX), then the Inlinks view to find pages that still link to the redirect source.

    PathResponse Codes → Redirection (3XX) → Inlinks

    Pass criterionCurrent internal links point directly to canonical final URLs unless a deliberate exception is documented.

  3. 3

    Export chains and loops as release defects

    Use the dedicated redirect-chain report for internal chains/loops, and the migration All Redirects report for source-to-final verification. Assign the defect to the owning rule or internal-link source rather than merely counting hops.

    PathReports → Redirects → Redirect Chains

    Pass criterionNo controlled source depends on another planned redirect and no loop remains.

Signal convergence

Redirects, canonicals, internal links, sitemap and hreflang should converge on the same final URL

Canonicalization becomes harder to debug when each system names a different preferred URL.

The important canonical tags SEO lesson during a migration is not to add more canonical tags; it is to remove contradictions. Google's canonical documentation calls redirects and rel=canonical strong signals and sitemap inclusion a weaker signal, and says those methods can stack. It also recommends self-referencing canonicals on canonical pages and consistent internal linking to the canonical URL. During a migration, that means the new page should not redirect from the old URL while its canonical, sitemap entry, hreflang or primary internal links still point backward.

Custom diagram

A migration is easier to interpret when every signal names the same final URL

An old URL permanently redirects to the final 200 URL while the final page self-canonical, sitemap, hreflang and internal links also point to that same URL.

A migration is easier to interpret when every signal names the same final URLAn old URL permanently redirects to the final 200 URL while the final page self-canonical, sitemap, hreflang and internal links also point to that same URL.Old URL301 / 308Final URL200 · indexableself-canonicalintended landing pageSitemapfinal URLhreflangnew cluster URLsInternal linksdirect to finalrel=canonicalself-referenceAll discovery/canonical signals name the same destination
Canonical declarations are preferences rather than absolute commands, so signal convergence reduces ambiguity instead of trying to overpower contradictory implementation.
Canonical-signal consistency checks after a URL change
LayerExpected stateFailure patternFix owner
RedirectOld URL → final new URLOld → intermediate → final; wrong locale/categoryRouting/platform
rel=canonicalFinal URL self-canonicalFinal page canonicals back to old URL or another variantTemplate/SEO
Internal linksLink directly to final canonical URLNavigation/body links still hit 3xxFrontend/content
XML sitemapContains final canonical URLsOld redirecting URLs remain submitted as current canonicalsSEO/platform
hreflangUses new reciprocal URLsOld/redirecting URLs remain in language clusterInternational SEO/platform

Google may still select a different canonical when its systems see stronger or conflicting evidence; a user-declared canonical is not an absolute command. That is why migration validation should inspect the whole signal stack rather than conclude 'the canonical tag is correct, so the migration is correct.' The Technical SEO Audit Checklist covers this consistency at audit breadth; here it is treated as a migration release contract.

Release gate

Validate the redirect map as a graph before the first request reaches production

Static preflight can catch duplicate sources, planned chains, cycles and invalid deletion states before server configuration is deployed.

A spreadsheet review is necessary but weak at finding graph defects across thousands of rows. The following small validator treats the migration map as structured data. It deliberately supports permanent migration rows (301/308) and explicit deletions (404/410). Temporary 302/307 behavior should live in a separate operational list with an owner and rollback condition, not be silently mixed into a permanent migration map.

VERIFIED · Preflight redirect_map.csv as a graph, not a list

#!/usr/bin/env python3
"""Validate a planned redirect_map.csv before release.

Required columns:
source_url,target_url,expected_status,reason

For 301/308 rows target_url is required.
For 404/410 rows target_url must be empty.
This is a static mapping check: it does not request live URLs.
"""

import csv
import sys
from pathlib import Path
from urllib.parse import urlparse

REDIRECT_STATUSES = {301, 308}
DELETION_STATUSES = {404, 410}
ALLOWED_STATUSES = REDIRECT_STATUSES | DELETION_STATUSES
REQUIRED_COLUMNS = {"source_url", "target_url", "expected_status", "reason"}


def absolute_http_url(value: str) -> bool:
    parsed = urlparse(value)
    return parsed.scheme in {"http", "https"} and bool(parsed.netloc)


def load_rows(path: Path):
    with path.open(newline="", encoding="utf-8-sig") as handle:
        reader = csv.DictReader(handle)
        missing = REQUIRED_COLUMNS - set(reader.fieldnames or [])
        if missing:
            raise ValueError(f"Missing columns: {', '.join(sorted(missing))}")
        return [{k: (v or "").strip() for k, v in row.items()} for row in reader]


def validate(rows):
    errors = []
    warnings = []
    by_source = {}

    for number, row in enumerate(rows, start=2):
        source = row["source_url"]
        target = row["target_url"]
        reason = row["reason"]
        try:
            status = int(row["expected_status"])
        except ValueError:
            errors.append(f"row {number}: expected_status must be an integer")
            continue

        if not absolute_http_url(source):
            errors.append(f"row {number}: invalid source_url {source!r}")
        if source in by_source:
            errors.append(f"row {number}: duplicate source_url {source}")
        by_source[source] = {**row, "status": status, "row": number}

        if status not in ALLOWED_STATUSES:
            errors.append(f"row {number}: status {status} is outside the release contract")
        if not reason:
            errors.append(f"row {number}: reason is required for reviewability")

        if status in REDIRECT_STATUSES:
            if not absolute_http_url(target):
                errors.append(f"row {number}: 301/308 requires an absolute target_url")
            if source == target:
                errors.append(f"row {number}: self-redirect {source}")
        elif status in DELETION_STATUSES and target:
            errors.append(f"row {number}: 404/410 row must not have target_url")

    # The planned map should point directly to final destinations.
    # If a target is also a redirect source, the release would create a chain.
    for source, row in by_source.items():
        if row["status"] not in REDIRECT_STATUSES:
            continue
        target = row["target_url"]
        if target in by_source and by_source[target]["status"] in REDIRECT_STATUSES:
            errors.append(f"planned chain: {source} -> {target} -> {by_source[target]['target_url']}")

    # Independent cycle detection produces a clearer error for A -> B -> A.
    for source in by_source:
        seen = []
        current = source
        while current in by_source and by_source[current]["status"] in REDIRECT_STATUSES:
            if current in seen:
                cycle = seen[seen.index(current):] + [current]
                errors.append("redirect cycle: " + " -> ".join(cycle))
                break
            seen.append(current)
            current = by_source[current]["target_url"]

    # Many-to-one is valid for real consolidation, but it deserves review.
    targets = {}
    for source, row in by_source.items():
        if row["status"] in REDIRECT_STATUSES:
            targets.setdefault(row["target_url"], []).append(source)
    for target, source_list in targets.items():
        if len(source_list) >= 3:
            warnings.append(f"review consolidation: {len(source_list)} sources -> {target}")

    return sorted(set(errors)), sorted(set(warnings))


def main():
    path = Path(sys.argv[1] if len(sys.argv) > 1 else "redirect_map.csv")
    rows = load_rows(path)
    errors, warnings = validate(rows)
    for warning in warnings:
        print("WARN:", warning)
    if errors:
        for error in errors:
            print("ERROR:", error)
        print(f"FAIL: {len(errors)} release-blocking mapping issue(s)")
        raise SystemExit(1)
    print(f"PASS: {len(rows)} mapping row(s); no planned chains, cycles or schema violations")


if __name__ == "__main__":
    main()

Executed with Python 3 on 2026-09-04 against synthetic valid and deliberately broken fixtures. It validates mapping structure only; it does not make network requests or prove that live targets return 200.

Use the validator as a release-blocking preflight

  1. 1

    Export the approved redirect_map.csv

    Use the four required columns exactly: source_url, target_url, expected_status, reason. Keep content-equivalence reasoning in the map so a reviewer can challenge questionable many-to-one mappings.

    Pass criterionThe CSV is versioned with the migration release and every row has an accountable reason.

  2. 2

    Run the validator before generating server rules

    Execute python validate_redirect_map.py redirect_map.csv. Treat FAIL as a release blocker; review WARN consolidations rather than automatically rejecting them.

    Pathpython validate_redirect_map.py redirect_map.csv

    Pass criterionThe script exits 0 with PASS and no planned chain/cycle/schema error.

  3. 3

    Then test the generated redirects against a staging or live endpoint

    Static mapping validation cannot prove HTTP behavior. After rules are deployed, verify response status, Location header, hop count and final response with a crawler, URL Inspection for representative Google-facing URLs, or command-line checks.

    Pass criterionObserved HTTP paths match the approved mapping and final pages satisfy the intended indexability/canonical contract.

For Apache, the official documentation shows server-side permanent redirects with Redirect permanent or an R=301 rewrite where appropriate; NGINX return supports 301/302/303/307/308 and its rewrite ... permanent flag returns 301. Generate configuration to match your actual server topology rather than copying a generic rule around proxies, locale routing or application state.

Observed evidence

Post-launch validation asks three different questions: does the redirect work, is the final URL canonical, and is the migration being processed?

HTTP correctness, canonical/index state and migration progress have different evidence sources and different timelines.

Custom diagram

Migration validation is a loop from mapping to observed crawl/search evidence

The approved mapping is deployed, checked via HTTP crawl and logs, reviewed in Search Console and traffic data, then anomalies return to the owning mapping or template rule.

Migration validation is a loop from mapping to observed crawl/search evidenceThe approved mapping is deployed, checked via HTTP crawl and logs, reviewed in Search Console and traffic data, then anomalies return to the owning mapping or template rule.Approved mappingsource · target · status · reasonDeploy rulesrouting / edge / appHTTP crawl + logsstatus · hops · final responseSearch evidenceGSC · sitemap · trafficDecisionaccept / fixdefect → owner + mapping/template ruleTechnical verification precedes traffic/ranking interpretation
Do not use ranking or traffic movement as the first proof that a redirect is technically correct. Verify the HTTP path and final canonical state first; then monitor migration processing over time.

Search Console's labels matter here. Page with redirect describes a non-canonical URL that redirects and therefore is not indexed; if that is your intended legacy URL state, it is not automatically a defect to 'validate away.' Redirect error is different: Google lists conditions such as an overly long chain, a loop, a URL that grows beyond limits, or a bad/empty URL in the chain. The live URL Inspection test follows redirects and tests the final URL, although it does not display every hop.

Run post-launch checks in order of causality

  1. 1

    Prove HTTP behavior on the migration inventory

    Re-crawl the saved old-URL list and compare observed start status, hop count, final address and final status against the approved map. Do this before interpreting Search Console or traffic changes.

    Pass criterionRepresentative and high-value old URLs resolve directly to the intended final destination with the intended permanent status.

  2. 2

    Verify final-page signals

    Crawl the new site for self-canonical, indexability, internal links, sitemap and hreflang. Use URL Inspection on representative/high-value final URLs when Google-selected canonical evidence matters.

    Pass criterionThe final page is the version the site consistently advertises as canonical; unexpected canonical selection becomes a separate diagnosis.

  3. 3

    Monitor old and new properties/cohorts over time

    Track crawling/error logs, Search Console indexing/search performance, analytics and new-sitemap processing. Google notes that significant moves can fluctuate while recrawling and reindexing proceeds and may take weeks or longer depending on site size and server capacity.

    Pass criterionOld-URL activity declines as intended, new-URL discovery/indexing/search activity increases, and unexpected 4xx/5xx/redirect-error cohorts have owners.

  4. 4

    Use Change of Address only for the move types it supports

    Google says the tool is for domain or subdomain moves. It is not needed for HTTP→HTTPS, www↔non-www on the same domain, or path changes within the same domain.

    Pass criterionThe migration runbook uses Change of Address only when the actual move is domain/subdomain scoped.

Durable policy

A migration is finished when legacy traffic is safely absorbed—not when launch day passes

Redirect retention, internal-link cleanup and monitoring need explicit owners after the release window.

Google recommends keeping migration redirects as long as possible, generally at least one year, so signals from old URLs can be recrawled and reassigned; for users, it suggests considering redirects indefinitely. That does not mean preserving internal redirect debt indefinitely. Update internal links and important external/profile/ad destinations to the new URLs so normal traffic does not depend on the compatibility layer.

Migration operating policy after launch
ControlOwner questionGood stateEscalation
Redirect inventoryAre all legacy URLs still covered?Known old URLs resolve according to approved mapNew 404/redirect-error cohort appears
Internal linksDoes current site still depend on redirects?Navigation/templates/content link to final URLs3xx inlinks increase after releases
Canonical/sitemap/hreflangDo current signals agree?All current discovery signals name final canonical URLsOld/alternate URLs re-enter current signals
Logs/Search ConsoleIs migration processing normally?Crawl shifts toward new URLs; no unexpected error clusterOld high-value URLs stop being crawled without target discovery
RetentionCan any redirect safely be removed?Age + traffic/backlink/user need reviewed before retirementRule removed only because 'a year passed'

For a CMS replatform, the same principles apply even when the domain does not change. A CMS migration SEO plan should preserve URL identity where possible; where paths do change, the redirect map, canonical templates, sitemap generation and internal-link components become one release surface. If indexing behavior remains unclear after HTTP validation, the Crawled — Currently Not Indexed diagnostic workflow is the better next step than repeatedly rewriting redirect rules.

Primary sources and documentation

Sources

Every changing search, browser, interface or technical-behavior claim in this guide is tied to a current primary source.

  1. Google Search Central Redirects and Google Search (opens in a new tab)Primary source for permanent vs temporary redirect interpretation, server-side redirects, 301/302/307/308 and JavaScript fallback limitations.
  2. Google Search Central Site Moves and Migrations (opens in a new tab)Primary source for URL mapping, site-move redirects, chain guidance, Change of Address, migration monitoring and redirect retention.
  3. Google Search Central How to Specify a Canonical with rel=canonical and Other Methods (opens in a new tab)Primary source for redirect/canonical/sitemap signal strength, self-canonicals and consistent internal linking.
  4. Google Search Central What is URL Canonicalization (opens in a new tab)Primary source for how Google selects representative URLs and why canonical declarations remain preferences rather than absolute commands.
  5. Google Search Console Help Page indexing report (opens in a new tab)Primary source for Page with redirect, Redirect error and URL Inspection behavior.
  6. RFC Editor RFC 9110: HTTP Semantics — Redirection 3xx (opens in a new tab)HTTP semantics for 301, 302, 307 and 308, including method-preservation differences.
  7. Screaming Frog Check Redirects In Bulk (opens in a new tab)Current SEO Spider workflow for finding 3xx responses, redirect inlinks, chains and loops.
  8. Screaming Frog How To Audit Redirects In A Site Migration Using The SEO Spider (opens in a new tab)Current list-mode migration workflow and All Redirects report fields for start/final URL, hops, loops, temporary redirects and final status.
  9. Apache HTTP Server Project Redirecting and Remapping with mod_rewrite (opens in a new tab)Official Apache examples for server-side permanent redirects and HTTPS canonical-host migrations.
  10. NGINX Module ngx_http_rewrite_module (opens in a new tab)Official NGINX semantics for return/rewrite directives and 301/302/303/307/308 responses.

Need help diagnosing and implementing the fix?

Turn a redirect list into a migration release contract

Metricum Lab can map legacy URL states, design redirect/canonical rules, validate migration paths before launch and monitor crawl/indexation evidence after release with explicit acceptance criteria.

Explore Technical SEO services