Free tool

HTTP status codes

An HTTP status code is the three-digit number a server sends with every response to say how the request went: 2xx worked, 3xx redirects, 4xx is a client error and 5xx is a server error. Below is every code in the IANA registry, plus the nginx, Cloudflare and AWS codes you meet in logs, each with when to use it, common causes and the spec. Search by number, name, symptom or Go/Python constant, or switch to the compact list to scan them all.

Free · no sign-up · runs in your browser, nothing is uploaded

82 codes
1xx Informational

The request was received and the server is still working on it. These are interim responses before the final one.

  • 100

    Continue

    The server got the request headers and the client should go ahead and send the body.

    When to use: Sent automatically by servers when a client sends Expect: 100-continue before a large upload.

    Common causes: You rarely see it: HTTP clients handle it and wait for the final response.

    RFC 9110 §15.2.1 ↗Go: http.StatusContinuePython: HTTPStatus.CONTINUE
    [send it in code]
    • Node.jsres.writeContinue(); // usually automatic
  • 101

    Switching Protocols

    The server agrees to switch to the protocol named in the Upgrade header.

    When to use: Answer a WebSocket handshake (Upgrade: websocket) or another protocol upgrade you accept.

    Common causes: Normal for WebSocket connections. If a WebSocket fails instead, a proxy may be dropping the Upgrade and Connection headers.

    RFC 9110 §15.2.2 ↗Go: http.StatusSwitchingProtocolsPython: HTTPStatus.SWITCHING_PROTOCOLS
  • 102

    Processing

    WebDAV interim response: the server accepted the request and is still working on it.

    When to use: Rarely used today. It was dropped from the later WebDAV spec (RFC 4918).

    RFC 2518 ↗Go: http.StatusProcessingPython: HTTPStatus.PROCESSING
  • 103

    Early Hints

    Interim response with Link headers so the browser can preload assets while the server prepares the page.

    When to use: Send Link: </style.css>; rel=preload before a slow HTML response. Browser support for each hint type varies, so treat it as a speed-up, not a requirement.

    RFC 8297 ↗Go: http.StatusEarlyHintsPython: HTTPStatus.EARLY_HINTS
    [send it in code]
    • Node.jsres.writeEarlyHints({ link: '</style.css>; rel=preload; as=style' });
    • Gow.Header().Add("Link", "</style.css>; rel=preload; as=style"); w.WriteHeader(http.StatusEarlyHints)
  • 104

    Upload Resumption Supported

    temporary

    Interim response telling the client the server supports resumable uploads for this request.

    Temporary IANA registration for the resumable uploads draft, set to expire on 2026-11-13 unless renewed.

2xx Success

The request was received, understood and accepted.

  • 200

    OK

    cacheable by default

    The request succeeded. The body holds the result: the resource for GET, the outcome for POST.

    When to use: The default success response for reads and for actions that return data.

    Common causes: An error page served with 200 (a "soft 404") hides failures from clients and search engines. Send the real error code.

    RFC 9110 §15.3.1 ↗Go: http.StatusOKPython: HTTPStatus.OK
    [send it in code]
    • Expressres.status(200).json(data);
    • Next.jsreturn Response.json(data, { status: 200 });
    • Gow.WriteHeader(http.StatusOK)
    • Flaskreturn data, HTTPStatus.OK
  • 201

    Created

    The request created a new resource.

    When to use: After a POST (or PUT) that creates something. Put the new resource URL in the Location header.

    RFC 9110 §15.3.2 ↗Go: http.StatusCreatedPython: HTTPStatus.CREATED
    [send it in code]
    • Expressres.status(201).json(data);
    • Next.jsreturn Response.json(data, { status: 201 });
    • Gow.WriteHeader(http.StatusCreated)
    • Flaskreturn data, HTTPStatus.CREATED
  • 202

    Accepted

    The request was accepted for processing, but the work is not done yet.

    When to use: Queued or async jobs. Return a URL where the client can check the job status.

    RFC 9110 §15.3.3 ↗Go: http.StatusAcceptedPython: HTTPStatus.ACCEPTED
    [send it in code]
    • Expressres.status(202).json(data);
    • Next.jsreturn Response.json(data, { status: 202 });
    • Gow.WriteHeader(http.StatusAccepted)
    • Flaskreturn data, HTTPStatus.ACCEPTED
  • 203

    Non-Authoritative Information

    cacheable by default

    Success, but a transforming proxy changed the content from the origin's 200 response.

    When to use: Only proxies that modify responses should send it.

    RFC 9110 §15.3.4 ↗Go: http.StatusNonAuthoritativeInfoPython: HTTPStatus.NON_AUTHORITATIVE_INFORMATION
    [send it in code]
    • Expressres.status(203).json(data);
    • Next.jsreturn Response.json(data, { status: 203 });
    • Gow.WriteHeader(http.StatusNonAuthoritativeInfo)
    • Flaskreturn data, HTTPStatus.NON_AUTHORITATIVE_INFORMATION
  • 204

    No Content

    cacheable by default

    The request succeeded and there is no body to send.

    When to use: DELETE, PUT or PATCH that returns nothing, and CORS preflight responses. A 204 must not have a body.

    Common causes: Calling response.json() on a 204 throws in JavaScript. Check the status before parsing.

    RFC 9110 §15.3.5 ↗Go: http.StatusNoContentPython: HTTPStatus.NO_CONTENT
    [send it in code]
    • Expressres.sendStatus(204);
    • Next.jsreturn new Response(null, { status: 204 });
    • Gow.WriteHeader(http.StatusNoContent)
    • Flaskreturn "", HTTPStatus.NO_CONTENT
  • 205

    Reset Content

    Success: the client should reset the document view, for example clear a form.

    When to use: Rare. A form submission where the user is expected to enter another one.

    RFC 9110 §15.3.6 ↗Go: http.StatusResetContentPython: HTTPStatus.RESET_CONTENT
    [send it in code]
    • Expressres.sendStatus(205);
    • Next.jsreturn new Response(null, { status: 205 });
    • Gow.WriteHeader(http.StatusResetContent)
    • Flaskreturn "", HTTPStatus.RESET_CONTENT
  • 206

    Partial Content

    cacheable by default

    The server is sending only the byte range the client asked for in the Range header.

    When to use: Video streaming, resumable downloads. Include Content-Range.

    RFC 9110 §15.3.7 ↗Go: http.StatusPartialContentPython: HTTPStatus.PARTIAL_CONTENT
    [send it in code]
    • Expressres.status(206).json(data);
    • Next.jsreturn Response.json(data, { status: 206 });
    • Gow.WriteHeader(http.StatusPartialContent)
    • Flaskreturn data, HTTPStatus.PARTIAL_CONTENT
  • 207

    Multi-Status

    WebDAV: the body is an XML document with a separate status for each resource.

    When to use: Batch operations where some parts can succeed and others fail.

    RFC 4918 ↗Go: http.StatusMultiStatusPython: HTTPStatus.MULTI_STATUS
    [send it in code]
    • Expressres.status(207).json(data);
    • Next.jsreturn Response.json(data, { status: 207 });
    • Gow.WriteHeader(http.StatusMultiStatus)
    • Flaskreturn data, HTTPStatus.MULTI_STATUS
  • 208

    Already Reported

    WebDAV: members of this binding were already listed earlier in the same 207 response.

    When to use: Inside a 207 body, to avoid listing the same resource twice.

    RFC 5842 ↗Go: http.StatusAlreadyReportedPython: HTTPStatus.ALREADY_REPORTED
    [send it in code]
    • Expressres.status(208).json(data);
    • Next.jsreturn Response.json(data, { status: 208 });
    • Gow.WriteHeader(http.StatusAlreadyReported)
    • Flaskreturn data, HTTPStatus.ALREADY_REPORTED
  • 226

    IM Used

    The server applied delta encoding (instance manipulations) to the response.

    When to use: Almost never seen in practice.

    RFC 3229 ↗Go: http.StatusIMUsedPython: HTTPStatus.IM_USED
    [send it in code]
    • Expressres.status(226).json(data);
    • Next.jsreturn Response.json(data, { status: 226 });
    • Gow.WriteHeader(http.StatusIMUsed)
    • Flaskreturn data, HTTPStatus.IM_USED
3xx Redirection

The client has to take another step, usually follow the Location header, to finish the request.

  • 300

    Multiple Choices

    cacheable by default

    There are several representations of the resource and the client should pick one.

    When to use: Rare. Most servers pick a representation through content negotiation instead.

    RFC 9110 §15.4.1 ↗Go: http.StatusMultipleChoicesPython: HTTPStatus.MULTIPLE_CHOICES
    [send it in code]
    • Expressres.status(300).json({ error: 'Multiple Choices' });
    • Next.jsreturn Response.json({ error: 'Multiple Choices' }, { status: 300 });
    • Gohttp.Error(w, http.StatusText(300), http.StatusMultipleChoices)
    • Flaskreturn {"error": "Multiple Choices"}, HTTPStatus.MULTIPLE_CHOICES
  • 301

    Moved Permanently

    cacheable by default

    The resource has a new permanent URL, given in the Location header.

    When to use: Permanent URL changes and HTTP to HTTPS redirects for GET pages. Search engines move ranking signals to the new URL.

    Common causes: Browsers may turn a POST into a GET when following a 301. Use 308 when the method must stay the same. Browsers cache 301s, so test with care.

    RFC 9110 §15.4.2 ↗Go: http.StatusMovedPermanentlyPython: HTTPStatus.MOVED_PERMANENTLY
    [send it in code]
    • Expressres.redirect(301, '/new-url');
    • Next.jsreturn Response.redirect(new URL('/new-url', request.url), 301);
    • Gohttp.Redirect(w, r, "/new-url", http.StatusMovedPermanently)
    • Flaskreturn redirect("/new-url", code=301)
  • 302

    Found

    The resource is temporarily at another URL, given in the Location header.

    When to use: Temporary redirects, like sending a logged-out user to /login.

    Common causes: Like 301, browsers usually switch POST to GET. Use 307 to keep the method, or 303 to force GET.

    RFC 9110 §15.4.3 ↗Go: http.StatusFoundPython: HTTPStatus.FOUND
    [send it in code]
    • Expressres.redirect(302, '/new-url');
    • Next.jsreturn Response.redirect(new URL('/new-url', request.url), 302);
    • Gohttp.Redirect(w, r, "/new-url", http.StatusFound)
    • Flaskreturn redirect("/new-url", code=302)
  • 303

    See Other

    Go GET a different URL to see the result of this request.

    When to use: After a form POST, redirect to a result page (the Post/Redirect/Get pattern) so a reload does not resubmit.

    RFC 9110 §15.4.4 ↗Go: http.StatusSeeOtherPython: HTTPStatus.SEE_OTHER
    [send it in code]
    • Expressres.redirect(303, '/new-url');
    • Next.jsreturn Response.redirect(new URL('/new-url', request.url), 303);
    • Gohttp.Redirect(w, r, "/new-url", http.StatusSeeOther)
    • Flaskreturn redirect("/new-url", code=303)
  • 304

    Not Modified

    The cached copy the client has is still current, so no body is sent.

    When to use: Reply to conditional requests (If-None-Match with an ETag, or If-Modified-Since) when nothing changed.

    Common causes: Normal and good: it saves bandwidth. Seeing 304 in DevTools means the browser used its cache.

    RFC 9110 §15.4.5 ↗Go: http.StatusNotModifiedPython: HTTPStatus.NOT_MODIFIED
    [send it in code]
    • Expressres.sendStatus(304);
    • Next.jsreturn new Response(null, { status: 304 });
    • Gow.WriteHeader(http.StatusNotModified)
    • Flaskreturn "", HTTPStatus.NOT_MODIFIED
  • 305

    Use Proxy

    deprecated

    Deprecated. It told the client to use the proxy in the Location header.

    Deprecated for security reasons. Browsers ignore it.

    RFC 9110 §15.4.6 ↗Go: http.StatusUseProxyPython: HTTPStatus.USE_PROXY
    [send it in code]
    • Expressres.status(305).json({ error: 'Use Proxy' });
    • Next.jsreturn Response.json({ error: 'Use Proxy' }, { status: 305 });
    • Gohttp.Error(w, http.StatusText(305), http.StatusUseProxy)
    • Flaskreturn {"error": "Use Proxy"}, HTTPStatus.USE_PROXY
  • 306

    (Unused)

    Reserved. It was used in an earlier draft ("Switch Proxy") and is no longer used.

  • 307

    Temporary Redirect

    Temporarily at another URL, and the client must repeat the same method and body there.

    When to use: Temporary redirects of POST, PUT or DELETE requests. Browsers also show an internal 307 for HSTS upgrades to HTTPS.

    RFC 9110 §15.4.8 ↗Go: http.StatusTemporaryRedirectPython: HTTPStatus.TEMPORARY_REDIRECT
    [send it in code]
    • Expressres.redirect(307, '/new-url');
    • Next.jsreturn Response.redirect(new URL('/new-url', request.url), 307);
    • Gohttp.Redirect(w, r, "/new-url", http.StatusTemporaryRedirect)
    • Flaskreturn redirect("/new-url", code=307)
  • 308

    Permanent Redirect

    cacheable by default

    Permanently at another URL, and the client must repeat the same method and body there.

    When to use: Permanent moves of API endpoints that receive POST or PUT. Next.js and Vercel use 308 for permanent redirects.

    RFC 9110 §15.4.9 ↗Go: http.StatusPermanentRedirectPython: HTTPStatus.PERMANENT_REDIRECT
    [send it in code]
    • Expressres.redirect(308, '/new-url');
    • Next.jsreturn Response.redirect(new URL('/new-url', request.url), 308);
    • Gohttp.Redirect(w, r, "/new-url", http.StatusPermanentRedirect)
    • Flaskreturn redirect("/new-url", code=308)
4xx Client error

The request is wrong or not allowed. Sending it again unchanged will fail again.

  • 400

    Bad Request

    The server cannot process the request because of a client error, such as malformed syntax.

    When to use: Invalid JSON, a missing required parameter, a bad query string. Say what is wrong in the body.

    Common causes: Malformed JSON or headers, a body that does not match Content-Type, cookies that are too large, invalid characters in the URL.

    RFC 9110 §15.5.1 ↗Go: http.StatusBadRequestPython: HTTPStatus.BAD_REQUEST
    [send it in code]
    • Expressres.status(400).json({ error: 'Bad Request' });
    • Next.jsreturn Response.json({ error: 'Bad Request' }, { status: 400 });
    • Gohttp.Error(w, http.StatusText(400), http.StatusBadRequest)
    • Flaskreturn {"error": "Bad Request"}, HTTPStatus.BAD_REQUEST
  • 401

    Unauthorized

    The request has no valid credentials. Despite the name, it means "unauthenticated".

    When to use: Missing, expired or invalid token or password. You must send a WWW-Authenticate header with it.

    Common causes: Expired JWT or session, missing Authorization header, wrong API key, clock skew on token expiry.

    RFC 9110 §15.5.2 ↗Go: http.StatusUnauthorizedPython: HTTPStatus.UNAUTHORIZED
    [send it in code]
    • Expressres.status(401).json({ error: 'Unauthorized' });
    • Next.jsreturn Response.json({ error: 'Unauthorized' }, { status: 401 });
    • Gohttp.Error(w, http.StatusText(401), http.StatusUnauthorized)
    • Flaskreturn {"error": "Unauthorized"}, HTTPStatus.UNAUTHORIZED
  • 402

    Payment Required

    Reserved for future use. Some APIs use it for billing problems.

    When to use: Not standardised. Some services send it when a plan quota is used up or a payment failed.

    RFC 9110 §15.5.3 ↗Go: http.StatusPaymentRequiredPython: HTTPStatus.PAYMENT_REQUIRED
    [send it in code]
    • Expressres.status(402).json({ error: 'Payment Required' });
    • Next.jsreturn Response.json({ error: 'Payment Required' }, { status: 402 });
    • Gohttp.Error(w, http.StatusText(402), http.StatusPaymentRequired)
    • Flaskreturn {"error": "Payment Required"}, HTTPStatus.PAYMENT_REQUIRED
  • 403

    Forbidden

    The server understood who you are (or does not care) and refuses the request.

    When to use: The user is logged in but lacks permission. Logging in again will not help.

    Common causes: Missing role or scope, file permissions on the server, IP or country blocks, WAF or bot rules, a CSRF check failing, S3 bucket policies.

    RFC 9110 §15.5.4 ↗Go: http.StatusForbiddenPython: HTTPStatus.FORBIDDEN
    [send it in code]
    • Expressres.status(403).json({ error: 'Forbidden' });
    • Next.jsreturn Response.json({ error: 'Forbidden' }, { status: 403 });
    • Gohttp.Error(w, http.StatusText(403), http.StatusForbidden)
    • Flaskreturn {"error": "Forbidden"}, HTTPStatus.FORBIDDEN
  • 404

    Not Found

    cacheable by default

    The server has nothing at this URL.

    When to use: Unknown routes and missing records. Also fine for hiding that a private resource exists.

    Common causes: A typo or old link, a deleted page, a wrong base path behind a proxy, trailing slash mismatch, a SPA route the server does not know about.

    RFC 9110 §15.5.5 ↗Go: http.StatusNotFoundPython: HTTPStatus.NOT_FOUND
    [send it in code]
    • Expressres.status(404).json({ error: 'Not Found' });
    • Next.jsreturn Response.json({ error: 'Not Found' }, { status: 404 });
    • Gohttp.Error(w, http.StatusText(404), http.StatusNotFound)
    • Flaskreturn {"error": "Not Found"}, HTTPStatus.NOT_FOUND
  • 405

    Method Not Allowed

    cacheable by default

    The URL exists but does not accept this HTTP method.

    When to use: A POST to a read-only endpoint, for example. You must send an Allow header listing the methods that work.

    Common causes: Calling GET on a POST-only route, a missing route handler for the method, a static host that rejects POST.

    RFC 9110 §15.5.6 ↗Go: http.StatusMethodNotAllowedPython: HTTPStatus.METHOD_NOT_ALLOWED
    [send it in code]
    • Expressres.status(405).json({ error: 'Method Not Allowed' });
    • Next.jsreturn Response.json({ error: 'Method Not Allowed' }, { status: 405 });
    • Gohttp.Error(w, http.StatusText(405), http.StatusMethodNotAllowed)
    • Flaskreturn {"error": "Method Not Allowed"}, HTTPStatus.METHOD_NOT_ALLOWED
  • 406

    Not Acceptable

    The server cannot produce a response matching the Accept headers the client sent.

    When to use: Rare. Most servers send their default format instead.

    Common causes: A strict Accept header, or a security module (like mod_security) that rejects the request.

    RFC 9110 §15.5.7 ↗Go: http.StatusNotAcceptablePython: HTTPStatus.NOT_ACCEPTABLE
    [send it in code]
    • Expressres.status(406).json({ error: 'Not Acceptable' });
    • Next.jsreturn Response.json({ error: 'Not Acceptable' }, { status: 406 });
    • Gohttp.Error(w, http.StatusText(406), http.StatusNotAcceptable)
    • Flaskreturn {"error": "Not Acceptable"}, HTTPStatus.NOT_ACCEPTABLE
  • 407

    Proxy Authentication Required

    Like 401, but the client has to authenticate with a proxy first.

    When to use: Sent by proxies with a Proxy-Authenticate header.

    Common causes: A corporate proxy that needs credentials. Set them in your proxy settings or HTTPS_PROXY URL.

    RFC 9110 §15.5.8 ↗Go: http.StatusProxyAuthRequiredPython: HTTPStatus.PROXY_AUTHENTICATION_REQUIRED
    [send it in code]
    • Expressres.status(407).json({ error: 'Proxy Authentication Required' });
    • Next.jsreturn Response.json({ error: 'Proxy Authentication Required' }, { status: 407 });
    • Gohttp.Error(w, http.StatusText(407), http.StatusProxyAuthRequired)
    • Flaskreturn {"error": "Proxy Authentication Required"}, HTTPStatus.PROXY_AUTHENTICATION_REQUIRED
  • 408

    Request Timeout

    retry later

    The server gave up waiting for the client to finish sending the request.

    When to use: Sent by servers that close idle connections. The client can repeat the request.

    Common causes: A slow or stalled upload, a flaky network, or browsers pre-opening connections that they never use.

    RFC 9110 §15.5.9 ↗Go: http.StatusRequestTimeoutPython: HTTPStatus.REQUEST_TIMEOUT
    [send it in code]
    • Expressres.status(408).json({ error: 'Request Timeout' });
    • Next.jsreturn Response.json({ error: 'Request Timeout' }, { status: 408 });
    • Gohttp.Error(w, http.StatusText(408), http.StatusRequestTimeout)
    • Flaskreturn {"error": "Request Timeout"}, HTTPStatus.REQUEST_TIMEOUT
  • 409

    Conflict

    The request conflicts with the current state of the resource.

    When to use: Duplicate unique keys (email already registered), edit conflicts, version mismatches.

    RFC 9110 §15.5.10 ↗Go: http.StatusConflictPython: HTTPStatus.CONFLICT
    [send it in code]
    • Expressres.status(409).json({ error: 'Conflict' });
    • Next.jsreturn Response.json({ error: 'Conflict' }, { status: 409 });
    • Gohttp.Error(w, http.StatusText(409), http.StatusConflict)
    • Flaskreturn {"error": "Conflict"}, HTTPStatus.CONFLICT
  • 410

    Gone

    cacheable by default

    The resource used to exist and was removed on purpose, with no forwarding address.

    When to use: Deleted content you want search engines to drop. It is a stronger signal than 404.

    RFC 9110 §15.5.11 ↗Go: http.StatusGonePython: HTTPStatus.GONE
    [send it in code]
    • Expressres.status(410).json({ error: 'Gone' });
    • Next.jsreturn Response.json({ error: 'Gone' }, { status: 410 });
    • Gohttp.Error(w, http.StatusText(410), http.StatusGone)
    • Flaskreturn {"error": "Gone"}, HTTPStatus.GONE
  • 411

    Length Required

    The server requires a Content-Length header.

    When to use: Sent by servers that do not accept chunked request bodies.

    Common causes: A POST with no body and no Content-Length: 0, or a streamed upload to a server that needs the size.

    RFC 9110 §15.5.12 ↗Go: http.StatusLengthRequiredPython: HTTPStatus.LENGTH_REQUIRED
    [send it in code]
    • Expressres.status(411).json({ error: 'Length Required' });
    • Next.jsreturn Response.json({ error: 'Length Required' }, { status: 411 });
    • Gohttp.Error(w, http.StatusText(411), http.StatusLengthRequired)
    • Flaskreturn {"error": "Length Required"}, HTTPStatus.LENGTH_REQUIRED
  • 412

    Precondition Failed

    A condition in the request headers (If-Match, If-Unmodified-Since) was false.

    When to use: Optimistic locking: the client sent If-Match with an old ETag, so someone else changed the resource.

    RFC 9110 §15.5.13 ↗Go: http.StatusPreconditionFailedPython: HTTPStatus.PRECONDITION_FAILED
    [send it in code]
    • Expressres.status(412).json({ error: 'Precondition Failed' });
    • Next.jsreturn Response.json({ error: 'Precondition Failed' }, { status: 412 });
    • Gohttp.Error(w, http.StatusText(412), http.StatusPreconditionFailed)
    • Flaskreturn {"error": "Precondition Failed"}, HTTPStatus.PRECONDITION_FAILED
  • 413

    Content Too Large

    The request body is bigger than the server allows. It used to be called Payload Too Large.

    When to use: Uploads over your size limit. Add Retry-After if the limit is temporary.

    Common causes: nginx client_max_body_size (default 1 MB), body parser limits in Express, serverless body limits on hosting platforms.

    RFC 9110 §15.5.14 ↗Go: http.StatusRequestEntityTooLargePython: HTTPStatus.REQUEST_ENTITY_TOO_LARGE
    [send it in code]
    • Expressres.status(413).json({ error: 'Content Too Large' });
    • Next.jsreturn Response.json({ error: 'Content Too Large' }, { status: 413 });
    • Gohttp.Error(w, http.StatusText(413), http.StatusRequestEntityTooLarge)
    • Flaskreturn {"error": "Content Too Large"}, HTTPStatus.REQUEST_ENTITY_TOO_LARGE
  • 414

    URI Too Long

    cacheable by default

    The URL is longer than the server will handle.

    When to use: Send large data in the body of a POST instead of the query string.

    Common causes: Huge query strings, redirect loops that keep adding parameters.

    RFC 9110 §15.5.15 ↗Go: http.StatusRequestURITooLongPython: HTTPStatus.REQUEST_URI_TOO_LONG
    [send it in code]
    • Expressres.status(414).json({ error: 'URI Too Long' });
    • Next.jsreturn Response.json({ error: 'URI Too Long' }, { status: 414 });
    • Gohttp.Error(w, http.StatusText(414), http.StatusRequestURITooLong)
    • Flaskreturn {"error": "URI Too Long"}, HTTPStatus.REQUEST_URI_TOO_LONG
  • 415

    Unsupported Media Type

    The server does not accept the format of the request body.

    When to use: A JSON API that receives form data or no Content-Type.

    Common causes: Missing Content-Type: application/json, or sending multipart to an endpoint that expects JSON.

    RFC 9110 §15.5.16 ↗Go: http.StatusUnsupportedMediaTypePython: HTTPStatus.UNSUPPORTED_MEDIA_TYPE
    [send it in code]
    • Expressres.status(415).json({ error: 'Unsupported Media Type' });
    • Next.jsreturn Response.json({ error: 'Unsupported Media Type' }, { status: 415 });
    • Gohttp.Error(w, http.StatusText(415), http.StatusUnsupportedMediaType)
    • Flaskreturn {"error": "Unsupported Media Type"}, HTTPStatus.UNSUPPORTED_MEDIA_TYPE
  • 416

    Range Not Satisfiable

    The byte range in the Range header is outside the file.

    When to use: Include Content-Range: bytes */{size} so the client knows the real size.

    Common causes: Resuming a download of a file that changed or shrank.

    RFC 9110 §15.5.17 ↗Go: http.StatusRequestedRangeNotSatisfiablePython: HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE
    [send it in code]
    • Expressres.status(416).json({ error: 'Range Not Satisfiable' });
    • Next.jsreturn Response.json({ error: 'Range Not Satisfiable' }, { status: 416 });
    • Gohttp.Error(w, http.StatusText(416), http.StatusRequestedRangeNotSatisfiable)
    • Flaskreturn {"error": "Range Not Satisfiable"}, HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE
  • 417

    Expectation Failed

    The server cannot meet the Expect request header.

    Common causes: A server or proxy that does not support Expect: 100-continue. Remove the header from the client (curl adds it for large POSTs).

    RFC 9110 §15.5.18 ↗Go: http.StatusExpectationFailedPython: HTTPStatus.EXPECTATION_FAILED
    [send it in code]
    • Expressres.status(417).json({ error: 'Expectation Failed' });
    • Next.jsreturn Response.json({ error: 'Expectation Failed' }, { status: 417 });
    • Gohttp.Error(w, http.StatusText(417), http.StatusExpectationFailed)
    • Flaskreturn {"error": "Expectation Failed"}, HTTPStatus.EXPECTATION_FAILED
  • 418

    (Unused)

    "I'm a teapot", from the 1998 April Fools' RFC 2324. Reserved so it is never given a real meaning.

    Many frameworks keep it as a joke (Go StatusTeapot, Python IM_A_TEAPOT). Do not use it for real errors.

    RFC 9110 §15.5.19 ↗Go: http.StatusTeapotPython: HTTPStatus.IM_A_TEAPOT
  • 421

    Misdirected Request

    The request reached a server that cannot answer for this host name.

    Common causes: HTTP/2 connection reuse across host names that share an IP or certificate but are served differently.

    RFC 9110 §15.5.20 ↗Go: http.StatusMisdirectedRequestPython: HTTPStatus.MISDIRECTED_REQUEST
    [send it in code]
    • Expressres.status(421).json({ error: 'Misdirected Request' });
    • Next.jsreturn Response.json({ error: 'Misdirected Request' }, { status: 421 });
    • Gohttp.Error(w, http.StatusText(421), http.StatusMisdirectedRequest)
    • Flaskreturn {"error": "Misdirected Request"}, HTTPStatus.MISDIRECTED_REQUEST
  • 422

    Unprocessable Content

    The request is well formed, but its content fails validation. It used to be called Unprocessable Entity.

    When to use: Validation errors: a valid JSON body with an invalid email or a missing field. Rails, Laravel and FastAPI use it for this.

    RFC 9110 §15.5.21 ↗Go: http.StatusUnprocessableEntityPython: HTTPStatus.UNPROCESSABLE_ENTITY
    [send it in code]
    • Expressres.status(422).json({ error: 'Unprocessable Content' });
    • Next.jsreturn Response.json({ error: 'Unprocessable Content' }, { status: 422 });
    • Gohttp.Error(w, http.StatusText(422), http.StatusUnprocessableEntity)
    • Flaskreturn {"error": "Unprocessable Content"}, HTTPStatus.UNPROCESSABLE_ENTITY
  • 423

    Locked

    WebDAV: the resource is locked.

    RFC 4918 ↗Go: http.StatusLockedPython: HTTPStatus.LOCKED
    [send it in code]
    • Expressres.status(423).json({ error: 'Locked' });
    • Next.jsreturn Response.json({ error: 'Locked' }, { status: 423 });
    • Gohttp.Error(w, http.StatusText(423), http.StatusLocked)
    • Flaskreturn {"error": "Locked"}, HTTPStatus.LOCKED
  • 424

    Failed Dependency

    WebDAV: the request failed because another action it depended on failed.

    RFC 4918 ↗Go: http.StatusFailedDependencyPython: HTTPStatus.FAILED_DEPENDENCY
    [send it in code]
    • Expressres.status(424).json({ error: 'Failed Dependency' });
    • Next.jsreturn Response.json({ error: 'Failed Dependency' }, { status: 424 });
    • Gohttp.Error(w, http.StatusText(424), http.StatusFailedDependency)
    • Flaskreturn {"error": "Failed Dependency"}, HTTPStatus.FAILED_DEPENDENCY
  • 425

    Too Early

    retry later

    The server will not process a request sent in TLS early data (0-RTT), because it could be replayed.

    When to use: The client should retry after the TLS handshake is complete.

    RFC 8470 ↗Go: http.StatusTooEarlyPython: HTTPStatus.TOO_EARLY
    [send it in code]
    • Expressres.status(425).json({ error: 'Too Early' });
    • Next.jsreturn Response.json({ error: 'Too Early' }, { status: 425 });
    • Gohttp.Error(w, http.StatusText(425), http.StatusTooEarly)
    • Flaskreturn {"error": "Too Early"}, HTTPStatus.TOO_EARLY
  • 426

    Upgrade Required

    The server refuses the request over the current protocol. The Upgrade header says which one to use.

    RFC 9110 §15.5.22 ↗Go: http.StatusUpgradeRequiredPython: HTTPStatus.UPGRADE_REQUIRED
    [send it in code]
    • Expressres.status(426).json({ error: 'Upgrade Required' });
    • Next.jsreturn Response.json({ error: 'Upgrade Required' }, { status: 426 });
    • Gohttp.Error(w, http.StatusText(426), http.StatusUpgradeRequired)
    • Flaskreturn {"error": "Upgrade Required"}, HTTPStatus.UPGRADE_REQUIRED
  • 428

    Precondition Required

    The server requires a conditional request, such as If-Match, to prevent lost updates.

    RFC 6585 ↗Go: http.StatusPreconditionRequiredPython: HTTPStatus.PRECONDITION_REQUIRED
    [send it in code]
    • Expressres.status(428).json({ error: 'Precondition Required' });
    • Next.jsreturn Response.json({ error: 'Precondition Required' }, { status: 428 });
    • Gohttp.Error(w, http.StatusText(428), http.StatusPreconditionRequired)
    • Flaskreturn {"error": "Precondition Required"}, HTTPStatus.PRECONDITION_REQUIRED
  • 429

    Too Many Requests

    retry later

    The client sent too many requests in a given time (rate limiting).

    When to use: Rate limits. Add a Retry-After header with the seconds to wait.

    Common causes: Hitting an API rate limit. Slow down, honour Retry-After, and retry with exponential backoff and jitter.

    RFC 6585 ↗Go: http.StatusTooManyRequestsPython: HTTPStatus.TOO_MANY_REQUESTS
    [send it in code]
    • Expressres.status(429).json({ error: 'Too Many Requests' });
    • Next.jsreturn Response.json({ error: 'Too Many Requests' }, { status: 429 });
    • Gohttp.Error(w, http.StatusText(429), http.StatusTooManyRequests)
    • Flaskreturn {"error": "Too Many Requests"}, HTTPStatus.TOO_MANY_REQUESTS
  • 431

    Request Header Fields Too Large

    One header, or all headers together, are too large.

    Common causes: Too many or too large cookies on the domain, or a huge Authorization token. Clearing cookies for the site usually fixes it.

    RFC 6585 ↗Go: http.StatusRequestHeaderFieldsTooLargePython: HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE
    [send it in code]
    • Expressres.status(431).json({ error: 'Request Header Fields Too Large' });
    • Next.jsreturn Response.json({ error: 'Request Header Fields Too Large' }, { status: 431 });
    • Gohttp.Error(w, http.StatusText(431), http.StatusRequestHeaderFieldsTooLarge)
    • Flaskreturn {"error": "Request Header Fields Too Large"}, HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE
  • 451

    Unavailable For Legal Reasons

    The resource is blocked for legal reasons, such as a court order or government censorship.

    When to use: Content you must block in some countries. The number refers to the novel Fahrenheit 451.

    RFC 7725 ↗Go: http.StatusUnavailableForLegalReasonsPython: HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS
    [send it in code]
    • Expressres.status(451).json({ error: 'Unavailable For Legal Reasons' });
    • Next.jsreturn Response.json({ error: 'Unavailable For Legal Reasons' }, { status: 451 });
    • Gohttp.Error(w, http.StatusText(451), http.StatusUnavailableForLegalReasons)
    • Flaskreturn {"error": "Unavailable For Legal Reasons"}, HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS
5xx Server error

The request looked valid but the server failed to handle it.

  • 500

    Internal Server Error

    Something went wrong on the server and there is no more specific code.

    When to use: Unhandled exceptions. Log the details, but do not send stack traces to clients.

    Common causes: A bug or uncaught exception, a missing environment variable, a failed database query, a bad .htaccess file. Check the server logs.

    RFC 9110 §15.6.1 ↗Go: http.StatusInternalServerErrorPython: HTTPStatus.INTERNAL_SERVER_ERROR
    [send it in code]
    • Expressres.status(500).json({ error: 'Internal Server Error' });
    • Next.jsreturn Response.json({ error: 'Internal Server Error' }, { status: 500 });
    • Gohttp.Error(w, http.StatusText(500), http.StatusInternalServerError)
    • Flaskreturn {"error": "Internal Server Error"}, HTTPStatus.INTERNAL_SERVER_ERROR
  • 501

    Not Implemented

    cacheable by default

    The server does not support the feature needed to handle the request, such as an unknown method.

    When to use: A method the server does not know at all. For a known method on the wrong route, use 405.

    RFC 9110 §15.6.2 ↗Go: http.StatusNotImplementedPython: HTTPStatus.NOT_IMPLEMENTED
    [send it in code]
    • Expressres.status(501).json({ error: 'Not Implemented' });
    • Next.jsreturn Response.json({ error: 'Not Implemented' }, { status: 501 });
    • Gohttp.Error(w, http.StatusText(501), http.StatusNotImplemented)
    • Flaskreturn {"error": "Not Implemented"}, HTTPStatus.NOT_IMPLEMENTED
  • 502

    Bad Gateway

    retry later

    A proxy or gateway got an invalid response from the upstream server.

    Common causes: The app behind nginx or a load balancer crashed, is not listening on the expected port, closed the connection early, or sent a malformed response.

    RFC 9110 §15.6.3 ↗Go: http.StatusBadGatewayPython: HTTPStatus.BAD_GATEWAY
    [send it in code]
    • Expressres.status(502).json({ error: 'Bad Gateway' });
    • Next.jsreturn Response.json({ error: 'Bad Gateway' }, { status: 502 });
    • Gohttp.Error(w, http.StatusText(502), http.StatusBadGateway)
    • Flaskreturn {"error": "Bad Gateway"}, HTTPStatus.BAD_GATEWAY
  • 503

    Service Unavailable

    retry later

    The server cannot handle the request right now, usually because of overload or maintenance.

    When to use: Planned maintenance and load shedding. Add Retry-After. Search engines treat it as temporary.

    Common causes: Too much traffic, no healthy instances behind the load balancer, maintenance mode, a dependency that is down.

    RFC 9110 §15.6.4 ↗Go: http.StatusServiceUnavailablePython: HTTPStatus.SERVICE_UNAVAILABLE
    [send it in code]
    • Expressres.status(503).json({ error: 'Service Unavailable' });
    • Next.jsreturn Response.json({ error: 'Service Unavailable' }, { status: 503 });
    • Gohttp.Error(w, http.StatusText(503), http.StatusServiceUnavailable)
    • Flaskreturn {"error": "Service Unavailable"}, HTTPStatus.SERVICE_UNAVAILABLE
  • 504

    Gateway Timeout

    retry later

    A proxy or gateway did not get a response from the upstream server in time.

    Common causes: A slow query or API call in the app, a proxy timeout lower than the app's work time (nginx proxy_read_timeout defaults to 60s), serverless function time limits.

    RFC 9110 §15.6.5 ↗Go: http.StatusGatewayTimeoutPython: HTTPStatus.GATEWAY_TIMEOUT
    [send it in code]
    • Expressres.status(504).json({ error: 'Gateway Timeout' });
    • Next.jsreturn Response.json({ error: 'Gateway Timeout' }, { status: 504 });
    • Gohttp.Error(w, http.StatusText(504), http.StatusGatewayTimeout)
    • Flaskreturn {"error": "Gateway Timeout"}, HTTPStatus.GATEWAY_TIMEOUT
  • 505

    HTTP Version Not Supported

    The server does not support the HTTP version used in the request.

    RFC 9110 §15.6.6 ↗Go: http.StatusHTTPVersionNotSupportedPython: HTTPStatus.HTTP_VERSION_NOT_SUPPORTED
    [send it in code]
    • Expressres.status(505).json({ error: 'HTTP Version Not Supported' });
    • Next.jsreturn Response.json({ error: 'HTTP Version Not Supported' }, { status: 505 });
    • Gohttp.Error(w, http.StatusText(505), http.StatusHTTPVersionNotSupported)
    • Flaskreturn {"error": "HTTP Version Not Supported"}, HTTPStatus.HTTP_VERSION_NOT_SUPPORTED
  • 506

    Variant Also Negotiates

    A server configuration error in transparent content negotiation: the chosen variant negotiates again, causing a loop.

    RFC 2295 ↗Go: http.StatusVariantAlsoNegotiatesPython: HTTPStatus.VARIANT_ALSO_NEGOTIATES
    [send it in code]
    • Expressres.status(506).json({ error: 'Variant Also Negotiates' });
    • Next.jsreturn Response.json({ error: 'Variant Also Negotiates' }, { status: 506 });
    • Gohttp.Error(w, http.StatusText(506), http.StatusVariantAlsoNegotiates)
    • Flaskreturn {"error": "Variant Also Negotiates"}, HTTPStatus.VARIANT_ALSO_NEGOTIATES
  • 507

    Insufficient Storage

    WebDAV: the server does not have the space to store what the request needs.

    RFC 4918 ↗Go: http.StatusInsufficientStoragePython: HTTPStatus.INSUFFICIENT_STORAGE
    [send it in code]
    • Expressres.status(507).json({ error: 'Insufficient Storage' });
    • Next.jsreturn Response.json({ error: 'Insufficient Storage' }, { status: 507 });
    • Gohttp.Error(w, http.StatusText(507), http.StatusInsufficientStorage)
    • Flaskreturn {"error": "Insufficient Storage"}, HTTPStatus.INSUFFICIENT_STORAGE
  • 508

    Loop Detected

    WebDAV: the server found an infinite loop while processing the request.

    RFC 5842 ↗Go: http.StatusLoopDetectedPython: HTTPStatus.LOOP_DETECTED
    [send it in code]
    • Expressres.status(508).json({ error: 'Loop Detected' });
    • Next.jsreturn Response.json({ error: 'Loop Detected' }, { status: 508 });
    • Gohttp.Error(w, http.StatusText(508), http.StatusLoopDetected)
    • Flaskreturn {"error": "Loop Detected"}, HTTPStatus.LOOP_DETECTED
  • 510

    Not Extended

    obsolete

    The request needs an HTTP extension the server does not support.

    Marked obsolete in the IANA registry: the HTTP Extension Framework was moved to Historic.

    RFC 2774 ↗Go: http.StatusNotExtendedPython: HTTPStatus.NOT_EXTENDED
    [send it in code]
    • Expressres.status(510).json({ error: 'Not Extended' });
    • Next.jsreturn Response.json({ error: 'Not Extended' }, { status: 510 });
    • Gohttp.Error(w, http.StatusText(510), http.StatusNotExtended)
    • Flaskreturn {"error": "Not Extended"}, HTTPStatus.NOT_EXTENDED
  • 511

    Network Authentication Required

    The client has to sign in to the network, for example a hotel or airport Wi-Fi captive portal.

    When to use: Sent by captive portals, never by origin servers.

    RFC 6585 ↗Go: http.StatusNetworkAuthenticationRequiredPython: HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED
    [send it in code]
    • Expressres.status(511).json({ error: 'Network Authentication Required' });
    • Next.jsreturn Response.json({ error: 'Network Authentication Required' }, { status: 511 });
    • Gohttp.Error(w, http.StatusText(511), http.StatusNetworkAuthenticationRequired)
    • Flaskreturn {"error": "Network Authentication Required"}, HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED
Unofficial and vendor codes

Not in the IANA registry, but you will meet them in logs and error pages: nginx, Cloudflare, AWS load balancers, Laravel.

  • 419

    Page Expired

    Laravel

    Laravel's response when the CSRF token is missing or does not match.

    Causes and fixes: An expired session, a form without @csrf, or a cached page with an old token.

  • 444

    No Response

    nginx

    nginx closes the connection without sending any response. It shows up only in nginx logs.

    When to use: return 444; to drop requests from scanners or for unknown host names.

  • 460

    Client Closed Connection

    AWS ALB

    The client closed the connection to the load balancer before the idle timeout elapsed.

    Causes and fixes: A client timeout shorter than the load balancer's idle timeout.

  • 463

    Too Many Forwarded IPs

    AWS ALB

    The load balancer got an X-Forwarded-For header with more than 30 IP addresses.

  • 464

    Incompatible Protocol

    AWS ALB

    The request protocol does not match the target group protocol version (for example HTTP/1.1 to a gRPC target).

  • 494

    Request Header Too Large

    nginx

    nginx's internal code for request headers bigger than large_client_header_buffers. The client gets a 400 "Request Header Or Cookie Too Large" page.

  • 495

    SSL Certificate Error

    nginx

    An error occurred while verifying the client certificate (mutual TLS). The client gets a 400; the code is for error_page handling.

  • 496

    SSL Certificate Required

    nginx

    The client did not present a required client certificate. The client gets a 400; the code is for error_page handling.

  • 497

    HTTP Request Sent to HTTPS Port

    nginx

    A plain HTTP request was sent to a port that expects HTTPS. The client gets a 400; the code is for error_page handling.

    Causes and fixes: A URL with http:// and an explicit HTTPS port, like http://example.com:443.

  • 499

    Client Closed Request

    nginxretry later

    The client closed the connection while nginx was still waiting for the upstream. It appears only in logs.

    Causes and fixes: A slow upstream and an impatient client: the user navigated away, a fetch was aborted, or a client timeout fired first.

  • 520

    Web Server Returned an Unknown Error

    Cloudflareretry later

    The origin sent an empty, unknown or unexpected response to Cloudflare.

    Causes and fixes: The origin crashed or reset the connection, or sent headers that are too large or invalid.

  • 521

    Web Server Is Down

    Cloudflareretry later

    The origin refused the connection from Cloudflare.

    Causes and fixes: The web server is stopped, or a firewall blocks Cloudflare's IP ranges.

  • 522

    Connection Timed Out

    Cloudflareretry later

    Cloudflare could not complete a TCP connection to the origin in time.

    Causes and fixes: An overloaded origin, a firewall silently dropping packets, a wrong DNS record pointing to the wrong IP.

  • 523

    Origin Is Unreachable

    Cloudflareretry later

    Cloudflare could not reach the origin at all.

    Causes and fixes: A wrong origin IP in DNS, or a routing problem between Cloudflare and the host.

  • 524

    A Timeout Occurred

    Cloudflareretry later

    Cloudflare connected to the origin, but the origin did not send a response before the timeout.

    Causes and fixes: Long-running requests. Move slow work to a background job and poll for the result.

  • 525

    SSL Handshake Failed

    Cloudflare

    The TLS handshake between Cloudflare and the origin failed.

    Causes and fixes: No certificate on the origin, no common cipher, or SNI not configured on the origin.

  • 526

    Invalid SSL Certificate

    Cloudflare

    In Full (strict) mode, Cloudflare could not validate the origin's certificate.

    Causes and fixes: An expired or self-signed origin certificate, or one for the wrong host name.

  • 530

    Origin Error

    Cloudflare

    Returned together with a 1xxx Cloudflare error. The 1xxx code on the page explains the cause.

The five classes of HTTP status codes

The first digit is the class. Clients must understand the class even when they do not know the exact code.

ClassMeaningMost common codes
1xxInformational: received, still working. Interim, never the final answer.100, 101, 103
2xxSuccess: the request was received, understood and accepted.200, 201, 204
3xxRedirection: the client must take another step, usually follow Location.301, 302, 304, 307, 308
4xxClient error: the request is wrong or not allowed. Repeating it unchanged fails again.400, 401, 403, 404, 409, 422, 429
5xxServer error: the request looked valid, the server failed.500, 502, 503, 504

RFC 9110 says a client that gets an unknown code treats it as the x00 code of its class: an unknown 471 is handled like 400 Bad Request. Codes outside 100-599 are invalid and should be treated as a server error. Type any three-digit number in the search box to see how it is handled.

Which status code should my API return?

Most APIs need about a dozen codes. These are the usual choices:

SituationCodeNotes
Read succeeded200 OKBody holds the data.
Created a resource201 CreatedSend the new URL in Location.
Job queued, not done yet202 AcceptedReturn a URL to poll for status.
Success with nothing to return204 No ContentNo body at all. Common for DELETE.
Malformed request (bad JSON, wrong type)400 Bad RequestSay which field or line is wrong.
Valid JSON, invalid data422 Unprocessable ContentOr 400; pick one and stay consistent.
Not logged in, token missing or expired401 UnauthorizedMust include WWW-Authenticate.
Logged in but not allowed403 ForbiddenOr 404 to hide that the resource exists.
No such resource404 Not FoundUse 410 Gone if it was deleted for good.
Wrong method on a real route405 Method Not AllowedMust include Allow: GET, POST.
Duplicate or edit conflict409 ConflictOr 412 when using If-Match and ETags.
Rate limit hit429 Too Many RequestsAdd Retry-After.
Unhandled error500 Internal Server ErrorLog details; do not leak stack traces.
Maintenance or overload503 Service UnavailableAdd Retry-After.

301 vs 302 vs 303 vs 307 vs 308

Five redirect codes, split by two questions: is the move permanent, and may the client change the method?

CodePermanentMethod on the new URLUse for
301YesMay change POST to GETMoved pages, HTTP to HTTPS for GET
302NoMay change POST to GETTemporary redirects, login pages
303NoAlways GETRedirect after a form POST
307NoSame method and bodyTemporary redirect of an API call
308YesSame method and bodyMoved API endpoints

301 and 308 are cacheable by default, so browsers may remember them for a long time. When testing a redirect, start with 302 or 307, and switch to the permanent code once it is right.

Check a status code with curl, JavaScript, Python and Go

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

# Status line and headers (HEAD request)
curl -sI https://example.com

# Follow redirects and print every hop's status
curl -sIL https://example.com | grep -i '^HTTP/'
// JavaScript: fetch only rejects on network errors, not on 404 or 500
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);   // ok is true for 200-299
const data = res.status === 204 ? null : await res.json();
# Python (requests)
r = requests.get(url, timeout=10)
print(r.status_code, r.reason)   # 404 Not Found
r.raise_for_status()             # raises HTTPError for 4xx and 5xx

from http import HTTPStatus
HTTPStatus(404).phrase           # 'Not Found'
// Go
resp, err := http.Get(url)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("got %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
}

Status code gotchas

  • Soft 404s. An error page sent with 200 OK looks like success to clients, monitoring and search engines. Send the real code with your error page.
  • fetch does not throw on errors. A 404 or 500 resolves normally. Check response.ok or response.status.
  • 204 has no body. Calling response.json() on it throws. Servers must not send a body with 204 or 304.
  • Reason phrases are optional. The text after the number ("Not Found") can be anything, and HTTP/2 and HTTP/3 do not send it at all. Code against the number.
  • Some codes never reach the client. nginx 444 and 499 exist only in logs, and nginx turns 494-497 into a 400 for the client.
  • Old names still appear. RFC 9110 renamed 413 Payload Too Large to Content Too Large and 422 Unprocessable Entity to Unprocessable Content. Libraries keep older names too: Go has StatusRequestEntityTooLarge and StatusRequestURITooLong, from RFC 2616. Search for either name above.

Debugging an API? Turn a request from DevTools into code with the curl converter, read query strings with the URL parser, or inspect a token behind a 401 with the JWT decoder. For monitoring and testing products, browse API tools on DevHunt.

FAQ

What are the five classes of HTTP status codes?+

The first digit gives the class. 1xx informational (interim responses such as 100 Continue), 2xx success (200 OK, 201 Created), 3xx redirection (301, 302, 304), 4xx client error (400, 401, 403, 404, 429) and 5xx server error (500, 502, 503, 504). Valid codes run from 100 to 599.

What is the difference between 401 and 403?+

401 Unauthorized means the request has no valid credentials: log in or send a valid token and try again. The server must include a WWW-Authenticate header. 403 Forbidden means the server knows who you are, or does not need to, and still refuses: logging in again will not help, you need different permissions.

What is the difference between 301 and 302 redirects?+

301 Moved Permanently says the new URL replaces the old one for good, so browsers cache it and search engines move ranking to the new URL. 302 Found is temporary, so clients keep using the old URL. Both let browsers change a POST into a GET. If the method must stay the same, use 308 (permanent) or 307 (temporary).

What does 502 Bad Gateway mean?+

A proxy, load balancer or CDN in front of your app got an invalid response, or none, from the server behind it. The usual causes are an app that crashed or is restarting, an app listening on a different port than the proxy expects, or an upstream that closed the connection early. Check the app logs first, then the proxy logs.

What is a 499 status code?+

499 Client Closed Request is an nginx code, not a standard one. nginx logs it when the client closed the connection before nginx could send a response, usually because the upstream was slow and the user navigated away or a client timeout fired. The client never receives a 499; you only see it in nginx logs.

Should I return 400 or 422 for validation errors?+

Use 400 Bad Request when the request cannot be parsed, such as malformed JSON or a wrong Content-Type. Use 422 Unprocessable Content when the request is well formed but the data fails validation, such as an invalid email. Many frameworks follow this split (Rails, Laravel, FastAPI). Either is valid if your API is consistent and says what is wrong in the body.

What is the difference between 404 and 410?+

404 Not Found says there is nothing at the URL, without saying whether that is permanent. 410 Gone says the resource existed and was removed on purpose. Search engines treat both as removed pages; 410 is the clearer signal when you deleted content and will not bring it back.

What does HTTP 418 I'm a teapot mean?+

418 comes from an April Fools' joke, RFC 2324, about controlling coffee pots. RFC 9110 lists it as "(Unused)" and reserves it so it never gets a real meaning, because many servers and libraries implement it as a joke. Do not use it for real errors.

Sources: IANA HTTP Status Code Registry · RFC 9110: HTTP Semantics · RFC 6585: Additional HTTP Status Codes · RFC 9113 §8.3.2 (HTTP/2 responses) · MDN: HTTP response status codes · MDN: Response.ok · nginx ngx_http_ssl_module · Cloudflare 5xx errors · AWS Application Load Balancer troubleshooting · Go net/http status constants · Python http.HTTPStatus. Checked 2026-10-31.