X Xerobit

HTTP Status Codes Reference — Complete Guide to All Response Codes

HTTP status codes tell clients what happened with their request. Here's a complete reference for all status code classes (1xx-5xx), when to use each code, and common...

Mian Ali Khalid · · 7 min read
Use the tool
HTTP Status Codes
Full HTTP status code reference with explanations and when to use each.
Open HTTP Status Codes →

HTTP status codes are three-digit numbers in the server’s response that tell the client what happened. The first digit defines the class: 1xx (informational), 2xx (success), 3xx (redirect), 4xx (client error), 5xx (server error). Using the right code makes APIs predictable and debuggable.

Use the HTTP Status Codes reference tool for a searchable guide to all codes.

1xx — Informational

Rarely used in application code. The server is processing the request.

CodeNameWhen used
100ContinueClient should continue sending request body
101Switching ProtocolsUpgrading from HTTP to WebSocket
103Early HintsSend preload headers before final response

2xx — Success

The request was received, understood, and accepted.

CodeNameWhen to use
200OKGeneral success. GET, PUT, PATCH responses
201CreatedResource was created. POST that creates a resource
202AcceptedRequest received, processing async. Job queued
204No ContentSuccess, no response body. DELETE, PATCH with no body
206Partial ContentRange request success. File download chunks

200 vs 201 vs 204

// 200 OK — general success:
app.get('/users/:id', (req, res) => {
  res.status(200).json(user);
});

// 201 Created — resource was created:
app.post('/users', (req, res) => {
  const user = await createUser(req.body);
  res.status(201)
    .header('Location', `/users/${user.id}`)  // Where to find the new resource
    .json(user);
});

// 204 No Content — success, nothing to return:
app.delete('/users/:id', (req, res) => {
  await deleteUser(req.params.id);
  res.status(204).send();  // No body
});

// 202 Accepted — async processing:
app.post('/videos/transcode', (req, res) => {
  const jobId = enqueueTranscoding(req.body.videoId);
  res.status(202).json({ jobId, status: 'queued' });
});

3xx — Redirects

The client must take additional action to complete the request.

CodeNameWhen to use
301Moved PermanentlyURL changed forever. Old URL should be updated
302FoundTemporary redirect. Keep original URL
303See OtherAfter POST, redirect to GET (Post/Redirect/Get pattern)
304Not ModifiedBrowser cached version is still fresh (ETag/If-None-Match)
307Temporary RedirectTemporary, preserve HTTP method
308Permanent RedirectPermanent, preserve HTTP method

301 vs 302 vs 307 vs 308

301 Permanent:
- Old URL → New URL, forever
- Browser and search engines update bookmarks
- HTTP method may change (POST → GET is common)

302 Found:
- Temporary redirect
- Search engines keep the original URL indexed
- Common for login redirects ("you need to log in first")

307 Temporary Redirect:
- Like 302, but MUST preserve HTTP method
- POST to /checkout → 307 → POST stays as POST

308 Permanent Redirect:
- Like 301, but MUST preserve HTTP method
- POST to /api/v1/users → 308 → /api/v2/users (POST preserved)

4xx — Client Errors

The request has an error on the client side.

CodeNameWhen to use
400Bad RequestMalformed syntax, invalid parameters
401UnauthorizedNot authenticated (no/invalid credentials)
403ForbiddenAuthenticated but not authorized
404Not FoundResource doesn’t exist
405Method Not AllowedHTTP method not supported for this endpoint
409ConflictConflict with current state (duplicate, version mismatch)
410GoneResource permanently deleted (stronger than 404)
422Unprocessable EntitySyntactically valid but semantically invalid
429Too Many RequestsRate limit exceeded

401 vs 403

401 Unauthorized: "Who are you? Prove your identity."
- Client should try again with credentials
- Sends WWW-Authenticate header with auth scheme

403 Forbidden: "I know who you are, but you can't do this."
- Client is authenticated but lacks permission
- No amount of re-authenticating will help
// 401 — not authenticated:
if (!req.headers.authorization) {
  return res.status(401)
    .header('WWW-Authenticate', 'Bearer')
    .json({ error: 'authentication_required' });
}

// 403 — authenticated but unauthorized:
if (!user.hasPermission('admin:write')) {
  return res.status(403).json({ error: 'insufficient_permissions' });
}

400 vs 422

400 Bad Request: Malformed request (JSON syntax error, wrong content-type)
422 Unprocessable Entity: Valid JSON, but data fails business validation
// 400 — malformed input:
try {
  JSON.parse(req.body);
} catch {
  return res.status(400).json({ error: 'invalid_json' });
}

// 422 — valid JSON, invalid data:
if (!isValidEmail(body.email)) {
  return res.status(422).json({
    error: 'validation_failed',
    details: [{ field: 'email', message: 'Invalid email format' }]
  });
}

409 Conflict

// Creating duplicate resource:
const existing = await User.findOne({ email: body.email });
if (existing) {
  return res.status(409).json({ 
    error: 'conflict',
    message: 'Email already registered' 
  });
}

// Optimistic concurrency conflict:
if (entity.version !== body.version) {
  return res.status(409).json({
    error: 'version_conflict',
    message: 'Resource was modified by another process'
  });
}

5xx — Server Errors

Something went wrong on the server.

CodeNameWhen to use
500Internal Server ErrorUnexpected server failure
501Not ImplementedMethod or feature not implemented
502Bad GatewayUpstream server returned bad response
503Service UnavailableTemporarily down, overloaded, or in maintenance
504Gateway TimeoutUpstream server didn’t respond in time

502 vs 503 vs 504

502 Bad Gateway: Your reverse proxy got an invalid response from the app server
503 Service Unavailable: App server is down (restarting, deploying, overloaded)
504 Gateway Timeout: App server is up but took too long to respond

These typically indicate infrastructure issues, not application bugs.

Common mistakes

Using 200 for errors:

// WRONG: 200 with error in body — clients can't programmatically detect failure:
res.status(200).json({ success: false, error: 'Not found' });

// CORRECT: use proper status code:
res.status(404).json({ error: 'not_found' });

Using 400 for everything:

// WRONG: Using 400 for all input problems:
res.status(400).json({ error: 'User with that email exists' });  // Should be 409

// CORRECT: Use specific codes:
res.status(409).json({ error: 'email_already_exists' });

Not returning 404 for missing resources:

// WRONG: Empty 200 response:
res.status(200).json(null);

// CORRECT: 404 when resource doesn't exist:
const user = await User.findById(req.params.id);
if (!user) return res.status(404).json({ error: 'user_not_found' });
res.json(user);

Related posts

Related tool

HTTP Status Codes

Full HTTP status code reference with explanations and when to use each.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.