How to check if a redirect is 301 or 302

Run curl -sI <url> without -L and read the status line. HTTP/2 301 is permanent and browsers cache it, HTTP/2 302 is temporary and they ask again. For a one-line answer use curl -s -o /dev/null -w '%{http_code} %{redirect_url}' <url>.

Why check this

The code decides how long the redirect lives in other people's caches. A 301 that should have been a 302 is close to permanent: browsers store it, and a visitor who hit the wrong version keeps being sent there after the rule is removed. That is the failure this check prevents, and it takes a cache clear on every affected client to undo.

Run it whenever a route moves, a domain is merged, or a marketing redirect is added for a campaign with an end date. A campaign redirect must be a 302.

Prerequisites

Steps

  1. Step 1.

    Ask for the headers only, and do not follow. Without -L, curl shows the redirect itself instead of the destination.

    curl -sI 'https://httpbin.org/redirect-to?url=https://example.com/&status_code=301' | grep -i -E '^HTTP|location'
    
    HTTP/2 301
    location: https://example.com/
  2. Step 2.

    Repeat against the temporary form of the same redirect and compare the status line.

    curl -sI 'https://httpbin.org/redirect-to?url=https://example.com/&status_code=302' | grep -i -E '^HTTP|location'
    
    HTTP/2 302
    location: https://example.com/

    The location is identical. Only the code differs, and only the code tells a client whether to remember it.

  3. Step 3.

    Get the code and the destination on one line, which is the form to paste into a bug report.

    curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' 'https://httpbin.org/redirect-to?url=https://example.com/&status_code=301'
    
    301 https://example.com/
  4. Step 4.

    Check what the code does to a POST. Send a body through a 302, then through a 307.

    curl -sL -d 'a=b' -o /dev/null -w '%{num_redirects} redirects, final %{http_code}, method %{method}\n' 'https://httpbin.org/redirect-to?url=/post&status_code=302'
    
    1 redirects, final 405, method GET

    The 302 turned the POST into a GET, and /post answered 405. With status_code=307 the same command returned 1 redirects, final 200, method POST.

  5. Step 5.

    Follow a chain and count the hops, so a single check covers every code on the way.

    curl -sIL https://httpbin.org/redirect/2 | grep -i -E '^HTTP|^location'
    
    HTTP/2 302
    location: /relative-redirect/1
    HTTP/2 302
    location: /get
    HTTP/2 200

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | 301 or 308 | Permanent. Clients and search engines may cache it without an expiry | Correct only when the old URL is never coming back. | | 302, 303 or 307 | Temporary. The client asks the original URL again next time | Correct for campaigns, maintenance pages and locale guesses. | | 301 with no location | The redirect is broken | The client has nowhere to go. Treat it as a failure, not a redirect. | | 200 where a redirect was expected | The rule did not fire | Check the exact path, the trailing slash and the scheme you requested. |

Common mistakes

Sign: curl prints 200 and the redirect seems to have gone.Cause: The command used -L, so curl followed the hop and printed the destination status. Drop -L, or add -I -L and read every status line the way step 5 does.
Sign: The redirect was changed from 301 to 302 and testers still land on the old target.Cause: A 301 has no expiry of its own, so the browser keeps it until the cache is cleared. Test the change in a fresh profile or with curl, which caches nothing, before believing a report from a browser.
Sign: A form submission breaks after a redirect rule is added.Cause: A 301 or a 302 lets the client change POST to GET, and most do. On this run a POST through a 302 arrived as a GET and the endpoint answered 405, while the same POST through a 307 arrived as a POST and answered 200. Use 307 or 308 on anything that carries a body.

What to check next

FAQ

What is the difference between a 301 and a 302 redirect?

A 301 says the resource moved for good, so clients and search engines may cache the mapping and pass ranking signals to the new URL. A 302 says the move is temporary, so the client keeps using the original URL and asks again next time.

How to test 301 redirects?

Request the URL with curl -sI and read the status line, then confirm location points where you expect. Repeat from a fresh browser profile, because an old 301 may still be cached.

Does a 302 hurt search rankings?

It tells the crawler to keep indexing the original URL. That is correct for a temporary move and wrong for a permanent one, where the old URL stays in the index instead of the new one.

Which code should a campaign redirect use?

302, or 307 if the request may carry a body. The rule is removed when the campaign ends, and a permanent code would outlive it in client caches.

Does curl follow redirects by default?

No. Without -L it prints the redirect response and stops, which is what this check needs. A production example: curl -sI http://www.cloudflare.com/ returned HTTP/1.1 301 Moved Permanently with Location: https://www.cloudflare.com/.

Verified

Verified by Maks Vernycurl 8.21.0

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic4 minpublished updated Maks Verny