Apache Security Hardening in Practice: Server Configuration Defense Guide
Apache is the world's most widely used web server; default configs leak version info, allow directory listing, and load excess modules. This article gives a copy-paste Apache hardening checklist: hide version strings, tighten directory permissions, restrict dangerous methods, and add WAF rules.
Apache security hardening is baseline work before a web server goes live. Default Apache configs expose the version string, allow directory listing, and load many unnecessary modules—attackers use this to fingerprint the stack or exploit directory traversal to read sensitive files. This article gives a drop-in Apache hardening config covering information hiding, permission tightening, and request filtering.
Applicable scenarios
- Production servers running Apache 2.4.x on self-hosted or cloud hosts (CentOS / Ubuntu / Debian)
- Security scans that flag medium-risk findings such as `Server` response header version leak or directory indexing enabled
- Need to shrink the web server attack surface before an MLPS assessment or pentest
- Business systems using Apache as a front-end proxy or static-file server
Prerequisites
- Server root or sudo, able to edit Apache config and run reload/restart
- Confirm Apache version: `apache2 -v` (Debian/Ubuntu) or `httpd -v` (CentOS/RHEL)
- The app is currently reachable so you can regression-test after hardening
How it works
Apache's main security risks come from four default behaviors:
- Information leak: default response headers carry version and OS (e.g. `Server: Apache/2.4.41 (Ubuntu)`). Attackers match fingerprints to known CVEs for targeted exploits.
- Directory listing: when `Options Indexes` is on, directories without an index file list all files—source and backup archives can be exposed.
- Oversized module and request surface: default load of `mod_info`, `mod_status`, `mod_autoindex`, and similar. If exposed to the internet they leak server status and config.
- Dangerous HTTP methods and symlinks: `PUT/DELETE` can be used to write files; `FollowSymLinks` can enable path traversal.
Hardening principle is "minimum exposure, explicit deny": hide fingerprints, disable directory indexes, unload unused modules, whitelist allowed HTTP methods, then layer mod_security rules.
Steps
1. Hide version and server fingerprint
`# Edit the main configuration file
# Debian/Ubuntu: /etc/apache2/apache2.conf
# CentOS/RHEL: /etc/httpd/conf/httpd.conf
ServerTokens Prod
ServerSignature Off
# ServerTokens Prod only outputs "Apache"; does not display version or OS
# ServerSignature Off disables the version line in error-page footers`
2. Disable directory listing and tighten default permissions
`# Globally disable Indexes and FollowSymLinks; re-enable on specific directories as needed
<Directory />
Options -Indexes -FollowSymLinks
AllowOverride None
Require all denied
</Directory>
# Allow the website document root separately
<Directory /var/www/html>
Options -Indexes
Require all granted
</Directory>`
Key parameters: Options -Indexes disables directory indexes; AllowOverride None disables .htaccess overrides (reduces risk of uploaded malicious .htaccess); root Directory Require all denied is a deny-by-default fallback.
3. Unload/disable dangerous modules
`# Debian/Ubuntu
a2dismod autoindex status info userdir
systemctl reload apache2
# CentOS/RHEL: comment out the corresponding LoadModule lines under /etc/httpd/conf.modules.d/
# or delete the module files directly
rm -f /etc/httpd/modules/mod_info.so /etc/httpd/modules/mod_status.so
systemctl reload httpd`
4. Restrict HTTP methods and request header size
`<Location />
# Allow only common safe methods; deny PUT/DELETE/TRACE
<LimitExcept GET POST HEAD OPTIONS>
Require all denied
</LimitExcept>
# Disable TRACE (prevent XST cross-site tracing)
TraceEnable Off
</Location>
# Limit request-line and request-header size to mitigate buffer-style attacks
LimitRequestLine 4096
LimitRequestFieldSize 4096
LimitRequestFields 100`
5. Deploy mod_security rule-layer protection
`# Debian/Ubuntu install
apt-get install -y libapache2-mod-security2
a2enmod security2
systemctl restart apache2
# Enable the OWASP CRS core rule set
apt-get install -y modsecurity-crs
cp /etc/modsecurity/crs/crs-setup.conf.example /etc/modsecurity/crs/crs-setup.conf
echo 'Include /etc/modsecurity/crs/crs-setup.conf' >> /etc/apache2/mods-enabled/security2.conf
echo 'Include /etc/modsecurity/crs/rules/*.conf' >> /etc/apache2/mods-enabled/security2.conf
systemctl restart apache2`
In production, run DetectionOnly (log only, do not block) for 24-48 hours, watch false positives, then switch `SecRuleEngine On`.
6. Minimal TLS config (if HTTPS is enabled)
`# Enable only TLS 1.2/1.3; disable weak protocols and weak cipher suites
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
SSLHonorCipherOrder on
# Disable insecure SSL session reuse and compression
SSLCompression off
SSLSessionTickets off`
Verify the config
`# 1. syntax check and graceful reload
apachectl -t && systemctl reload apache2
# 2. Verify the version string is hidden
curl -sI http://your-server/ | grep -i server
# Expected: Server: Apache (no version number)
# 3. Verify directory listing is disabled
curl -s http://your-server/uploads/ | grep -i "index of"
# Expect no "Index of" output; return 403 or the default page
# 4. Verify dangerous methods are rejected
curl -s -o /dev/null -w "%{http_code}\n" -X PUT http://your-server/test.txt # 403
curl -s -o /dev/null -w "%{http_code}\n" -X TRACE http://your-server/ # 405/403
# 5. Verify mod_security is active (injection test, test environment only)
curl -s -o /dev/null -w "%{http_code}\n" "http://your-server/?id=1%27%20OR%20%271%27%3D%271" # 403`
FAQ
Q1: After disabling Indexes, some business directories became inaccessible?
Directory listing should not be a business dependency. If a specific directory truly needs listing (e.g. a public download site), enable it only there: `<Directory /var/www/html/download> Options +Indexes </Directory>`. Confirm there are no sensitive files, and restrict source with `Require`.
Q2: mod_security is blocking legitimate requests?
Confirm DetectionOnly mode and check `/var/log/apache2/modsec_audit.log` for the blocked rule ID. For confirmed false positives, exclude with `SecRuleRemoveById` in `modsecurity.conf`, or downgrade a given URI with `SecRuleUpdateActionById` to log-only.
Q3: How to find existing Apache malicious config or backdoors?
Check `apachectl -M` for unexpected modules; `grep -r "LoadModule" /etc/httpd/conf* /etc/apache2/` to review module sources; also check the end of `httpd.conf` for suspicious appended `<Location>` or `RewriteRule` directives (common backdoor pattern).
Summary
Apache hardening follows a five-step method: hide fingerprints, tighten directories, slim modules, whitelist methods, rules as last line. Hide version to cut fingerprint exploits, `-Indexes` to close directory listing, disable autoindex/status/info to shrink attack surface, `LimitExcept` to whitelist HTTP methods, then mod_security + OWASP CRS for rule-layer defense in depth. After these configs pass verification, Apache's main attack surface is reduced. Bake this checklist into a server init script and run it automatically on new hosts.