303 See Other: the redirect after a form POST
303 See Other tells the client that the request was handled and the result is at a different URL, which it should fetch with GET. It is the correct response to a form submission that should land the visitor on a result or confirmation page rather than leave them on the POST.
What 303 is for
A 303 is not really about moving a resource. It is about separating an action from its result. The client sends a POST, the server performs the action, and then instead of returning the result in the POST response it answers with 303 and a Location header. The client makes a fresh GET to that location, and the browser's address bar ends up on an ordinary URL that can be bookmarked and reloaded.
The defining rule is the method: whatever the original request method was, the follow-up request to the Location URL is a GET. That is the opposite of 307 and 308, which require the original method and body to be preserved.
Post/Redirect/Get, and the problem it solves
Without a redirect after a POST, the result page belongs to the POST request, which causes familiar problems:
- Pressing reload re-submits the form, so the order is placed or the comment posted twice. Browsers warn about this with a "confirm form resubmission" dialog, which is itself a poor experience.
- The page cannot be bookmarked or shared, because the URL does not describe the content.
- The back button lands on a resubmission prompt instead of the previous page.
Post/Redirect/Get fixes all of them. The POST performs the action and answers 303; the browser GETs the result URL; a reload now just re-fetches a harmless page. This is why almost every framework has a "redirect after save" helper.
303 vs 302 vs 307
- 302 Found is a temporary redirect whose method handling is historically ambiguous. Most clients turn a POST into a GET when following it, which is usually what you wanted, but the standard does not require it. Many frameworks still emit 302 for post-then-redirect and it works in practice.
- 303 See Other makes that behavior explicit and required. If your intent is "do not repeat my POST, go and GET this instead", 303 states it unambiguously.
- 307 Temporary Redirect is the one to avoid here. It requires the POST to be repeated against the new URL, which for a form submission means performing the action twice.
One practical caveat: 303 was introduced in HTTP/1.1, so a client speaking HTTP/1.0 may not understand it. Some frameworks default to 302 for that reason. On the modern web it is rarely a real constraint, but it explains the split in defaults you will see across languages.
Where else 303 shows up
- APIs that start long-running work. A POST kicks off a job and answers 303 with the status URL of that job, or 202 with a status URL in the body. Both patterns are in use; 303 has the advantage that a browser follows it automatically.
- OAuth and sign-in flows. Some authorization servers redirect back to the client application with a 303 so the callback is fetched with GET regardless of how the request arrived.
- Linked-data and content-negotiation setups that distinguish a real-world thing from the document describing it, and use 303 to send clients from the identifier to the document.
How to use and verify it
- After a successful POST, return 303 with an absolute
Locationpointing at the result page. Do not render the result inline. - Make the destination idempotent. It will be fetched again on every reload and every back-navigation, so it must be safe to GET repeatedly.
- Carry any one-time message, such as "your changes were saved", in session state or a query parameter rather than in the redirect body. A 303 response body is not normally displayed.
- Verify the code and the target:
You should seecurl -si -X POST -d "name=test" https://example.com/submit | grep -Ei "^(HTTP|location)"HTTP/1.1 303 See Otherand the result URL. - Follow it and confirm the second request is a GET that returns 200. If you see a repeated POST in your server logs, the rule is a 307 and needs changing.
- Check that the destination is not itself a redirect. A 303 into a 301 into the real page is three round trips for one form submission.
Monitoring the endpoints behind a form
A broken post-then-redirect flow is invisible to a check that only fetches the home page. The site is up, the form renders, and only the people who submit it hit the failure. Catch it early by watching the endpoints behind the action, such as the result page, the confirmation URL and the job status route, and asserting the expected status code and content on each. HostTracker offers 13 monitor types and alerts by email, SMS, voice call, Slack, Telegram and more. Check what any URL returns with the HTTP check tool, and read 301 vs 302 and the full 3xx redirection family guide for the neighboring codes.