Module 033 Intermediate 15 min read

HTTP Status Codes

200, 301, 302, 304, 307, 308, 404, 410, 451, 500, 503 — what each means to a crawler, when to use which, and how to diagnose redirect chains and soft 404s.

By SEO Mastery Editorial

HTTP status codes are how your server tells crawlers what just happened. 200 means “here’s the page.” 301 means “moved permanently — pass the link equity.” 404 means “I have no record of this.” 503 means “I’m broken, come back later.” Wrong status codes are quietly destructive: a 302 used in place of a 301 leaks ranking signal for months.

Redirect chain simulator

Define your URL rules, pick a starting URL, watch Googlebot walk the chain. Spot loops, dead ends, and equity bleed.

URL rules

Crawler trace

01/best-crm-2022301

Moved PermanentlyPermanent redirect. Equity transfers; canonical updates over time.

/best-crm-2023

02/best-crm-2023301

Moved PermanentlyPermanent redirect. Equity transfers; canonical updates over time.

/best-crm-2024

03/best-crm-2024302

Found (temporary)Temporary redirect. Google passes equity post-2016 but treats it as soft.

/best-crm-software

04/best-crm-software200

OKFinal destination — content served.

Resolved cleanly in 4 hops — equity preserved 89%

3 redirect hops — collapse intermediate 301s into a single hop to maximise equity and crawl budget.

In production, change the second 301 above to a 302 and watch the equity number drop. Add a rule pointing back to an earlier URL to trigger a loop. The simulator stops at 12 hops to mirror browser/crawler behaviour.

TL;DR

  • 301 vs 302 still matters in 2026, despite Google’s “we treat them similarly” language. John Mueller and Gary Illyes have both confirmed Google passes link equity through both, but 302s leave the original URL in the index for longer and create ambiguity that delays consolidation. Use 301 for permanent moves, period.
  • Soft 404s are the silent killer. A page that returns 200 OK but renders “no results” content is treated as a soft 404 by Google’s classifier and gets dropped from the index. Common culprits: empty search results, expired listings, deleted profile pages.
  • Use 410, not 404, when content is permanently gone. Google deindexes 410 URLs roughly 2x faster than 404s. For removed products, deleted accounts, or expired campaigns, 410 is the honest signal.

The mental model

HTTP status codes are like the post office’s stamps on returned mail. Each stamp tells the sender something different: “delivered” (200), “moved, here’s the new address” (301), “moved temporarily, write to old address” (302), “no such address, never was” (404), “this address has been retired” (410), “the post office is closed today” (503).

Crawlers calibrate their behavior to these stamps. Googlebot retries 503s (assumed temporary) for up to ~14 days before treating the URL as permanently lost. It deindexes 410s faster than 404s. It follows 301 chains up to about 5 hops before giving up. It treats 451 (“unavailable for legal reasons”) as honest — the URL stays in the index but is hidden in the affected jurisdiction.

A status code is also a contract about caching. 304 Not Modified is a 200’s lighter sibling: the server confirms the content has not changed since the client’s last fetch, saving bandwidth and signaling stable freshness. Browsers, CDNs, and Googlebot all respect 304s when they have the conditional headers (If-Modified-Since, If-None-Match) to make the request.

Deep dive: the 2026 reality

The status codes every SEO must know cold:

CodeNameMeaning to GooglebotWhen to use
200OKIndex this pageNormal content delivery
301Moved PermanentlyReplace old URL with new in indexPermanent URL change
302Found / Temporary RedirectKeep old URL; new URL is temporaryA/B tests, geo-redirects, login flows
304Not ModifiedDon’t recrawl; reuse cached copyConditional requests with If-Modified-Since
307Temporary Redirect (strict)Like 302 but preserves HTTP methodAPI redirects, POST preservation
308Permanent Redirect (strict)Like 301 but preserves HTTP methodPermanent moves where method matters
404Not FoundDrop URL after extended absenceDeleted page, no replacement
410GoneDrop URL faster than 404Permanently retired content
451Unavailable For Legal ReasonsHide in affected region; keep elsewhereDMCA, GDPR, court order
500Internal Server ErrorCrawl error; retry laterDon’t use deliberately — fix the bug
503Service UnavailableTemporarily skip; retry up to ~14 daysMaintenance, rate limit
429Too Many RequestsSlow crawl rateRate limiting; honored by Bingbot, partially by Googlebot

301 vs 302 in 2026. Google’s official line for years has been “PageRank flows through both.” Both Mueller (2016) and Illyes (2020, 2024) have repeated this. In practice, the index behavior differs:

  • A 301 typically deindexes the source URL within 1–4 weeks of stable observation.
  • A 302 keeps the source URL indexed indefinitely; if you redirect from /old/ to /new/ with a 302, both URLs may stay in the index, with /old/ outranking /new/ for months.
  • 302s on home/category-level redirects are particularly disruptive — they suggest the redirect is testing, and Google holds the index in suspense.

Use 301 unless the redirect is genuinely temporary. Document any 302 in your codebase with a comment explaining why.

Soft 404 detection. Google flags pages as soft 404 when:

  • The HTTP status is 200 but the rendered content reads like a 404 (small text, “Page not found,” “no results”).
  • The page has substantially less content than other pages on the site.
  • The URL is reachable but the resource it depended on (a product, a profile) was deleted.

The fix is binary: either return a real 404/410 status or beef up the page with genuine content. Empty search results pages are the most common cause; either return 410 for queries with zero results or render meaningfully different content per query.

Redirect chains and loops. Each hop costs crawl budget and a small amount of link equity. Google has repeatedly stated it follows up to 5 hops in a chain; beyond that it gives up and may not pass full equity. Loops (A → B → A) are a hard fail and the URL is treated as broken. Bingbot is stricter — it follows up to 3 hops.

Visualizing it

flowchart TD
  A[Bot requests URL] --> B{Server responds with status}
  B -->|2xx| C[Read content]
  B -->|301| D[Follow to new URL, mark old as moved]
  B -->|302| E[Follow temporarily, keep old indexed]
  B -->|304| F[Use cached copy]
  B -->|404| G[Mark missing, retry occasionally]
  B -->|410| H[Drop from index quickly]
  B -->|451| I[Index but hide in jurisdiction]
  B -->|500| J[Treat as transient error, retry]
  B -->|503| K[Skip, retry within 14 days]
  D --> L{Chain depth>5?}
  L -->|Yes| M[Give up, treat as broken]
  L -->|No| C

Bad vs. expert

The bad approach

The classic redirect chain born from years of incremental URL changes:

GET /products/red-widget
→ 301 /products/widget-red
→ 301 /products/widgets/red
→ 301 /shop/widgets/red
→ 301 /shop/widgets/red/
→ 200 OK

Five hops to deliver the same product page. Googlebot follows it (just barely), but every hop costs crawl budget across thousands of similar URLs. Bingbot drops the chain at hop three.

The soft 404 pattern, often produced by ecommerce inventory systems:

<!-- /products/discontinued-widget returns 200 OK -->
<h1>Sorry, this product is no longer available</h1>
<p>Browse our <a href="/products">full catalog</a>.</p>

The status is 200; the content is empty; Google’s classifier flags this as a soft 404 and drops the URL. The product gets no SEO value during its phase-out window, and external links to it bleed equity.

The silent 302:

# Bad: 302 used for a permanent move
location /old-blog {
  return 302 /blog;
}

Six months later, /old-blog still ranks for the brand query while /blog languishes. The team blames “Google” when the cause is one missing digit.

The expert approach

Flatten redirect chains to one hop. Maintain a redirect map keyed to the original URL, not the most recent one:

# Each old URL points directly to the final destination
map $request_uri $final_redirect {
  ~^/products/red-widget$            /shop/widgets/red/;
  ~^/products/widget-red$            /shop/widgets/red/;
  ~^/products/widgets/red$           /shop/widgets/red/;
  ~^/shop/widgets/red$               /shop/widgets/red/;
  default                            "";
}

server {
  if ($final_redirect != "") {
    return 301 $final_redirect;
  }
}

Real 410 for permanently retired URLs:

location ~ ^/products/(discontinued-widget|expired-bundle-2024)$ {
  return 410;
}

Custom 410/404 page that helps users without faking the status:

<!-- Served with HTTP 410 status, not 200 -->
<!doctype html>
<html lang="en">
<head>
  <title>This product is no longer available</title>
  <meta name="robots" content="noindex">
</head>
<body>
  <h1>This product is no longer available</h1>
  <p>You might be looking for one of these:</p>
  <ul>
    <li><a href="/shop/widgets/red/">Red widget (replacement)</a></li>
    <li><a href="/shop/widgets/">Browse all widgets</a></li>
  </ul>
</body>
</html>

Maintenance window with 503 + Retry-After:

location / {
  return 503;
  add_header Retry-After "Sat, 10 May 2026 14:00:00 GMT";
}

Conditional 304s for static-ish content via Last-Modified and ETag:

GET /blog/canonicals HTTP/1.1
If-Modified-Since: Wed, 01 May 2026 12:00:00 GMT

HTTP/1.1 304 Not Modified
ETag: "abc123"
Last-Modified: Wed, 01 May 2026 12:00:00 GMT

Do this today

  1. Pull a Screaming Frog SEO Spider crawl. Open Response Codes > Redirection (3xx) and Reports > Redirects > All Redirects. Anything with Chain Length > 1 is a hop you can flatten.
  2. In Screaming Frog Reports > Redirects > Redirect Chains, export and review. For each chain, update your nginx/Cloudflare/CDN map to point the original URL directly at the final URL.
  3. Filter Screaming Frog Response Codes > Client Error (4xx) for 404. For each 404 with significant inbound internal links, decide: is there a relevant replacement (301), or is the content gone (return 410)?
  4. In GSC > Indexing > Pages, expand Not indexed > Soft 404. Each URL listed is leaking value. Visit each and decide: return real 410, render genuine content, or 301 to a related URL.
  5. Check 302 usage. Search your config and codebase for 302 and Found. Each instance needs a comment justifying why this is temporary. Convert any without justification to 301.
  6. Verify your maintenance / rate-limit story uses 503, not 500. Test by making a request to your maintenance page and confirming curl -I returns HTTP/1.1 503 Service Unavailable plus a sane Retry-After header.
  7. Set up Bing Webmaster Tools > Crawl Information monitoring. Bing reports 4xx/5xx more aggressively than GSC and surfaces issues earlier — especially relevant since ChatGPT Search and Copilot pipe through Bing.
  8. For non-HTML resources, run curl -I on representative URLs (PDFs, images, CSV downloads). Confirm they return 200 with sensible cache headers, not 304 errors or 5xx.
  9. Add a synthetic monitor (Pingdom, Uptime Robot, or your APM) that asserts your homepage returns 200, your sitemap returns 200, your robots.txt returns 200, and a known-deleted URL returns 410. Alert on any drift.
  10. For migrations, build a status-code regression test that runs after every deploy: a CSV of 50 URLs and their expected status codes, plus a script that hits each and fails the deploy on mismatch.

Mark complete

Toggle to remember this module as mastered. Saved to your browser only.

More in this part

Part 5: Technical SEO

View all on the home page →
  1. 026 Technical SEO Fundamentals 12m
  2. 027 Site Architecture 20m
  3. 028 Crawling & Indexing 17m
  4. 029 robots.txt Deep Dive 15m
  5. 030 XML Sitemaps 12m
  6. 031 Canonical Tags 20m
  7. 032 Meta Robots & X-Robots-Tag 13m
  8. 033 HTTP Status Codes You're here 15m
  9. 034 Crawl Budget Management 16m
  10. 035 JavaScript SEO 26m
  11. 036 Core Web Vitals 17m
  12. 037 Site Speed & Performance 19m
  13. 038 HTTPS & Site Security 12m
  14. 039 Mobile SEO & Mobile-First Indexing 14m
  15. 040 Structured Data & Schema Markup 17m
  16. 041 International SEO (hreflang) 19m
  17. 042 Pagination 12m
  18. 043 Faceted Navigation 26m
  19. 044 Duplicate Content 13m
  20. 045 Site Migrations 24m