
A 500 internal server error means the server hit an unexpected condition and could not finish the request, without saying why. The fault is almost always on the server: a crashed script, a broken config line, a lost database connection. The fix starts in the web-server and application logs at the exact time of the failed request.
The code is deliberately generic: the server knows something failed but does not disclose details to the client, for security reasons. For an administrator, a 500 is a red flag that calls for immediate log investigation; for a visitor, it is a signal to wait and retry. This guide covers what the code really means, 8 typical causes, the real error strings you will find in PHP, nginx, Apache, IIS and Node.js logs, and step-by-step fixes for WordPress, Laravel, Symfony, Express and sites behind Cloudflare.
What HTTP 500 Means
Per RFC 9110 §15.6.1, 500 indicates the server encountered an unexpected condition that prevented it from fulfilling the request. MDN calls it a catch-all: the server uses it when no more specific 5xx code fits. Unlike 502 (bad gateway) or 503 (service unavailable), a 500 usually points to an application-level exception or an interpreter problem on the machine that actually generated the response.
That last part matters when there is a proxy in front. With nginx, a load balancer or Cloudflare between the browser and the application, the 500 can be produced by any hop. Before touching code, find out which layer emitted it: the Server response header, the look of the error page and the access logs on each hop tell you that.
500 vs 502, 503, 504 and Cloudflare 520
| Code | Who generated it | What it usually means | Where to look first |
|---|---|---|---|
| 500 Internal Server Error | The application or the web server itself | Unhandled exception, fatal error, broken config directive | Application log, PHP-FPM log, web-server error log |
| 501 Not Implemented | Server | The request method is not supported at all | Server config, allowed methods |
| 502 Bad Gateway | A proxy (nginx, load balancer) | The upstream sent an invalid response or refused the connection | Proxy error log, whether the backend process is running |
| 503 Service Unavailable | Server or proxy | Overload, maintenance mode, no healthy backends | Maintenance flags, worker limits, health checks |
| 504 Gateway Timeout | A proxy | The upstream did not answer in time | Slow queries, proxy timeouts |
| 520 (Cloudflare) | Cloudflare edge | The origin returned an empty or unexpected response | Origin logs, crashes that close the connection |
Details on the neighbors: 502 Bad Gateway, 503 Service Unavailable and Cloudflare errors 520–530.
Is 500 Internal Server Error My Fault?
If you are visiting someone else's site or app, almost certainly not. A 500 is by definition a server-side failure, and nothing in your browser, network or device causes the server's code to crash. Clearing your cache or switching browsers, the advice many pages give first, rarely helps with a 500, because the broken part is not on your side.
There is one narrow exception: a request that carries something the server's code does not handle. A stale or corrupted session cookie, an oversized form field, an unusual character in a search box. A well-written application answers that with a 400; a buggy one throws an exception and returns 500. So if a single page fails for you while it works for others, signing out and back in, or deleting cookies for that one site, is worth a try. The bug is still the site's to fix.
The same logic applies to large services such as Discord or an online store: a 500 there is a problem on their servers, and the only things a user can do are retry later and check the service's official status page.
Does a 500 Error Mean the Site Is Down?
Not necessarily. A site is "down" when it does not answer at all, or answers every request with an error. A 500 is often much narrower: one page, one API endpoint, one form submission or one logged-in feature fails while the homepage returns 200 as usual. That is also why a monitor that only checks the homepage can stay green through a broken checkout.
Quick test: open two or three other pages of the same site, or check the URL from outside with a status checker. If everything returns 500, the whole application is failing (a fatal error at boot, a broken config, a dead database). If only some URLs do, the bug is in a specific code path.
Will Error 500 Fix Itself?
Sometimes. Transient 500s come and go on their own: a request that landed in the middle of a deploy, a database that was restarting, a worker that briefly ran out of memory, a disk that filled up and was cleaned by log rotation. Retrying after a minute or two is reasonable for a visitor.
A 500 caused by code or configuration does not heal. A syntax error, a missing dependency, a wrong file permission or a bad .htaccess line returns 500 on every request until someone changes the server. If the same URL still fails after several minutes, treat it as persistent and go to the logs.
8 Common Causes of 500 Errors
- Fatal error in PHP/Python/Node.js — unhandled exception, syntax error, out of memory.
- Wrong file permissions — chmod 644 for files, 755 for directories.
- Database connection failure — bad credentials, MySQL down, max_connections exhausted.
- Memory limit exceeded in PHP (
php.ini) or heap size in Node.js. - Infinite loop or recursion — script hangs,
max_execution_timereached. - Broken directive in .htaccess or nginx config — typo, missing module.
- Missing dependency — uninstalled composer package, npm module, Python library.
- Framework cache issue — stale Laravel config cache, Symfony
var/cache.
How to Fix Internal Server 500 Error: Step by Step
- Reproduce and timestamp it. Note the exact URL, method and time.
curl -sS -o /dev/null -w "%{http_code}\n" https://example.com/pageprints just the status code. - Find which layer answered.
curl -i https://example.com/pageshows theServerheader and the error body. An nginx-styled page, an Apache "misconfiguration" page, an IIS page and your framework's own error page each point to a different layer. - Read the error log at that timestamp. Web-server log first, then the application or PHP-FPM log. Look for the first error line, not the last.
- Undo the last change. Most 500s appear right after a deploy, a plugin update, a config edit or a package upgrade. Rolling back is the fastest confirmation.
- Validate the config.
nginx -t,apachectl configtest,php -l file.phpfor a single PHP file. - Check resources.
df -hfor a full disk,free -mfor memory, and whether the database answers. - Fix, then verify from outside and keep a monitor on the URL so a recurrence is noticed before users report it.
Where to Find Logs
The first step with 500 is reading logs. Never guess — always check the stack trace.
# PHP-FPM error log
tail -100 /var/log/php-fpm/error.log
tail -100 /var/log/php8.4-fpm.log
# nginx
tail -100 /var/log/nginx/error.log
# Apache
tail -100 /var/log/apache2/error.log
# Laravel
tail -100 storage/logs/laravel.log
# Node.js (pm2)
pm2 logs --lines 100
# Node.js or any app running as a systemd service
journalctl -u myapp -n 100 --no-pager
Paths vary by distribution: RHEL-based systems use /var/log/httpd/error_log for Apache. More on reading and rotating them in the nginx logs guide.
Real error strings and what they mean
| Log line (excerpt) | Where you see it | Cause and fix |
|---|---|---|
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted | PHP-FPM / PHP error log | Script hit memory_limit (here 128M). Fix the leak or large query; raise the limit only if the load is legitimate. |
PHP Parse error: syntax error, unexpected ... | PHP error log | Broken file after an edit or failed upload. Run php -l on it and redeploy. |
PHP Fatal error: Uncaught Error: Call to undefined function ... | PHP error log | Missing extension or dependency, or a PHP version mismatch after an upgrade. |
rewrite or internal redirection cycle while internally redirecting to ... | nginx error log | A try_files or rewrite loop, often a fallback to a file that does not exist. |
Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration | Apache error log | .htaccess uses mod_rewrite, which is not enabled. a2enmod rewrite on Debian/Ubuntu, then reload Apache. |
Request exceeded the limit of 10 internal redirects due to probable configuration error | Apache error log | A rewrite loop in .htaccess; add a condition that stops the rule from matching its own target. |
End of script output before headers | Apache error log (CGI/FastCGI) | The script crashed or printed nothing; check its own log and file permissions. |
For external diagnosis, use the HTTP Header Checker and Security Scanner by Enterno.io — they reveal server response details and whether information leaks through headers.
Platform-Specific Solutions
WordPress: "There has been a critical error"
Since WordPress 5.2 a fatal PHP error often shows as "There has been a critical error on this website." with a 500 status, and the admin email receives a recovery-mode link. The usual suspects are a plugin, the theme, .htaccess or memory.
- Rename plugins/ → identify which plugin broke.
- Increase memory_limit in
wp-config.php:define('WP_MEMORY_LIMIT', '256M'); - Enable debug:
define('WP_DEBUG', true);+define('WP_DEBUG_LOG', true); - Check .htaccess — rename it and visit Settings → Permalinks to regenerate.
With shell access, WP-CLI is faster than renaming folders:
wp plugin list --status=active
wp plugin deactivate --all
# then re-activate one by one:
wp plugin activate plugin-slug
With WP_DEBUG_LOG on, the error lands in wp-content/debug.log. Add define('WP_DEBUG_DISPLAY', false); so errors are logged, not printed to visitors. Keeping WordPress healthy over time is covered in monitoring WordPress sites.
Laravel
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
chmod -R 775 storage bootstrap/cache
chown -R www-data:www-data storage bootstrap/cache
A blank white page with 500 in Laravel is very often storage/ not writable by the web-server user, so the framework cannot even write its own log. Fix ownership first, then read storage/logs/laravel.log.
Symfony
php bin/console cache:clear --env=prod
tail -100 var/log/prod.log
Node.js / Express
// Global error handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
ok: false,
error: process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message
});
});
The handler must be registered after all routes and must take four arguments; Express recognizes an error handler by its arity.
500 Errors on nginx
nginx itself rarely invents a 500. In most cases it passes through a 500 generated by PHP-FPM, a Node.js process or another upstream, and the real error is in that application's log. nginx produces its own 500 mainly for internal problems: a rewrite loop, a broken config or running out of file descriptors. To see which is which, add $upstream_status to the access log format:
log_format upstream '$remote_addr [$time_local] "$request" '
'$status upstream=$upstream_status rt=$upstream_response_time';
access_log /var/log/nginx/access.log upstream;
If $status is 500 and upstream=500, the backend generated it. If the upstream field is empty, nginx answered by itself. Buffer and timeout settings for FastCGI:
nginx + PHP-FPM
fastcgi_read_timeout 60s;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
# increase on FastCGI timeout / buffer overflow
Apache and .htaccess
The default Apache page reads "The server encountered an internal error or misconfiguration and was unable to complete your request." The most common trigger is a line in .htaccess that Apache cannot parse or that needs a module that is not loaded. Temporarily rename .htaccess: if the site comes back, the problem is in that file. apachectl configtest checks the main config, but not .htaccess files, which are read per request.
IIS: Substatus Codes 500.0, 500.19, 500.21
IIS adds a substatus code that narrows the cause. It is written in the sc-substatus field of the IIS logs (by default under %SystemDrive%\inetpub\logs\LogFiles). Microsoft documents the full list in its IIS status code reference.
| Substatus | Meaning | Typical fix |
|---|---|---|
| 500.0 | Module or ISAPI error | Application exception; enable detailed errors locally or Failed Request Tracing |
| 500.19 | Configuration data is invalid | Malformed web.config, or a section used by a module that is not installed (for example URL Rewrite) |
| 500.21 | Module not recognized | Handler points to a module that is missing, such as an ASP.NET version not installed |
| 500.50 / 500.52 | URL Rewrite error (inbound / outbound rule) | Fix the rewrite rule in web.config |
Detailed IIS error pages are shown to requests from the server itself by default; keep it that way in production and read the details locally or in Failed Request Tracing logs.
500 Behind Cloudflare
When a site sits behind Cloudflare, a 500 page usually still comes from your origin: Cloudflare passes the origin's status and error page through to the visitor. To confirm, request the origin directly, bypassing the proxy, with the origin IP:
curl -i --resolve example.com:443:203.0.113.10 https://example.com/page
If the origin returns 500 too, fix it on the server as described above. If the origin answers 200 while visitors see 500 through Cloudflare, check the Cloudflare status page and your Workers, rules or transformations on that route. An empty or malformed origin response shows up as 520 instead, not 500.
500 in Postman
A 500 in Postman is the API's answer, not a Postman bug. Your request can still trigger it: a body the server does not validate (malformed JSON, a missing field, a wrong type), a missing Content-Type: application/json header, or an expired token that the code does not handle. Send the same request with curl -v (Postman's Code panel generates it) to rule out client settings, then read the API's logs for that timestamp. If you own the API, a request that is invalid should get a 4xx, so every such 500 is a validation bug worth fixing.
How to Check a 500 Error from Outside
- Status and headers: the HTTP header and availability checker shows the exact status code, the
Serverheader and the redirect chain, so you can see whether the 500 comes from the origin, a proxy or a CDN. - Information leaks: the security scanner flags headers such as
X-Powered-Byand version strings that error pages and misconfigured servers tend to expose. - Continuous checks: uptime monitors request key URLs on a schedule and alert when the status stops being the expected one.
Security: Never Expose Stack Traces in Production
Never show the full stack trace in production — this is OWASP A05 (Security Misconfiguration). An attacker learns code structure, framework versions, and filesystem paths.
# php.ini (production)
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
expose_php = OffIn Laravel: APP_DEBUG=false in .env. In Symfony: APP_ENV=prod.
A custom 500 page is fine and friendlier than the default, as long as it still returns the 500 status. A branded error page served with 200 hides the failure from monitoring and from search engines.
500 Errors and SEO
According to Google Search Central, 5xx responses make Googlebot temporarily slow its crawling, and URLs that keep returning server errors are eventually dropped from the index. A short outage is survivable; days of 500s on important pages are not. During planned maintenance, return 503 rather than 500, which tells crawlers the problem is temporary.
Preventing 500 Errors
- Uptime monitoring — configure Enterno.io Monitors with expected_code=200 on key URLs.
- APM / error tracking — Sentry, New Relic, DataDog catch exceptions before users see 500.
- Staging environment identical to production.
- Log rotation via logrotate — a full disk causes 500.
- Regular database backups and dependency-lock files in git.
- Config checks in deploys — run
nginx -torapachectl configtestbefore reloading, so a typo never reaches production.
FAQ
How do I fix an internal server 500 error?
Read the web-server and application error logs at the time of the failed request, undo the most recent change (deploy, plugin, config edit), validate the config with nginx -t or apachectl configtest, and check disk, memory and the database.
Is a 500 error my fault?
As a visitor, almost never. It is a server-side failure. Retry later; if only one page fails for you, try signing out or deleting that site's cookies.
Does a 500 error mean the site is down?
Not always. Often one page or feature fails while the rest of the site works. If every URL returns 500, the whole application is failing.
Will error 500 fix itself?
Transient ones (a deploy in progress, a restarting database) clear up within minutes. Errors caused by code or configuration persist until the server is fixed.
How does 500 differ from 502 and 503?
500 is an application exception. 502 means a proxy could not get a valid response from the upstream. 503 means the service is temporarily unavailable (overload, maintenance).
How do I catch 500s before users complain?
Monitoring. Enterno.io checks your URLs on a schedule and sends Telegram or email alerts when a check fails, and an error tracker reports exceptions with their stack traces.
Conclusion
500 is a server-side problem fixed only on the server. Always start with logs, never expose error details to users, and set up monitoring and APM. For quick external diagnosis, use the HTTP Header Checker by Enterno.io.