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.

HTTP 303 quick reference →

Quick reference

Code303
NameSee Other
Category3xx Redirect
SpecificationRFC 9110 §15.4.4
Method changeAlways 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

CodeMethod preserved?Permanent?Use case
302No (POST may become GET)NoTemporary redirect (legacy; 303 or 307 are more explicit)
303No (always GET)NoPost/Redirect/Get; always redirects with GET
307YesNoTemporary redirect preserving method
308YesYesPermanent 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

HTTP 303 vs 302 · HTTP 303 vs 307

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 →