500 Internal Server Error on Nginx: How to Diagnose and Fix It Fast
A visitor hits your site and gets a blank, unhelpful page: “500 Internal Server Error.” No details, no clue what broke. If you see this on an Nginx-served site, here is the first thing to understand: the error is vague *on purpose* for the visitor, but it is never vague in your logs. Something on the server side failed, and the exact reason is already written down somewhere — you just have to go read it.
This guide walks through what a 500 actually means, why it almost always comes from your application code rather than from Nginx itself, and how to diagnose it the way a troubleshooter does: by reading the logs first and guessing never.
Key Takeaways
• A 500 Internal Server Error is a *generic* server-side failure — the server hit a problem it couldn’t handle and refused to say what, on purpose, for security.
• It is different from a 502 Bad Gateway, which specifically means Nginx got a bad response from an upstream/backend process. A 500 is usually thrown by the backend application itself.
• The real cause is in the logs: `/var/log/nginx/error.log` plus your application/PHP-FPM log. Read those *before* changing anything.
• Most common causes: a PHP/application fatal error, a bad config, wrong file permissions, exhausted memory, broken rewrite rules, or a failed database connection.
• The fix is almost never “restart and hope” — it is “read the exact error line, then fix the one thing it names.”
What does a 500 Internal Server Error actually mean?
A 500 Internal Server Error is the HTTP status code a server returns when it encounters an unexpected condition that stops it from fulfilling the request — and it has no more specific code to describe it. It is the server’s way of saying *”something went wrong on my side, and I’m not going to tell the visitor what.”*
That vagueness is deliberate. Exposing the underlying error to a public visitor — a stack trace, a file path, a database string — would leak information an attacker could use. So the browser gets a clean, generic 500, and the real error is logged privately on the server.
The key mental model: a 500 is generic and almost always application-side. Nginx is acting as a web server (and often a reverse proxy to something like PHP-FPM). When your application code throws a fatal error, that error bubbles up and Nginx reports it to the browser as a 500. Nginx is the messenger, not usually the culprit.
How is a 500 different from a 502 Bad Gateway?
This distinction matters because it tells you *where* to look:
- A 500 Internal Server Error is generic. The server (often your application via PHP-FPM) ran, hit a fatal problem, and produced an error response. The fault is usually inside the application or its environment.
- A 502 Bad Gateway is specific. Nginx, acting as a reverse proxy, tried to reach an upstream process (PHP-FPM, a Node app, a backend service) and got no valid response at all — the upstream was down, crashed, timed out, or wasn’t listening.
In short: a 502 means the gateway/upstream failed to respond; a 500 means the upstream responded, but with an error. If you are actually fighting a gateway or upstream issue, read the companion guide instead: .
Where does the real cause of a 500 hide?
In the logs. Always start here. The visitor sees a blank page; the server wrote down exactly what failed.
There are usually two logs to check, and the second one is the one people forget:
1. The Nginx error log:
“`bash sudo tail -n 50 /var/log/nginx/error.log “`
A 500 caused by a PHP fatal might appear here as something like:
“` 2026/06/27 14:22:09 [error] 1843#1843: *5012 FastCGI sent in stderr: “PHP message: PHP Fatal error: Uncaught Error: Call to undefined function get_field() in /var/www/example/wp-content/themes/site/functions.php:212” while reading response header from upstream, client: 203.0.113.7, server: example.com “`
That line names the exact file and line number. That is your fix, sitting right there.
2. The application / PHP-FPM log:
Nginx hands PHP requests to PHP-FPM, and PHP-FPM keeps its own log. So does your framework (Laravel, WordPress debug log, Django, etc.). Check them:
“`bash
sudo tail -n 50 /var/log/php8.2-fpm.log
sudo tail -n 50 /var/log/php/error.log tail -n 50 /var/www/example/storage/logs/laravel.log tail -n 50 /var/www/example/wp-content/debug.log “`
Here is the single most important thing to internalize about a 500: it is deliberately vague to the visitor, but it is never vague in the logs. The blank page the browser shows is a security feature, not a dead end. The actual error — a PHP fatal, a permission denial, a syntax error on a specific config line — is sitting in `/var/log/nginx/error.log` and/or your application log with the precise file, line, and reason. So the entire job of fixing a 500 is this: stop guessing, open the two logs, and read the real error. Nine times out of ten, the log line *is* the answer; you are not debugging a mystery, you are reading a confession.
What are the common causes of a 500 on Nginx, and how do you fix each?
Once you have the log line, match it to one of these patterns.
Application or PHP code error
The most common cause. A PHP fatal error, an uncaught exception, a typo, or a missing function brings the request down. The Nginx log will show `PHP Fatal error` or `PHP message`, and the app log will show the full stack trace.
Fix: Read the file and line named in the log. Temporarily enable your application’s debug mode (for example, `WP_DEBUG` in WordPress or `APP_DEBUG=true` in Laravel) on a non-production or carefully controlled environment to surface the full error, fix the code, then disable debug again. Never leave verbose debug output exposed to the public.
Configuration mistakes
A wrong file path, a missing environment variable, a bad value in a config file, or a malformed `nginx.conf` directive. If the issue is in Nginx config itself, the service often won’t even reload cleanly.
Fix: Validate the Nginx config before anything else:
“`bash sudo nginx -t “`
If it reports a syntax error, it names the file and line. For application config, check that required environment variables are set and that paths in the config actually exist on disk.
File permissions and ownership
The application can’t read or write a file it needs — a cache directory, an upload folder, a config file. The log will say something like `Permission denied`.
Fix: Confirm the files are owned by the user Nginx/PHP-FPM runs as (often `www-data`) and have sane permissions:
“`bash sudo chown -R www-data:www-data /var/www/example sudo find /var/www/example -type d -exec chmod 755 {} \; sudo find /var/www/example -type f -exec chmod 644 {} \; “`
Avoid the lazy `chmod 777` — it masks the problem and opens a security hole.
Exhausted resources (memory limit hit)
A script that tries to use more memory than allowed dies mid-request. The log shows `Allowed memory size of N bytes exhausted`.
Fix: Raise `memory_limit` in `php.ini` (or the relevant runtime config) to a reasonable value, restart PHP-FPM, and — just as important — investigate *why* the script needed that much. A runaway query or an unbounded loop is the real bug.
Broken rewrite rules (the .htaccess equivalent)
Apache uses `.htaccess`; Nginx uses `location` blocks and `rewrite` directives in its config. A malformed rewrite or a rewrite loop can produce a 500.
Fix: Review your `rewrite` and `try_files` directives, run `sudo nginx -t`, and check the error log for `rewrite or internal redirection cycle` messages.
Broken database connection
The app boots, tries to connect to the database, and fails — wrong credentials, the database server is down, or a socket path changed. This throws a fatal exception that surfaces as a 500.
Fix: Verify the database is running and reachable, confirm credentials and host/socket in your app config, and test the connection directly from the command line.
Causes and fixes at a glance
| Symptom in the log | Likely cause | First fix to try |
|---|---|---|
| `PHP Fatal error` / stack trace | Application/PHP code error | Read named file & line; enable debug temporarily; fix code |
| `nginx -t` fails with syntax error | Bad Nginx config | Fix the named line, reload Nginx |
| `Permission denied` | Wrong file permissions/ownership | Set correct owner (`www-data`) and 755/644 perms |
| `Allowed memory size … exhausted` | Memory limit hit | Raise `memory_limit`; find the runaway script |
| `rewrite or internal redirection cycle` | Bad rewrite/`try_files` rules | Review `location`/`rewrite` blocks |
| `SQLSTATE` / `could not connect` | Broken database connection | Verify DB is up, check credentials/host |
| Empty/blank with no app log | App fatal before logging starts | Check Nginx error log + PHP-FPM log directly |
How do you diagnose a 500 step by step?
A repeatable troubleshooting loop beats random changes every time:
- Reproduce it. Hit the exact URL that 500s and note the timestamp.
- Open the Nginx error log and read the entries matching that timestamp: `sudo tail -n 50 /var/log/nginx/error.log`.
- Open the PHP-FPM / application log for the same window. The app log usually has the richer stack trace.
- Match the error to a cause from the table above — code, config, permissions, memory, rewrite, or database.
- Fix the one thing the log names. Don’t shotgun five changes at once.
- Reload and re-test: `sudo systemctl reload nginx` (and restart PHP-FPM if you changed PHP config), then reload the URL.
- If still failing, enable application debug temporarily on a controlled environment to surface deeper detail, fix, then turn it off.
The discipline here is the whole point: a 500 feels like a mystery only until you read the log. After that, it’s a to-do item with a file path attached.
Hosting that makes 500 errors easy to catch — and rare
A 500 Internal Server Error is fundamentally a *diagnostic* problem: you can only fix what you can read. That’s where your hosting environment makes the difference.
DarazHost is built so you’re never debugging blind. Shared and managed plans give you straightforward access to your error logs — Nginx and PHP logs — through cPanel and file access, so the moment a 500 appears, the real error is one click away rather than buried behind a support ticket. Our servers ship with correctly configured permissions and sensible resource limits, which is exactly why permission-denied and memory-exhausted 500s are rare in the first place.
Need to dig into application-side errors yourself? Our VPS plans give you full root access to read every log, tail PHP-FPM in real time, and tune `memory_limit` or rewrite rules directly. And when you’d rather hand it off, our 24/7 support team can read the logs with you and pinpoint a 500 fast. Reliable infrastructure plus real log access means fewer 500s — and quicker fixes when one does appear.
Frequently asked questions
Is a 500 Internal Server Error a problem with Nginx or with my website? Almost always your website (the application). Nginx is usually just reporting a fatal error that your backend code, PHP-FPM, or a misconfiguration produced. Nginx itself causing a 500 is uncommon and would show up as a config error in `nginx -t`.
Why does the visitor see a blank page but I can’t tell what’s wrong? By design. The generic 500 page hides internal details from the public for security. The actual error — file, line, and reason — is written to your server logs (`/var/log/nginx/error.log` and your PHP/application log), which only you can read.
What’s the difference between a 500 and a 502 error on Nginx? A 500 is a generic server-side error, usually thrown by the application after it runs. A 502 Bad Gateway means Nginx, acting as a proxy, couldn’t get a valid response from the upstream process at all. See our .
Can wrong file permissions cause a 500 error? Yes. If the user Nginx/PHP-FPM runs as (often `www-data`) can’t read or write a needed file or directory, the application throws a `Permission denied` fatal that surfaces as a 500. Fixing ownership and permissions resolves it.
How do I enable more detailed error messages safely? Turn on your application’s debug mode (e.g. `WP_DEBUG`, `APP_DEBUG=true`) temporarily, ideally on a staging environment or with output written to a log rather than the screen. Fix the error, then disable debug before exposing the site publicly again.