Bali, Indonesia
Insights

WordPress 500 Error Isn't Always .htaccess. Read the PHP Error Log First

November 5, 2026
0:00
When WordPress returns a 500 Internal Server Error, the usual first move is to rename .htaccess, regenerate the permalinks, and maybe reinstall a plugin or two. Sometimes the site comes back, and that teaches people the fix works. It mostly doesn't. A 500 status only says something on the server side failed. It doesn't say what failed, and on a WordPress site the cause is usually a PHP fatal error that has already been written to a log, in plain words, with a file path and a line number. The .htaccess habit is understandable. On older shared hosting, broken rewrite rules were a common cause of 500s, and renaming the file is fast. On a production server it becomes guesswork. Each change you make while guessing is another variable, and some of them, like reinstalling a plugin, can wipe settings or leave the site in a worse state than you found it.
A request to a WordPress page passes through at least two layers. The web server (Apache, Nginx, or LiteSpeed) receives it, applies any rewrite rules, and hands it to PHP. PHP then loads WordPress core, every active plugin, and the active theme. A failure at either layer can end in a 500. If the web server can't parse its configuration, including directives in .htaccess, it gives up before PHP ever runs. If PHP hits a fatal error, such as a call to a function that doesn't exist, a syntax error, or running out of memory, the script stops, and with display_errors turned off (which it should be in production) PHP returns a 500 with an empty or generic body. Current WordPress versions catch many fatals and show a page saying there has been a critical error on the site, which still carries a 500 status. From the browser, all of these look identical. From the logs, they look completely different. That's the whole argument for reading the log first: it tells you which layer failed before you touch anything. This matters more under real traffic. Some fatals only happen on certain code paths: a checkout page, a cron run, a REST API call from a mobile app, or a request that pushes memory over the limit. Regenerating .htaccess won't change any of that, and if the error is intermittent, the site may look fine right after your change and fail again an hour later. You end up believing the wrong fix worked.
Log locations depend on the distro, the web server, the PHP handler, and the control panel, so the first job is to find out which combination you're on. The table below covers the common defaults. Treat these as starting points to confirm, not guarantees.
StackWeb server errorsPHP errors
Apache on Debian/Ubuntu/var/log/apache2/error.logSame file with mod_php; with PHP-FPM, check the pool's error_log setting
Apache on RHEL-family/var/log/httpd/error_logSame idea as above
Nginx + PHP-FPM/var/log/nginx/error.logPool error_log if set, otherwise often relayed into the Nginx error log
cPanel (Apache or LiteSpeed)/usr/local/apache/logs/error_logOften a file named error_log next to the script that failed, e.g. in public_html
LiteSpeed (standalone)/usr/local/lsws/logs/error.logDepends on lsphp settings; check php.ini error_log and the site directory
WordPress itselfn/awp-content/debug.log when WP_DEBUG and WP_DEBUG_LOG are enabled
The Nginx case confuses people the most. If the PHP-FPM pool has no error_log of its own, PHP's error output travels back over FastCGI and Nginx records it in its own error log, prefixed with FastCGI sent in stderr. So the PHP fatal is sitting in the Nginx log, which is why people who only check the FPM service log conclude there is nothing there. On a VPS, a few read-only commands get you to the right line quickly. Reproduce the error in a browser while the tail is running so you can see the line appear in real time.
BASH
# Debian/Ubuntu: see whether any PHP-FPM pool sets its own error log
grep -Rn 'error_log' /etc/php/*/fpm/

# Watch the web server error log while you reload the failing page
sudo tail -f /var/log/nginx/error.log

# Pull the most recent PHP fatal errors from that log
sudo grep -n 'PHP Fatal error' /var/log/nginx/error.log | tail -n 20
Swap the log path for the one that matches your stack. On shared hosting without root, look for an error_log file in the site's document root or in the directory of whatever script was requested, such as wp-admin when the dashboard is the part that fails. If you can't find any PHP log at all, that's a finding too. It usually means log_errors is off or error_log points somewhere unwritable, and fixing that is worth doing before the next incident.
A PHP fatal error line looks noisy, but only a few parts matter. Here is the general shape, with made-up plugin names and paths.
TEXT
Illustrative output format:

2026/01/01 10:00:00 [error] 1234#1234: *5678 FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught Error: Call to undefined function example_get_settings() in /var/www/site/wp-content/plugins/example-forms/includes/render.php:42
Stack trace:
#0 /var/www/site/wp-content/themes/example-theme/functions.php(118): example_forms_render()
#1 /var/www/site/wp-includes/class-wp-hook.php(324): example_theme_footer()
...
thrown in /var/www/site/wp-content/plugins/example-forms/includes/render.php on line 42" while reading response header from upstream, client: 203.0.113.10, request: "GET / HTTP/1.1"

PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted (tried to allocate 20480 bytes) in /var/www/site/wp-includes/class-wpdb.php on line 2200
Read it in this order. First, the error type: Uncaught Error, Parse error, and Allowed memory size each point in a different direction. Second, the file path. Anything under wp-content/plugins/ names the plugin folder, and wp-content/themes/ names the theme. Third, the stack trace, which shows who called what. In the first example, the plugin's function crashed, but frame #0 shows the theme's functions.php called it. That pattern often means the theme depends on a plugin that was deactivated or updated, so the fix may belong to the theme, not the plugin. The second line is a trap. The file named is wp-includes, which is WordPress core, but core is rarely the actual problem. A memory exhaustion error is reported wherever PHP happened to be when it crossed the limit, not where the memory was consumed. Raising memory_limit can hide it for a while. The article on why raising memory_limit doesn't always fix Allowed memory size exhausted covers how to find what is actually growing. Two other messages show up often. Parse error: syntax error, unexpected ... almost always follows a manual edit, typically to functions.php or wp-config.php, and the line number points at or just after the typo. Call to undefined function or Class not found right after a PHP version change usually means a plugin or theme relies on a function or extension that the new version removed or that isn't installed. If the log is busy, match timestamps. Note when you reproduced the error, find the matching request in the access log, and look for the error line at the same second. In the Nginx format above, the client and request fields help confirm you're looking at your own request and not someone else's.
.htaccess does cause 500s, and the web server log says so directly. Apache errors from .htaccess mention the file and the directive, not PHP. Common ones include Invalid command 'php_value', perhaps misspelled or defined by a module not included in the server configuration, which appears when a plugin or an old guide added php_value lines and the site now runs under PHP-FPM, where those directives aren't supported. Another is Request exceeded the limit of 10 internal redirects, which points to a rewrite loop, often after a URL or HTTPS change. If you see lines like these, editing .htaccess is the right move, and now you know which line to change instead of replacing the whole file. If the log shows a PHP fatal instead, .htaccess is not involved, and regenerating it only costs time. One support pattern worth knowing: customers often report that renaming .htaccess fixed the error, when the actual effect was that the request no longer reached WordPress at all. The home page may load as a directory listing or a 404, which looks different from a 500 but isn't a working site.
Once the log names a plugin or theme, disable it in a way you can undo. Reinstalling deletes and replaces files, may run activation hooks again, and doesn't fix a conflict that comes from somewhere else. Renaming the plugin's folder or using WP-CLI is reversible and leaves settings in the database alone.
BASH
# Run from the WordPress root. --skip-plugins and --skip-themes keep WP-CLI
# from loading the broken code while you work.
wp plugin list --status=active --skip-plugins --skip-themes
wp plugin deactivate example-forms --skip-plugins --skip-themes

# Without WP-CLI: rename the folder so WordPress can't load it (reversible)
mv wp-content/plugins/example-forms wp-content/plugins/example-forms.disabled
Reload the page and watch the log again. If a new fatal appears from a different file, you've uncovered a second problem, or a dependency such as the theme calling the plugin you just disabled. Work through them one at a time, using the log each time, rather than disabling everything at once and losing track of which change mattered. If the culprit is the active theme, switching to a default theme with WP-CLI (wp theme activate followed by the theme slug) works the same way. Check that the default theme is actually installed first. For production, the lasting fix is boring configuration. Keep display_errors off so visitors never see paths, keep log_errors on, and point each PHP-FPM pool or site at an error log you know the location of and that gets rotated. Enable WP_DEBUG_LOG only while investigating, since debug.log inside wp-content can grow large and may be reachable over HTTP unless it's blocked. The practical rule is short: when WordPress returns a 500, open the error log before changing anything. Find the line from the moment you reproduced the error, read the error type and the file path, and let that decide whether you're editing .htaccess, disabling a plugin, fixing a theme, or looking at PHP settings. It's usually faster than guessing, and it leaves you with a clear answer when someone asks what broke.