Every time your browser loads a web page, makes an API call, or downloads a file, the server responds with an HTTP status code — a three-digit number that tells the client exactly what happened with the request. Understanding these codes is fundamental to web development, server administration, and debugging. When something goes wrong, the status code is your first clue.
This guide covers the most important HTTP status codes you will encounter in practice, what causes each one, and concrete steps to fix them when they indicate a problem.
The Five Categories
HTTP status codes are organized into five classes based on the first digit. Each class represents a fundamentally different type of response.
| Range | Category | Meaning | Who Needs to Act |
|---|---|---|---|
| 1xx | Informational | Request received, processing continues | No one (automatic) |
| 2xx | Success | Request was received, understood, and accepted | No one (all good) |
| 3xx | Redirection | Further action needed to complete the request | Client follows redirect |
| 4xx | Client Error | The request contains bad syntax or cannot be fulfilled | Client (fix the request) |
| 5xx | Server Error | The server failed to fulfill a valid request | Server admin (fix the server) |
2xx Success Codes
200 OK
The most common status code. The request succeeded, and the response body contains the requested resource. For a GET request, this means the resource was found and returned. For a POST request, this means the action was completed successfully.
HTTP/2 200
content-type: text/html; charset=UTF-8
201 Created
The request succeeded and a new resource was created. Typically returned by POST requests when creating a new database record, user account, or file. The response usually includes a Location header pointing to the newly created resource.
HTTP/2 201
Location: /items/42
204 No Content
The request succeeded, but there is no content to return. Common for DELETE requests (the resource was deleted successfully) and PUT/PATCH requests where the server does not need to return the updated resource.
3xx Redirection Codes
301 Moved Permanently
The resource has been permanently moved to a new URL. The client (and search engines) should update their bookmarks and links. This is the code to use when you change a URL structure, migrate to a new domain, or enforce HTTPS.
SEO Impact: Search engines transfer link equity ("link juice") from the old URL to the new one. This is the recommended redirect for SEO when you need to change URLs permanently.
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
302 Found (Temporary Redirect)
The resource is temporarily located at a different URL. The client should continue using the original URL for future requests. Use this for maintenance pages, A/B testing, or temporary redirects.
SEO Impact: Search engines do not transfer link equity. They keep the original URL in their index.
304 Not Modified
The resource has not changed since the last request. The client can use its cached version. This is an optimization — instead of re-downloading the same content, the server tells the browser "your cache is still valid." You will see this frequently in browser DevTools for static assets like CSS, JavaScript, and images.
4xx Client Error Codes
Client errors indicate that the problem is with the request itself — wrong URL, missing authentication, forbidden access, or invalid data.
400 Bad Request
The server cannot process the request because it is malformed. This could be invalid JSON in the request body, missing required parameters, invalid URL encoding, or a request that violates the protocol.
Common Causes:
- Malformed JSON in a POST/PUT request body
- Missing required query parameters or headers
- Request body exceeds the expected format
- Invalid characters in the URL
- Cookie data that is too large or corrupted
How to Fix: Check the request body for valid JSON, verify all required parameters are included, and examine the response body for specific error messages.
401 Unauthorized
The request requires authentication, and either no credentials were provided or the credentials are invalid. Despite the name "Unauthorized," this code actually means "Unauthenticated" — the server does not know who you are.
Common Causes:
- Missing or expired JWT token
- Invalid API key
- Expired session cookie
- Wrong username or password in Basic Auth
How to Fix: Ensure your authentication token or credentials are valid and included in the request. For JWT, check if the token has expired. For API keys, verify the key is correct and active.
403 Forbidden
The server understood the request and the client is authenticated, but the client does not have permission to access the resource. Unlike 401, re-authenticating will not help — the server knows who you are and has decided you are not allowed.
Common Causes:
- Insufficient permissions (e.g., non-admin trying to access admin routes)
- IP address blocked by firewall or access list
- Directory listing disabled and no index file
- File permissions preventing the web server from reading files
.htaccessor Nginx deny rules- ModSecurity WAF blocking the request
$ ls -la /var/www/mysite/
drwx------ 2 root root 4096 Mar 17 10:00 .
# Problem: www-data cannot read this directory!
# Fix permissions
$ sudo chmod 755 /var/www/mysite/
$ sudo chown -R www-data:www-data /var/www/mysite/
404 Not Found
The server cannot find the requested resource. This is the most recognized HTTP error — everyone has seen a 404 page. It means the URL does not correspond to any resource on the server.
Common Causes:
- Typo in the URL
- Page or file was deleted or moved without a redirect
- Broken internal links
- Missing Nginx rewrite rules (e.g., Laravel/WordPress pretty URLs not configured)
- Case sensitivity (Linux filesystems are case-sensitive:
/Aboutis not/about)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
405 Method Not Allowed
The HTTP method (GET, POST, PUT, DELETE, etc.) is not supported for the requested URL. For example, trying to POST to an endpoint that only accepts GET requests.
How to Fix: Check the API documentation for allowed methods. The response should include an Allow header listing the supported methods.
429 Too Many Requests
The client has sent too many requests in a given time period (rate limiting). The server is protecting itself from abuse or overload.
Common Causes:
- API rate limit exceeded
- Too many login attempts
- Aggressive web scraping or crawling
- Misconfigured automated scripts
How to Fix: Check the Retry-After header for when you can retry. Implement exponential backoff in your client code. If you are the server admin, adjust rate limits or whitelist the client IP.
5xx Server Error Codes
Server errors are the most critical because they indicate a problem on the server side. The client's request was valid, but the server failed to process it.
500 Internal Server Error
The most generic server error. Something went wrong on the server, but the server cannot be more specific about what. This is the catch-all for unhandled exceptions, fatal errors, and unexpected conditions.
Common Causes:
PHP Applications
- Syntax errors in PHP files
- Missing PHP extensions
- Database connection failure
- Permission denied on config files
- Memory limit exceeded
- Invalid
.htaccessdirectives
Node.js / Python Apps
- Unhandled promise rejections
- Module import errors
- Environment variable missing
- Database migration not run
- Disk full (cannot write logs)
- Application crash loop
$ tail -20 /var/log/php8.3-fpm.log
# Check Nginx error log
$ tail -20 /var/log/nginx/error.log
# Check application logs
$ tail -20 /var/www/myapp/storage/logs/laravel.log
502 Bad Gateway
Nginx (acting as a reverse proxy or gateway) received an invalid response from the upstream server. This means the backend application either crashed, is not running, or returned a response that Nginx cannot understand.
Common Causes:
- PHP-FPM is not running or has crashed
- Backend application (Node.js, Python, etc.) is down
- PHP-FPM socket file does not exist or has wrong permissions
- Backend is returning malformed responses
- Upstream timeout on initial connection
$ sudo systemctl status php8.3-fpm
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Active: inactive (dead)
# Restart PHP-FPM
$ sudo systemctl restart php8.3-fpm
# Check if the socket exists
$ ls -la /var/run/php/php8.3-fpm.sock
srw-rw---- 1 www-data www-data 0 Mar 17 10:00 /var/run/php/php8.3-fpm.sock
When you see a 502, Nginx is working fine — it is the upstream server (PHP-FPM, Node.js, Python, etc.) that is the problem. Do not restart Nginx. Instead, check and restart the backend service. The Nginx error log will tell you exactly which upstream failed and why.
503 Service Unavailable
The server is temporarily unable to handle the request. This is commonly used during planned maintenance or when the server is overloaded. Unlike 502 (upstream is broken), 503 means the server itself is aware of the issue and is intentionally refusing requests.
Common Causes:
- Server is in maintenance mode
- Too many active connections (all workers busy)
- Rate limiting triggered at the server level
- Application deploying (brief window during restart)
- Resource exhaustion (CPU, memory, disk)
$ uptime
10:30:01 up 45 days, load average: 28.50, 25.10, 20.00
# Check memory usage
$ free -h
total used free shared buff/cache available
Mem: 31Gi 29Gi 512Mi 256Mi 1.2Gi 1.5Gi
# Check PHP-FPM active processes
$ sudo systemctl status php8.3-fpm | grep Processes
504 Gateway Timeout
Nginx did not receive a timely response from the upstream server. The backend is running but took too long to respond. This is different from 502 (backend is down) — in a 504, the backend is alive but slow.
Common Causes:
- Long-running database queries
- External API calls that are slow or hanging
- Large file processing (image manipulation, report generation)
- Deadlocks in the application
- PHP max_execution_time exceeded
- Nginx proxy_read_timeout too short
location / {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 300s;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
}
# For PHP-FPM, also increase PHP limits:
; php.ini
max_execution_time = 300
; PHP-FPM pool conf
request_terminate_timeout = 300s
Quick Reference: Troubleshooting by Status Code
| Code | First Check | Second Check | Third Check |
|---|---|---|---|
| 400 | Request body format | Required parameters | Content-Type header |
| 401 | Auth token/API key | Token expiration | Correct auth header format |
| 403 | File permissions | Firewall/IP block | User role/permissions |
| 404 | URL spelling | Nginx rewrite rules | File exists on disk |
| 405 | HTTP method used | API documentation | CORS preflight |
| 429 | Retry-After header | Sending rate | Rate limit config |
| 500 | Application error log | PHP/Node error log | File permissions |
| 502 | Backend service running? | Socket file exists? | Nginx error log |
| 503 | Server load (uptime) | Memory usage (free) | Maintenance mode? |
| 504 | Backend response time | Database slow queries | Proxy timeout config |
Reading Status Codes in Practice
You can check HTTP status codes using various tools:
$ curl -o /dev/null -s -w "%{http_code}\n" https://example.com
200
# curl: Show response headers (includes status code)
$ curl -I https://example.com
HTTP/2 200
content-type: text/html
server: nginx
# curl: Follow redirects and show all status codes
$ curl -ILs https://example.com | grep HTTP
HTTP/2 301
HTTP/2 200
# wget: Show server response
$ wget --spider -S https://example.com 2>&1 | grep "HTTP/"
HTTP/1.1 200 OK
Status Codes and APIs
RESTful APIs rely heavily on status codes to communicate the result of operations. Here is how well-designed APIs use status codes:
| Operation | Success Code | Common Error Codes |
|---|---|---|
| GET /users | 200 OK | 401, 403, 500 |
| GET /users/42 | 200 OK | 401, 403, 404, 500 |
| POST /users | 201 Created | 400, 401, 409 (conflict), 422, 500 |
| PUT /users/42 | 200 OK | 400, 401, 403, 404, 422, 500 |
| PATCH /users/42 | 200 OK | 400, 401, 403, 404, 422, 500 |
| DELETE /users/42 | 204 No Content | 401, 403, 404, 500 |
Both indicate the request cannot be processed, but for different reasons. 400 means the request is syntactically invalid (bad JSON, wrong content type). 422 means the request is syntactically correct but semantically invalid (email already exists, password too short, invalid enum value). Many modern APIs use 422 for validation errors because it is more specific.
Status Codes and SEO
Search engines use status codes to understand your site structure and make indexing decisions.
Good for SEO
- 200 — Content is valid, index it
- 301 — Pass link equity to new URL
- 304 — Content unchanged, use cache
Bad for SEO
- 404 — Page removed from index after repeated occurrence
- 500/502/503 — Temporary: search engines retry. Persistent: page deindexed
- Soft 404 — Returns 200 but shows "page not found" content. Confuses crawlers
A redirect chain occurs when URL A redirects to URL B, which redirects to URL C. Each hop slows page load and dilutes link equity. Search engines follow up to 10 redirects but prefer direct paths. Audit your redirects and ensure each one goes directly to the final destination (301 A directly to C).
Less Common but Important Codes
| Code | Name | When You Will See It |
|---|---|---|
| 100 | Continue | Client sent headers, server says "go ahead with the body" |
| 101 | Switching Protocols | WebSocket upgrade response |
| 206 | Partial Content | Range requests (video streaming, file resume) |
| 307 | Temporary Redirect | Like 302, but preserves HTTP method (POST stays POST) |
| 308 | Permanent Redirect | Like 301, but preserves HTTP method |
| 409 | Conflict | Resource state conflict (e.g., duplicate entry) |
| 410 | Gone | Resource permanently deleted (stronger than 404 for SEO) |
| 413 | Payload Too Large | Upload exceeds client_max_body_size |
| 418 | I'm a Teapot | Easter egg from RFC 2324 (yes, it is real) |
| 422 | Unprocessable Entity | Validation errors in the request data |
| 520-527 | Cloudflare Custom | Cloudflare-specific errors (not standard HTTP) |
HTTP status codes are your first diagnostic tool when something goes wrong. Learn the big ones — 200, 301, 400, 401, 403, 404, 500, 502, 503, 504 — and you will be able to diagnose the vast majority of web issues. Always check the server error logs for 5xx errors, check your request for 4xx errors, and remember that 502 means "the backend is down" while 504 means "the backend is too slow." Bookmark this guide and refer to the troubleshooting table whenever you encounter an unfamiliar status code.