< HTTP/1.1 500 Internal Server ErrorHTTP 500 Internal Server Error: Meaning and Fixes
500 means the server hit an error it did not handle. Find which layer sent it, read the right log for nginx, Apache, WordPress, Django, Next.js or Express.
- Cacheable
- Only with explicit freshness
- Retry?
- Only idempotent requests, with backoff
- Usually sent by
- Origin app
- Spec
- RFC 9110 §15.6.1
On this page
TL;DR: HTTP 500 Internal Server Error means the server failed while handling your request. Find the layer that returned it, then match the request ID and timestamp to its error log; the exception there tells you what to fix.
What it means
RFC 9110 section 15.6.1: the server encountered an unexpected condition that prevented it from fulfilling the request. A bad payload can expose a server bug too: 500 describes the failure, not whether the input was valid. Start with the server exception rather than guessing from the status.
Server responses and client messages below are illustrative.
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error":"internal_error","requestId":"req-8f3a1c"}
What you see in your client
These messages assume the response reason is Internal Server Error; a server can use different wording.
- Axios:
AxiosError: Request failed with status code 500. With the defaultvalidateStatus, Axios rejects this response. Inspecterror.response.status,error.response.headersanderror.response.databefore changing the request. Axios source. - fetch: the promise resolves with
response.status === 500andresponse.ok === false. Checkokyourself; acatchblock alone misses HTTP error responses. Fetch behaviour. - Python requests:
requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://example.com/api. This appears when you callresponse.raise_for_status(). Save the response body and headers before raising. Requests source. - curl -f:
curl: (22) The requested URL returned error: 500. Usecurl -sS -D - https://example.com/apiwhile diagnosing so you retain the error body. curl source. - .NET:
System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).This is the English message fromEnsureSuccessStatusCode()with that reason phrase. InspectStatusCodeand read the body before calling it. Runtime source, message resource. - Spring WebClient:
org.springframework.web.reactive.function.client.WebClientResponseException$InternalServerError: 500 Internal Server Error from GET https://example.com/api.retrieve()uses this subclass by default; readgetResponseBodyAsString(). RestTemplate usesHttpServerErrorException.InternalServerErrorfor this 5xx status. WebClient source, RestTemplate handler.
Who sent it?
| Signal | Layer |
|---|---|
| Body is your app’s JSON or framework error page | Application: stack trace in app logs |
Server: Apache, “The server encountered an internal error or misconfiguration” | Apache: .htaccess or CGI/handler failure |
Server: nginx with the default page “500 Internal Server Error” | nginx itself: check error.log for rewrite or internal redirection cycle |
Server: cloudflare and “Error 1101: Worker threw exception” | A Cloudflare Worker threw; see Workers logs |
Server: cloudflare with your origin’s 500 body | Origin returned 500; Cloudflare passed it through |
| Cloudflare page “Error 520” | Not a 500; origin response was empty or invalid, see 520 |
If a CDN is involved, bypass it and hit the origin directly to see whether the 500 is yours:
curl -sv https://example.com/api/orders -o /dev/null 2>&1 | grep -iE '^< (HTTP|server|cf-ray|via|x-request-id|x-amz)'
curl -sv --resolve example.com:443:ORIGIN_IP https://example.com/api/orders -o /dev/null
Every response should carry a request ID header (X-Request-ID) that is also logged by the app, so you can find the exact log line.
Fix it by stack
Match the exception to the deployed application version and configuration before changing a setting.
Common causes by stack
Apache and .htaccess
[core:alert] [pid 1234] /var/www/html/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration
[cgi:error] Premature end of script headers: index.php
The first points to an unavailable directive: check that mod_rewrite is loaded (a2enmod rewrite on Debian/Ubuntu). With both AllowOverride None and AllowOverrideList None, Apache ignores .htaccess entirely; it does not report this syntax error. The second points to a CGI handler that ended without headers: read its stderr/PHP log. Run apachectl configtest for the main configuration, then reproduce the request to exercise .htaccess. Check ownership and access for the actual Apache user. Apache configuration.
WordPress and PHP
PHP fatal errors are hidden when display_errors is off, so enable logging instead of display:
// wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true); // writes wp-content/debug.log
define('WP_DEBUG_DISPLAY', false);
define('WP_MEMORY_LIMIT', '256M');
Read wp-content/debug.log for the failing plugin, theme or PHP fatal. If it says Allowed memory size of 134217728 bytes exhausted, that number is the limit in that message, not a universal WordPress default. The 256M above is a chosen troubleshooting value. Disable the identified plugin; check .htaccess when Apache logs a directive error. WordPress debugging.
Node, Express and Next.js
Express uses err.status or err.statusCode when it is a valid 4xx/5xx value, otherwise 500. Express 5 forwards rejected route promises to next; in Express 4, use try/catch and next(err). Put the four-argument error handler after routes, and delegate if headers were already sent. Express error handling.
// Global error handler: must have 4 parameters and come last
app.use((err, req, res, next) => {
if (res.headersSent) return next(err)
req.log?.error({ err, path: req.path }) // full detail server-side only
const candidate = err.status ?? err.statusCode
const status = Number.isInteger(candidate) && candidate >= 400 && candidate < 600 ? candidate : 500
res.status(status).json({ error: status < 500 ? err.message : 'internal_error', requestId: req.id })
})
Next.js hides Server Component error details in production. Use error.digest from the error boundary to match the server log entry; the digest is generated for that error. Next.js error boundary.
Django, Rails, Spring
- Django/DRF: an exception outside DRF’s handled exceptions is re-raised and becomes Django’s 500 response. With
DEBUG = False, read the server traceback rather than the generic page; inspect database/migration errors there. DRF exception handling. - Rails: exceptions absent from
config.action_dispatch.rescue_responsesmap to 500. Match the request ID in your production log and fix the exception, rather than mapping every exception to 200. Rails exception mapping. - Spring Boot: the browser’s Whitelabel Error Page comes from the default error handling. Read the application stack trace for the failing controller or dependency. Spring Boot error handling.
- FastAPI: returning data that fails
response_modelvalidation produces a server error. Fix the returned fields/types; this is different from invalid request input. Response validation. - ASP.NET Core: an unhandled exception before response headers produces 500; after headers, the server closes the connection. Use
UseExceptionHandlerin production and find the exception in application logs. ASP.NET Core error handling. - Laravel: an exception that is not an HTTP exception becomes a 500 in the default handler. Read the exception in the configured log channel; keep
APP_DEBUG=falsein production. Laravel handler, production configuration. - API Gateway/Lambda: a rejected Lambda invocation produces 500; a function error or invalid response produces 502 instead. Inspect invocation permissions and the gateway/Lambda logs. AWS error mapping.
- nginx/Cloudflare:
rewrite or internal redirection cycleis nginx’s 500 after its internal-redirect limit; fix therewrite/error_pageloop. Workers error 1101 means a JavaScript exception; inspect Workers logs. nginx internal redirects, Workers errors.
Resource exhaustion, whatever the stack
A handled resource failure can become 500; a killed process can instead leave a proxy returning 502. Check the exception for No space left on device, a database pool timeout or EMFILE, and check Kubernetes pod state for OOMKilled. Check df -h, free -m, dmesg | grep -i oom, and the database connection count.
A 500 on POST /login
Reproduce the failing method and path, then search the server log by request ID. Trace user lookup, password verification and session creation in that order; stop at the first exception. Redact passwords, cookies and tokens from the log. If the traceback points to a database/schema error, verify the deployment’s database connection and migration state. An Axios 500 here sends you to those server logs, not to Axios configuration.
Build it better
Return a short, stable body with a request ID, and never leak internals. RFC 9457 application/problem+json is the standard shape:
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
{"type":"about:blank","title":"Internal Server Error","status":500,"instance":"/req/8f3a1c"}
Alert on the 5xx rate, not on individual errors, and keep stack traces in your logging system (Sentry, OpenTelemetry), not in responses.
Related codes
- 502 Bad Gateway: a proxy got a bad response from the app behind it.
- 503 Service Unavailable: the app cannot take the request right now.
- 504 Gateway Timeout: a gateway timed out connecting or waiting for an upstream.
- 520 Web Server Returned an Unknown Error: Cloudflare received an empty or invalid origin response, often a crashed app.
- debug: 500 Internal Server Error: step-by-step triage.
In practice
Express.js
// Async route — explicit forwarding also works in Express 4
app.get('/api/data', async (req, res, next) => {
try {
const data = await fetchData()
res.json(data)
} catch (err) {
next(err) // Passes to the error handler below
}
})
// Global error handler — must have 4 params
app.use((err, req, res, next) => {
if (res.headersSent) return next(err)
// Log full error server-side
console.error({ err, path: req.path, method: req.method })
// Never expose internals to clients
const candidate = err.status ?? err.statusCode
const status = Number.isInteger(candidate) && candidate >= 400 && candidate < 600 ? candidate : 500
res.status(status).json({
error: status < 500 ? err.message : 'Internal server error'
})
})Next.js App Router
// app/api/data/route.ts
export async function GET() {
try {
const data = await fetchData()
return Response.json(data)
} catch (err) {
// Log server-side
console.error('GET /api/data failed:', err)
// Return generic 500 to client
return Response.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
// app/error.tsx — catches render errors in page routesDjango
# Django returns 500 automatically for unhandled exceptions.
# Customize the response in views.py:
import logging
from django.http import JsonResponse
logger = logging.getLogger(__name__)
def api_view(request):
try:
data = fetch_data()
return JsonResponse(data)
except Exception as e:
logger.exception('api_view failed')
return JsonResponse(
{'error': 'Internal server error'},
status=500
)
# settings.py — disable DEBUG in production
# DEBUG = False
# ADMINS = [('Admin', 'admin@example.com')]Frequently asked questions
What does 500 Internal Server Error mean?
The server hit an unexpected condition and could not complete the request. It is a catch-all for unhandled exceptions, bad configuration and crashed dependencies, and the server traceback identifies the failing code or dependency.
How do I fix a 500 error on my own site?
Find the log line for that request: the web server error log, the application log, or the PHP-FPM log. On Apache check .htaccess syntax and permissions, on WordPress use wp-content/debug.log to identify the failing plugin or a memory-exhaustion error, on app frameworks read the stack trace and the log entry matching the request ID.
Is a 500 error my fault as a visitor?
Almost never. Retrying once or twice, or coming back later, is all a visitor can do. If it only happens on one request that you build yourself, such as an API call, a malformed payload that the server fails to validate can trigger it, but that is still a server bug.
What is the difference between 500 and 502?
500 means the responding server hit an unexpected failure; an application, web server or gateway can generate it. 502 means a gateway could not get a usable response from an upstream server. Match the response to the logs of the layer that generated it.
Why do I see 500 errors intermittently?
Intermittent 500s point to load-dependent causes: connection pool exhaustion, memory growth, one bad instance behind a load balancer, race conditions, or a flaky downstream dependency. Correlate by instance and timestamp rather than by URL.
Why does POST /login return 500?
Match the login request to its server traceback. Inspect user lookup, password verification and session creation, then fix the first failing dependency or code path. Redact credentials from logs; an Axios 500 is the client reporting the server response.
Should I show 500 error details to users?
No. Return a generic message plus a request ID in production, log the stack trace server-side, and never include stack traces, SQL or config values in the response body.
Sources
Related
561 Unauthorized (AWS ALB Authentication)
AWS ALB 561 means the identity provider returned an error during listener authentication. Read error_reason in access logs and inspect the IdP response.
501 Not Implemented
The server doesn't support the functionality required to fulfill the request. Learn about unimplemented features.
504 Gateway Timeout: nginx, ALB and Cloudflare Fixes
Fix 504 Gateway Timeout: nginx proxy_read_timeout (60s default), ALB 60s idle, API Gateway 29s, Cloudflare 524 at 125s, with error-log strings and curl timing.
505 HTTP Version Not Supported
Learn what 505 HTTP Version Not Supported means when servers reject protocol versions. Understand HTTP/1.1, HTTP/2 compatibility and version negotiation.