Expert ReviewedUpdated 2025developer
developer
11 min readOctober 3, 2024Updated Dec 7, 2025

HTTP Status Codes Guide: Complete Reference for Developers

Master HTTP status codes from 1xx to 5xx. Learn when to use each code, common mistakes, and best practices for REST APIs and web applications.

HTTP status codes tell clients what happened with their request. Choosing the right code improves API usability, debugging, and SEO. This guide covers every status code category with practical usage examples.

Key Takeaways

  • 1
    2xx = success (200 OK, 201 Created, 204 No Content)
  • 2
    3xx = redirect (301 permanent, 302 temporary, 304 cached)
  • 3
    4xx = client error (400 bad request, 401 unauthenticated, 403 forbidden, 404 not found)
  • 4
    5xx = server error (500 internal, 502 bad gateway, 503 unavailable)
  • 5
    401 means ’who are you?’ (login required); 403 means ’you can’t do that’ (no permission)

1Status Code Categories

HTTP status codes are grouped into five categories based on the first digit. Each category indicates a different type of response.
HTTP status code categories
RangeCategoryMeaning
1xxInformationalRequest received, continuing process
2xxSuccessRequest successfully received and processed
3xxRedirectionFurther action needed to complete request
4xxClient ErrorRequest contains bad syntax or cannot be fulfilled
5xxServer ErrorServer failed to fulfill valid request
The most common codes you\

22xx Success Codes

Success codes indicate the request was received, understood, and accepted. Choose the most specific code for your response.
2xx success codes
CodeNameWhen to Use
200OKStandard success; response has body (GET, POST with result)
201CreatedResource created; include Location header with new resource URL
202AcceptedRequest accepted for processing, but not completed (async jobs)
204No ContentSuccess with no response body (DELETE, PUT with no return)
206Partial ContentRange request fulfilled (video streaming, large file downloads)
// Express.js examples
// 200 OK - Standard response with body
app.get('/users/:id', (req, res) => {
  const user = findUser(req.params.id);
  res.status(200).json(user);
});

// 201 Created - Resource created
app.post('/users', (req, res) => {
  const user = createUser(req.body);
  res.status(201)
    .location(`/users/${user.id}`)
    .json(user);
});

// 204 No Content - Success, no body needed
app.delete('/users/:id', (req, res) => {
  deleteUser(req.params.id);
  res.status(204).send();
});
Use 201 for POST that creates a resource, 200 for POST that performs an action without creating. Use 204 when clients don\

33xx Redirection Codes

Redirection codes tell clients to look elsewhere. The distinction between permanent and temporary affects caching and SEO.
3xx redirection codes
CodeNameWhen to Use
301Moved PermanentlyURL changed forever; browsers cache and update bookmarks; SEO passes link equity
302FoundTemporary redirect; don't cache; original URL may return
303See OtherRedirect to GET a different resource (after POST form submission)
304Not ModifiedCached version is still valid (conditional GET with ETag/If-Modified-Since)
307Temporary RedirectLike 302 but preserves HTTP method (POST stays POST)
308Permanent RedirectLike 301 but preserves HTTP method
// 301 - Permanent redirect (URL change, SEO migration)
app.get('/old-page', (req, res) => {
  res.redirect(301, '/new-page');
});

// 302 - Temporary redirect (A/B test, maintenance)
app.get('/promo', (req, res) => {
  res.redirect(302, '/current-sale');
});

// 303 - See Other (redirect after POST)
app.post('/orders', (req, res) => {
  const order = createOrder(req.body);
  // Redirect to GET the confirmation page
  res.redirect(303, `/orders/${order.id}/confirmation`);
});

// 304 - Not Modified (caching)
app.get('/api/data', (req, res) => {
  const etag = '"abc123"';
  if (req.headers['if-none-match'] === etag) {
    return res.status(304).send();
  }
  res.set('ETag', etag).json(data);
});
301 redirects are cached aggressively by browsers. If you accidentally 301 to the wrong URL, users may be stuck until they clear cache. Use 302 during testing.

44xx Client Error Codes

Client errors indicate problems with the request. These help clients understand what they did wrong.
4xx client error codes
CodeNameWhen to Use
400Bad RequestMalformed syntax, invalid parameters, validation failed
401UnauthorizedAuthentication required or credentials invalid
403ForbiddenAuthenticated but not authorized for this resource
404Not FoundResource doesn't exist at this URL
405Method Not AllowedHTTP method not supported (GET on POST-only endpoint)
409ConflictRequest conflicts with current state (duplicate, version mismatch)
410GoneResource permanently deleted (unlike 404, we know it existed)
415Unsupported Media TypeContent-Type not supported (JSON sent to XML-only endpoint)
422Unprocessable EntitySyntax correct but semantically invalid (valid JSON, invalid data)
429Too Many RequestsRate limit exceeded; include Retry-After header
// 400 - Bad Request (validation error)
app.post('/users', (req, res) => {
  if (!req.body.email) {
    return res.status(400).json({
      error: 'Bad Request',
      message: 'Email is required',
      field: 'email'
    });
  }
});

// 401 - Unauthorized (no/invalid auth)
app.get('/profile', (req, res) => {
  if (!req.headers.authorization) {
    return res.status(401)
      .set('WWW-Authenticate', 'Bearer')
      .json({ error: 'Authentication required' });
  }
});

// 403 - Forbidden (authenticated but not allowed)
app.delete('/users/:id', (req, res) => {
  if (!req.user.isAdmin) {
    return res.status(403).json({
      error: 'Forbidden',
      message: 'Admin access required'
    });
  }
});

// 404 - Not Found
app.get('/users/:id', (req, res) => {
  const user = findUser(req.params.id);
  if (!user) {
    return res.status(404).json({
      error: 'Not Found',
      message: `User ${req.params.id} not found`
    });
  }
});

// 429 - Rate Limited
app.use((req, res, next) => {
  if (isRateLimited(req)) {
    return res.status(429)
      .set('Retry-After', '60')
      .json({ error: 'Too many requests. Try again in 60 seconds.' });
  }
  next();
});
401 vs 403: Use 401 when identity is unknown (not logged in or invalid token). Use 403 when identity is known but lacks permission. 401 says "who are you?"; 403 says "I know who you are, but no."

55xx Server Error Codes

Server errors indicate problems on the server side. The client\'s request may be valid, but the server couldn\'t fulfill it.
5xx server error codes
CodeNameWhen to Use
500Internal Server ErrorUnhandled exception, unexpected error (catch-all)
501Not ImplementedServer doesn't support the requested functionality
502Bad GatewayUpstream server returned invalid response (proxy/load balancer)
503Service UnavailableServer temporarily down (maintenance, overload)
504Gateway TimeoutUpstream server didn't respond in time
// Global error handler
app.use((err, req, res, next) => {
  console.error('Unhandled error:', err);
  
  // Don't leak error details in production
  const message = process.env.NODE_ENV === 'production'
    ? 'Internal server error'
    : err.message;
  
  res.status(500).json({
    error: 'Internal Server Error',
    message,
    requestId: req.id // For debugging
  });
});

// 503 - Maintenance mode
app.use((req, res, next) => {
  if (isMaintenanceMode()) {
    return res.status(503)
      .set('Retry-After', '3600')
      .json({
        error: 'Service Unavailable',
        message: 'Server is under maintenance. Please try again later.'
      });
  }
  next();
});
Never expose stack traces, database errors, or internal paths in 5xx responses to production clients. Log the full error server-side and return a generic message with a request ID for debugging.

6REST API Best Practices

Consistent status code usage makes APIs predictable and easier to consume. Follow these conventions.
Status codes by REST operation
OperationSuccessCommon Errors
GET /items200401, 403, 404
GET /items/:id200401, 403, 404
POST /items201400, 401, 403, 409
PUT /items/:id200 or 204400, 401, 403, 404
PATCH /items/:id200 or 204400, 401, 403, 404
DELETE /items/:id204401, 403, 404
  • Return consistent error response format: { error, message, details }
  • Include Content-Type: application/json for JSON responses
  • Use 400 for client input validation errors, 422 for business logic validation
  • Always return 401 with WWW-Authenticate header for auth challenges
  • Include Retry-After header with 429 and 503 responses
  • Use 404 for missing resources, not empty arrays (return 200 with [])
  • Return 201 with Location header for created resources
  • Log all 5xx errors with request context for debugging

71xx Informational Codes

Informational codes are provisional responses. You\'ll rarely use these directly, but they\'re important in specific scenarios.
1xx informational codes
CodeNameWhen Used
100ContinueServer received headers, client can send body (Expect: 100-continue)
101Switching ProtocolsServer switching to different protocol (HTTP → WebSocket)
102ProcessingServer is processing, response coming (WebDAV)
103Early HintsPreload resources while server prepares response
// 101 - WebSocket upgrade
// Happens automatically with ws library
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server });
// Client sends: Upgrade: websocket
// Server responds: 101 Switching Protocols

// 103 - Early Hints (Node.js 18+)
app.get('/page', (req, res) => {
  res.writeEarlyHints({
    link: '</style.css>; rel=preload; as=style'
  });
  // Continue processing...
  res.render('page');
});
Most developers never send 1xx codes directly. They\

8Common Mistakes to Avoid

These patterns seem logical but confuse clients and break conventions.
  • Using 200 for everything with { success: false } in body—use proper error codes
  • Returning 500 for validation errors—these are 400 (client errors)
  • Using 403 when 401 is correct—unauthenticated vs unauthorized
  • Returning 404 for empty search results—use 200 with empty array
  • Using 302 for permanent URL changes—use 301 for SEO
  • Returning 204 when clients expect a response body—use 200
  • Generic 400 for all validation errors—include field-level details
  • Missing Content-Type header on JSON responses
// ❌ Bad: 200 with error in body
res.status(200).json({ success: false, error: 'Not found' });

// ✅ Good: Proper status code
res.status(404).json({ error: 'Not Found' });

// ❌ Bad: 404 for empty results
if (results.length === 0) {
  return res.status(404).json({ error: 'No results' });
}

// ✅ Good: 200 with empty array
res.status(200).json({ results: [], total: 0 });

// ❌ Bad: 500 for validation error
if (!isValid(data)) {
  return res.status(500).json({ error: 'Invalid data' });
}

// ✅ Good: 400 for client error
res.status(400).json({ error: 'Validation failed', details });

Boost Your Developer Workflow

Free online tools for encoding, formatting, hashing, and more.

Explore Dev Tools

Frequently Asked Questions

What is the difference between 401 and 403?
401 Unauthorized means authentication is required but missing or invalid—the server doesn’t know who you are. 403 Forbidden means authentication succeeded but you lack permission—the server knows who you are but won’t allow access. Use 401 for ’please log in’ and 403 for ’you can’t do that.’
When should I use 404 vs 410?
404 Not Found means the resource doesn’t exist at this URL—it may never have existed or may exist elsewhere. 410 Gone means the resource existed but was intentionally deleted and won’t return. Use 410 for removed content to help search engines de-index faster.
Should I use 400 or 422 for validation errors?
400 Bad Request is for malformed syntax (unparseable JSON, missing required header). 422 Unprocessable Entity is for semantically invalid data (valid JSON but invalid email format). Many APIs use 400 for both since 422 is less widely known. Be consistent within your API.
What status code should I return for rate limiting?
Use 429 Too Many Requests with a Retry-After header indicating when the client can try again. The value can be seconds (Retry-After: 60) or an HTTP date. Include the limit and remaining count in response headers (X-RateLimit-Limit, X-RateLimit-Remaining).
Do status codes affect SEO?
Yes significantly. 200 pages are indexed normally. 301 passes ~90% link equity to the new URL. 302 preserves original URL in index (temporary). 404/410 remove pages from index (410 faster). 503 tells crawlers to retry later. Using wrong codes can hurt rankings or cause de-indexing.