Apache troubleshooting
Tip: apache2 vs httpd
Debian/Ubuntu use the apache2 service and
apache2ctl; RHEL/CentOS use httpd and
httpd -t. Examples below may use either name —
systemctl status apache2 vs
systemctl status httpd,
journalctl -u apache2 vs journalctl -u httpd.
Service won't start after config change
Run apache2ctl configtest or httpd -t. Look for
duplicate Listen directives, bad DocumentRoot
paths, or syntax errors. Check the journal:
journalctl -u apache2 -n 50.
403 Forbidden on a valid URL
Check directory permissions, missing Require all granted in the
<Directory> block, SELinux contexts on RHEL
(restorecon -Rv /var/www/), or a wrong DocumentRoot.
Confirm the request hits the intended vhost — match ServerName
to the Host header.
Missing index file — Apache may return 403 if
Options -Indexes (directory listing disabled) and no
index.html or other DirectoryIndex target exists in
that folder.
502 Bad Gateway / 504 Gateway Timeout
When Apache is configured as a reverse proxy, 502/504 usually indicate backend behind ProxyPass availability or timeout issues.
Verify the upstream is listening (ss -tlnp | grep 8000).
Check error_log for AHxxxx proxy errors or grep 'proxy' error_log. Confirm firewall
rules allow Apache to reach the backend. On RHEL/CentOS, SELinux may block
outbound proxy connections — see SELinux below.
SELinux blocking Apache (RHEL/CentOS)
On Red Hat systems with SELinux enforcing, Apache may fail to serve files or reach backends even when permissions and firewall look correct. Check denials:
getenforce
ausearch -m avc -ts recent | grep httpd
grep httpd /var/log/audit/audit.log | tail -20
Proxy / upstream connections — allow httpd to
connect to network backends:
setsebool -P httpd_can_network_connect 1
File access — wrong context on DocumentRoot often
causes 403; fix with restorecon -Rv /var/www/ (see 403 Forbidden
above). Prefer adjusting booleans and contexts over disabling SELinux.
Wrong site or default page served
The first matching vhost for a port acts as the default. Run
apache2ctl -S to see order and names. On Debian, disable
000-default with a2dissite 000-default if it
steals traffic.
Also, requests may be landing on the wrong vhost because the Host header does not match any configured ServerName or ServerAlias.
Changes made but Apache still serves old content
Config or files updated on disk, but the browser or client still sees the old page. Common causes:
- Browser cache — hard refresh or test with
curl - CDN or cache layer — edge still serving a cached copy
- Reverse proxy cache — nginx, Varnish, or Cloudflare in front of Apache
- Wrong vhost being edited — change applied to a site that is not handling the request
Useful checks:
apache2ctl -S
curl -I https://site
curl -I -H "Cache-Control: no-cache" https://site
Confirm Apache reloaded (systemctl reload apache2), then bypass
caches: curl directly to the origin, compare
DocumentRoot from apache2ctl -S to the path you edited.
Address already in use (Listen conflict)
Another process holds port 80 or 443 — often nginx, a stale Apache instance,
or a duplicate Listen in config. Find it with
ss -tlnp and remove the conflicting directive
or stop the other service.
Module not found / invalid command
A directive requires a module that is not loaded. Run apache2ctl -M to list loaded modules. On Debian:
a2enmod proxy proxy_http rewrite ssl then reload. On RHEL,
confirm LoadModule lines are uncommented in the main config.
.htaccess not taking effect
A very common Apache issue — .htaccess files are
silently ignored unless AllowOverride is set to
something other than None in the <Directory>
block for that path. This is distinct from 403 errors: the server responds,
but redirects, rewrites, and auth rules in .htaccess never run.
Diagnostic: find the effective vhost and
check AllowOverride in its config:
apache2ctl -S
# Note the config file path for your vhost, then:
grep -n AllowOverride /etc/apache2/sites-enabled/example.conf
# RHEL: grep -n AllowOverride /etc/httpd/conf.d/example.conf
Set AllowOverride All (or FileInfo for rewrites only)
inside <Directory /var/www/html>, run
apache2ctl configtest, and reload. If still ignored, confirm the
request hits that vhost (apache2ctl -S) and the
.htaccess file is in the correct directory under
DocumentRoot.
Rewrite rules not behaving as expected
Symptoms: URLs not rewriting, redirect loops, or WordPress/Laravel/Django front-controller routing broken (404 on clean URLs).
Checks:
apache2ctl -M | grep rewrite # or: httpd -M | grep rewriteVerify:
LoadModule rewrite_moduleis present (ora2enmod rewriteon Debian)AllowOverrideallows.htaccess— see.htaccessnot taking effect above- Rules in
.htaccessor the vhost are actually loaded — no typo inRewriteBase, wrong vhost, or overridden by a laterRewriteRule
After changes: apache2ctl configtest and reload. For redirect loops,
check RewriteCond guards and HTTPS redirects fighting each other.
TLS / certificate issues
HTTPS failures often show in error_log as certificate or
handshake errors:
# Check cert paths in the vhost
SSLCertificateFile /etc/ssl/certs/example.crt
SSLCertificateKeyFile /etc/ssl/private/example.key
# Test TLS from the client
openssl s_client -connect example.com:443 -servername example.com
# Check certificate expiry
openssl x509 -in cert.pem -noout -dates
On Apache 2.4.8+, the full chain can be concatenated into SSLCertificateFile, making SSLCertificateChainFile unnecessary — but many real-world configs still use separate chain files, and omitting it is a very common cause of TLS errors (handshake succeeds but browsers show "certificate not trusted").
Permission denied on logs
Apache cannot start if it cannot write to ErrorLog or
CustomLog paths. Verify the Apache user (www-data, apache, or httpd depending on distro) can write to the configured log path.
ls -ld /var/log/apache2
grep -E '^(User|Group)' /etc/apache2/apache2.confDebugging workflow
1. Config test and vhost map
apache2ctl configtest
apache2ctl -S2. Error log while reproducing
tail -f /var/log/apache2/error.log
curl -v -H "Host: www.example.com" http://127.0.0.1/3. Confirm listeners and service
systemctl status apache2
ss -tlnp | grep ':80'4. TLS troubleshooting
curl -vk https://127.0.0.1/Practice scenarios
Hands-on Apache scenarios on live Linux VMs: apache