I’ve inherited more than one Apache server that had been running untouched for years — default configuration, ancient module list, version numbers broadcast in every response header. Nothing had gone wrong yet, but it was only a matter of time. Securing Apache isn’t a box I check once during setup; it’s a habit I keep up for as long as the server stays online.
Here’s the layered approach I use every time: shrinking the attack surface, hardening the configuration, controlling access, encrypting traffic, and keeping an eye on things afterward.
Why I Take Apache Security Seriously
An insecure Apache server can end up:
- Serving malware to visitors
- Leaking files it was never supposed to expose (backups,
.envfiles, source code) - Acting as a pivot point into the rest of my network
- Getting conscripted into a botnet
- Quietly tanking SEO rankings and user trust once something goes wrong
Because Apache is often the public front door to an entire stack, its security posture affects everything sitting behind it.
Prerequisites
- Root or sudo access
- Apache already installed (
httpdon RHEL-based systems,apache2on Debian-based) - Familiarity with config locations:
- RHEL/CentOS:
/etc/httpd/conf/httpd.conf,/etc/httpd/conf.d/ - Debian/Ubuntu:
/etc/apache2/apache2.conf,/etc/apache2/sites-available/
- RHEL/CentOS:
Step 1: Keep Apache and Modules Updated
Most real-world Apache compromises I’ve read about — and the one I helped clean up once — exploited vulnerabilities that already had patches available.
# Debian/Ubuntu
sudo apt update && sudo apt upgrade apache2
# RHEL/CentOS
sudo dnf update httpd
I enable automatic security updates where policy allows, and at minimum I subscribe to security advisories for my distro and for Apache itself.
Step 2: Hide Version Information
By default, Apache happily tells everyone its version and OS in the response headers and error pages — a head start I don’t want to give attackers.
ServerTokens Prod
ServerSignature Off
ServerTokens Prod trims the Server header down to just Apache. ServerSignature Off removes the version footer on error pages.
Step 3: Disable Modules I Don’t Need
Every enabled module is more attack surface. I list what’s active:
apache2ctl -M # Debian/Ubuntu
httpd -M # RHEL/CentOS
Then I disable anything unused, especially mod_status, mod_info, mod_userdir, and mod_autoindex:
sudo a2dismod status
sudo a2dismod autoindex
sudo systemctl restart apache2
On RHEL-based systems I comment out the matching LoadModule lines instead.
Step 4: Restrict Directory Access
I set a strict default in the main config, then explicitly open up only what’s needed:
<Directory />
AllowOverride None
Require all denied
</Directory>
<Directory /var/www/html>
Options -Indexes -Includes
AllowOverride None
Require all granted
</Directory>
Step 5: Disable Directory Listing and Symlink Following
<Directory /var/www/html>
Options -Indexes -FollowSymLinks
</Directory>
Directory listing can expose files I never meant to publish; FollowSymLinks can be abused to escape the intended document root if something else is misconfigured.
Step 6: Protect Sensitive Files
<FilesMatch "^\.">
Require all denied
</FilesMatch>
<FilesMatch "(\.bak|\.old|\.orig|\.save|~)$">
Require all denied
</FilesMatch>
<DirectoryMatch "/\.git">
Require all denied
</DirectoryMatch>
Step 7: Enforce HTTPS
I install a TLS certificate (Let’s Encrypt covers most of my sites for free) and redirect all HTTP to HTTPS:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
I’ve got a dedicated post walking through full SSL/HTTPS setup if you want the details.
Step 8: Add Security Headers
sudo a2enmod headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set Content-Security-Policy "default-src 'self'"
I always test Content-Security-Policy carefully — an overly strict one has broken legitimate functionality on me more than once.
Step 9: Limit Request Sizes and Timeouts
LimitRequestBody 10485760
Timeout 30
KeepAliveTimeout 5
Step 10: Run Apache as a Restricted User
I never let Apache run as root for normal request handling:
# /etc/apache2/envvars (Debian) or httpd.conf (RHEL)
User www-data
Group www-data
I make sure web root files are owned appropriately and not writable by the Apache user unless a specific app genuinely needs it.
Step 11: Tune Logging
LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
Logs are what let me piece together what happened after the fact, and they feed tools like fail2ban.
Step 12: Add a Web Application Firewall
mod_security with the OWASP Core Rule Set gives me application-layer protection against SQL injection, XSS, and similar attacks:
sudo apt install libapache2-mod-security2
sudo a2enmod security2
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
I only flip SecRuleEngine On once I’ve tuned the rules enough to avoid blocking legitimate traffic.
Mistakes I See (and Have Made)
- Leaving
ServerTokens Full, broadcasting exact version numbers. - Forgetting to remove default sample content and admin pages.
- Running Apache with overly permissive file ownership.
- Enabling
mod_securityin blocking mode without testing first, which rejects legitimate traffic. - Not monitoring logs, missing early warning signs of a compromise.
Security Best Practices Checklist
- Keep Apache, modules, and the OS patched.
- Minimize enabled modules.
- Enforce HTTPS everywhere.
- Apply strict directory permissions, disable indexing.
- Use security headers and a WAF.
- Pair with
fail2banand a host firewall. - Regularly audit configuration with
apachectl -tand vulnerability scanners.
Performance Considerations
Security and performance aren’t opposites in my experience. Disabling unused modules cuts memory footprint per worker. Enabling mod_deflate and mod_expires improves performance without weakening security. Rate limiting protects both security and availability under load.
Troubleshooting
Site broken after adding Content-Security-Policy I temporarily switch to Content-Security-Policy-Report-Only to test without enforcing, then tighten gradually.
mod_security blocking legitimate requests I check /var/log/apache2/modsec_audit.log for the rule ID triggered and add a targeted exception rather than disabling the module wholesale.
Configuration won’t reload I always test before restarting:
sudo apachectl configtest
sudo systemctl restart apache2
FAQs
Is Apache inherently less secure than Nginx? Not in my view — both can be equally secure or insecure depending on configuration. Apache’s flexibility means more configuration surface, which needs more deliberate hardening.
Do I need mod_security for a small personal site? It adds value but isn’t mandatory for low-risk sites. I prioritize HTTPS, updates, and basic hardening first.
How often should I audit my Apache configuration? At minimum after every major update, and I like to revisit it quarterly alongside a broader security review.
Summary and Key Takeaways
- Securing Apache is layered: patch management, minimal attack surface, strict access controls, encryption, and monitoring.
- Small changes — hiding version tokens, disabling directory listing, adding security headers — meaningfully cut risk for very little effort.
- A WAF and
fail2bangive me active defense against automated attacks. - I revisit hardening regularly instead of treating it as a one-time setup task.
References
- Apache HTTP Server Security Tips: https://httpd.apache.org/docs/current/misc/security_tips.html
- OWASP ModSecurity Core Rule Set: https://owasp.org/www-project-modsecurity-core-rule-set/
- Mozilla SSL Configuration Generator: https://ssl-config.mozilla.org/
- fail2ban Documentation: https://www.fail2ban.org/