Código de status HTTP · Redirecionamento (3xx)
303 See Other
O status 303 See Other manda o cliente buscar outra URL, indicada no Location, com um GET, como resposta ao que ele acabou de fazer. O uso clássico é o padrão Post/Redirect/Get: depois do POST de um formulário, o servidor redireciona para uma página de resultado, e atualizar a página não envia o formulário de novo.
| Classe | 3xx, Redirecionamento |
|---|---|
| Definido em | RFC 9110 §15.4.4 |
| Pode ir para o cache por padrão | Só com Cache-Control ou Expires explícitos |
| Pode repetir o pedido | Não repita o pedido original; faça um GET na URL do Location |
| Cabeçalhos relevantes |
|
O que significa o 303
A RFC 9110, seção 15.4.4, faz do 303 o único redirecionamento que sempre significa GET (ou HEAD) em seguida, seja qual for o método original, e diz que ele vale para qualquer método. Ele surgiu porque o 302 era ambíguo nos anos 1990: alguns navegadores reenviavam o POST, outros trocavam para GET. Com o 303 não há o que adivinhar.
A URL do Location não é um novo endereço do recurso original; é outro recurso que descreve o resultado, como /pedidos/1042 depois de POST /pedidos. Essa página de resultado pode ser salva nos favoritos, compartilhada e guardada em cache por conta própria, coisa que uma resposta a POST não permite. O 303 em si só vai para o cache se tiver cabeçalhos de validade explícitos.
Quando usar
- Depois de um POST de formulário bem-sucedido (cadastro, checkout, comentário), para mostrar a página de resultado com GET.
- Depois de aceitar uma tarefa demorada, apontando o cliente para o recurso de status ou de resultado.
- No Next.js, redirect() dentro de uma Server Action já responde 303, então você ganha o Post/Redirect/Get sem escolher código.
Causas comuns
Se você administra o servidor
- No FastAPI ou Starlette, devolver RedirectResponse depois de um POST de formulário sem status_code: o padrão é 307, então o navegador envia o formulário de novo por POST para a URL de resultado, que responde 405 Method Not Allowed.
- Usar 302 depois de um POST funciona nos navegadores, mas deixa outros clientes HTTP livres para repetir o POST na nova URL.
Como resolver
Se você administra o servidor
- Responda 303 explicitamente depois de qualquer POST que altera dados e deve terminar numa página, e faça do destino uma rota GET comum.
- Faça o trabalho antes de redirecionar: se o POST falhar, responda 4xx com o formulário e os erros, não um 303.
Como enviar um 303
app.post('/orders', async (req, res) => {
const order = await createOrder(req.body);
// The browser loads the receipt with GET; refresh will not re-submit
res.redirect(303, `/orders/${order.id}`);
});// app/orders/route.ts
export async function POST(request: Request) {
const order = await createOrder(await request.formData());
return Response.redirect(new URL(`/orders/${order.id}`, request.url), 303);
}
// In a Server Action, redirect() from next/navigation already sends 303.mux.HandleFunc("POST /orders", func(w http.ResponseWriter, r *http.Request) {
id, err := createOrder(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
http.Redirect(w, r, "/orders/"+id, http.StatusSeeOther) // 303
})from fastapi import FastAPI, Form
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.post("/orders")
def create_order(product_id: int = Form(...)):
order = save_order(product_id)
# Without status_code=303 the default 307 would re-POST the form
return RedirectResponse(f"/orders/{order.id}", status_code=303)Costuma ser confundido com
- 303 vs 302
- O 302 só tolera a troca para GET por motivos históricos; o 303 exige a troca, deixando a intenção clara para qualquer cliente.
- 303 vs 307
- O 307 tem a regra oposta: repetir o mesmo método e corpo. Depois do POST de um formulário, isso significa enviá-lo de novo.
- 303 vs 201
- O 201 Created responde o POST diretamente e aponta o novo recurso no Location; o 303 manda o cliente fazer GET numa página sobre o resultado.
Perguntas frequentes
- Quando usar 303 em vez de 302?
- Sempre que o redirecionamento vier depois de um POST, PUT ou DELETE e o cliente deva carregar uma página de resultado com GET. Nesse caso o navegador trata os dois igual, mas o 303 declara a intenção no protocolo, e clientes de API e bibliotecas não precisam adivinhar.
- Uma API REST deve responder 201 ou 303 depois de um POST?
- Responda 201 Created com o cabeçalho Location e, normalmente, o novo recurso no corpo. O 303 se encaixa melhor quando o resultado já existe, como um envio duplicado apontando para o registro que já estava lá.
- O 303 também transforma PUT e DELETE em GET?
- Sim. A RFC 9110 diz que o 303 vale para qualquer método, e o padrão Fetch que os navegadores seguem troca o método para GET e descarta o corpo em todos os casos, exceto HEAD.
Revisado em por Arielton Oberek.