×

9 Practical Ways to Use Apache2: Complete Configuration Guide

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>