- Home
- HTTP Status Codes
- 303 See Other
Status Code
303 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.
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
```text
**Account Creation:**
```http
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...
```text
## Real-World Example
Imagine you're processing a payment on an e-commerce site:
**User Submits Payment:**
```http
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>
```text
**Browser Automatically Follows with GET:**
```http
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>
```text
## 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:**
```http
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
```text
## Common Mistakes
**❌ Using 302 instead of 303 after POST**
```http
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
})
```text
**❌ Using 303 for permanent redirects**
```http
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
```javascript
## Best Practices
**Implement Post-Redirect-Get Pattern:**
```javascript
// 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)
```text
**Include Helpful Response Body:**
```html
<!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
```text
## Advanced Use Cases
**RESTful API Resource Creation:**
```http
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
```javascript
## Implementation Examples
**Express.js:**
```javascript
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')
```javascript
**ASP.NET Core:**
```csharp
[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
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).