A 500 error means the server accepted your request and then crashed while building the page — it is not a WordPress message, not a browser problem, and not something a cache purge will fix. The reason is written in exactly one place: the server’s error log, not the browser console. So the fastest path is always the same: note the time of the failure, open the PHP error log for that minute, and read the first fatal line.
I have chased this error on my own sites and on client sites more times than I want to admit, and the pattern never changes: people start deactivating plugins one by one. The log already says which part failed. Here is the order I use now.
The short version
- 500 = the server failed while generating the page. PHP crashed, or the web server died before PHP even ran. The browser is shown a generic page on purpose.
- The log you want is the PHP error log, or the nginx/Apache
error_logif PHP never got a chance to run. - Read the first fatal error in the time window, not the last line. One crash usually drags a dozen warnings and timeouts behind it.
- 502, 503 and 504 are different animals. Check the status code before you touch anything.
- No log entry at all usually means
.htaccess, a firewall rule, wrong permissions, or a PHP handler mismatch after a version change on the host.
First, confirm which error you have
The number matters: each one points at a different layer of the stack. I have wasted an hour reading PHP logs for what was actually an nginx timeout.
| Status | What it means | Where to look first |
|---|---|---|
| 500 Internal Server Error | PHP threw a fatal error, or the web server hit a configuration problem it cannot recover from. | PHP error log, then the web server error_log. |
| 502 Bad Gateway | The web server asked PHP-FPM for the page and got garbage or nothing back. | nginx/Apache error_log, PHP-FPM log (pool crashed, socket missing). |
| 503 Service Unavailable | The server is deliberately refusing work: maintenance mode, overload, or the PHP pool is down. | Host status page, server load, PHP-FPM pool status. |
| 504 Gateway Timeout | The request took longer than the proxy was willing to wait. | PHP max_execution_time, proxy timeouts, a slow external API call. |
If the page shows 502 or 504, skip straight to the server logs — WordPress never finished the request, so it has nothing to say.
Where the log actually lives
It depends on who runs PHP. Three places cover almost everything I see.
1. Shared hosting (cPanel-style). The account usually has a logs directory next to public_html with a per-domain error log inside — on LiteSpeed servers it is named after the domain, like example.com.error.log. The cPanel Errors page lists recent PHP errors too, and it is the fastest way in when you only have the file manager.
2. A VPS or dedicated server you manage. The web server and PHP-FPM keep their own logs:
- nginx:
/var/log/nginx/error.log - Apache on Debian/Ubuntu:
/var/log/apache2/error.log - Apache on RHEL/AlmaLinux:
/var/log/httpd/error_log - PHP-FPM:
/var/log/php-fpm/www-error.log, or/var/log/php8.x-fpm.logdepending on the package
3. Inside WordPress. If you have file access, you can make WordPress keep its own log. This only catches errors that happen after WordPress has loaded, so it is a complement to the server log, not a replacement.
// wp-config.php — log PHP errors to a file instead of printing them
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true ); // writes wp-content/debug.log
define( 'WP_DEBUG_DISPLAY', false ); // keeps errors out of the HTML
@ini_set( 'display_errors', 0 );
Then follow the file while you reload the broken page:
# shared hosting (adjust the account name and domain)
tail -f /home/USERNAME/logs/example.com.error.log
# VPS with nginx + PHP-FPM
sudo tail -n 200 /var/log/nginx/error.log
sudo tail -n 200 /var/log/php-fpm/www-error.log
The log line that answers the question
A typical PHP fatal looks like this, and every part of it is useful:
[21-Oct-2026 13:43:07 UTC] PHP Fatal error: Uncaught Error: Call to undefined function
wc_get_orders() in /home/user/public_html/wp-content/plugins/acme-tools/acme.php:42
Stack trace: #0 {main} thrown in /home/user/public_html/wp-content/plugins/acme-tools/acme.php on line 42
Four things to pull out of it:
- The timestamp. Compare it to the time the page broke. Logs are often in the server’s timezone or UTC, so account for the offset before deciding the entry is unrelated.
- The severity. Fatal error and Uncaught Error stop the request. Warning and Notice usually do not, and chasing those first wastes an afternoon.
- The path and line number. That path is usually the answer: it names the theme or plugin file that was running when the process died.
- The message. It names the mechanism: undefined function, memory exhausted, execution time exceeded, permission denied.
A few messages come up again and again, and each one has a standard first move:
- “Allowed memory size of N bytes exhausted” — something is loading more than the limit allows. Raise
memory_limita step to confirm, then find the code or import doing it. - “Maximum execution time exceeded” — a loop, a huge import, or a slow external request. If the same URL hangs in a browser tab, it is a page load, not a background job.
- “Call to undefined function” — a plugin calling code that is not loaded: a plugin deleted while another depends on it, or a PHP extension missing on the new server.
- “Cannot redeclare” — the same function defined twice, usually two plugins from the same codebase.
When you see dozens of warnings in the same minute, ignore them until you find the fatal. The last line in a crash window is almost never the cause.
My five-minute triage order
- Write down the URL and the time — one line in a notes app. “It broke earlier today” is not a time.
- Open the account error log from the panel’s file manager or the cPanel Errors page.
- Find the fatal in that window and read the file path. If it points into a plugin, you know the culprit.
- If the log is empty, test the three things that produce a 500 without a PHP entry: rename
.htaccessto.htaccess.bakand reload (a bad rule leaves no PHP trace); check whether the host changed the PHP version overnight; check the panel’s firewall log for a blocked request. - If it is a plugin, rename the plugins folder through the file manager so WordPress disables everything, then put plugins back in halves. Faster than the admin screen when the admin screen also returns 500.
- Check disk usage. A full disk or exhausted inode limit makes writes fail and pages return 500 with a log entry that looks unrelated.
When it is not your code
Some 500s start and end without you changing anything, and that is a strong signal. If several sites on the same server break in the same minute, the PHP-FPM pool or the web server went down and plugin bisecting will not help. If only one action fails — a form submission, a checkout — check the firewall log first, because a security rule update can start blocking a POST body it used to allow.
Two checks settle it fast: does a plain HTML file in the same account load, and did the failure start without a deploy? A static file that loads while every PHP page fails points at the PHP layer, not the site. And if the log is empty while the site serves pages or links you did not write, treat it as a compromise rather than an error — my five-minute infection check covers the first things to look at, and the recovery walkthrough covers what to do when you find injected files.
What I do after the fix
The fix is only half the job. The other half is making the next 500 take one minute instead of one evening:
- Keep the log path in the notes for every site — host, panel, log location, PHP version.
- Leave
WP_DEBUG_LOGon for staging, andWP_DEBUG_DISPLAYoff everywhere. - Watch the size of
debug.log. An unrotated debug log can fill a disk, and a full disk causes the exact 500 you were debugging. - Update plugins one at a time on staging, not twelve at once at midnight.
If you are stuck in the middle of one right now and want a second pair of eyes, my contact page is the place to reach me. Otherwise: open your site’s error log today, before you need it. Ten minutes now saves the panic later, because you will already know where the answer lives.




