HTTP Status Codes: A Complete Developer Reference Guide

Every web developer encounters HTTP status codes daily. This comprehensive guide explains what each code means, when servers should send them, and how to handle them correctly in your applications.

What Are HTTP Status Codes?

Returning 200 for everything is one of the most common API mistakes — and one of the most annoying to debug. Status codes are free information: they tell clients what happened without them having to parse a response body. A 201 for creation, 204 for deletion, 422 for bad input — use them correctly and your API becomes self-documenting.

Status codes are grouped into five classes based on their first digit. Each class has a specific meaning: informational, success, redirection, client error, or server error. Understanding the full spectrum of codes allows you to build more robust APIs, debug issues faster, and provide better error messages to your users.

1xx — Informational Responses

Informational status codes indicate that the request was received and the server is continuing to process it. These are rarely seen by developers directly.

  • 100 Continue: The server has received the request headers and the client should proceed to send the request body. Used to avoid sending large bodies before knowing if the server will accept the request.
  • 101 Switching Protocols: The server agrees to switch protocols as requested. Commonly seen when upgrading HTTP connections to WebSocket.
  • 103 Early Hints: Primarily used with the Link header to allow browsers to start preloading resources while the server prepares the final response.

2xx — Success Codes

Success codes confirm that the request was received, understood, and accepted. These are the codes you want to return from your APIs.

CodeNameWhen to Use
200OKStandard success for GET, PUT, PATCH requests
201CreatedSuccessful POST that created a new resource
202AcceptedRequest accepted but processing not yet complete (async)
204No ContentSuccess but no body to return (DELETE operations)
206Partial ContentPartial GET response (used for file downloads/streaming)

The most common mistake is returning 200 for everything, including errors. A successful deletion should return 204. A successful creation should return 201. Using precise codes makes your API self-documenting and easier for clients to handle correctly.

3xx — Redirection Codes

Redirection codes tell the client that further action is needed to complete the request, typically by following a different URL.

  • 301 Moved Permanently: The resource has been permanently moved to a new URL. Browsers and search engines update their records. Use this for permanent URL changes.
  • 302 Found: Temporary redirect. The resource is temporarily at a different URL. The client should keep using the original URL for future requests.
  • 304 Not Modified: The cached version of the resource is still valid. The client can use its cached copy instead of downloading again. Critical for performance.
  • 307 Temporary Redirect: Like 302, but guarantees the HTTP method won't change (a POST redirect stays a POST).
  • 308 Permanent Redirect: Like 301, but guarantees the HTTP method won't change.
301 vs 302: SEO Implications

For SEO, always use 301 for permanent URL changes. Search engines transfer "link juice" (ranking authority) from the old URL to the new one. Using 302 tells search engines the move is temporary and they won't update their index.

4xx — Client Error Codes

Client error codes indicate that the request contained bad syntax, referenced a resource that doesn't exist, or lacked the required authentication. The problem is on the client's side.

CodeNameCommon Cause
400Bad RequestMalformed request syntax, invalid parameters
401UnauthorizedAuthentication required or failed
403ForbiddenAuthenticated but lacks permission
404Not FoundResource doesn't exist at this URL
405Method Not AllowedHTTP method not supported for this endpoint
409ConflictRequest conflicts with current state (duplicate entry)
410GoneResource permanently deleted (stronger than 404)
422Unprocessable EntitySyntax valid but semantically wrong (validation errors)
429Too Many RequestsRate limit exceeded

401 vs 403: A Common Confusion

Developers frequently confuse these two codes. 401 Unauthorized means the client must authenticate itself to get the requested response — the user isn't logged in or their token is invalid. 403 Forbidden means the client's identity is known but they don't have permission — the user is logged in but doesn't have the right role.

A practical example: A logged-out user trying to access a dashboard gets 401. A logged-in standard user trying to access an admin-only page gets 403.

5xx — Server Error Codes

Server error codes indicate that the server encountered an unexpected condition that prevented it from fulfilling the request. Unlike 4xx errors, the client did nothing wrong — the problem is on the server's side.

CodeNameMeaning
500Internal Server ErrorGeneric catch-all for unhandled server errors
501Not ImplementedServer doesn't support the functionality
502Bad GatewayUpstream server returned an invalid response
503Service UnavailableServer temporarily down (overload or maintenance)
504Gateway TimeoutUpstream server didn't respond in time

A 502 or 504 often indicates a problem with your reverse proxy (like Nginx) failing to reach your application server. A 503 is often returned intentionally during maintenance windows. Always include a Retry-After header with 503 responses so clients know when to try again.

Best Practices for API Design

Be Specific, Not Generic

Avoid returning 200 for everything and encoding the error in the response body. This forces clients to parse every response body to determine success, breaking conventional HTTP semantics. Use the right status code — it's free information that clients can use without parsing JSON.

Always Include a Response Body on Errors

For 4xx and 5xx errors, always return a JSON body explaining what went wrong. A good error response includes the error code, a human-readable message, and optionally a link to documentation. Never return a bare 400 with no explanation.

{
  "error": "validation_failed",
  "message": "Email address is invalid",
  "field": "email",
  "docs": "https://api.example.com/docs/errors"
}

Use 422 for Validation Errors

When a request is syntactically valid JSON but contains invalid values (e.g., an email field that isn't an email address), return 422 rather than 400. This distinction helps clients understand they need to fix their data, not their request format.

Return 429 with Retry-After

When rate limiting, always include the Retry-After header so clients know when they can try again. This prevents clients from hammering your server in a retry loop and helps them implement proper backoff strategies.

Handling Status Codes in JavaScript

// Using fetch API with proper status handling
async function fetchUser(id) {
  const response = await fetch(`/api/users/${id}`);

  if (response.status === 404) {
    throw new Error('User not found');
  }

  if (response.status === 401) {
    redirectToLogin();
    return;
  }

  if (response.status === 429) {
    const retryAfter = response.headers.get('Retry-After');
    throw new Error(`Rate limited. Try again in ${retryAfter}s`);
  }

  if (!response.ok) {
    // Catches all 4xx and 5xx not handled above
    const error = await response.json();
    throw new Error(error.message);
  }

  return response.json();
}

Conclusion

HTTP status codes are a rich vocabulary for communicating between clients and servers. Using them correctly makes your APIs more intuitive, easier to debug, and better aligned with web standards. The key is being specific: use 201 for creation, 204 for successful deletion, 422 for validation failures, and 429 for rate limiting. Reserve 500 for truly unexpected errors, and always include helpful error messages in your response bodies.

Take time to review the APIs you build and consume — using the right status code at the right time is one of the simplest ways to make your APIs feel professional and well-designed.