pingrobot

HTTP status codes / 303

303 See Other — HTTP Status Code

· 4 min read

Short answer

303 See Other tells the client to fetch a different URL, with GET, to get the answer to its request. Its main use is after a form or API POST, so the browser lands on a result page and a refresh does not submit the form again. Unlike 302, the switch to GET is part of the definition, not a historical quirk.

303 See Other points the client to a different resource that holds the answer to its request, and that resource is always fetched with GET. It is the cleanest way to end a form submission: the server processes the POST, then sends the browser to a page it can safely reload, bookmark or share.

What 303 means

RFC 9110 (Section 15.4.4) defines 303 as a redirect to a different resource, named in the Location header, that provides an indirect response to the original request. The client can retrieve that URI with a GET or HEAD request, and present the result as the answer.

Points worth knowing from the RFC:

  • It applies to any method. Its main purpose is letting the output of a POST become a separate resource that can be identified, bookmarked and cached.
  • The new URI is not the same resource. Unlike 301 or 302, a 303 does not claim the original resource moved. It says "the answer you want is over there."
  • On a GET, a 303 means the server has no representation of the target it can transfer over HTTP, but the linked resource describes it. This is used, for example, by some linked-data systems that identify real-world things with URLs.

The RFC's history note explains the code's origin. Early clients did not agree on whether to repeat a POST after a 301 or 302; most switched to GET. 303 was introduced so that a server could ask for that behavior explicitly instead of relying on a quirk.

When you see it

  • Form submissions. Contact forms, checkout steps, comment forms and settings pages. After the POST succeeds, the server sends 303 to a thank-you page, an order summary or the updated settings view.
  • APIs that create resources asynchronously. After a POST, an API may send 303 to a status or result resource that the client then GETs.
  • Deletes from the browser. Some frameworks recommend 303 after a non-GET request, so the browser's follow-up is a GET rather than a repeated DELETE or PATCH.
  • Payment and sign-in flows that hand the browser back to a result URL after processing a POST.

The Post/Redirect/Get pattern

Without a redirect, a page rendered directly as the response to a POST is fragile. Pressing refresh asks the browser to resubmit the form, and many users click "continue" on the warning, creating a duplicate order or a second message.

With a 303 after the POST:

  1. The browser POSTs the form to /orders.
  2. The server creates the order and answers 303 See Other with Location: /orders/5521.
  3. The browser GETs /orders/5521 and shows the confirmation.
  4. A refresh now repeats step 3 only. No duplicate order.

In Express that is res.redirect(303, "/orders/5521"); most frameworks have a similar way to pass the status explicitly.

Common mistakes

  • Using 303 for pages that moved. A URL change is a job for 301 or 308, not 303.
  • Expecting the body to follow. A 303 never carries the original POST body to the next URL. If you need the next server to receive the same request, use 307 or 308.
  • Redirecting to a page that needs data from the POST. The target page must be able to load on its own, for example by reading the order id from the URL, because nothing else is passed along.

How to check it

A 303 usually answers a POST, so send one and look at the headers without following them:

curl -s -D - -o /dev/null -X POST https://www.example.com/contact \
  -d "name=Test&message=hello"
HTTP/2 303
location: /contact/thanks

Add -L to follow it. curl, like browsers, switches to GET when following a 303:

curl -sL -o /dev/null -w "%{http_code} %{url_effective}\n" -X POST \
  https://www.example.com/contact -d "name=Test&message=hello"

Use a test form or staging site for this, since each run submits real data.

What it means for uptime monitoring

Uptime checks rarely meet 303, because they do not submit forms. PingRobot's checker sends a GET, follows any redirects, and judges the final response, with 200 to 399 counted as up. If a GET did receive a 303, the checker would follow it and report what the target returned.

The part of your site that 303 protects, the form handler, is not exercised by a GET check. If checkout or sign-up matters, monitor the pages that lead to it, add a keyword check so you know the form actually rendered, and consider a scheduled end-to-end test that submits a test entry and reports to a heartbeat monitor when it succeeds.

Start monitoring free — 10 monitors, no credit card.

Frequently asked questions

When should I use 303 instead of 302?

Use 303 when you deliberately want the client to follow up with GET, such as after a form submission. 302 only allows the method change for historical reasons, so 303 states your intent more clearly.

What is the Post/Redirect/Get pattern?

The server handles a POST, then answers 303 with a Location pointing to a result page. The browser loads that page with GET. Refreshing it repeats only the GET, so the form is not submitted twice.

Is a 303 response cached?

Not by default. RFC 9110 does not list 303 as heuristically cacheable, so caches store it only when explicit headers allow it. The page it points to can be cached on its own terms.