2xx and 3xx Status Codes
2xx Success Codes
The request was successfully received, understood, and accepted.
| Code | Name | When to Use |
|---|---|---|
| 200 | OK | Standard success response |
| 201 | Created | New resource created (POST) |
| 202 | Accepted | Request accepted for processing (async) |
| 204 | No Content | Success but no body to return (DELETE) |
| 206 | Partial Content | Range request (partial download) |
200 OK
<?php
// GET request returns 200 with data
http_response_code(200);
header('Content-Type: application/json');
echo json_encode([
'id' => 1,
'name' => 'Widget Pro',
'price' => 29.99
]);
201 Created
<?php
// POST request creates new resource
$product = createProduct($data);
http_response_code(201);
header('Content-Type: application/json');
header('Location: /api/products/' . $product['id']);
echo json_encode($product);
204 No Content
<?php
// DELETE request removes resource
deleteProduct($id);
http_response_code(204);
// No body sent - resource was deleted
3xx Redirection Codes
The client needs to take additional action to complete the request.
| Code | Name | When to Use |
|---|---|---|
| 301 | Moved Permanently | URL has changed forever (SEO friendly) |
| 302 | Found | Temporary redirect (legacy) |
| 303 | See Other | Redirect after POST (PRG pattern) |
| 304 | Not Modified | Use cached version |
| 307 | Temporary Redirect | Temporary redirect, preserve method |
| 308 | Permanent Redirect | Permanent redirect, preserve method |
301 Moved Permanently
<?php
// Force HTTP to HTTPS (permanent)
if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
$url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
header('HTTP/1.1 301 Moved Permanently');
header("Location: $url");
exit;
}
// www to non-www redirect
if (substr($_SERVER['HTTP_HOST'], 0, 4) === 'www.') {
$url = 'https://' . substr($_SERVER['HTTP_HOST'], 4) . $_SERVER['REQUEST_URI'];
header('HTTP/1.1 301 Moved Permanently');
header("Location: $url");
exit;
}
304 Not Modified
<?php
// Cache validation - check if resource has changed
$fileMtime = filemtime('/path/to/resource.js');
$etag = md5_file('/path/to/resource.js');
// Set cache headers
header('Cache-Control: public, max-age=3600');
header('Last-Modified: ' . date('r', $fileMtime));
header('ETag: "' . $etag . '"');
// Check if client has cached version
$ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
$ifNoneMatch = $_SERVER['HTTP_IF_NONE_MATCH'] ?? '';
if ($ifModifiedSince === date('r', $fileMtime) || $ifNoneMatch === '"' . $etag . '"') {
http_response_code(304); // Not Modified
exit; // No body needed
}
// Send new content
http_response_code(200);
echo file_get_contents('/path/to/resource.js');
4xx Client Error Codes
4xx Client Errors
The request contains bad syntax or cannot be fulfilled by the server.
| Code | Name | When to Use |
|---|---|---|
| 400 | Bad Request | Malformed syntax, invalid data |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | HTTP method not supported |
| 409 | Conflict | Resource state conflict |
| 415 | Unsupported Media Type | Wrong Content-Type |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate limit exceeded |
400 Bad Request
<?php
// Invalid JSON body
$rawBody = file_get_contents('php://input');
data = json_decode($rawBody, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode([
'error' => 'Bad Request',
'message' => 'Invalid JSON: ' . json_last_error_msg()
]);
exit;
}
401 Unauthorized vs 403 Forbidden
<?php
// 401 - Not authenticated (no credentials)
function checkAuthentication(): void
{
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (empty($token)) {
http_response_code(401);
header('WWW-Authenticate: Bearer realm="api"');
echo json_encode(['error' => 'Authentication required']);
exit;
}
$user = validateToken($token);
if (!$user) {
http_response_code(401);
echo json_encode(['error' => 'Invalid token']);
exit;
}
}
// 403 - Authenticated but not authorized (no permission)
function checkAuthorization(User $user, string $resource): void
{
if (!$user->hasPermission($resource)) {
http_response_code(403);
echo json_encode(['error' => 'Access denied']);
exit;
}
}
404 Not Found
<?php
$product = $productRepository->getById($productId);
if (!$product) {
http_response_code(404);
header('Content-Type: application/json');
echo json_encode([
'error' => 'Not Found',
'message' => "Product with ID $productId not found"
]);
exit;
}
422 Unprocessable Entity
<?php
// Validation errors
class Validator
{
public function validate(array $data): array
{
$errors = [];
if (empty($data['name'])) {
$errors['name'] = 'Name is required';
}
if (!isset($data['price']) || $data['price'] < 0) {
$errors['price'] = 'Price must be a positive number';
}
if (empty($data['sku'])) {
$errors['sku'] = 'SKU is required';
} elseif (strlen($data['sku']) > 64) {
$errors['sku'] = 'SKU must be 64 characters or less';
}
return $errors;
}
}
$validator = new Validator();
$errors = $validator->validate($data);
if (!empty($errors)) {
http_response_code(422);
echo json_encode([
'error' => 'Validation Failed',
'errors' => $errors
]);
exit;
}
429 Too Many Requests
<?php
function checkRateLimit(string $apiKey): void
{
$redis = new Redis();
$redis->connect('127.0.0.1');
$key = "rate_limit:$apiKey";
$requests = (int)$redis->get($key);
if ($requests >= 100) { // 100 requests per hour
http_response_code(429);
header('Retry-After: 3600');
header('X-RateLimit-Limit: 100');
header('X-RateLimit-Remaining: 0');
echo json_encode(['error' => 'Rate limit exceeded']);
exit;
}
$redis->incr($key);
$redis->expire($key, 3600);
header('X-RateLimit-Limit: 100');
header('X-RateLimit-Remaining: (100 - $requests - 1)');
}
5xx Server Error Codes and Magento Errors
5xx Server Errors
The server failed to fulfill a valid request.
| Code | Name | When to Use |
|---|---|---|
| 500 | Internal Server Error | Generic server error |
| 502 | Bad Gateway | Upstream server returned invalid response |
| 503 | Service Unavailable | Server temporarily overloaded/down |
| 504 | Gateway Timeout | Upstream server too slow |
500 Internal Server Error
<?php
// PHP error handling for production
set_error_handler(function ($severity, $message, $file, $line) {
throw new \ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function (\Throwable $e) {
error_log($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
http_response_code(500);
header('Content-Type: application/json');
// Don't expose internal details in production
echo json_encode([
'error' => 'Internal Server Error',
'message' => 'An unexpected error occurred'
]);
});
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR])) {
http_response_code(500);
echo json_encode(['error' => 'Fatal error occurred']);
}
});
503 Service Unavailable
<?php
// Check if maintenance mode is active
function isMaintenanceMode(): bool
Maintenance flag file
if (file_exists('/var/.maintenance.flag')) {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
$allowed = json_decode(file_get_contents('/var/.maintenance.ip'), true) ?? [];
// Allow specific IPs during maintenance
if (in_array($ip, $allowed)) {
return false;
}
return true;
}
return false;
}
if (isMaintenanceMode()) {
http_response_code(503);
header('Retry-After: 3600'); // Try again in 1 hour
echo json_encode([
'error' => 'Service Unavailable',
'message' => 'System is under maintenance'
]);
exit;
}
Magento Common Error Codes
| Code | Magento Component | Typical Cause |
|---|---|---|
| 400 | Web API | Invalid request body, missing required fields |
| 401 | Web API | Missing or invalid API token |
| 403 | Admin | Insufficient permissions |
| 404 | Frontend | Product/category not found, wrong URL |
| 422 | Web API | Validation failure (invalid SKU, price format) |
| 500 | Application | PHP error, module conflict, compilation error |
| 503 | Varnish/Nginx | Backend server down, maintenance mode |
Error Response Best Practices
<?php
// Standardized error response format
class ErrorResponse
{
public static function json(int $code, string $message, array $details = []): void
{
http_response_code($code);
header('Content-Type: application/json');
$response = [
'error' => [
'code' => $code,
'message' => $message,
'timestamp' => date('c'),
'request_id' => uniqid('req_')
]
];
if (!empty($details)) {
$response['error']['details'] = $details;
}
echo json_encode($response);
}
}
// Usage
ErrorResponse::json(422, 'Validation failed', [
'field' => 'price',
'message' => 'Price must be greater than 0'
]);
Key Takeaway
Always use the most specific status code available. 422 is better than 400 for validation errors. 404 is better than 400 when a resource doesn't exist. Proper status codes help clients understand what happened and how to proceed.
Quiz
1. What status code should you return when a product is not found?
2. What is the difference between 401 and 403?
3. Which status code should be returned after successfully creating a new product with POST?
4. What does 429 Too Many Requests indicate?
5. What is 304 Not Modified used for?
Flashcards
Question
What are the 5 HTTP status code categories?
Click to reveal answer
Answer
1xx (Informational), 2xx (Success), 3xx (Redirection), 4xx (Client Error), 5xx (Server Error)
Question
When do you use 201 vs 200?
Click to reveal answer
Answer
201 Created: POST request that created a new resource. 200 OK: GET request or successful operation that doesn't create a new resource.
Question
When do you use 401 vs 403?
Click to reveal answer
Answer
401: Client hasn't provided credentials (not authenticated). 403: Client is authenticated but lacks permission (not authorized).
Question
What status code is used for maintenance mode?
Click to reveal answer
Answer
503 Service Unavailable. Include a Retry-After header to tell clients when to retry.
Question
What is 422 Unprocessable Entity used for?
Click to reveal answer
Answer
Validation errors - when the request syntax is correct but the data fails business validation (invalid email, negative price, missing required fields).
Question
What does 304 Not Modified do?
Click to reveal answer
Answer
Tells the client the resource hasn't changed. The client can use its cached copy without downloading the resource again. Used with ETag and If-Modified-Since headers.
Question
What status code should a DELETE request return on success?
Click to reveal answer
Answer
204 No Content - the resource was successfully deleted and there's no response body to return.
Question
What does 502 Bad Gateway mean?
Click to reveal answer
Answer
The server received an invalid response from an upstream server (like PHP-FPM returning invalid data to Nginx). Common when backend processes crash.
Revision Notes
Key Takeaways
- 1. 2xx codes indicate success: 200 (OK), 201 (Created), 204 (No Content)
- 2. 3xx codes handle redirects: 301 (Permanent), 304 (Not Modified), 307 (Temporary)
- 3. 4xx codes indicate client errors: 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found)
- 4. 5xx codes indicate server errors: 500 (Internal), 503 (Unavailable)
- 5. 422 (Unprocessable Entity) is ideal for validation errors
- 6. 429 (Too Many Requests) is used for rate limiting
- 7. Always use the most specific status code available
Interview Tips
- • Know the difference between 401 and 403 (authentication vs authorization)
- • Understand when to use 201 vs 200 for creation responses
- • Explain the purpose of 304 Not Modified and ETag caching
- • Know Magento's common error codes and their causes
- • Describe best practices for error response formats
Cheat Sheet
HTTP Status Codes Cheat Sheet
2xx Success:
- 200 OK - Standard success
- 201 Created - Resource created (POST)
- 204 No Content - Success, no body (DELETE)
3xx Redirection:
- 301 Moved Permanently - URL changed forever
- 304 Not Modified - Use cached version
- 307 Temporary Redirect - Temporary, preserve method
4xx Client Error:
- 400 Bad Request - Malformed syntax
- 401 Unauthorized - Not authenticated
- 403 Forbidden - Not authorized
- 404 Not Found - Resource doesn't exist
- 405 Method Not Allowed - Invalid HTTP method
- 422 Unprocessable Entity - Validation error
- 429 Too Many Requests - Rate limit exceeded
5xx Server Error:
- 500 Internal Server Error - Generic server error
- 502 Bad Gateway - Invalid upstream response
- 503 Service Unavailable - Server overloaded/maintenance
- 504 Gateway Timeout - Upstream too slow