Key Takeaways
ServerTokens Prodimmediately: Hides Apache version from all HTTP responses — one-line change. Compare with Caddy Reverse Proxy for a modern security-first alternative.- ModSecurity + CRS: Free WAF that blocks OWASP Top 10 attacks. Install in detection mode first, then enforcement. Combine with Apache SSL/HTTPS for complete security.
Options -Indexes: Never allow directory browsing in production.- Security headers: HSTS, X-Frame-Options, X-Content-Type-Options — same headers as Nginx, same impact. See Let’s Encrypt SSL Guide to enable HTTPS with hardened headers.
Introduction
Direct Answer: How do I harden Apache on Ubuntu 24.04 in 2026?
Six steps: (1) ServerTokens Prod and ServerSignature Off in /etc/apache2/apache2.conf — hides version; (2) Options -Indexes FollowSymLinks in default virtual host — prevents directory listing; (3) sudo apt-get install libapache2-mod-security2 && sudo a2enmod security2 — install ModSecurity WAF; (4) Configure security headers with Header always set Strict-Transport-Security "max-age=31536000", Header always set X-Frame-Options SAMEORIGIN, and Header always set X-Content-Type-Options nosniff using mod_headers; (5) sudo apt-get install libapache2-mod-evasive && sudo a2enmod evasive — install rate limiting; (6) Block common attack paths with <Location "/.git"> Require all denied </Location>. Test with apache2ctl configtest after each change.
Part 1: Hide Server Information
# Edit main Apache config
sudo tee -a /etc/apache2/conf-available/security-tokens.conf << 'EOF'
# Hide Apache version and OS from HTTP headers and error pages
ServerTokens Prod # Returns "Server: Apache" instead of "Server: Apache/2.4.58 (Ubuntu)"
ServerSignature Off # Remove Apache version from error pages
# Disable TRACE method (potential information disclosure)
TraceEnable Off
EOF
sudo a2enconf security-tokens
sudo apache2ctl configtest && sudo systemctl reload apache2
# Verify
curl -sI http://localhost | grep "^Server:"
Expected output (before → after):
Before: Server: Apache/2.4.58 (Ubuntu)
After: Server: Apache
Part 2: Security Headers
sudo a2enmod headers
sudo tee /etc/apache2/conf-available/security-headers.conf << 'EOF'
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
Header always unset X-Powered-By # Remove PHP version if present
# CSP — start in report-only mode
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
</IfModule>
EOF
sudo a2enconf security-headers
sudo apache2ctl configtest && sudo systemctl reload apache2
# Verify headers
curl -sI https://yourdomain.com | grep -E "Strict|X-Frame|X-Content|Referrer|Permissions"
Part 3: Directory and File Protection
sudo tee /etc/apache2/conf-available/directory-security.conf << 'EOF'
# Disable directory browsing globally
<Directory /var/www/>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# Block access to sensitive files and directories
<DirectoryMatch "(\.git|\.svn|\.env|\.htaccess|\.htpasswd)">
Require all denied
</DirectoryMatch>
<FilesMatch "(\.git|\.env|composer\.(json|lock)|package\.(json|lock))$">
Require all denied
</FilesMatch>
# Prevent access to backup files
<FilesMatch "\.(bak|backup|old|orig|save|swp|tmp)$">
Require all denied
</FilesMatch>
EOF
sudo a2enconf directory-security
sudo apache2ctl configtest && sudo systemctl reload apache2
# Test: blocked paths should return 403
curl -sI http://localhost/.git/config | grep "^HTTP"
curl -sI http://localhost/.env | grep "^HTTP"
Expected output:
HTTP/1.1 403 Forbidden
HTTP/1.1 403 Forbidden
Part 4: ModSecurity WAF
# Install ModSecurity
sudo apt-get install -y libapache2-mod-security2
sudo a2enmod security2
# Use the recommended configuration
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
# Set to DetectionOnly first (log, don't block) — switch to On after tuning
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine DetectionOnly/' /etc/modsecurity/modsecurity.conf
# Install OWASP Core Rule Set
sudo apt-get install -y modsecurity-crs
# Enable CRS
sudo tee /etc/apache2/conf-available/modsecurity-crs.conf << 'EOF'
Include /etc/modsecurity/crs/crs-setup.conf
Include /etc/modsecurity/crs/rules/*.conf
EOF
sudo a2enconf modsecurity-crs
sudo apache2ctl configtest && sudo systemctl reload apache2
# Test: attempt SQL injection — should be logged
curl "http://localhost/?id=1' OR '1'='1"
sudo tail -5 /var/log/apache2/modsec_audit.log
Expected output (detection mode — logged but not blocked):
--[audit log entry]--
Message: Warning. detected SQLi using libinjection with fingerprint 's&1'
Severity: CRITICAL
File: /etc/modsecurity/crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
# Switch to enforcement mode after tuning false positives
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
sudo systemctl reload apache2
Part 5: Rate Limiting with mod_evasive
sudo apt-get install -y libapache2-mod-evasive
sudo a2enmod evasive
sudo tee /etc/apache2/mods-available/evasive.conf << 'EOF'
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 5 # Max requests to same page in DOSPageInterval
DOSSiteCount 50 # Max requests to entire site in DOSSiteInterval
DOSPageInterval 1 # Seconds
DOSSiteInterval 1 # Seconds
DOSBlockingPeriod 600 # Block for 600 seconds (10 minutes)
DOSLogDir /var/log/apache2/evasive/
# DOSEmailNotify [email protected]
</IfModule>
EOF
sudo mkdir -p /var/log/apache2/evasive
sudo chown www-data /var/log/apache2/evasive
sudo apache2ctl configtest && sudo systemctl reload apache2
Part 6: Security Audit
echo "=== APACHE SECURITY AUDIT ==="
echo "[ Server token ]"
curl -sI http://localhost | grep "^Server:" | sed 's/^/ /'
echo ""
echo "[ Security headers ]"
curl -sI https://yourdomain.com 2>/dev/null | \
grep -iE "strict-transport|x-frame|x-content" | \
awk '{print " ✓", $0}'
echo ""
echo "[ ModSecurity enabled ]"
apache2ctl -M 2>/dev/null | grep security2_module | \
awk '{print " ✓ ModSecurity active"}'
echo ""
echo "[ Directory listing disabled ]"
code=$(curl -sI -o/dev/null -w "%{http_code}" http://localhost/)
[ "$code" != "200" ] || echo " Check: request to / returned 200 — verify Options -Indexes"
curl -sI http://localhost/.git/config 2>/dev/null | \
grep "^HTTP" | grep -q "403" && echo " ✓ .git access blocked" || echo " ✗ .git access NOT blocked"
Conclusion
Apache is now hardened: version disclosure eliminated, security headers blocking browser-based attacks, directory browsing disabled, ModSecurity WAF blocking OWASP Top 10 attacks, and mod_evasive rate-limiting abusive IPs. Run the audit script after every configuration change.
See How to Enable HTTPS on Apache with Let’s Encrypt for SSL, and UFW Firewall Tutorial 2026 for the network perimeter.
People Also Ask
How is ModSecurity different from a firewall like UFW?
UFW operates at the network layer — it allows or blocks connections based on IP addresses and ports, before HTTP traffic is examined. ModSecurity operates at the application layer — it inspects the content of HTTP requests (URLs, headers, body) for attack patterns after the connection is established. UFW blocks port scanning and connection-level attacks; ModSecurity blocks SQL injection, XSS, and application-level attacks. You need both: UFW for network perimeter, ModSecurity for application-level protection.
Related Vucense Guides
- Apache SSL & Let’s Encrypt Setup 2026 — add HTTPS encryption to hardened Apache
- Docker Networking 2026 — isolate Apache services with network security
- Self-Hosted Web Infrastructure 2026 — deploy Apache in a complete web stack
Further Reading
Vucense Guides
- Apache Web Server Setup on Ubuntu 24.04 — base Apache installation
- How to Enable HTTPS on Apache with Let’s Encrypt — SSL configuration
- Caddy Reverse Proxy 2026 — modern security-first web server alternative
- Database Security Hardening 2026 — secure your backend databases
Official Documentation & Tools
- Apache HTTP Server Documentation — official Apache 2.4 reference
- ModSecurity Official Documentation — WAF rules and configuration
- OWASP ModSecurity Core Rule Set (CRS) — production WAF rules; current version 4.x
- OWASP Top 10 Web Application Security Risks — vulnerabilities to prevent
- Apache2ctl Manual — Apache control and testing tool
- Let’s Encrypt Certbot for Apache — automated HTTPS setup
Security Tools
- Burp Suite Community — web security testing
- OWASP ZAP — open-source security scanning tool
- nmap Security Scanner — port scanning and service detection
Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Apache 2.4.58, ModSecurity 2.9.7, OWASP CRS 3.3.5. Last verified: May 16, 2026.