500 Internal Server Error in WordPress

A 500 Internal Server Error means the WordPress application errored before it could produce a valid response. This article explains the exact mechanism, the five real causes, how to tell them apart in the logs, and how to fix each one.

Your WordPress site shows a blank page with the text "500 Internal Server Error", or Chrome reports "This page isn't working: HTTP ERROR 500". The response inspector shows status code 500. The error appears for visitors and for you, in every browser, on mobile and on desktop. Sometimes a refresh fixes it for a few seconds, sometimes the site is fully down until you intervene.

What a 500 actually means

RFC 9110 §15.6.1 defines 500 as: "the server encountered an unexpected condition that prevented it from fulfilling the request." That sentence is doing a lot of work. The server is alive, the request reached the application, the application started processing it, and then the application errored in a way it could not turn into a normal response. The web server then returned a 500 to the visitor instead of HTML.

The word that matters in that definition is unexpected. A 500 is the server admitting that something inside the application went wrong and it does not know how to recover. It is not a network problem and it is not a browser problem. The fix is always somewhere on the server.

A 500 is not the same as the other 5xx errors in this category, and the difference matters for your diagnosis:

  • 500 Internal Server Error: the application itself errored cleanly and returned a 500 to the proxy. This article.
  • 502 Bad Gateway: the upstream returned an invalid or empty response, usually because the worker crashed mid-request.
  • 503 Service Unavailable: the upstream signaled that it cannot accept the request right now (overload, maintenance mode, rate limit).
  • 504 Gateway Timeout: the upstream was reachable but took too long to answer. PHP is still alive, just slow.

In short: a 502 is a corpse, a 504 is silence, a 503 is a "go away", and a 500 is an admission of guilt.

One important wrinkle: since WordPress 5.2, most PHP fatals inside WordPress no longer surface as a bare "500 Internal Server Error" page. WordPress 5.2 shipped fatal error recovery mode, which catches a fatal error, sends the admin a recovery email, and shows the visitor the "There has been a critical error on this website" screen instead of a blank 500. If you are seeing the bare 500 page in 2026, the failure is usually one of the things WordPress could not catch from PHP land: a parse error, a .htaccess problem, a missing file, or a memory exhaustion that killed the process before WordPress's shutdown handler ran.

Common causes, ordered by likelihood

1. A PHP fatal error before WordPress could catch it

This is the cause behind most bare 500s on a WordPress site. PHP loaded the file, started running it, and hit something it could not complete: a memory exhaustion, a maximum execution time, a parse error, a missing function. Each of these has its own canonical article and its own fix. The 500 is just the surface symptom:

The 500 page tells you nothing on its own. The error log tells you which of those it is.

2. A .htaccess syntax error (Apache only)

If you run on Apache and your site root contains a .htaccess file with a bad directive, Apache returns a 500 to the client and writes the specific syntax error to the Apache error log. This is the second-most-common cause I see in the wild because .htaccess is the file plugins, security tools, and migration scripts edit most aggressively. A typical trigger is a security plugin that just installed a new ruleset, a caching plugin that wrote a malformed rewrite block, or a manual edit that pasted Windows line endings into the file. nginx does not read .htaccess at all, so this cause is Apache-only.

3. A buggy plugin or theme runs code that crashes outside WordPress's catch

Most plugin and theme bugs in 2026 produce the critical error screen, not a bare 500. But code that runs before WordPress boots (a custom mu-plugin, a wp-config.php snippet, a drop-in like object-cache.php) runs outside the recovery handler. If one of those crashes, the visitor sees a bare 500. So does any code that triggers an error PHP cannot catch from user space, like a stack overflow, a native extension segfault, or a fatal in PHP's startup phase.

4. Wrong file permissions on a key file

If PHP cannot read wp-config.php, index.php, or any file in wp-includes, the request errors before WordPress can render anything. The Apache or nginx error log will say Permission denied and name the file. WordPress's own permission guidance is 644 for files, 755 for directories, and 440 or 400 for wp-config.php. A migration tool that copied files with the wrong owner, or an chmod run that set everything to 000, will produce 500s on every request until it is fixed.

5. A server-level configuration error

Less common but worth ruling out: the web server itself is misconfigured and responds with 500 before the request ever reaches PHP. A broken php_value line in an Apache vhost, a corrupted opcache file, a PHP-FPM pool that boots but cannot read its socket, or a mod_security rule that fires on every request can each return 500. On managed hosting this is rarely your problem to fix, but you do need to know how to recognize it so you do not waste an hour deactivating plugins.

Diagnose which cause applies

Run these checks before changing any setting. They are non-destructive and tell you exactly which of the five causes is yours.

Check 1: read the server error log. This is the single most important step, and the one most readers skip. The server error log contains the actual PHP error message that produced the 500. Open your hosting control panel (cPanel, Plesk, DirectAdmin, or whatever your host provides) and look for "Error logs" or "Error log viewer". For a 500 you want the entry whose timestamp matches the visitor report. If you have SSH access: on Apache the log lives at /var/log/apache2/error.log or /var/log/httpd/error_log per the Apache documentation. On nginx it lives at /var/log/nginx/error.log per the nginx documentation. Either way, the entry you are looking for will look something like:

PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
(tried to allocate 8192 bytes) in /home/yoursite/public_html/wp-content
/plugins/example-plugin/render.php on line 1024

Or for an .htaccess problem:

.htaccess: Invalid command 'RewriteCondX', perhaps misspelled or
defined by a module not included in the server configuration

That single line tells you which of the five causes you have. You will know it worked when: you can quote the exact error message and you can match its timestamp to the visitor report.

Check 2: enable WordPress debug logging if the server log is empty. If the server error log shows nothing (some shared hosts hide it from you), turn on WordPress's own error log. The WordPress debugging documentation says to add these lines to wp-config.php, just above the /* That's all, stop editing! */ comment:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

The next time the 500 happens, the error is written to wp-content/debug.log. The WP_DEBUG_DISPLAY line is the important one: it keeps the error out of the visitor's browser while still capturing it in the file. Turn WP_DEBUG back to false once you have the error message.

You will know it worked when: wp-content/debug.log contains a Fatal error or Parse error line that matches the timestamp of the 500.

Check 3: rename .htaccess if you are on Apache. If you can reach the site root over SFTP or a file manager, rename the existing .htaccess to .htaccess.bak and reload the failing URL. If the site comes back, the file was the cause. If not, rename it back to .htaccess (the broken file is usually not the only problem and a missing .htaccess will break permalinks and security plugins). On nginx, skip this check entirely: nginx ignores .htaccess.

You will know it worked when: the URL that returned 500 now returns a 200 with the renamed file in place, or the 500 still appears with .htaccess removed and you can rule the file out.

Check 4: file existence and permissions on the key files. Open your hosting panel's file manager and browse to the site root. Confirm that wp-config.php, index.php, and wp-load.php exist. Most file managers show the permission column next to each file: wp-config.php should be 440 or 644, and directories should be 755. If the file manager lets you change permissions, fix any file set to 000 right there. If you have SSH access: run ls -l wp-config.php in the site root and look for at least r-- on the owner. Fix it with chmod 440 wp-config.php if needed.

You will know it worked when: every key file is present and readable, and either the 500 is gone or you have ruled permissions out.

Solutions, per cause

Cause #1 fix: a PHP fatal error

The fix depends on which fatal it was. The 500 is just the wrapper:

  • Allowed memory size exhausted: follow the dedicated article on raising the WordPress memory limit. It covers WP_MEMORY_LIMIT, the underlying PHP memory_limit, and how to find the request that is asking for too much. I do not duplicate the configuration here on purpose: the canonical fix lives there and stays current there.
  • Maximum execution time exceeded: see the article on the PHP max execution time for raising it and for moving the long task out of the request lifecycle.
  • Parse error: see the parse error article. The error message names the exact file and line. The fix is almost always reverting that file to its previous version over SFTP.
  • Anything else: read the error message, search for the exact text, and update or remove the offending plugin.

Verification: the URL that returned 500 now returns a 200, and the server error log shows no new fatal lines for the same file.

Cause #2 fix: a broken .htaccess (Apache only)

If Check 3 confirmed the file was the cause, regenerate a clean default. Log into wp-admin (it works because WordPress permalinks fall back to the default rewrite rules when .htaccess is missing), go to Settings → Permalinks, and click Save changes without changing anything. WordPress writes a clean .htaccess with the default WordPress rewrite block. If a security or caching plugin needs its own rules, re-enable that plugin afterward and let it write its block to the new file.

If you cannot reach wp-admin, create a fresh .htaccess with the default WordPress contents from the WordPress permalinks documentation and upload it to the site root.

Verification: the failing URL now returns a 200, and the Apache error log no longer contains .htaccess: Invalid command lines.

Cause #3 fix: a buggy plugin, theme, or drop-in

If the error log named a file inside wp-content/plugins/example-plugin/, deactivate that plugin. With wp-admin working, do it from Plugins → Installed Plugins. Without wp-admin access, rename the plugin's folder over SFTP from example-plugin to example-plugin.off. WordPress treats the plugin as gone and the 500 stops. Then update the plugin to a fixed version, replace it with an alternative, or report the bug to the developer.

If the error log named a file inside wp-content/mu-plugins/, wp-content/themes/yourtheme/, or a drop-in like wp-content/object-cache.php, do the same with that file or directory. A renamed mu-plugin is the fastest way to rule out a custom plugin you forgot existed.

Verification: the URL that returned 500 now returns a 200 with the offending file out of the way, and the error log no longer references that file.

Cause #4 fix: file permissions

Open your hosting panel's file manager, select all files in the WordPress root, and set permissions to 644. Then select all directories and set them to 755. Finally, set wp-config.php to 440. These are the safe defaults per the WordPress permissions guidance. On some shared hosts the values that work are 640 for files and 750 for directories instead. If 644 does not resolve the error, try 640 next.

If you have SSH access:

find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod 440 wp-config.php

Verification: the failing URL returns a 200, and the server error log no longer contains Permission denied for any WordPress file.

Cause #5 fix: server-level configuration

This is the cause where you are not in charge. If the server error log points at an Apache vhost, an nginx config, an opcache file, or a mod_security rule, the fix is to tell your host exactly what the log says. They are the only ones who can edit those files. Send them the escalation checklist below in your first message so the ticket goes to the right engineer immediately.

Verification: support confirms the change, the failing URL returns a 200, and the relevant error log line stops appearing.

When to escalate

If the steps above do not pinpoint the cause within 30 minutes, hand the incident off to your host or developer. Have these ready, because the first thing they will ask for is exactly this list:

  • The exact URL that triggers the 500.
  • The time the error happens, with timezone, and whether it is reproducible or only sporadic.
  • Your hosting tier and stack (shared, VPS, managed, container; nginx or Apache; PHP version).
  • The matching line from the server error log (Apache or nginx).
  • The matching line from wp-content/debug.log if you enabled WP_DEBUG_LOG.
  • A list of what changed in the last 48 hours (plugin updates, theme updates, WordPress core updates, code deploys, server changes, DNS changes).
  • The list of currently active plugins, especially anything that touches .htaccess, caching, security, or object caching.
  • Whether the site is behind Cloudflare or another edge proxy, and whether the 500 also happens when you bypass it.

Send those in the first message. It saves a full round trip and routes the ticket straight to the right engineer.

How to prevent it from coming back

A persistent 500 is almost always a recent change that was not tested. Three habits keep the error rare on a healthy site:

  • Stage updates before applying them to production. A staging environment that mirrors your production stack catches .htaccess regressions, plugin incompatibilities, and PHP fatal errors before your visitors do. If your host does not offer a one-click staging environment, even a local Docker copy of the site catches most of them.
  • Keep WP_DEBUG_LOG ready to flip on. Leave the constants in wp-config.php with WP_DEBUG set to false. The day you need them, change one line and you immediately have a real error message instead of a blank 500. The WP_DEBUG_DISPLAY and display_errors lines from Check 2 keep the visitor side clean.
  • Pin recoverable configuration. A .htaccess regenerated from Settings → Permalinks is recoverable. A .htaccess hand-edited and not committed to version control is not. Either commit your custom server configuration to a repo, or let a plugin own it so a plugin reinstall puts it back.

If a single request to your site can answer in under one second cold, with no extra moving parts and no recent configuration changes, then a 500 should be impossible during normal operation. Anything else is the system telling you that something errored on the server, and the 500 is just where the symptom surfaces.

Want this to stop being your problem?

If outages or errors keep repeating, the fix is often consistency: updates, backups and monitoring that don't get skipped.

See WordPress maintenance

Search this site

Start typing to search, or browse the knowledge base and blog.