How to Fix 500 Internal Server Error

A 500 Internal Server Error is the HTTP status code servers return when something goes wrong on their side and they cannot — or will not — tell you exactly what. Unlike a 404 (page not found) or a 503 (server temporarily unavailable), a 500 offers no direct clue. The browser simply shows "Internal Server Error" or, just as often, a blank white page.

That vagueness is what makes the 500 so frustrating. The root cause can live anywhere in the request path: a permission error on a single file, a broken .htaccess rule, an exhausted PHP memory limit, a fatal parse error in a recently edited script, a database the application can no longer reach, or a misconfigured web server block. The good news is that 500 errors follow a predictable set of causes, and each one has a known fix.

This guide is the generic 500 fix playbook. It covers Nginx, Apache, PHP, and general web applications — not a specific CMS. (If you are on WordPress specifically, see our WordPress 500 Internal Server Error guide.) Work through the seven steps in order; most site owners find the fix within the first three.

What Is a 500 Internal Server Error?

HTTP 500 Internal Server Error is defined in RFC 7231 as a generic status indicating that the server encountered an unexpected condition that prevented it from fulfilling the request. The key word is generic. The 500 exists precisely because the server cannot classify the failure more specifically — so it falls back to this catch-all.

On a typical request, the browser asks the web server (Nginx or Apache) for a resource, the web server hands the request to an application runtime (usually PHP via PHP-FPM), and the runtime executes application code that may talk to a database. If any link in that chain throws an unrecoverable error — a fatal PHP error, a permission denial, a dead database connection, a broken rewrite rule — the server has nothing valid to return, so it answers with 500.

Because production servers suppress error display by default, you usually see only the generic "Internal Server Error" message. That is why the first step of every 500 fix is to open the error logs: they contain the exact line, file, and message the server hid from the browser. Once you can read the real error, the rest is mechanical.

Common Causes of 500 Errors

The table below maps the seven most common root causes of a 500 error to how you identify each one and the fix that resolves it. Use it as a triage before diving into the step-by-step guide.

Cause How to Identify Fix
Incorrect file permissions Log shows Permission denied or 403 masking as 500 Set dirs to 755, files to 644; fix ownership
Broken .htaccess / web.config 500 appears only after a rewrite or redirect change Rename the file to test; restore a known-good version
Exhausted PHP memory limit Log: Allowed memory size of ... exhausted Raise memory_limit in php.ini
PHP fatal / syntax error Log: PHP Parse error or Fatal error Run php -l; fix the offending file
Database connection failure Log: Connection refused or missing credentials Verify host, port, credentials; restart DB
Web server misconfiguration nginx -t or apachectl configtest fails Correct the syntax error and reload
Corrupted or incompatible files 500 starts after a deploy or PHP version change Redeploy; verify PHP extension compatibility

Step-by-Step Fix Guide

Follow these seven steps in order. They narrow the problem from a blank 500 to a specific, fixable cause. The HowTo schema in this page's metadata mirrors these exact steps.

Step 1: Check server error logs

The error log is the single most important tool for a 500. It contains the exact message the server hid behind the generic response. Check both the web server log and the application/PHP log:

# Nginx
sudo tail -n 100 /var/log/nginx/error.log

# Apache
sudo tail -n 100 /var/log/apache2/error.log     # Debian/Ubuntu
sudo tail -n 100 /var/log/httpd/error_log       # CentOS/RHEL

# PHP-FPM (location varies by distribution)
sudo tail -n 100 /var/log/php-fpm/error.log
sudo tail -n 100 /var/log/php8.2-fpm.log

Look for lines containing Fatal error, Parse error, Allowed memory size ... exhausted, Permission denied, or Connection refused. Each points to a different root cause that the remaining steps address. If the log is empty, the request may never have reached the application — which itself points to a web server or permission problem.

Step 2: Check file permissions

Wrong permissions are a leading cause of 500s, especially after a migration or a chmod -R gone wrong. If the web server cannot read a required file or cannot write to a needed directory, the application often fails with a 500 rather than a 403. Confirm the standard layout:

# Directories should be 755
sudo find /var/www/html -type d -exec chmod 755 {} \;

# Files should be 644
sudo find /var/www/html -type f -exec chmod 644 {} \;

# Ownership: web server user (www-data on Debian/Ubuntu, apache on CentOS)
sudo chown -R www-data:www-data /var/www/html

Watch for files that must be writable by the application — configuration caches, upload directories, log files — and ensure the web server user can write to them. Conversely, configuration files containing secrets should not be world-readable. A quick ls -la on the document root will reveal obvious ownership mismatches.

Step 3: Check .htaccess or web.config

A single malformed directive in .htaccess (Apache) or web.config (IIS) is enough to turn every request into a 500. This commonly happens after a redirect rule change, a CMS permalink update, or a copy-paste from an incompatible tutorial. The fastest way to confirm the culprit is to temporarily disable the file:

# Apache: rename .htaccess and retest
mv /var/www/html/.htaccess /var/www/html/.htaccess.bak

# IIS: rename web.config
mv C:\inetpub\wwwroot\web.config C:\inetpub\wwwroot\web.config.bak

If the site loads after the rename, the configuration file is the problem. Restore it and fix the offending directive. For Apache, validate the syntax of an .htaccess from the command line:

apachectl configtest
# or, for a specific virtual host
apachectl -t -D DUMP_VHOSTS

On Nginx, there is no .htaccess equivalent — rewrite rules live in the server block. Run sudo nginx -t to validate the entire configuration; a syntax error here will produce a 500 (or prevent a reload entirely).

Step 4: Increase the PHP memory limit

When a PHP script tries to allocate more memory than memory_limit allows, PHP aborts with a fatal error and the server returns 500. The error log will show Allowed memory size of N bytes exhausted. Raise the limit in php.ini and restart PHP-FPM:

; /etc/php/8.2/fpm/php.ini
memory_limit = 256M
sudo systemctl restart php8.2-fpm

For a per-application override, you can also set it inside the script (handy for a single heavy task), though the php.ini value is the real fix:

ini_set('memory_limit', '256M');

Note that raising the limit only treats the symptom. If a script repeatedly exhausts memory, it likely has a memory leak or is loading too much data at once — investigate the underlying code rather than endlessly increasing the limit.

Step 5: Check for PHP syntax errors

A syntax error — a missing semicolon, an unclosed brace, a stray character introduced in the last deploy — produces a fatal parse error and a 500. PHP's built-in linter pinpoints the exact file and line:

# Lint a single file
php -l /var/www/html/index.php

# Lint every PHP file in the document root
find /var/www/html -name "*.php" -exec php -l {} \; | grep -v "No syntax errors"

To make errors visible during debugging (and never on a live production site shown to users), enable error reporting temporarily:

// At the top of the entry script
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

Or, more safely, log errors to a file without displaying them:

; php.ini
log_errors = On
error_log = /var/log/php_errors.log
display_errors = Off

Once the offending file is identified, fix the syntax and re-run php -l until it reports No syntax errors detected.

Step 6: Check database connections

Many applications fail with a 500 when they cannot reach the database — credentials changed, the DB server restarted on a new port, or a connection pool was exhausted. The error log typically shows Connection refused, Access denied for user, or Unknown database. Verify each piece of the connection:

# Is the database server up?
sudo systemctl status mysql
sudo systemctl status postgresql

# Can the app host reach it on the right port?
nc -zv db.example.com 3306

# Do the credentials work from the command line?
mysql -u appuser -p -h db.example.com -P 3306 appdb

Compare the connection settings in your application's configuration file (for example, wp-config.php, a Laravel .env, or a Django settings.py) against the actual database host, port, name, and credentials. A common pitfall after a server move is a stale localhost when the database now lives on a separate host.

Step 7: Restart the web server

After applying fixes, restart the affected services so they pick up new configuration and clear any stale state, then verify with an HTTP check:

# Restart the full stack
sudo systemctl restart php8.2-fpm
sudo systemctl reload nginx         # or: sudo systemctl restart apache2

# Test locally
curl -I http://localhost

# Expected: HTTP/1.1 200 OK
# Test from outside
curl -I https://example.com

You should see a 200 OK (or a 301/302 redirect). If the 500 persists, loop back to Step 1 — the error log will now show a more specific message reflecting your changes, narrowing the remaining cause.

Nginx & Apache Configuration Examples

Sometimes the 500 originates not in the application but in the web server configuration itself. A common Nginx cause is a fastcgi_pass that points at a PHP-FPM socket that does not exist or that the web server user cannot access. A common Apache cause is a missing module referenced by a directive.

Nginx — passing PHP to a correctly configured upstream and validating the config:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.php index.html;

    location ~ \.php$ {
        # Point at the real PHP-FPM socket/port
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        # fastcgi_pass 127.0.0.1:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
sudo nginx -t && sudo nginx -s reload

Apache — ensuring mod_rewrite and mod_php (or proxy_fcgi) are enabled, then validating:

sudo a2enmod rewrite
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.2-fpm
sudo apachectl configtest && sudo systemctl reload apache2

If nginx -t or apachectl configtest reports a syntax error, fix the reported line before reloading — reloading a broken config will not take effect and can leave the server serving 500s.

PHP Error Reporting Configuration

For reliable debugging, configure PHP to log errors to a file rather than suppressing them. This is the single biggest time-saver when chasing an intermittent 500. A robust production setting in php.ini:

display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

Make sure the web server user can write to /var/log/php_errors.log, then restart PHP-FPM. When a 500 next occurs, tail -f /var/log/php_errors.log will show the exact fatal error in real time.

For temporary on-screen debugging during development only, switch the display flags on and raise reporting to E_ALL — but always turn them back off before the code reaches production, since exposed errors leak implementation details and file paths.

Quick Reference Table

The 5xx error family is closely related. Use this table to tell them apart at a glance.

Code Name Meaning
500 Internal Server Error The server hit an unexpected error (application bug, syntax error, or permission issue).
502 Bad Gateway The gateway/proxy received an invalid or no response from the upstream.
503 Service Unavailable The server is temporarily unable to handle the request (overload or maintenance).
504 Gateway Timeout The gateway/proxy did not receive a timely response from the upstream.

A simple heuristic: 500 is the application's fault, 502 is the upstream's fault, 503 is the server saying "not right now," and 504 is the upstream being too slow.

FAQ

Is a 500 Internal Server Error a client-side problem?

No. The 5xx range is server-side by definition — the browser's request was valid, but the server failed to fulfill it. Clearing your cache or using a different browser will not fix a true 500; the fix must be applied on the server. The one exception is when a specific malformed request reliably triggers a server bug, but even then the root cause is server-side.

Why does my site show a blank white page instead of an error?

Production PHP configurations set display_errors = Off for security, so fatal errors are suppressed and you see only a blank page or a generic 500. The real message is in the server error log. Enable log_errors and check /var/log/php_errors.log (or your framework's log) to see the underlying fatal error.

How is a 500 different from a 502?

A 500 means the server itself (or the application it ran) crashed or hit an error while processing the request. A 502 Bad Gateway means a proxy or gateway received an invalid or no response from the upstream server it was talking to. In short: 500 = the app that handled the request failed, 502 = the layer in front could not reach a working backend.

Can a 500 fix itself?

Rarely. A 500 caused by a transient resource spike may resolve when load drops, and one caused by a partial deploy usually clears once the deploy completes. But a 500 from a syntax error, a broken config, or wrong permissions will persist until you intervene. Always check the error log rather than waiting for it to clear on its own.

Conclusion

A 500 Internal Server Error is deliberately vague — it is the server's catch-all for "something broke and I cannot say exactly what." That vagueness is why the first step is always the same: read the error log. The log converts a blank 500 into a specific file, line, and message, after which the remaining steps are mechanical.

The seven-step process in this guide works across Nginx, Apache, PHP, and general web applications because it targets the layers where 500s originate: check the logs, verify permissions, isolate the configuration files, raise the memory limit, lint for syntax errors, confirm the database connection, and restart the stack. Once you resolve the immediate issue, invest in prevention — keep error logging enabled, validate configs before reloads, lint PHP during deploy, and set resource alerts so a climbing memory footprint is caught before it becomes a 500. A 500 should be a quick, diagnosable event, not a recurring mystery.

Related Guides