< HTTP/1.1 303 See Other303 See Other
Redirect to a different resource using GET. Learn when to use 303 to prevent form resubmission and implement the Post-Redirect-Get pattern.
- Cacheable
- Only with explicit freshness
- Client action
- GET the Location
- Usually sent by
- Origin app (after POST)
- Spec
- RFC 9110 §15.4.4
On this page
TL;DR: 303 See Other redirects you to a different URL using GET. Used after form submissions to prevent resubmission on refresh.
A 303 See Other status code tells the client to retrieve the requested resource at a different URL using a GET request, regardless of the original request method. Think of it like submitting a form at a post office—once they process your submission (POST), they hand you a receipt (redirect to GET) instead of making you fill out the form again.
This is the cornerstone of the Post-Redirect-Get (PRG) pattern, preventing the dreaded “Do you want to resubmit the form?” browser warning.
When Does This Happen?
You’ll see a 303 See Other response in these common situations:
1. Form Submission Success
User submits form → Server processes → 303 to success page
POST /checkout → 303 → GET /order/12345/confirmation
2. Post-Redirect-Get Pattern
Prevent duplicate form submissions
POST /create-account → 303 → GET /welcome
3. Upload Completion
File uploaded successfully → Redirect to file details
POST /upload → 303 → GET /files/document-123
4. API Resource Creation
Resource created → Redirect to resource view
POST /api/articles → 303 → GET /api/articles/new-article-id
5. Search Results
Search form submitted → Redirect to results page
POST /search → 303 → GET /search?q=keyword
Example Responses
Form Submission Redirect:
HTTP/1.1 303 See Other
Location: https://example.com/order/12345/confirmation
Content-Type: text/html
Content-Length: 0
Account Creation:
HTTP/1.1 303 See Other
Location: https://app.com/welcome
Set-Cookie: user_id=67890; Path=/; HttpOnly; Secure
Cache-Control: no-cache
<!DOCTYPE html>
<html>
<body>
<h1>Account Created!</h1>
<p>Redirecting to your dashboard...</p>
<p><a href="/welcome">Click here if not redirected</a></p>
</body>
</html>
File Upload Success:
HTTP/1.1 303 See Other
Location: https://storage.example.com/files/doc-abc123
ETag: "upload-complete"
X-Upload-ID: abc123
Content-Type: text/plain
Upload successful. Redirecting to file details...
Real-World Example
Imagine you’re processing a payment on an e-commerce site:
User Submits Payment:
POST /checkout/pay HTTP/1.1
Host: shop.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 156
card_number=4111111111111111&expiry=12/28&cvv=123&amount=99.99
Server Processes and Redirects:
HTTP/1.1 303 See Other
Location: https://shop.com/order/ORD-2026-001/success
Set-Cookie: order_id=ORD-2026-001; Path=/
Cache-Control: no-store, no-cache
Content-Type: text/html
<!DOCTYPE html>
<html>
<head><title>Payment Processed</title></head>
<body>
<h1>Payment Successful!</h1>
<p>Your payment has been processed. Redirecting to confirmation page...</p>
<p>If not redirected, <a href="/order/ORD-2026-001/success">click here</a></p>
</body>
</html>
Browser Automatically Follows with GET:
GET /order/ORD-2026-001/success HTTP/1.1
Host: shop.com
Cookie: order_id=ORD-2026-001
Success Page Response:
HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>
<head><title>Order Confirmation</title></head>
<body>
<h1>Thank You for Your Order!</h1>
<p>Order ID: ORD-2026-001</p>
<p>Amount: $99.99</p>
<p>Confirmation email sent to your address.</p>
</body>
</html>
303 vs Other Redirect Codes
| Code | Meaning | Method Change | Use Case | Caching |
|---|---|---|---|---|
| 303 | See other | Always becomes GET | After POST/PUT/DELETE | Not cached |
| 302 | Found (temporary) | May change to GET | Temporary redirects | Short-term cache |
| 307 | Temporary redirect | Preserves method | Temporary, keep method | Not cached |
| 301 | Moved permanently | May change to GET | Permanent redirects | Long-term cache |
Important Characteristics
Always Changes to GET:
POST /form-submit
↓
HTTP/1.1 303 See Other
Location: /success
↓
GET /success ← Always GET, never POST
Prevents Form Resubmission:
Without 303:
User submits form → 200 OK → User refreshes → "Resubmit form?" warning
With 303:
User submits form → 303 redirect → GET success page → User refreshes → Safe GET request
Not Cached by Default:
HTTP/1.1 303 See Other
Location: /result
Cache-Control: no-store ← Typically not cached
Common Mistakes
❌ Using 302 instead of 303 after POST
POST /form-submit
HTTP/1.1 302 Found ← Browser behavior is unpredictable
Location: /success ← Should use 303 for POST-redirect-GET
❌ Not implementing PRG pattern
// Bad: Returning HTML directly after POST
app.post('/submit', (req, res) => {
processForm(req.body)
res.render('success') // ← User refresh will resubmit
})
❌ Using 303 for permanent redirects
HTTP/1.1 303 See Other ← Wrong for permanent moves
Location: /permanently-moved ← Should use 301
✅ Correct usage
POST /form-submit
HTTP/1.1 303 See Other
Location: https://example.com/success
Cache-Control: no-cache
Getting 303 See Other right
Implement Post-Redirect-Get Pattern:
// Express.js example
app.post('/checkout', async (req, res) => {
const order = await processOrder(req.body)
// Redirect with 303 to prevent resubmission
res.redirect(303, `/order/${order.id}/confirmation`)
})
app.get('/order/:id/confirmation', (req, res) => {
// Safe to refresh - it's just a GET request
res.render('confirmation', { orderId: req.params.id })
})
Use Absolute URLs:
HTTP/1.1 303 See Other
Location: https://example.com/success ✓ Absolute
Location: /success ✗ Relative (works but not recommended)
Include Helpful Response Body:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0;url=/success" />
<title>Redirecting...</title>
</head>
<body>
<h1>Processing Complete</h1>
<p>Redirecting to confirmation page...</p>
<p>If not redirected, <a href="/success">click here</a></p>
</body>
</html>
Don’t Cache 303 Responses:
HTTP/1.1 303 See Other
Location: /result
Cache-Control: no-store, no-cache
Pragma: no-cache
Advanced Use Cases
RESTful API Resource Creation:
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "Alice", "email": "alice@example.com"}
→
HTTP/1.1 303 See Other
Location: https://api.example.com/users/12345
Content-Type: application/json
{"message": "User created", "id": 12345}
Search Form Submission:
POST /search HTTP/1.1
Content-Type: application/x-www-form-urlencoded
query=best+practices&category=http
→
HTTP/1.1 303 See Other
Location: /search?query=best+practices&category=http
→ Now shareable and bookmarkable URL
Implementation Examples
Express.js:
app.post('/register', async (req, res) => {
try {
const user = await createUser(req.body)
req.session.userId = user.id
// PRG pattern with 303
res.redirect(303, `/welcome?name=${encodeURIComponent(user.name)}`)
} catch (error) {
res.status(400).render('register', { error: error.message })
}
})
Django:
from django.http import HttpResponseSeeOther
def submit_form(request):
if request.method == 'POST':
# Process form
form_data = process_form(request.POST)
# Redirect with 303
return HttpResponseSeeOther(f'/success/{form_data.id}')
return render(request, 'form.html')
ASP.NET Core:
[HttpPost]
public IActionResult SubmitOrder(OrderModel order)
{
var orderId = _orderService.ProcessOrder(order);
// 303 See Other redirect
return RedirectToAction("Confirmation", "Order",
new { id = orderId }, true);
}
PHP:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$orderId = processOrder($_POST);
// Send 303 redirect
header('HTTP/1.1 303 See Other');
header("Location: /order/$orderId/success");
header('Cache-Control: no-store, no-cache');
exit;
}
Try It Yourself
Visit our request builder and see a 303 redirect:
- Set method to POST
- Set path to /form-submit
- Add body data:
{"name": "test", "email": "test@example.com"} - Click Send request
- Watch the 303 redirect to GET request
Try it with curl
Post a form, then let curl follow the redirect with -L. Do not add -X POST: -X forces the method on the redirected request too, which hides the behavior you are trying to see.
curl -i -L -d 'name=test' https://api.example.com/forms
Example output (illustrative, not captured from a real server):
HTTP/2 303
location: /forms/17
HTTP/2 200
content-type: text/html
After a 303, curl sends the follow-up request as GET and drops the body. Pass --post303 to keep it as POST, which is rarely what you want. Use -v to see the second request line.
Related Status Codes
- 302 Found - Temporary redirect (may or may not change method)
- 307 Temporary Redirect - Temporary redirect preserving method
- 301 Moved Permanently - Permanent redirect
- 201 Created - Resource successfully created
Frequently asked questions
What does 303 See Other mean?
303 See Other tells the browser to redirect to a different URL using GET, regardless of the original request method. It is commonly used after form submissions to prevent resubmission when users refresh the page.
What is the difference between 303 and 302?
303 always changes the method to GET, while 302 behavior varies by browser and may preserve the original method. Use 303 after POST/PUT/DELETE when you want a guaranteed GET redirect.
What is the Post-Redirect-Get pattern?
PRG is a web pattern where after processing a POST request, the server responds with 303 redirect to a GET page. This prevents duplicate form submissions when users refresh or use the back button.
When should I use 303 vs 307?
Use 303 when you want the redirect to become a GET request (like after form submission). Use 307 when you need to preserve the original HTTP method (POST stays POST).
Sources
Related
300 Multiple Choices
The request has multiple possible responses. Learn when to use 300 Multiple Choices for content negotiation and alternative resource locations.
307 Temporary Redirect
Temporary redirect that preserves the HTTP method. Learn when to use 307 instead of 302 for method-sensitive redirects.
308 Permanent Redirect
Permanent redirect that preserves the HTTP method. Learn when to use 308 instead of 301 for method-sensitive permanent redirects.
HTTP 302 Found: Temporary Redirect
Learn what 302 redirect means, when to use temporary vs permanent redirects, and how 302 differs from 301, 307, and 308.