Web Server Security¶
Secure configuration of web servers: Apache and Nginx virtual hosts, TLS/SSL setup with Let's Encrypt, reverse proxy patterns, load balancing, and security headers.
Key Facts¶
- Nginx is preferred as reverse proxy for security (minimal attack surface, event-driven)
- Let's Encrypt provides free TLS certificates with automatic renewal
- Load balancing algorithms: round-robin (default), least_conn, ip_hash, random
- Security headers (HSTS, X-Frame-Options, CSP) should be set at the proxy level
- Always disable server version disclosure in production
Apache Configuration¶
# /etc/apache2/sites-available/example.conf
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
a2ensite example.conf
a2enmod ssl rewrite
systemctl reload apache2
Nginx Configuration¶
server {
listen 80;
server_name example.com;
root /var/www/example;
location / {
try_files $uri $uri/ =404;
}
}
Reverse Proxy¶
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Load Balancing¶
upstream backend {
server 10.0.0.1:8080 weight=3;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
TLS with Let's Encrypt¶
apt install certbot python3-certbot-nginx
certbot --nginx -d example.com -d www.example.com
# Auto-renewal via systemd timer
certbot renew
Security Headers¶
# Add to server or location block
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
# Hide server version
server_tokens off;
Gotchas¶
- TLS termination at load balancer means backend traffic is unencrypted unless re-encrypted
proxy_set_header X-Forwarded-Foris essential - without it, backend sees proxy IP, not client IP- Let's Encrypt certificates expire in 90 days - ensure auto-renewal is working
add_headerin a nestedlocationblock overrides all headers from parent blocks in Nginx- Misconfigured reverse proxy can become an open proxy for attackers
See Also¶
- cryptography and pki - TLS protocol details
- firewall and ids ips - network-level protection
- web application security fundamentals - application-level attacks
- linux system hardening - OS-level hardening for web servers