HTTP Status Code Reference

Every HTTP status code, what it means, and when to send it. Filter as you type, or narrow to the handful worth memorizing.

1xx Informational

The request was received and the client should keep going. You almost never send these by hand; the server and the protocol handle them.

100
Continue

The client asked whether to send a large body with Expect: 100-continue, and the answer is yes.

101
Switching Protocols

The handshake that upgrades an HTTP connection to a WebSocket.

102
Processing

WebDAV only. The server is still working and wants to stop the client timing out.

103
Early Hints

Sends Link headers so the browser can start preloading before the real response is ready. Useful, and still rarely deployed.

2xx Success

The request worked. Which one you pick tells the client what to do next, so it is worth being deliberate.

200
OK common

The default success. A body is included and the client should read it.

201
Created common

A POST or PUT made a new resource. Include a Location header pointing at it.

202
Accepted

The work is queued, not done. For async jobs: return a URL the client can poll.

203
Non-Authoritative Information

A proxy modified the response. You will not send this yourself.

204
No Content common

Success with nothing to return, typically a DELETE. The client should not try to parse a body.

205
Reset Content

Tells the client to clear the form it just submitted. Rare.

206
Partial Content

A Range request succeeded. This is how video seeking and resumable downloads work.

207
Multi-Status

WebDAV. One response carrying several independent statuses.

208
Already Reported

WebDAV. The member was already listed elsewhere in this response.

226
IM Used

Delta encoding. Effectively unused in the wild.

3xx Redirection

The resource is somewhere else. The difference between these is whether the move is permanent and whether the request method survives.

300
Multiple Choices

Several representations exist and the server will not pick. Almost never used.

301
Moved Permanently common

A permanent move. Cached hard by browsers and passes ranking to the new URL. Getting this wrong is expensive: you cannot un-cache it remotely.

302
Found common

A temporary move. The original URL stays canonical. Historically allowed clients to turn POST into GET.

303
See Other

After a POST, send the client to a GET. This is the Post/Redirect/Get pattern that stops a refresh resubmitting a form.

304
Not Modified common

The client's cached copy is still good. Sent in response to If-None-Match or If-Modified-Since, with no body.

305
Use Proxy

Deprecated for security reasons. Browsers ignore it.

307
Temporary Redirect

Like 302 but the method and body must be preserved. Use it when a POST has to stay a POST.

308
Permanent Redirect

Like 301 but the method must be preserved. The strict, modern permanent redirect.

4xx Client error

The request was wrong. Repeating it unchanged will not help, and the response should say enough for the client to fix it.

400
Bad Request common

Malformed syntax: unparseable JSON, a missing required parameter. The catch-all when nothing more specific fits.

401
Unauthorized common

Not authenticated, or the credentials were rejected. The name is wrong: it means unauthenticated. Include a WWW-Authenticate header.

402
Payment Required

Reserved since 1997 and still not standardised. Some APIs use it for billing limits.

403
Forbidden common

Authenticated and still not allowed. Retrying with the same credentials will never work.

404
Not Found common

No such resource. Also the polite way to hide something that exists but the caller may not know about.

405
Method Not Allowed

The URL exists but not for this verb. You must include an Allow header listing what is permitted.

406
Not Acceptable

Nothing matches the client's Accept header. Most servers just send their default instead.

407
Proxy Authentication Required

Like 401, but it is the proxy asking.

408
Request Timeout

The client took too long to send the request. Rare in practice.

409
Conflict common

The request clashes with current state: a duplicate unique key, or an edit against a stale version.

410
Gone

Deliberately removed and not coming back. Tells search engines to drop it faster than a 404 does.

411
Length Required

The server wants a Content-Length and did not get one.

412
Precondition Failed

An If-Match or If-Unmodified-Since check failed. The basis of optimistic locking.

413
Content Too Large

The body exceeds the limit. Formerly Payload Too Large.

414
URI Too Long

Usually a GET that should have been a POST, or a redirect loop building up query parameters.

415
Unsupported Media Type

The Content-Type is not one the endpoint accepts. Commonly a missing application/json header.

416
Range Not Satisfiable

The requested byte range is outside the file.

417
Expectation Failed

The Expect header could not be met.

418
I'm a Teapot

From an April Fools RFC in 1998. Real, registered, and formally protected from removal in 2017. Do not use it.

421
Misdirected Request

The connection reached a server that cannot serve this authority. HTTP/2 connection coalescing.

422
Unprocessable Content common

Well-formed but semantically wrong: valid JSON that fails validation. The right code for a failed form.

423
Locked

WebDAV. The resource is locked.

424
Failed Dependency

WebDAV. A previous request in the chain failed.

425
Too Early

The server will not risk replaying a request sent in TLS early data.

426
Upgrade Required

The client must switch protocols, typically to TLS.

428
Precondition Required

The server insists on a conditional request, to stop the lost-update problem.

429
Too Many Requests common

Rate limited. Include a Retry-After header, or the client cannot back off properly.

431
Request Header Fields Too Large

Usually an enormous cookie.

451
Unavailable For Legal Reasons

Blocked by law. The number is a Fahrenheit 451 reference, deliberately.

5xx Server error

The server failed. The request may have been perfectly valid, so a client is often right to retry with backoff.

500
Internal Server Error common

An unhandled exception. Never leak the stack trace to the client; log it and return an opaque message.

501
Not Implemented

The server does not recognize the method at all. Different from 405, which is per-URL.

502
Bad Gateway common

A proxy got an invalid response from upstream. Usually your app crashed or is not listening.

503
Service Unavailable common

Temporarily down or overloaded. The correct code for maintenance, and it should carry Retry-After.

504
Gateway Timeout common

Upstream did not answer in time. Often a slow query or an external API hanging.

505
HTTP Version Not Supported

The protocol version is refused.

506
Variant Also Negotiates

A content negotiation misconfiguration.

507
Insufficient Storage

WebDAV. Out of space.

508
Loop Detected

WebDAV. An infinite loop while processing.

510
Not Extended

Further extensions are required. Effectively unused.

511
Network Authentication Required

A captive portal wants you to log in. The hotel wifi code.

1 Four pairs that are easy to mix up

301 vs 302

301 is permanent, cached hard by the browser and passes ranking. 302 keeps the old URL canonical. A 301 sent by mistake sits in visitors' caches and you cannot clear it remotely.

401 vs 403

401 means not authenticated: try again with credentials. 403 means authenticated and still refused: retrying is pointless. The name Unauthorized is misleading: 401 means unauthenticated.

400 vs 422

400 for a body you could not parse, 422 for one you parsed and rejected. Plenty of APIs use 400 for both, which works but tells the client less.

404 vs 410

404 says it is not here. 410 says it was removed on purpose and is not coming back, and search engines drop a 410 from the index faster.

2 Four rules for status codes in an API

Send Retry-After with 429 and 503. Without it a client has to guess how long to wait, so it either retries too soon or gives up. For example, Retry-After: 120 tells it to wait two minutes.

405 requires an Allow header. The spec makes it mandatory, and it is what lets a client discover the right verb instead of guessing.

Never put a stack trace in a 500. Log it with a correlation id and return that id. The trace tells an attacker your framework, versions and file paths.

Do not return 200 with an error inside. An API that answers every request with 200 and a body saying success: false confuses every retry policy, monitor and cache in the chain, because they read the status line and not the body.

How to use the HTTP Status Code Reference tool

  1. Type in the filter box to search by number, name or meaning. Typing 404 or timeout both work.
  2. Narrow to a range with the dropdown when you only care about, say, 4xx.
  3. Tick Common only for the seventeen codes worth knowing by heart.
  4. Every entry says when to send the code, not only what the name means.

Returning the right status in code

Express

app.post("/users", async (req, res) => {
    const parsed = UserSchema.safeParse(req.body);
    if (!parsed.success) {
        // 422: well-formed JSON that failed validation
        return res.status(422).json({ errors: parsed.error.issues });
    }

    const existing = await db.users.findByEmail(parsed.data.email);
    if (existing) {
        // 409: clashes with current state, not a bad request
        return res.status(409).json({ error: "That email is already registered" });
    }

    const user = await db.users.create(parsed.data);
    // 201 with a Location header pointing at the new resource
    res.status(201).location(`/users/${user.id}`).json(user);
});

app.delete("/users/:id", async (req, res) => {
    await db.users.delete(req.params.id);
    res.status(204).end();   // nothing to return, so no body
});

PHP / Laravel

<?php
// Laravel
return response()->json($user, 201)
    ->header('Location', "/users/{$user->id}");

return response()->noContent();                 // 204
return response()->json(['errors' => $e], 422); // validation
abort(409, 'Already registered');

// Rate limiting: Retry-After is what lets a client back off properly
return response()->json(['error' => 'Slow down'], 429)
    ->header('Retry-After', 60);

// Plain PHP
http_response_code(422);

Checking what you send

# Headers only, following every redirect
curl -sIL https://example.com/old-path

# Just the status code
curl -s -o /dev/null -w "%{http_code}\n" https://example.com

# The full redirect chain with each hop
curl -sIL https://example.com | grep -E "^(HTTP|location)"

# Worth doing on staging before shipping a batch of 301s:
# a permanent redirect is cached on visitors' machines and
# you cannot clear it remotely.

Common errors and how to fix them

The API returns 200 with success: false
Retries, caches and monitors read the status line, so they treat the failure as a success. Use a real error status and put the detail in the body.
405 without an Allow header
The spec makes it mandatory. Without it a client cannot discover which verb it should have used.
A 301 was sent by mistake
Browsers cache it, sometimes indefinitely, and you cannot clear it remotely. Serve the old URL again with a 302 to correct course; visitors with the cached 301 keep being redirected until their cache clears.
429 with no Retry-After
The client either retries too soon or gives up, because it has nothing to base a backoff on. Add Retry-After with the number of seconds to wait, for example Retry-After: 60.
A stack trace in a 500 response
It hands an attacker your framework, versions and file paths. Log it against a correlation id and return only the id.

Good to know

The 401 versus 403 confusion comes from the spec itself: 401's registered name is Unauthorized, and it means unauthenticated. 403 is the one that means authenticated and refused. The names will not change, so remember the behavior instead: 401 means try again with credentials, and 403 means retrying will not help.

Specifications and references

This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.

Frequently Asked Questions

301 or 302 for a moved page?

301 if it is permanent. Browsers cache it, sometimes indefinitely, and search engines transfer the ranking. 302 keeps the original URL as the canonical one, which is what you want for a temporary maintenance page or an A/B test.

401 or 403?

401 means you have not authenticated, or your credentials were rejected: try again with valid ones. 403 means you are authenticated and still not allowed, so retrying will not help. The names are misleading: 401 says Unauthorized but means unauthenticated.

What should a validation failure return?

422 if the request was well-formed but semantically wrong, 400 if the body could not be parsed at all. Plenty of APIs use 400 for both, but the split tells a client whether fixing the values or fixing the syntax will help.

When would I send 204 rather than 200?

204 No Content is for a successful request with nothing to return, typically a DELETE or a PUT where the client already knows the state. It tells the client there is no body to parse.

Is 418 real?

It comes from an April Fools RFC in 1998 (RFC 2324, the coffee pot protocol), and several frameworks implement it as a joke. After a 2017 attempt to remove it, RFC 9110 reserved 418 as unused so no other meaning can take the number. Do not use it in an API.

What do the 1xx, 2xx, 3xx, 4xx and 5xx ranges mean?

The first digit is the class. A 1xx code is informational and rarely sent by hand, and 2xx means the request worked. A 3xx code means the resource is elsewhere. With 4xx the client sent something wrong and repeating it will not help; with 5xx the server failed and a retry may succeed. A client that sees an unknown code treats it like the x00 code of its class. Use the range dropdown to show one class at a time.

Related

When you move pages, the redirect rule generator writes the Apache and nginx config for 301s and 302s. For the other half of a response header, see the MIME type lookup.

More Developer Reference

Browse all developer reference

Copied to clipboard!