302 vs 301 Redirects: Fixing 302s That Should Be 301s
A 302 tells browsers and search engines that a page is only borrowing another address for a while. That is the right message for a weekend sale or a maintenance page, and the wrong one for a URL you have changed for good.
Stray 302s rarely come from a deliberate choice: they come from software that sends a temporary redirect unless you tell it otherwise. This guide shows how to spot them, which tools and frameworks default to 302, how to switch each one to a permanent code, and when leaving a 302 alone is the correct decision.
What the two codes actually say
Both codes do the same thing for a visitor: the browser receives a Location header and
loads the new address. The difference is the promise each one makes about the future.
- 301 Moved Permanently says the old address is finished. Search engines move the page's place in their index to the new URL over time, and browsers may remember the redirect and skip the request to the old address next time.
- 302 Found says the content is somewhere else for now, and the old address is still the real one. Browsers do not keep a 302 unless caching headers tell them to, and search engines are more inclined to keep showing the original URL.
Google has said that both 301 and 302 redirects pass ranking signals to the target, and that a 302 left in place for a long time may be treated much like a permanent one. So a stray 302 does not throw away the value of your old links. What it does is blur your intent. For a permanent move you want the new URL in search results, in reports and in every crawler's records as soon as possible, and a 302 asks them to keep an eye on the old one instead. Other search engines, SEO crawlers and link tools may be less forgiving than Google, and plenty of them simply report a 302 as a problem.
301, 302, 303, 307 and 308 side by side
Five redirect codes turn up in real reports. The newer ones exist because older browsers changed a form submission (POST) into an ordinary page request (GET) when following a 301 or 302.
| Code | Permanent? | Method kept? | Typical use |
|---|---|---|---|
| 301 | Yes | Not guaranteed | Changed page URLs, domain moves, HTTPS and host rules |
| 308 | Yes | Yes | The same as 301; the default permanent code in some frameworks and hosts |
| 302 | No | Not guaranteed | Short campaigns, maintenance, sending logged-out visitors to a login page |
| 307 | No | Yes | Temporary moves of pages or API endpoints that receive form posts |
| 303 | No | Changes to GET | After a form is submitted, sending the visitor to a thank-you or results page |
For search purposes, 308 is treated like 301 and 307 like 302. If the Redirect Checker shows a 308 on a permanent move, there is nothing to fix. A 303 after a form post is normal and should not be changed to a 301.
One trap: in Chrome's Network panel you may see 307 Internal Redirect when you type an
http:// address. That is the browser upgrading the request itself because of HSTS, not your
server sending a 307, so it will not appear in a server-side check.
Where stray 302s come from
Nearly every accidental 302 comes from a default. These are the ones worth knowing, because each needs an explicit permanent flag:
| Where | What sends 302 | Permanent version |
|---|---|---|
| Apache mod_alias | Redirect /old /new | Redirect 301 /old /new
or Redirect permanent /old /new |
| Apache mod_rewrite | [R] or [R,L] | [R=301,L] |
| nginx rewrite | rewrite ^/old$ /new redirect; | return 301 /new;
or the permanent flag |
| PHP | header('Location: /new'); on its own | header('Location: /new', true, 301); |
| WordPress code | wp_redirect() and wp_safe_redirect() without
a status | Pass 301 as the second argument |
| Express (Node.js) | res.redirect('/new') | res.redirect(301, '/new') |
| Django | redirect(), HttpResponseRedirect | redirect(url, permanent=True),
HttpResponsePermanentRedirect |
| Laravel | redirect('/new'), Route::redirect() | Route::permanentRedirect()
or a 301 status argument |
| Next.js config | permanent: false gives 307 | permanent: true gives 308 |
Hosting panels, redirect plugins and CDN rule builders usually offer a choice, but the pre-selected
option varies and some forms remember whatever was picked last. Static hosts that read a _redirects
file differ too: some default to 301 and some to 302, so always write the code on each line rather than
relying on the default.
/en-gb/ with a 302, think carefully
before changing it: a 301 would tell search engines the homepage itself no longer exists.How to check which code a URL returns
Browsers hide redirects, so use a tool that shows each hop. The Redirect Checker records the status
code and Location header for every step, which makes a 302 in the middle of a chain easy
to see. From a terminal, curl gives the same view:
curl -sI https://www.example.co.uk/spring-offer | grep -iE "^HTTP|^location"
HTTP/2 302
location: https://www.example.co.uk/offers/spring/
curl -sIL http://example.co.uk/old-page | grep -iE "^HTTP|^location"
HTTP/1.1 301 Moved Permanently
Location: https://example.co.uk/old-page
HTTP/2 302
location: https://example.co.uk/new-page
HTTP/2 200
The second example is common and easy to miss: the first hop is a proper 301 from the host rule, and the 302 is further down, added by whoever set up the page redirect. Check the whole chain, not just the first response.
In Search Console, the page indexing report and the URL Inspection tool show which URL Google has chosen as canonical. If a URL you redirected months ago is still the one Google shows, a 302 on that redirect is one of the first things to rule out, alongside canonical tags and internal links that still point at the old address.
For a whole site, run your list of old URLs through the checker in batches, or crawl the site with a desktop crawler, and filter the results by status code. Export the 302s into a sheet with columns for the old URL, the target, the layer that sent it and whether the move is permanent.
The fix, step by step
Step 1
List every redirect that answers 302
Run the Redirect Checker on your homepage, top landing pages and a list of old URLs from past
site versions, old sitemaps and backlink reports. Note every hop that returns 302 or 307, including ones
buried in the middle of a chain behind a correct 301. Put each one in a sheet with the requested URL,
the Location it points to and the date you found it, so nothing gets lost while you work
through them.
Step 2
Sort permanent moves from genuine temporary ones
Go through the sheet and mark each redirect as permanent or temporary. Renamed pages, merged articles, deleted products with a close replacement, HTTPS rules and host rules are permanent. A seasonal campaign, a maintenance page, a login redirect for logged-out visitors, a test, or a language or country picker may be genuinely temporary and should keep its 302 or 307. When in doubt, ask whether the old address will ever serve content again.
Step 3
Trace each 302 to the layer that sends it
Look at the response headers for clues. A server or cf-ray
header can show a CDN answered, an x-redirect-by header is added by WordPress and some plugins,
and a response that arrives very quickly with no application cookies often came from the edge. Then search
each likely place in turn: CDN rules, hosting panel redirects, .htaccess or server config,
the CMS redirect manager and application code.
Step 4
Change the status code at the source
Edit
the rule where it lives rather than adding a new one on top. In Apache write Redirect 301
or [R=301,L], in nginx use return 301, in PHP pass 301 as the third argument
to header(), and in frameworks use the permanent option. In plugin or panel forms, open
the existing entry and change its type. Keep a note of each change so it can be reversed if a move turns
out to be temporary.
Step 5
Point the redirect straight at the final URL
While you are editing each rule, check where its target goes. If the target itself redirects, change the rule to point at the final address with a 200 response, including the right protocol, host and trailing slash. A permanent redirect into a chain of other redirects still works, but it wastes a round trip on every request and makes the next change harder. One rule, one hop, one final URL is the goal for every row.
Step 6
Confirm the new code and update your own links
Purge the CDN and any page cache, then re-run the Redirect Checker and confirm each changed row now shows 301 or 308 followed by a 200. Test in a private window or with curl, because your own browser may hold old responses. Finally, update internal links, canonical tags, the XML sitemap, email templates and ad destinations so they use the new URL directly and stop relying on the redirect.
When a 302 is the right answer
Switching everything to 301 is its own mistake, because browsers can hold on to a 301 for a long time and you cannot recall it from a visitor's machine. Keep a temporary code for these cases:
- Short promotions.
/salepointing to this season's offer page, where next season it will point somewhere else. - Maintenance and outages. Sending visitors to a holding page while a section is rebuilt.
For a whole-site outage, a
503with aRetry-Afterheader on the original URLs is usually better than redirecting at all. - Logged-out visitors. A members page that sends visitors without a session to
/login?next=.... - Tests and experiments. Splitting traffic between two page versions for a limited time.
- Geography or language choices made from the visitor's settings, where the original URL must stay the main entry point.
- Anything you are not sure about yet. If a move might be undone within weeks, start with a 302 and change it to a 301 once the decision is final.
What matters for these is that they really are temporary. A "temporary" redirect from a campaign that ended two years ago is a permanent redirect in disguise: either change it to a 301 or, if the old page has nothing to replace it, let the old address return a 404 or 410.
Platform notes
WordPress
Redirect plugins and SEO plugins with redirect managers let you pick the type per entry. Check the
default in each plugin's settings before a large import, because a CSV imported without a code column
may take the default. Code in themes and small plugins often calls wp_redirect() without
a status, which gives 302. The WordPress
redirects guide covers finding which plugin answered.
Shopify and hosted platforms
Shopify's URL redirects feature creates permanent redirects, so a 302 on a Shopify store usually comes from an app, a theme script or a rule at a CDN in front of the store. Other hosted builders vary; if the platform gives no choice of code and sends 302 for moved pages, raise it with the platform's support and record the limitation.
Cloudflare and other CDNs
Edge redirect rules ask for a status code when you create them. Old rules made in legacy features may carry whatever code was chosen years ago, so list them all rather than assuming. The Cloudflare guide covers where each kind of rule lives.
Custom code and APIs
Search the codebase for the redirect helper your framework uses and check every call has an explicit status. A linting rule or code review checklist item that requires the status argument stops the default from creeping back in.
Keeping 302s where they belong
- Always write the code. In every config file,
_redirectsfile, plugin form and function call, state 301 or 302 explicitly instead of trusting a default. - Record intent. Add a column to your redirect sheet for "permanent" or "temporary until" with a date, so temporary ones are reviewed when that date passes.
- Review after campaigns. When a promotion ends, decide what happens to its URLs straight away rather than leaving the 302 in place indefinitely.
- Check after releases. Run the Redirect Checker on a fixed list of important old URLs after each deployment, CMS update or CDN change and look for any code that has changed.
- Be careful going the other way. Before turning a 302 into a 301, be sure. Once browsers have cached a permanent redirect, some visitors will keep following it even after you remove it.
1. Does a 302 redirect hurt SEO?
Not in the way it is often described. Google has said that 302 redirects pass signals to the target page, just as 301s do, and that a long-lasting 302 may be treated as permanent. The real cost is ambiguity: the old URL may stay in results for longer, reports stay cluttered, and other search engines and tools may handle it less generously. For a move that will not be reversed, a 301 states your intent clearly, which is why it is still the right choice.
2. How do I change a 302 redirect to a 301?
Find the layer that sends the 302 and change the code there. In Apache use Redirect 301 or add R=301 to the rewrite flags; in nginx use return 301; in PHP pass 301 as the third argument of header(); in WordPress code pass 301 to wp_redirect(); in frameworks use the permanent option. In a plugin, hosting panel or CDN rule, edit the existing entry and change its type. Then purge caches and re-check the URL to confirm the new code.
3. Why does my redirect show 302 when I set it up as permanent?
Usually because a different layer is answering first. A CDN rule, hosting panel redirect or plugin may catch the request before your intended rule runs, or your 301 may be the first hop of a chain whose second hop is a 302. Cached responses can also mislead you. Check the full chain with the Redirect Checker or curl, read the response headers for signs of which system replied, and test in a private window after purging caches.
4. Is a 308 redirect the same as a 301 for SEO?
For search purposes, yes. Google treats 308 as a permanent redirect in the same way as a 301. The difference is technical: a 308 guarantees the browser repeats the same request method, so a form post stays a post, while a 301 may be turned into an ordinary GET by the browser. Some frameworks and hosts use 308 by default for permanent redirects, and there is no need to change those to 301 unless an old client cannot handle 308.
5. How long can I leave a 302 in place?
As long as the move is genuinely temporary. There is no fixed limit, but a 302 that has been in place for months with no plan to reverse it is really a permanent redirect with the wrong label, and Google may start treating it as one anyway. Give every temporary redirect an end date in your records. When that date arrives, either remove the redirect because the original page is back, or change it to a 301.
6. Should I use a 302 or a 503 during site maintenance?
For a short outage of the whole site, a 503 Service Unavailable with a Retry-After header on the original URLs is usually the better signal, because it tells crawlers to come back later without suggesting any page has moved. A 302 to a maintenance page is acceptable for a single section for a short time. Avoid a 301 for maintenance, because browsers may remember it and keep sending visitors to the holding page after the site is back.
7. Will changing a 302 to a 301 cause any problems?
Usually not, as long as the move really is permanent. The risk is that browsers can cache a 301 for a long time, so if you later want the old URL back, some returning visitors will keep being sent to the new one until their cache clears. Before switching, confirm the old address will never serve its own content again. Also make sure the target returns a 200, so you are not making a chain or loop permanent.
The Redirect Checker follows each URL through its full chain, recording every status code, Location header and hop time, and flags 302s that should be 301s alongside long chains, loops, HTTPS gaps and mixed content.
Open Redirect Fixes โ