HTTP 303 vs 302 — See Other vs Found Status Code Comparison
http 303 vs 302: 303 is the canonical 'POST/Redirect/GET' code that always converts to GET, while 302 is ambiguous. Why 303 fixes form-resubmission in modern web apps.
Quick Cheat Sheet
| Aspect | 303 See Other | 302 Found |
|---|---|---|
| What client should do | Always GET the new URL | "Implementation-defined" (usually GET, historically) |
| Standardized GET conversion? | Yes, mandatory | No (de facto only) |
| Designed for | POST/Redirect/GET pattern | Generic temporary redirect |
| RFC | 9110 § 15.4.4 | 9110 § 15.4.3 |
The Subtle but Crucial Difference
Both 303 and 302 are temporary redirects, but they differ in what the client must do:
303 See Other (RFC 9110 § 15.4.4) requires the client to issue a GET request to the new URL, regardless of the original method. This is unambiguous and standardized.
302 Found historically allowed the client to repeat the request with the same method, but virtually all clients convert POST → GET anyway. The behavior is de facto the same as 303, but not standardized.
The POST/Redirect/GET Pattern
303 was specifically designed for the POST/Redirect/GET (PRG) pattern, which prevents form re-submission on browser refresh:
- User submits form →
POST /comments - Server processes the comment, then responds →
303 See Other, Location: /comments/123 - Browser GETs
/comments/123and displays the result - If the user hits Refresh, the browser re-issues the GET, not the POST. No duplicate submission.
This pattern is so important that many web frameworks (Rails' redirect_to after create, Django's HttpResponseRedirect) default to using 302 — but 303 is more semantically correct.
When 303 vs 302 vs 307 Diverge
- POST → 303 → GET: forced GET (the PRG pattern)
- POST → 302 → ?: usually GET in practice, but spec-ambiguous
- POST → 307 → POST: forced POST (method-preserving)
If you want the original method preserved, use 307. If you want it forcibly converted to GET, use 303. Use 302 only if you don't care or are matching legacy behavior.
Why Most Code Still Uses 302
History: HTTP/1.0 only had 302. When HTTP/1.1 introduced 303 and 307, frameworks already had return redirect_to(...) calls baked in returning 302. Switching the default would break some weird legacy clients, so most stayed on 302.
For new code, prefer 303 for the PRG pattern. It's more explicit and avoids the small chance of a non-conforming client doing something weird.
Caching
Neither is cacheable by default. Both can be made cacheable with explicit headers, but it's rarely useful for temporary redirects.
Real-World Frameworks
- Rails:
redirect_to ..., status: :see_other→ 303 (the recommended choice in Rails 7+ for Hotwire compatibility, where 302 confuses Turbo) - Django:
redirect()→ 302 by default, override withstatus=303 - Express/Next.js:
res.redirect(303, '/path')
Real-World Use Case
In a Rails 7+ app using Hotwire/Turbo, a successful POST /comments must respond with 303 (not 302) so that Turbo correctly drops the form-submission Frame and navigates to the new comment page. For a generic web app where you just want 'send the user back to the index page after creating', 302 is functionally fine but 303 is more correct.
Look Up Any Status Code
Related Comparisons
HTTP 301 vs 302 — Moved Permanently vs Found Comparison
http 301 vs 302: 301 is permanent and transfers SEO link equity, 302 is temporary. Learn the right choice for redirects and how Google, browsers, and CDNs treat each.
HTTP 302 vs 307 — Temporary Redirect & Method Preservation Comparison
http 302 vs 307: both are temporary redirects, but 307 strictly preserves the HTTP method. When 307 fixes broken POST redirects in APIs and OAuth flows.
HTTP 301 vs 308 — Permanent Redirect & Method Preservation Comparison
http 301 vs 308: both are permanent redirects, but 308 strictly preserves the request method (POST stays POST). When to choose each for APIs vs web pages.
HTTP 304 vs 200 — Not Modified vs OK (Caching) Comparison
http 304 vs 200: 304 is sent when a conditional GET hits an unchanged resource, saving bandwidth. Learn ETag, If-None-Match, and how 304 cuts CDN/origin costs.