Apache2 (HTTP Server) is one of the most versatile and widely-used web servers. Here are 9 powerful ways to leverage its capabilities with complete configuration examples.
1. Basic Virtual Host Configuration
Static Website Hosting
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
ServerAdmin webmaster@example.com
DocumentRoot /var/www/example.com/public_html
# Directory permissions
<Directory /var/www/example.com/public_html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
# Security headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
</Directory>
# Logging
ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
# Performance optimizations
EnableSendfile on
FileETag MTime Size
</VirtualHost>
Enable the site:
bash
sudo a2ensite example.com.conf sudo systemctl reload apache2
2. SSL/TLS Configuration with Let’s Encrypt
HTTPS with Auto-Redirect
# First, HTTP to HTTPS redirect
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
# Redirect all HTTP traffic to HTTPS
Redirect permanent / https://example.com/
# Also for Let's Encrypt ACME challenge
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory /var/www/letsencrypt>
Require all granted
</Directory>
</VirtualHost>
# HTTPS configuration
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com/public_html
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# HSTS (Optional but recommended)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Modern SSL configuration
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
# Performance
SSLCompression off
SSLUseStapling on
SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
</VirtualHost>
Install Let’s Encrypt:
bash
sudo apt install certbot python3-certbot-apache sudo certbot --apache -d example.com -d www.example.com
3. Reverse Proxy Configuration
Proxying to Application Servers
# Enable required modules
sudo a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests
# Configuration
<VirtualHost *:80>
ServerName api.example.com
# Proxy settings
ProxyPreserveHost On
ProxyRequests Off
# Backend servers
<Proxy balancer://appcluster>
BalancerMember http://192.168.1.10:3000
BalancerMember http://192.168.1.11:3000
BalancerMember http://192.168.1.12:3000
# Load balancing method
ProxySet lbmethod=bytraffic
# Health check
ProxySet failonstatus=500,503
</Proxy>
# Route all requests to the cluster
ProxyPass / balancer://appcluster/
ProxyPassReverse / balancer://appcluster/
# WebSocket support
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule /(.*) ws://backend-server:3000/$1 [P,L]
# Headers
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
</VirtualHost>
4. Load Balancing with Apache
Advanced Load Balancer Setup
<Proxy balancer://myapp>
# Different balancing algorithms:
# byrequests = round robin (default)
# bytraffic = based on traffic
# bybusyness = fewest pending requests
# heartbeat = with health checks
BalancerMember http://app1.example.com:8080 loadfactor=10 route=1
BalancerMember http://app2.example.com:8080 loadfactor=20 route=2
BalancerMember http://app3.example.com:8080 loadfactor=30 route=3 status=+H
# +H = hot standby (only used if others fail)
# Session stickiness
ProxySet stickysession=JSESSIONID
# Timeouts
ProxySet timeout=300
ProxySet keepalive=On
# Health check
<Location /balancer-manager>
SetHandler balancer-manager
Require host localhost
</Location>
</Proxy>
<VirtualHost *:80>
ServerName app.example.com
ProxyPass / balancer://myapp/
ProxyPassReverse / balancer://myapp/
# Error handling
ProxyErrorOverride On
ErrorDocument 503 /maintenance.html
</VirtualHost>
5. URL Rewriting and Redirection
Comprehensive Rewrite Rules
<VirtualHost *:80>
ServerName example.com
# Enable rewrite engine
RewriteEngine On
LogLevel alert rewrite:trace6
# 1. Force HTTPS (if not already)
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# 2. Force www or non-www
RewriteCond %{HTTP_HOST} ^example\.com [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [L,R=301]
# 3. Remove trailing slashes
RewriteRule ^(.*)/$ /$1 [L,R=301]
# 4. Pretty URLs (hide .php extensions)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ $1.php [L,QSA]
# 5. Redirect old pages
RedirectMatch 301 ^/old-page/(.*)$ /new-page/$1
Redirect 301 /products/legacy.html /products/new-version.html
# 6. Block access to certain files
RewriteRule ^(.*)\.(sql|log|ini|conf)$ - [F,L,NC]
# 7. Maintenance mode
# RewriteCond %{REMOTE_ADDR} !^123\.456\.789\.000
# RewriteCond %{REQUEST_URI} !^/maintenance\.html$
# RewriteRule ^(.*)$ /maintenance.html [L]
# 8. Mobile detection and redirection
RewriteCond %{HTTP_USER_AGENT} "android|blackberry|iphone|ipod" [NC]
RewriteCond %{HTTP_HOST} ^example\.com$
RewriteRule ^$ http://m.example.com/ [L,R=302]
# 9. API versioning in URL
RewriteRule ^api/v1/(.*)$ /api/v1_handler.php?endpoint=$1 [QSA,L]
RewriteRule ^api/v2/(.*)$ /api/v2_handler.php?endpoint=$1 [QSA,L]
</VirtualHost>
6. Security Hardening
Complete Security Configuration
# /etc/apache2/conf-available/security.conf
# Disable server signature
ServerSignature Off
ServerTokens Prod
# Security headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com;"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Prevent MIME sniffing
<IfModule mod_headers.c>
Header set X-Content-Type-Options nosniff
</IfModule>
# Disable directory listing
Options -Indexes
# Protect sensitive files
<FilesMatch "\.(htaccess|htpasswd|ini|log|sh|sql)$">
Require all denied
</FilesMatch>
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
# Limit request methods
<LimitExcept GET POST>
Deny from all
</LimitExcept>
# Limit request size
LimitRequestBody 10485760 # 10MB
# Rate limiting
<IfModule mod_ratelimit.c>
<Location "/">
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 100
</Location>
</IfModule>
# Prevent clickjacking
<IfModule mod_headers.c>
Header always append X-Frame-Options SAMEORIGIN
</IfModule>
Virtual Host Security:
<VirtualHost *:80>
ServerName secure.example.com
# Disable TRACE and TRACK methods
TraceEnable off
# File access restrictions
<Directory /var/www/secure>
# Authentication
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
# IP restriction
Require ip 192.168.1.0/24
Require ip 10.0.0.0/8
# Disable execution
php_admin_flag engine off
RemoveHandler .php .phtml .php3
RemoveType .php .phtml .php3
</Directory>
# Protect wp-admin
<Location /wp-admin>
AuthType Basic
AuthName "WordPress Admin"
AuthUserFile /etc/apache2/.htpasswd-wp
Require valid-user
</Location>
</VirtualHost>
7. Performance Optimization
Tuning for High Traffic
# /etc/apache2/mods-available/mpm_prefork.conf
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150
MaxConnectionsPerChild 10000
</IfModule>
# /etc/apache2/mods-available/mpm_worker.conf (for better performance)
<IfModule mpm_worker_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 10000
</IfModule>
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
AddOutputFilterByType DEFLATE application/xml application/xhtml+xml application/rss+xml
AddOutputFilterByType DEFLATE application/javascript application/x-javascript
AddOutputFilterByType DEFLATE application/json
# Exclude problematic browsers
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
</IfModule>
# Enable caching
<IfModule mod_expires.c>
ExpiresActive On
# Default cache
ExpiresDefault "access plus 1 month"
# Specific types
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
# Enable keep-alive
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15
8. Multiple Applications on Single Server
Hosting Multiple Sites/Apps
# Main configuration for port-based hosting
Listen 80
Listen 8080
Listen 8443
# Site 1: Primary website
<VirtualHost *:80>
ServerName primary.example.com
DocumentRoot /var/www/primary
# PHP-FPM configuration
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>
# Site 2: Development/staging on different port
<VirtualHost *:8080>
ServerName staging.example.com
DocumentRoot /var/www/staging
# Different PHP version
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
</FilesMatch>
# Development only settings
php_flag display_errors on
php_value error_reporting E_ALL
</VirtualHost>
# Site 3: Node.js app via reverse proxy
<VirtualHost *:8443>
ServerName app.example.com
SSLProxyEngine On
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:3000/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:3000/$1 [P,L]
</VirtualHost>
# Site 4: Python Django application
<VirtualHost *:8000>
ServerName api.example.com
Alias /static /var/www/django/static
Alias /media /var/www/django/media
<Directory /var/www/django/static>
Require all granted
</Directory>
<Directory /var/www/django/media>
Require all granted
</Directory>
WSGIDaemonProcess django_app python-path=/var/www/django
WSGIProcessGroup django_app
WSGIScriptAlias / /var/www/django/project/wsgi.py
<Directory /var/www/django/project>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
</VirtualHost>
9. Advanced Features and Modules
Content Negotiation and Multilingual Sites
# Enable required modules
sudo a2enmod negotiation
sudo a2enmod headers
sudo a2enmod expires
# Configuration for multilingual site
<VirtualHost *:80>
ServerName multilingual.example.com
# Default language
DefaultLanguage en
# Content negotiation
Options +MultiViews
# Language priority
LanguagePriority en fr es de it
ForceLanguagePriority Prefer Fallback
# Add language to URLs
RewriteEngine On
RewriteBase /
# Detect language from browser
RewriteCond %{HTTP:Accept-Language} ^fr [NC]
RewriteRule ^$ /fr/ [L,R=301]
RewriteCond %{HTTP:Accept-Language} ^es [NC]
RewriteRule ^$ /es/ [L,R=301]
# Serve different content based on language
<Files "index">
SetHandler type-map
</Files>
# Language-specific content
<Directory /var/www/multilingual>
Options Indexes MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
WebDAV Configuration
# Enable WebDAV modules
sudo a2enmod dav
sudo a2enmod dav_fs
sudo a2enmod dav_lock
# WebDAV server configuration
<VirtualHost *:80>
ServerName dav.example.com
Alias /webdav /var/www/webdav
<Directory /var/www/webdav>
DAV On
# Authentication
AuthType Basic
AuthName "WebDAV"
AuthUserFile /etc/apache2/webdav.passwd
Require valid-user
# Permissions
Options Indexes
Order allow,deny
Allow from all
# Limit methods if needed
<LimitExcept GET POST PROPFIND OPTIONS MKCOL PUT DELETE>
Require valid-user
</LimitExcept>
</Directory>
# Enable locking
DavLockDB /var/lib/dav/lockdb
</VirtualHost>
Log Analysis and Monitoring
# Custom log format for better analysis
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %T %D" detailed
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy
# Virtual host with detailed logging
<VirtualHost *:80>
ServerName logs.example.com
# Different log files
ErrorLog "|/usr/bin/rotatelogs /var/log/apache2/error-%Y%m%d.log 86400"
CustomLog "|/usr/bin/rotatelogs /var/log/apache2/access-%Y%m%d.log 86400" detailed
# Log specific information
CustomLog /var/log/apache2/ssl_request.log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
# Conditional logging
SetEnvIf Request_URI "\.(gif|jpg|png|css|js)$" dontlog
CustomLog /var/log/apache2/skip-images.log common env=!dontlog
</VirtualHost>
Bonus: Apache as a Development Server
Local Development Environment
# Local development server with automatic virtual hosts
<VirtualHost *:80>
ServerName localhost
ServerAlias *.localhost
VirtualDocumentRoot /var/www/%-2+
# Enable .htaccess for development
<Directory /var/www/*>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
# Development settings
php_flag display_errors on
php_value error_reporting E_ALL
php_flag log_errors on
php_value error_log /var/log/php_errors.log
</Directory>
# Enable user directories
UserDir public_html
<Directory /home/*/public_html>
AllowOverride All
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
Require method GET POST OPTIONS
</Directory>
</VirtualHost>
# PHP development configuration
<IfModule mod_php.c>
php_value max_execution_time 300
php_value memory_limit 512M
php_value upload_max_filesize 100M
php_value post_max_size 105M
php_value session.gc_maxlifetime 1440
</IfModule>