301, 302 or 308? Picking the right redirect

What each code tells browsers and search engines, and how the wrong one leaks rankings or breaks POST requests

Updated Sep 30, 2026

One arrow splitting into a permanent stone path, a temporary movable path, and a rail path that carries a parcel unchanged

What a redirect actually says

Every redirect is a short message from your server to whoever asked: "not here, go there, and here is how much to trust that". The URL is the "there". The status code is the "how much to trust it". Browsers, search engines, link previewers and API clients all read that second part and behave differently depending on it, which is why the wrong code causes bugs that look like nothing else: a link you updated an hour ago that still sends people to the old page, an API call that silently turns into a GET, a campaign URL that Google decided to index instead of your landing page.

There are seven 3xx codes in the HTTP spec. Three of them cover almost everything a short link ever needs.

The three codes you actually need

CodeMeaningCached by browsersMethod preservedTypical use
301Moved permanentlyYes, aggressivelyNo, POST may become GETDomain moves, retired URLs
302Found, moved temporarilyNo, by defaultNo, POST may become GETCampaigns, bio links, anything you will re-point
308Moved permanently, strictYesYesAPI endpoints, webhooks, machine callers

A 301 tells the client the move is final. Browsers store that answer and skip your server on the next visit. Search engines consolidate ranking signals onto the new URL and drop the old one from the index over time. It is the right code exactly once: when the old URL will never mean anything else again.

A 302 says the opposite: the resource is elsewhere for now, keep asking me. Nothing is cached unless you add cache headers yourself, the original URL stays the canonical one in search results, and every visitor goes through your server again, where your rules and analytics live.

A 308 is the strict sibling of 301. It is permanent, but the client must repeat the request exactly as it was, same method, same body. That last property is the whole reason it exists.

The POST-to-GET trap

The original HTTP specification allowed clients to change a POST into a GET when following a 301 or 302, and most browsers still do. That is harmless for a person clicking a link. It is destructive for a webhook or an API client: the receiver gets a GET with no body, returns 200 because the URL exists, and the sender concludes the delivery succeeded. Nothing was delivered.

If a machine calls the URL with anything other than GET, use 307 or 308. If you do not know who calls it, assume a machine eventually will.

See which code a link sends
$ curl -sI https://go.example.com/launch | head -2
HTTP/2 302
location: https://example.com/spring-launch

Short links exist to be re-pointed. A campaign target changes when the landing page is rebuilt. A QR code on packaging outlives three product pages. A bio link rotates weekly. The moment you serve any of those as a 301, the browsers that already followed it stop asking. You update the destination, the analytics flatline for returning visitors, and someone files the classic bug: "I changed the link but nothing happened".

This is not a rii.link quirk. It is how permanent redirects are designed to work, and it is why every rii.link redirect is a 302 unless you choose otherwise. In the dashboard the status code is a per-redirect setting, so the exceptions are still one click away.

What search engines do with each code

Google has said for years that no 3xx redirect loses ranking signals in itself. What differs is what gets indexed. After a 301 or 308, the destination becomes the indexed URL. After a 302 or 307, the original URL usually stays indexed and the destination is treated as a temporary stand-in. Both are correct behaviours for the right situation:

  • Moving example.com/blog/old-name to example.com/blog/new-name for good: 301. You want the new URL to take over.
  • A short link go.example.com/spring pointing at this year's landing page: 302. You do not want example.com/spring-2026 to inherit the short link's history and then vanish when the campaign ends.

The confusion usually comes from treating short links as if they were page moves. They are not. They are pointers, and pointers should stay temporary.

Rules of thumb

  1. Will this URL ever point somewhere else? Then 302. Campaigns, bio links, QR codes, seasonal targets, anything with an owner who might change their mind.
  2. Is it a real, final move? Domain migrations, retired product pages, http to https, www to apex: 301.
  3. Do machines call it with a body? Webhooks, API base URLs, form actions: 308 for permanent, 307 for temporary.
  4. Not sure? Start with 302. You can always upgrade a redirect to permanent later. You cannot un-cache a 301 from the world's browsers.

Each redirect you build in the rii.link dashboard carries its own status code, chosen from 301, 302, 307 and 308, with 302 as the default; links created through the API always answer with a 302. Two neighbouring settings matter for the same reasons:

  • Append original query params adds the visitor's original query string to the destination, so ?utm_source=newsletter survives the hop and lands in your analytics. It is off by default.
  • Append original path carries the requested path over to the destination, so one catch-all 301 on an old domain moves every page to the new one, path preserved, and inbound links keep working.

Written by

Florian

Florian builds rii.link, the routing layer behind short links: redirects, dynamic responses and links that answer AI agents. He writes The Redirect about what happens between a click and its destination.

On this page