A 500 Internal Server Error means the application on the server ran into a problem it could not handle while building the response. Unlike most other errors, it doesn’t say what went wrong. It is the server’s way of saying “something broke, and I’m not telling the visitor what”.
The real error message exists, though. It’s in a log. This guide shows where to find it, the causes behind most 500 errors and how to make sure you’re the first to know when one appears.
What a 500 tells you
A 500 comes from the application itself, or from the web server running it. It means the request reached your code and the code failed: an exception nobody caught, a fatal error, a configuration the server couldn’t read. That sets it apart from the gateway errors, where the problem sits between two servers:
- 500 Internal Server Error: the application failed while handling the request.
- 502 Bad Gateway: a proxy got an invalid answer from the application. See our 502 guide.
- 503 Service Unavailable: the service is overloaded or in maintenance. See our 503 guide.
- 504 Gateway Timeout: the application was too slow to answer. See our 504 guide.
A 500 on one page usually means a bug in that page’s code. A 500 on every page usually means something shared broke: the configuration, the database connection, a file the whole application loads.
Start with what changed
Most 500 errors appear right after a change. Before reading a single log, ask what happened in the last hour:
- A deploy or a merge to the main branch
- A plugin, theme, framework or language update
- An edit to a configuration file,
.htaccessor environment variables - A rotated password or API key
- A server that filled its disk or had its permissions changed
If the timing fits, rolling back the change is often the fastest way to bring the site back. Find the cause afterwards, without customers watching.
Find the real error message
The 500 page is generic, the log is specific. Where to look depends on the stack:
# nginx and Apache
tail -n 50 /var/log/nginx/error.log
tail -n 50 /var/log/apache2/error.log
# PHP-FPM
tail -n 50 /var/log/php8.3-fpm.log
# Applications run by systemd
journalctl -u myapp --since "15 min ago"
# Docker
docker logs --since 15m myapp
Look for the timestamp of the failing request. The entry next to it usually names the file, the line and the reason: an undefined function, a missing class, a database that refused the password.
On WordPress, turn on the debug log in wp-config.php. It writes errors to wp-content/debug.log without showing them to visitors:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
Turn it off again when you’re done, since the log can grow large and may contain sensitive details.
Common causes and how to fix them
An error in the code
An uncaught exception or a fatal error in the code that builds the page. The log shows the file and line. Typical triggers are a function called with data it didn’t expect, a value that is unexpectedly empty, and code that works on the developer’s machine but needs a PHP extension or library version the server doesn’t have. Fix the code, or roll back to the last version that worked.
A plugin or theme update
On WordPress and other CMS platforms, an update to a plugin or theme is the most common cause. The log names the plugin’s folder. If you can’t reach the admin, rename that folder over SSH or SFTP to disable it:
mv wp-content/plugins/some-plugin wp-content/plugins/some-plugin.disabled
The site comes back without it. Then update it again, replace it, or contact its developer with the error from the log.
A broken .htaccess file
On Apache, one wrong line in .htaccess takes down the whole site with a 500. The error log says Invalid command or names the directive it didn’t understand. Rename the file to test, then restore the lines one by one. On WordPress, saving the permalink settings writes a fresh one.
PHP out of memory
Allowed memory size of 134217728 bytes exhausted means a request needed more memory than memory_limit allows. Raising the limit can help, but a request that needs hundreds of megabytes usually loads too much data at once. Find out what it loads before you give it more.
File permissions and ownership
After a deploy, a restore or a manual copy, files can end up owned by the wrong user. The web server then can’t read the code or write to its cache and upload folders, and the log says Permission denied. Set ownership back to the user the web server or PHP runs as. Directories normally need 755 and files 644; never fix it with 777.
A full disk
When the disk is full, applications can’t write sessions, caches, uploads or logs, and fail in ways that end in a 500. Check the space and the inodes:
df -h
df -i
Old logs, backups and caches are the usual culprits. Server monitoring with a disk alert at 80 or 90 percent catches this days before it breaks the site.
Database connection errors
A changed password, a database server that is down, or a connection limit that’s reached. The application can’t load anything and fails on every page. The log names the database error. Check that the database runs, that the credentials in the configuration are current, and how many connections are open compared with the limit.
The 500 that returns 200
Not every broken page says so. Some applications catch the error and show a friendly “Something went wrong” page with status 200, and a CMS can render an empty product list without complaining. To a monitor that only looks at the status code, everything looks fine.
Check the content too. An uptime monitor can require that a page contains a word that is always there when it works, such as the product name or “Add to cart”, and treat its absence as a failure. Our checklist of what to monitor lists which pages deserve that kind of check.
How monitoring catches 500s
A 500 after a deploy can sit unnoticed for hours if it only hits one page. Checkout breaks, the homepage still works, and the team finds out from a customer email or a dip in sales.
- Check the pages that earn money. Add uptime monitors for checkout, login, sign-up and the API, not only the homepage, with content checks where a 200 can hide an error.
- Confirm before alerting. Uptimecheck checks every minute from Stockholm, Sweden, and confirms each failure with a second check, so a single failed request doesn’t page anyone.
- See what broke. The incident carries the failing response, including the status code and the error page, and a screenshot of what a visitor saw.
- Test whole flows. A login or checkout that fails on step three returns 200 on step one. Multi-step flows walk through the steps and tell you which one failed.
500 checklist
- Ask what changed in the last hour, and roll back if the timing fits.
- Find the failing request’s timestamp in the error log.
- Read the error: file, line and reason.
- Rule out the usual causes: a plugin,
.htaccess, memory, permissions, disk, database. - Fix the cause, then remove any debug settings you turned on.
- Monitor key pages every minute, with content checks where a 200 can hide an error.
- Test logins and checkouts as flows, not single pages.