When to Use 302 Redirects During Phased Migrations

Problem Statement

Phased migrations route traffic in stages while content parity, infrastructure, and indexation are still being verified. The risk is that a permanent 301 deployed during staging, an A/B test, or an incremental rollout tells search engines the move is final before the new URL is actually validated — the original URL is dropped from the index, canonical signals conflict, and crawl budget is wasted re-evaluating an unstable target. The correct tool for a deliberately reversible state is a temporary 302, paired with strict cache headers so edge networks never persist the temporary route. This page sits under the 301 vs 302 Decision Trees section and isolates temporary routing from permanent equity transfer.

302 versus 301 decision tree for phased migrations A decision tree routing a migration phase to a 302 when the move is reversible and to a 301 once content parity and canonical tags are verified. 302 vs 301 in a Phased Move Is the move final? Use 302 (temporary) Use 301 (permanent) parity + canonicals? staging / A/B / rollout add no-store headers parity verified equity transfers No Yes Promote 302 to 301 once the phase is confirmed
A 302 holds the reversible state; once parity and canonicals are verified, the same route is promoted to a 301.

When to Use This Approach

  • The new URL is not yet at full content parity with the legacy URL and may be rolled back within the phase window.
  • You are running an A/B or canary test and want the original URL to retain its index position for the duration.
  • Staging or pre-production environments are being routed to from a shared host and must not leak into the index.
  • A maintenance window or geo-targeted experiment needs traffic diverted temporarily without signalling a permanent move.
  • You expect to promote the route to a permanent 301 within weeks, not months — a 302 that outlives its phase becomes a liability.

Step-by-Step Instructions

1. Generate Temporary Rules From Your Mapping CSV

Drive the 302 set from the same source of truth as your permanent rules so phases stay synchronised with the master CSV Mapping Workflows schema. Generate Apache rules directly from a two-column CSV.

# Generate 302 rules from a CSV with columns: old_path,new_path
awk -F',' 'NR>1 {
    gsub(/\//, "\\/", $1)                    # escape slashes in the source path
    print "RewriteRule ^" $1 "$ " $2 " [R=302,L]"
}' legacy_urls.csv > temp_redirects.conf

2. Deploy the 302 With Anti-Cache Headers

Edge networks and browsers will cache a 302 unless told not to. Always set Cache-Control: no-store on the temporary route so the move stays reversible.

# Nginx — temporary route, never cached at the edge
location ~ ^/old-path/(.*)$ {
    return 302 /new-path/$1;
    add_header Cache-Control "no-store, max-age=0";   # prevent edge-cache poisoning
}
# Apache .htaccess — NE keeps already-encoded characters intact
RewriteEngine On
RewriteRule ^old-path/(.*)$ /new-path/$1 [R=302,L,NE]
Header always set Cache-Control "no-cache, no-store, must-revalidate"

3. Mirror the Rule at the Edge When Needed

If a CDN sits in front of origin, replicate the 302 at the edge so the temporary route is consistent everywhere. The current Cloudflare Workers ES-modules syntax is below (the legacy addEventListener('fetch', ...) form is deprecated).

// Cloudflare Worker — temporary edge redirect, query string preserved
export default {
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname.startsWith('/old-path/')) {
      const newPath = url.pathname.replace('/old-path/', '/new-path/');
      return Response.redirect(url.origin + newPath + url.search, 302);
    }
    return fetch(request);
  }
}

4. Promote to 301 When the Phase Is Confirmed

Once content parity holds and canonical tags point at the new URL, switch the status code to lock in equity transfer. Deploy during a low-traffic window and resubmit the URL for inspection.

# Same rule, promoted to permanent once the phase is verified
RewriteRule ^old-path/(.*)$ /new-path/$1 [R=301,L,NE]
Header always set Cache-Control "public, max-age=86400"

Worked Example

A retailer migrating shop.example.com runs the new product templates behind a 10% canary for two weeks. During the canary, /old-path/blue-widget returns a 302 to /new-path/blue-widget:

GET /old-path/blue-widget HTTP/1.1
Host: shop.example.com

HTTP/1.1 302 Found
Location: https://shop.example.com/new-path/blue-widget
Cache-Control: no-store, max-age=0

Because the response is 302 Found with no-store, Google keeps /old-path/blue-widget indexed and the CDN never persists the hop — if the canary regresses, removing the rule instantly restores the original behaviour. After two weeks of clean parity checks the team flips the rule to R=301, the response becomes 301 Moved Permanently, and index consolidation begins. Flatten any intermediate hops introduced by the phase using Redirect Chain Elimination before the 301 goes live.

Verification

Confirm the response is a single-hop 302 with no caching, then re-verify after the promotion to 301. If indexation degrades during the phase, replace return 302 with return 410 (Gone) to halt crawler traversal, purge the CDN via the provider API, and fall through to normal routing.

# Confirm status, single hop, and final target
curl -sI -L -o /dev/null \
  -w "%{http_code} %{num_redirects} hops -> %{redirect_url}\n" \
  https://shop.example.com/old-path/blue-widget
# Expect: 302 1 hops -> https://shop.example.com/new-path/blue-widget

# Confirm the edge is not caching the temporary route
curl -sI https://shop.example.com/old-path/blue-widget | grep -i cache-control
# Expect: cache-control: no-store, max-age=0

FAQ

How do I stop a CDN from permanently caching a 302 during a phased migration? Set Cache-Control: no-store, max-age=0, must-revalidate and Pragma: no-cache on every 302 response, and add a CDN cache rule that bypasses caching for the matching paths. Validate with curl -sI https://domain.com/path | grep -i cache-control.

How do I safely convert a 302 to a 301 without triggering re-evaluation penalties? Verify content parity and canonical tags first, change the rule to R=301, deploy during a low-traffic window, then submit the URL via Google Search Console URL Inspection. Monitor the Indexing report for 72 hours before removing the old sitemap entry.

Can I preserve query parameters in CSV-generated 302 rules? Yes. In Apache use RewriteRule ^old/(.*)$ /new/$1 [R=302,L,QSA]; in Nginx use return 302 /new/$1$is_args$args;. Strip leading slashes from the CSV old_path column so the generated rule does not produce a double slash.

Related

← Back to 301 vs 302 Decision Trees