303 See Other
Redirect
The server is redirecting the client to a different URL using a GET request, regardless of the method used in the original request. 303 was introduced in HTTP/1.1 specifically to give servers a way to instruct clients to switch to GET. Solving the ambiguity around 302 where the spec said one thing and clients did another. The canonical use case is the Post/Redirect/Get pattern after a form submission.
When does this happen?
Return 303 after a successful POST when you want the browser to fetch a different resource with GET. Typically a result, confirmation, or detail page. The Post/Redirect/Get pattern prevents the dreaded "are you sure you want to resubmit the form?" dialog when a user refreshes the page after submitting. Without the 303, refreshing would resend the POST and could create duplicate orders, comments, or payments. The Location header is mandatory. Unlike 302, the method change to GET is required by spec, not just convention. Every conforming client will issue a GET on the next request even if the original was POST or PUT. The body of the 303 response can include a hypertext explanation for clients that do not auto-follow. 303 should not be used as a generic redirect for GET requests. Use 302 or 307 instead, since 303 carries the specific semantic of "and switch to GET".
Common causes
- Post/Redirect/Get pattern. Form submission succeeds and the server redirects to a result or detail page.
- Checkout completion that redirects from POST /checkout to GET /orders/{id}/confirmation.
- API endpoint that performs an action via POST and returns 303 to a GET endpoint describing the result, e.g. POST /jobs returning 303 to GET /jobs/{id}.
- Login form submission that redirects via 303 to the dashboard after authentication.
- OAuth callback handlers that complete a token exchange via POST and redirect to a profile or home page.
- PUT or DELETE operations that complete and want to redirect the client to a summary GET endpoint.
How to fix it
- Verify the destination URL accepts GET. Clients will issue GET regardless of the original method, so if the target is POST-only the redirect breaks.
- Confirm the Location header is set. A 303 without Location is malformed and clients will fail in confusing ways.
- Distinguish 303 from 302 in your handlers. Use 303 specifically when you want method downgrade to GET, and use 307 when you want to preserve the original method.
- For API endpoints, document that 303 means "go fetch this URL with GET" so callers do not retry with the original POST body.
- Do not return 303 for idempotent operations where 200 with a body would be simpler. 303 adds a round-trip and is only worth it when you want browser refresh-safe behavior.
- If your framework treats 302 and 303 identically, audit the actual HTTP responses with curl. Many libraries silently emit the wrong code.
Real-world examples
- User submits a contact form via POST /contact.
- Server saves the submission and returns 303 with Location: /contact/thanks. Browser issues GET /contact/thanks. Refreshing the thanks page does not resubmit the form.
- User completes checkout via POST /checkout with their cart and payment details.
- Server creates the order and returns 303 with Location: /orders/abc123/confirmation. Browser GETs the confirmation page. Refreshing the confirmation page does not double-charge.
- API client kicks off a long-running export via POST /v1/exports.
- Server creates a job and returns 303 with Location: /v1/exports/job-42. Client issues GET on that URL to poll status.
- User submits a comment via POST /posts/123/comments.
- Server appends the comment and returns 303 with Location: /posts/123#comment-456. Browser issues GET on the post page with the new comment visible.
How it differs from related codes
HTTP 302
302 is ambiguous about method handling. Most clients downgrade to GET but the spec does not require it. 303 explicitly mandates GET for the follow-up request.
HTTP 307
307 is the opposite of 303. It requires the client to preserve the original method and body. Use 303 to switch to GET, 307 to keep POST as POST.
HTTP 200
Returning 200 directly from a POST gives the client the result inline. 303 adds a round-trip but makes the browser refresh-safe. Pick 303 when refresh-resubmit is a real risk.
Related status codes
See HTTP 303 in your redirect chains?