HTTP 303 See Other
HTTP 303 See Other redirects the client to a different URL and mandates that the redirect be followed with a GET request — regardless of the original request method. It is the correct code for the Post/Redirect/Get pattern: after a form POST, the server processes the data and returns 303 to redirect the browser to a confirmation or result page. The browser fetches that page with GET. This prevents duplicate form submissions when the user refreshes the page.
Quick reference
| Code | 303 |
|---|---|
| Name | See Other |
| Category | 3xx Redirect |
| Specification | RFC 9110 §15.4.4 |
| Method change | Always redirects with GET — regardless of original method |
| Cacheable? | No — not cached by default |
The Post/Redirect/Get pattern
Without PRG — the duplicate submission problem:
User submits checkout form → Browser POSTs to /checkout → Server processes payment, returns 200 with "Thank you" HTML User refreshes the page → Browser: "Resend form data?" popup User clicks OK → Browser POSTs again → Duplicate charge, duplicate order
With PRG — safe refreshes:
User submits checkout form → Browser POSTs to /checkout → Server processes payment, returns: HTTP/1.1 303 See Other Location: /orders/7829/confirmation → Browser GETs /orders/7829/confirmation → Server returns 200 with "Order confirmed" HTML User refreshes the page → Browser re-GETs /orders/7829/confirmation → No form data, no duplicate submission
The 303 breaks the POST out of the browser history. The URL in the address bar changes to the confirmation URL. Refresh, back button, and bookmarking all point to the confirmation GET endpoint — never to the POST endpoint.
Full request-response example
POST /orders HTTP/1.1 Host: shop.example.com Content-Type: application/x-www-form-urlencoded item_id=widget-pro&quantity=2&coupon=SAVE10 HTTP/1.1 303 See Other Location: /orders/7829/confirmation Set-Cookie: flash=Order+placed+successfully; Path=/; SameSite=Strict GET /orders/7829/confirmation HTTP/1.1 Host: shop.example.com HTTP/1.1 200 OK Content-Type: text/html <h1>Order Confirmed</h1> <p>Your order #7829 has been placed.</p>
The flash cookie technique: set a short-lived cookie on the 303 response containing a success message. The confirmation page reads and clears the cookie to display the message. This avoids embedding the message in the URL (which would be bookmarkable and re-displayable) while still showing it once on the confirmation page.
Server-side implementation
Express.js:
app.post('/orders', async (req, res) => {
const order = await processOrder(req.body);
// Always redirect after POST
res.redirect(303, '/orders/' + order.id + '/confirmation');
});
Django:
from django.shortcuts import redirect
def create_order(request):
if request.method == 'POST':
order = process_order(request.POST)
return redirect('order_confirmation', pk=order.pk)
# Django's redirect() sends 302 by default;
# for explicit 303:
return HttpResponseRedirect(
reverse('order_confirmation', args=[order.pk]),
status=303
)
Ruby on Rails:
def create
@order = Order.new(order_params)
if @order.save
redirect_to order_confirmation_path(@order), status: :see_other
else
render :new, status: :unprocessable_entity
end
end
Rails uses 303 (not 302) by default for redirect_to in POST/PATCH/DELETE actions as of Rails 7.1, following the PRG best practice.
303 for non-form use cases
API resource creation: After a POST that creates a resource, redirect to the new resource’s URL. The client follows the 303 with a GET to retrieve the created resource. This is an alternative to returning 201 Created with the resource in the response body.
POST /api/reports/generate
{"type": "annual_sales", "year": 2025}
HTTP/1.1 303 See Other
Location: /api/reports/rpt-annual-2025
Long-running operation result: A POST kicks off processing. The server returns 303 pointing to the result URL when complete (or a 202 Accepted pointing to a polling URL if not yet complete).
Search result pages: Converting a GET search form to a canonical search URL. The user submits a search form (GET /search?q=query), and the server redirects to a canonical search result URL if the parameters need normalization.
303 vs 302 vs 307 vs 308
| Code | Method preserved? | Permanent? | Use case |
|---|---|---|---|
| 302 | No (POST may become GET) | No | Temporary redirect (legacy; 303 or 307 are more explicit) |
| 303 | No (always GET) | No | Post/Redirect/Get; always redirects with GET |
| 307 | Yes | No | Temporary redirect preserving method |
| 308 | Yes | Yes | Permanent redirect preserving method |
303 and 307 are both temporary redirects but with opposite method behavior. 303 forces GET; 307 preserves the original method. Use 303 after a state-changing POST that should redirect to a results page. Use 307 when temporarily moving an API endpoint and clients must retry with the same method.
Frequently asked questions
What does HTTP 303 mean?
The server is redirecting the client to a different URL and requiring that the redirect be followed with GET, regardless of what method was used for the original request. Most commonly used after a POST to prevent duplicate form submissions on page refresh.
What is the difference between 302 and 303?
Historically, 302 was supposed to preserve the method, but browsers implemented it as changing POST to GET. 303 was added to explicitly guarantee GET after redirect, making the behavior unambiguous. For a POST that should redirect to a GET result page, 303 is more explicit and semantically correct than 302.
Does 303 work for AJAX/fetch requests?
Yes, but the behavior may differ from browser form submissions. The Fetch API follows 303 redirects automatically and converts the method to GET. For AJAX that should not follow redirects (e.g., APIs), check for 303 explicitly and handle it in application code rather than relying on automatic redirect following.
Can 303 be cached?
Not by default. 303 is not cacheable without explicit Cache-Control or Expires headers. This is intentional — the redirect destination may change over time (the confirmation page for order 7829 is not a canonical URL worth caching for future visitors).
Related guides
HTTP 302 Found · HTTP 307 Temporary Redirect · HTTP 301 Moved Permanently
Comparisons
Standards reference
Definitions from the IANA HTTP Status Code Registry and RFC 9110 §15.4.4. Human-readable guidance by ErrorLookup. · HTTP 303 quick reference →