Fix: Apache 500 Internal Server Error
Quick Answer
Resolve Apache's 500 Internal Server Error by checking error logs, fixing .htaccess rules, correcting file permissions, and debugging PHP/CGI configuration.
The Error
You load a page served by Apache and get:
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.Or in your browser, a plain white page with:
500 Internal Server ErrorIn your Apache error log (/var/log/apache2/error.log on Debian/Ubuntu, /var/log/httpd/error_log on RHEL/CentOS), you see one of these:
[core:alert] [pid 12345] /var/www/html/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration[cgid:error] [pid 12345] [client 192.168.1.10:54321] End of script output before headers: index.cgi[proxy_fcgi:error] [pid 12345] (70007)The timeout specified has expired: AH01075: Error dispatching request to :AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration errorAll of these result in the same 500 status code sent to the client, but the root causes are entirely different. The error log is the only way to tell them apart.
Why This Happens
A 500 Internal Server Error is Apache’s generic “something went wrong on the server side” response. Unlike a 404 (page not found) or a 403 (forbidden), a 500 doesn’t tell the client what broke — it just says the server failed to process the request.
Common causes:
- Syntax errors in
.htaccessfiles. A single typo or unsupported directive crashes request processing for the entire directory tree. - Missing Apache modules. Your config references
mod_rewrite,mod_headers, or another module that isn’t loaded. - Incorrect file or directory permissions. Apache can’t read the files it needs to serve, or it can’t write to directories required by your application.
- PHP fatal errors. A syntax error, missing extension, or exceeded memory limit kills the PHP process mid-request.
- CGI script failures. A CGI script doesn’t output proper HTTP headers, lacks execute permissions, or has the wrong shebang line.
- SELinux blocking file access. On RHEL/CentOS/Fedora, SELinux prevents Apache from reading files outside its allowed context.
- PHP-FPM communication failure. Apache can’t reach the PHP-FPM socket or the worker pool is exhausted.
- Infinite redirect loops. Misconfigured
RewriteRuledirectives create a loop that Apache detects and aborts. - Exceeded resource limits. PHP runs out of memory, or Apache hits its
LimitRequestBodythreshold.
Fix 1: Check the Apache Error Log
Every single 500 fix starts here. The error log tells you exactly what went wrong.
Debian/Ubuntu:
sudo tail -50 /var/log/apache2/error.logRHEL/CentOS/Fedora:
sudo tail -50 /var/log/httpd/error_logIf you have virtual hosts with custom log paths, find them:
sudo grep -r "ErrorLog" /etc/apache2/sites-enabled/ 2>/dev/null
sudo grep -r "ErrorLog" /etc/httpd/conf.d/ 2>/dev/nullThen tail that specific log. If you’re not sure which virtual host is handling the request:
# Watch the log in real time while you trigger the error in your browser
sudo tail -f /var/log/apache2/error.logThe error message you see here determines which fix below applies. Don’t guess — read the log.
Fix 2: Fix .htaccess Syntax Errors
The most common cause of 500 errors. A single bad line in .htaccess takes down the entire directory.
Find the offending .htaccess file:
The error log will point to the exact file and line. If it says:
/var/www/html/mysite/.htaccess: Invalid command 'RewirteEngine'That’s a typo (RewirteEngine instead of RewriteEngine). Open the file and fix it.
Common .htaccess mistakes:
# WRONG: Typo in directive name
RewirteEngine On
# RIGHT:
RewriteEngine On# WRONG: Missing RewriteEngine On before rules
RewriteRule ^old-page$ /new-page [R=301,L]
# RIGHT: Must enable the engine first
RewriteEngine On
RewriteRule ^old-page$ /new-page [R=301,L]# WRONG: Using directives from a module that isn't loaded
<IfModule !mod_rewrite.c>
# This block runs when mod_rewrite is NOT available
RewriteEngine On # This will fail
</IfModule>
# RIGHT: Wrap in a positive check
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^old-page$ /new-page [R=301,L]
</IfModule>Test your Apache config without restarting:
sudo apachectl configtestIf you see Syntax OK, the main config is fine. Note that configtest doesn’t validate .htaccess files — those are parsed at request time. The only way to test .htaccess is to trigger an actual request and check the error log.
Temporarily disable .htaccess to confirm it’s the problem:
Rename it and reload:
mv /var/www/html/mysite/.htaccess /var/www/html/mysite/.htaccess.bakIf the 500 error disappears, the problem is in that file. Add lines back one by one until you find the bad one.
Fix 3: Enable Missing Apache Modules
Your config or .htaccess uses a directive from a module that isn’t loaded.
Check loaded modules:
sudo apachectl -MCommon modules that cause 500 errors when missing:
# mod_rewrite -- needed for URL rewriting (most WordPress/Laravel/Drupal sites)
sudo a2enmod rewrite
# mod_headers -- needed for custom HTTP headers, CORS
sudo a2enmod headers
# mod_expires -- needed for cache control headers
sudo a2enmod expires
# mod_ssl -- needed for HTTPS
sudo a2enmod ssl
# mod_proxy and mod_proxy_fcgi -- needed for PHP-FPM
sudo a2enmod proxy proxy_fcgiOn RHEL/CentOS, modules are loaded via config files in /etc/httpd/conf.modules.d/. Check if the module’s .so file exists:
ls /usr/lib64/httpd/modules/ | grep rewriteIf the module file exists but isn’t loaded, add it:
# /etc/httpd/conf.modules.d/00-base.conf
LoadModule rewrite_module modules/mod_rewrite.soRestart Apache after enabling modules:
sudo systemctl restart apache2 # Debian/Ubuntu
sudo systemctl restart httpd # RHEL/CentOSPro Tip: Wrap module-specific directives in
<IfModule mod_name.c>blocks in your.htaccess. This way, a missing module causes a silent skip instead of a 500 error. This is especially important for portable.htaccessfiles that may run on different servers with different module sets.
Fix 4: Fix File and Directory Permissions
Apache needs to read your files and traverse your directories. Incorrect permissions cause a 500 error (or sometimes a 403, depending on the configuration).
Standard permissions for web files:
# Directories: 755 (owner rwx, group rx, others rx)
sudo find /var/www/html/mysite -type d -exec chmod 755 {} \;
# Files: 644 (owner rw, group r, others r)
sudo find /var/www/html/mysite -type f -exec chmod 644 {} \;Set the correct owner:
# Apache runs as www-data on Debian/Ubuntu
sudo chown -R www-data:www-data /var/www/html/mysite
# Apache runs as apache on RHEL/CentOS
sudo chown -R apache:apache /var/www/html/mysiteCheck which user Apache runs as:
grep -E "^User|^Group" /etc/apache2/apache2.conf 2>/dev/null
grep -E "^User|^Group" /etc/httpd/conf/httpd.conf 2>/dev/nullWritable directories for uploads or cache:
Some applications need Apache to write to specific directories (upload folders, cache directories, log directories):
sudo chmod 775 /var/www/html/mysite/storage
sudo chmod 775 /var/www/html/mysite/storage/logs
sudo chmod 775 /var/www/html/mysite/bootstrap/cacheRelated: For more on Unix permission errors, see Fix: bash: Permission Denied.
Fix 5: Fix mod_rewrite and Redirect Loops
Misconfigured rewrite rules cause infinite redirect loops. Apache detects these after 10 iterations and returns a 500.
The error log will show:
AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration errorCommon redirect loop scenarios:
# WRONG: This rewrites /page to /page, which triggers another rewrite to /page...
RewriteEngine On
RewriteRule ^(.*)$ /index.php [L]The fix is to add a condition that stops the loop:
RewriteEngine On
# Don't rewrite if the request is already for an existing file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php [L]HTTPS redirect loops are another classic:
# WRONG: If Apache doesn't see HTTPS (e.g., behind a load balancer that terminates SSL),
# this creates an infinite redirect loop
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]If Apache sits behind a reverse proxy or load balancer that handles SSL, the connection between the proxy and Apache is plain HTTP. Apache sees HTTPS=off on every request and keeps redirecting.
Fix by checking the X-Forwarded-Proto header instead:
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]Also make sure AllowOverride is set correctly. If .htaccess rewrite rules are being ignored (and the fallback behavior causes errors), check your virtual host:
<Directory /var/www/html/mysite>
AllowOverride All
</Directory>AllowOverride None disables .htaccess entirely. AllowOverride All allows all directives.
Fix 6: Fix PHP Errors
PHP fatal errors return a 500 to the client. The Apache error log will include the PHP error, or PHP may log to its own file.
Check PHP’s error log:
# Find where PHP logs errors
php -i | grep error_log
# Common locations
sudo tail -50 /var/log/php_errors.log
sudo tail -50 /var/log/php8.3-fpm.logCommon PHP causes of 500 errors:
- Syntax error in PHP code:
Parse error: syntax error, unexpected '}' in /var/www/html/mysite/index.php on line 42Check the file at that line and fix the syntax.
- Missing PHP extension:
Fatal error: Uncaught Error: Call to undefined function mysqli_connect()Install the missing extension:
sudo apt install php8.3-mysql # Debian/Ubuntu
sudo dnf install php-mysqlnd # RHEL/CentOS
sudo systemctl restart apache2- Memory limit exceeded:
Fatal error: Allowed memory size of 134217728 bytes exhaustedIncrease in php.ini:
; /etc/php/8.3/apache2/php.ini
memory_limit = 256MOr per-directory in .htaccess:
php_value memory_limit 256MEnable error display temporarily for debugging (never in production):
# In .htaccess
php_flag display_errors on
php_flag log_errors onThis shows the actual PHP error in the browser instead of a generic 500 page.
Related: For PHP memory issues with Composer, see Fix: PHP Composer Memory Limit.
Fix 7: Fix CGI Script Issues
If you’re running CGI scripts (Perl, Python, Bash), several things can cause a 500.
The error log will typically show:
End of script output before headers: myscript.cgiThis means the script either didn’t produce any output, or it didn’t output valid HTTP headers before the body.
1. Missing or wrong shebang line:
# WRONG: Missing shebang -- Apache doesn't know how to execute the script
echo "Content-Type: text/html"
echo ""
echo "Hello World"
# RIGHT: Include the interpreter path
#!/usr/bin/perl
print "Content-Type: text/html\n\n";
print "Hello World";2. Missing execute permission:
chmod +x /var/www/cgi-bin/myscript.cgi3. Wrong line endings. If the script was created or edited on Windows, it may have \r\n line endings instead of \n. The shebang line becomes #!/usr/bin/perl\r, and Linux can’t find the interpreter.
# Fix line endings
sed -i 's/\r$//' /var/www/cgi-bin/myscript.cgi4. Script must output headers first:
#!/usr/bin/python3
# WRONG: Body before headers
print("Hello World")
# RIGHT: Headers, blank line, then body
print("Content-Type: text/html")
print("")
print("Hello World")5. Make sure CGI is enabled:
sudo a2enmod cgid
sudo systemctl restart apache2Fix 8: Fix SELinux Contexts (RHEL/CentOS/Fedora)
On RHEL-based distributions, SELinux prevents Apache from reading files that don’t have the correct security context, even if Unix permissions are fine.
Check if SELinux is the problem:
# Is SELinux enforcing?
getenforce
# Check for recent denials involving Apache
sudo grep httpd /var/log/audit/audit.log | grep denied | tail -20Fix the file context for your web directory:
# Set the correct SELinux context for web content
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/html/mysite(/.*)?"
sudo restorecon -Rv /var/www/html/mysiteIf Apache needs to write to a directory (uploads, cache, logs):
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/mysite/storage(/.*)?"
sudo restorecon -Rv /var/www/html/mysite/storageIf Apache needs to make network connections (to a database, API, or mail server):
sudo setsebool -P httpd_can_network_connect 1If Apache needs to connect to a database specifically:
sudo setsebool -P httpd_can_network_connect_db 1If Apache needs to send mail:
sudo setsebool -P httpd_can_sendmail 1Common Mistake: Disabling SELinux entirely (
setenforce 0or settingSELINUX=disabledin/etc/selinux/config) “fixes” the problem but removes an important security layer. Always use targeted booleans and file contexts instead. If you temporarily setsetenforce 0and the error goes away, you’ve confirmed SELinux is the cause — now fix it properly with the commands above.
Fix 9: Fix PHP-FPM Communication
If you’re using PHP-FPM with Apache (via mod_proxy_fcgi), the connection between Apache and PHP-FPM can fail.
Check if PHP-FPM is running:
sudo systemctl status php8.3-fpmIf it’s stopped or crashed:
sudo systemctl start php8.3-fpm
sudo journalctl -u php8.3-fpm --no-pager -n 50Related: If you see service failures in systemd, see Fix: systemctl Service Failed.
Verify the socket or port matches:
Check what PHP-FPM is listening on:
sudo grep "^listen " /etc/php/8.3/fpm/pool.d/www.confCheck what Apache expects:
sudo grep -r "proxy:fcgi\|proxy:unix" /etc/apache2/sites-enabled/ 2>/dev/null
sudo grep -r "proxy:fcgi\|proxy:unix" /etc/httpd/conf.d/ 2>/dev/nullIf using a Unix socket:
Apache config should look like:
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>If using TCP:
PHP-FPM listens on 127.0.0.1:9000, and Apache connects to:
<FilesMatch \.php$>
SetHandler "proxy:fcgi://127.0.0.1:9000"
</FilesMatch>PHP-FPM worker pool exhaustion:
If all PHP-FPM workers are busy, new requests queue up and eventually time out. Check worker status:
# Enable PHP-FPM status page in pool config
# pm.status_path = /status
# Then check it
curl http://localhost/statusIncrease workers in /etc/php/8.3/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20Restart PHP-FPM after changes:
sudo systemctl restart php8.3-fpmFix 10: Fix Memory and Resource Limits
PHP or Apache hitting resource limits causes a 500.
PHP memory limit:
Fatal error: Allowed memory size of 134217728 bytes exhaustedEdit php.ini:
# Find the right php.ini
php --ini | grep "Loaded Configuration"
# Typical location for Apache module
# /etc/php/8.3/apache2/php.ini
# Typical location for PHP-FPM
# /etc/php/8.3/fpm/php.inimemory_limit = 256M
max_execution_time = 120
post_max_size = 64M
upload_max_filesize = 64MApache request body limit:
If users are uploading large files and getting a 500, check LimitRequestBody:
# In your virtual host or .htaccess
# Allow up to 100MB uploads
LimitRequestBody 104857600The default is unlimited (0), but some hosting providers set restrictive limits.
Apache process limits:
If Apache runs out of worker processes, new requests fail. Check your MPM configuration:
sudo apachectl -V | grep MPMFor the prefork MPM (common with mod_php):
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150
MaxConnectionsPerChild 3000
</IfModule>For the event MPM (common with PHP-FPM):
<IfModule mpm_event_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 1000
</IfModule>Restart Apache after any MPM changes:
sudo systemctl restart apache2Fix 11: Fix Ownership of the Document Root Path
Apache needs to traverse every directory in the path from / to your document root. A common oversight is that one of the parent directories isn’t accessible.
Check the full path:
# Test each directory in the path
namei -l /var/www/html/mysiteThis shows permissions for every component. If any directory is missing x (execute) permission for the Apache user, you get a 500 or 403.
# Common fix: Ensure the home directory is traversable if serving from /home/user/public_html
chmod 711 /home/usernameVirtual host DocumentRoot must exist:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html/mysite
</VirtualHost>If /var/www/html/mysite doesn’t exist, Apache returns a 500 on any request to this virtual host. Create the directory:
sudo mkdir -p /var/www/html/mysite
sudo chown www-data:www-data /var/www/html/mysiteRelated: For SSH key permission issues that might affect deployment, see Fix: Git Permission Denied (publickey).
Fix 12: Debug with a Minimal Test
When all else fails, isolate the problem by creating a minimal test file.
Test basic Apache serving:
echo "<h1>Apache works</h1>" | sudo tee /var/www/html/test.html
curl http://localhost/test.htmlIf this works, Apache itself is fine. The problem is in your application or configuration.
Test PHP:
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
curl -I http://localhost/info.phpIf you get a 500, PHP is misconfigured. If you get a 200 but see raw PHP code, PHP isn’t being processed (module or FPM not configured).
Test .htaccess processing:
echo "This is invalid syntax on purpose" | sudo tee /var/www/html/.htaccess
curl -I http://localhost/If you get a 500, .htaccess is being processed. If you get a 200, AllowOverride is set to None and .htaccess files are being ignored.
Remove the test files after debugging:
sudo rm /var/www/html/test.html /var/www/html/info.phpIncrease Apache log verbosity temporarily:
# In your virtual host config
LogLevel debugThis floods the error log with details but helps diagnose obscure issues. Set it back to warn or error when done:
sudo systemctl reload apache2Still Not Working?
Narrow it down systematically
- Check the error log. This is step one, always. The error log tells you what broke.
- Determine if it’s Apache, PHP, or your app. Serve a plain HTML file. If that works, serve a
phpinfo()page. If that works, the problem is in your application code. - Disable
.htaccess. Rename it and test. If the error disappears, add rules back one at a time. - Check if it’s a specific URL. If only
/adminthrows a 500 but/works, the problem is in the code that handles/admin.
Check for disk space issues
A full disk causes all sorts of 500 errors — Apache can’t write logs, PHP can’t write sessions, your app can’t write temp files:
df -hIf any partition is at 100%, free up space. Common culprits: old log files, PHP session files, temp files.
# Check log sizes
sudo du -sh /var/log/apache2/ /var/log/httpd/ 2>/dev/null
# Clean old PHP sessions
sudo find /var/lib/php/sessions/ -type f -mtime +7 -deleteRelated: For Docker disk space issues, see Fix: Docker No Space Left on Device.
Permissions look right but it still fails
If file permissions are correct (644 for files, 755 for directories) and the owner matches the Apache user, but you still get a 500, check:
- ACLs (Access Control Lists):
getfacl /var/www/html/mysitemight show restrictive ACL entries overriding standard Unix permissions. - File attributes:
lsattr /var/www/html/mysite/index.phpmight show an immutable flag. - SELinux (see Fix 8 above).
- AppArmor (on Ubuntu): Check
sudo aa-statusand look for Apache profiles that might restrict file access.
The error only happens under load
Intermittent 500 errors that increase with traffic usually mean a resource limit is being hit:
- PHP-FPM workers exhausted (see Fix 9).
- Apache
MaxRequestWorkersreached (see Fix 10). - Database connection limit hit (check your database logs).
- Disk I/O saturation (check
iostatoriotop).
Monitor in real time:
# Watch Apache status (enable mod_status first)
sudo a2enmod status
# Then access http://localhost/server-status
# Watch system resources
top -u www-dataRelated: For Nginx reverse proxy issues, see Fix: Nginx 502 Bad Gateway. For connection issues reaching your server, see Fix: curl Failed to Connect. For permission errors when deploying via SSH, see Fix: bash: Permission Denied.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Cannot Connect to the Docker Daemon. Is the Docker Daemon Running?
How to fix the 'Cannot connect to the Docker daemon' error on Linux, macOS, and Windows, including Docker Desktop, systemctl, WSL2, and Docker context issues.
Fix: bash: command not found
How to fix bash command not found error caused by missing PATH, uninstalled packages, wrong shell, typos, missing aliases, and broken symlinks on Linux and macOS.
Fix: Nginx 504 Gateway Timeout
How to fix the Nginx 504 Gateway Timeout error by tuning proxy timeout settings, fixing unresponsive upstream servers, adjusting PHP-FPM timeouts, and debugging with error logs.
Fix: Nginx upstream timed out (110: Connection timed out) while reading response header
How to fix Nginx upstream timed out error caused by slow backend responses, proxy timeout settings, PHP-FPM hangs, and upstream server configuration issues.