Configure Apache HTTP Server 2.4 with mod_proxy_balancer for high availability load balancing, SSL termination using Let's Encrypt certificates, and automated backend health monitoring with mod_proxy_hcheck for production environments.
Prerequisites
- Root access to server
- Domain name pointing to server
- Multiple backend servers running web applications
- Basic Apache administration knowledge
What this solves
Apache load balancing with SSL termination centralizes certificate management while distributing traffic across multiple backend servers. This setup provides high availability, automatic failover, and simplified SSL certificate handling for web applications that need to scale beyond a single server.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you get the latest Apache version with security patches.
sudo apt update && sudo apt upgrade -y
Install Apache and required modules
Install Apache HTTP Server along with the proxy and SSL modules needed for load balancing and SSL termination.
sudo apt install -y apache2 certbot python3-certbot-apache
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod proxy_hcheck
Configure the load balancer virtual host
Create a virtual host configuration that defines your backend servers and load balancing rules. This example uses two backend servers with health checks.
ServerName example.com
ServerAlias www.example.com
# Redirect all HTTP traffic to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
ServerName example.com
ServerAlias www.example.com
# SSL Configuration (certificates will be added by certbot)
SSLEngine on
# Security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
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"
# Define backend servers with health checks
ProxyPreserveHost On
ProxyRequests Off
# Health check configuration
ProxyHCMethod GET
ProxyHCUri /health
ProxyHCInterval 10
# Define the balancer cluster
ProxyPass /balancer-manager !
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
# Backend server definitions
# Backend server 1
BalancerMember http://203.0.113.10:8080 route=app1 hcmethod=GET hcuri=/health
# Backend server 2
BalancerMember http://203.0.113.11:8080 route=app2 hcmethod=GET hcuri=/health
# Load balancing method
ProxySet lbmethod=byrequests
# Health check settings
ProxySet hcmethod=GET
ProxySet hcuri=/health
ProxySet hcinterval=10
ProxySet retry=5
# Balancer manager (restrict access in production)
SetHandler balancer-manager
Require ip 127.0.0.1
Require ip 203.0.113.0/24
# Logging
ErrorLog ${APACHE_LOG_DIR}/loadbalancer_error.log
CustomLog ${APACHE_LOG_DIR}/loadbalancer_access.log combined
Enable required Apache modules and site
Enable the necessary Apache modules and activate your load balancer configuration.
sudo a2enmod rewrite
sudo a2ensite loadbalancer.conf
sudo a2dissite 000-default.conf
sudo systemctl enable --now apache2
Configure firewall rules
Open the necessary ports for HTTP, HTTPS, and the balancer manager interface.
sudo ufw allow 'Apache Full'
sudo ufw enable
Obtain SSL certificates
Use Certbot to automatically obtain and configure SSL certificates from Let's Encrypt.
sudo certbot --apache -d example.com -d www.example.com
Configure advanced load balancing options
Create additional configuration for session stickiness and advanced failover behavior.
# Advanced load balancer settings
Enable mod_proxy_express for better performance
LoadModule proxy_express_module modules/mod_proxy_express.so
Connection pooling settings
ProxyTimeout 30
ProxyBadHeader Ignore
For session stickiness (if your app requires it)
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/; HttpOnly; Secure"
Health check configuration
# Global health check settings
ProxyHCTPsize 16
Status monitoring
SetHandler server-status
Require ip 127.0.0.1
Require ip 203.0.113.0/24
SetHandler server-info
Require ip 127.0.0.1
Require ip 203.0.113.0/24
Enable advanced configuration
Activate the advanced balancer settings and restart Apache to apply all changes.
sudo a2enconf balancer-advanced
sudo systemctl restart apache2
Set up certificate auto-renewal
Configure automatic renewal of Let's Encrypt certificates to prevent expiration.
sudo systemctl enable --now certbot.timer
sudo systemctl list-timers certbot
Configure backend server health endpoints
Create health check endpoints
Your backend servers need to respond to health check requests. Here's an example for different server types.
// Simple health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
@app.route('/health')
def health_check():
return {
'status': 'healthy',
'timestamp': datetime.now().isoformat()
}, 200
Verify your setup
Test Apache configuration
Verify the Apache configuration syntax and check service status.
sudo apache2ctl configtest
sudo systemctl status apache2
Check SSL certificate status
Verify SSL certificates are properly installed and configured.
sudo certbot certificates
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Test load balancing
Access the balancer manager interface and verify backend server status.
curl -I https://example.com/balancer-manager
curl -k https://example.com/
curl -k https://example.com/ | grep -i server
Verify health checks
Check that health checks are working and backend servers are being monitored.
sudo tail -f /var/log/apache2/loadbalancer_access.log
sudo grep "health check" /var/log/apache2/error.log
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| 502 Bad Gateway errors | Backend servers unreachable | Check backend server status and firewall rules. Verify ProxyPass URLs are correct |
| Health checks failing | Backend health endpoint missing | Implement /health endpoint on backend servers or change hcuri setting |
| SSL certificate errors | Domain not pointing to load balancer | Update DNS records to point to load balancer IP before running certbot |
| Session stickiness not working | Backend servers not setting route cookies | Enable cookie-based routing or use IP-based stickiness with Header add Set-Cookie |
| Balancer manager access denied | IP restrictions too strict | Update Require ip directives to include your management network |
| Performance issues | Default connection limits | Tune ProxyTimeout, KeepAlive settings and backend connection pools |
Advanced load balancing strategies
Configure weighted round-robin
Assign different weights to backend servers based on their capacity.
# High-capacity server gets more traffic
BalancerMember http://203.0.113.10:8080 route=app1 loadfactor=3
# Standard capacity server
BalancerMember http://203.0.113.11:8080 route=app2 loadfactor=1
# Backup server (only used when others fail)
BalancerMember http://203.0.113.12:8080 route=app3 status=+H loadfactor=1
ProxySet lbmethod=byrequests
Configure response time-based balancing
Use response time to determine the best backend server for new requests.
# Enable mod_proxy_express for response time tracking
a2enmod proxy_express
BalancerMember http://203.0.113.10:8080 route=app1
BalancerMember http://203.0.113.11:8080 route=app2
# Use response time-based load balancing
ProxySet lbmethod=heartbeat
# Fallback to requests if heartbeat unavailable
ProxySet lbmethod=byrequests
Monitoring and maintenance
Monitor your Apache load balancer with tools like Apache log analysis with GoAccess and ELK stack for real-time traffic insights. Set up Prometheus alerting with AlertManager to monitor backend server health and response times.
Regular maintenance includes monitoring certificate expiration, reviewing load balancing metrics through the balancer-manager interface, and analyzing access logs for traffic patterns and performance optimization opportunities.
Next steps
- Configure Apache security headers and Content Security Policy
- Implement Apache web application firewall with ModSecurity 3
- Configure Apache rate limiting and DDoS protection
- Setup Apache log monitoring with Elasticsearch and Kibana
- Configure Apache performance tuning for high traffic websites
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Global variables
DOMAIN=""
BACKEND1=""
BACKEND2=""
PKG_MGR=""
PKG_INSTALL=""
APACHE_SERVICE=""
APACHE_CONF_DIR=""
APACHE_LOG_DIR=""
SITES_DIR=""
# Usage function
usage() {
echo "Usage: $0 <domain> <backend1_ip:port> <backend2_ip:port>"
echo "Example: $0 example.com 192.168.1.10:8080 192.168.1.11:8080"
exit 1
}
# Cleanup function
cleanup() {
echo -e "${RED}[ERROR] Script failed. Cleaning up...${NC}"
if [[ -f "${SITES_DIR}/lb-${DOMAIN}.conf" ]]; then
rm -f "${SITES_DIR}/lb-${DOMAIN}.conf"
fi
if [[ "$PKG_MGR" == "apt" ]] && [[ -L "/etc/apache2/sites-enabled/lb-${DOMAIN}.conf" ]]; then
a2dissite "lb-${DOMAIN}.conf" >/dev/null 2>&1 || true
fi
}
# Set error trap
trap cleanup ERR
# Check arguments
if [[ $# -ne 3 ]]; then
usage
fi
DOMAIN="$1"
BACKEND1="$2"
BACKEND2="$3"
# Validate domain format
if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.[a-zA-Z]{2,}$ ]]; then
echo -e "${RED}[ERROR] Invalid domain format${NC}"
exit 1
fi
# Validate backend format
for backend in "$BACKEND1" "$BACKEND2"; do
if [[ ! "$backend" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+$ ]]; then
echo -e "${RED}[ERROR] Invalid backend format: $backend${NC}"
exit 1
fi
done
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}[ERROR] This script must be run as root${NC}"
exit 1
fi
echo -e "${GREEN}Apache Load Balancer with SSL Termination Setup${NC}"
# Detect distribution
echo "[1/8] Detecting distribution..."
if [[ -f /etc/os-release ]]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
APACHE_SERVICE="apache2"
APACHE_CONF_DIR="/etc/apache2"
APACHE_LOG_DIR="/var/log/apache2"
SITES_DIR="/etc/apache2/sites-available"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
APACHE_SERVICE="httpd"
APACHE_CONF_DIR="/etc/httpd"
APACHE_LOG_DIR="/var/log/httpd"
SITES_DIR="/etc/httpd/conf.d"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
APACHE_SERVICE="httpd"
APACHE_CONF_DIR="/etc/httpd"
APACHE_LOG_DIR="/var/log/httpd"
SITES_DIR="/etc/httpd/conf.d"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
APACHE_SERVICE="httpd"
APACHE_CONF_DIR="/etc/httpd"
APACHE_LOG_DIR="/var/log/httpd"
SITES_DIR="/etc/httpd/conf.d"
;;
*)
echo -e "${RED}[ERROR] Unsupported distribution: $ID${NC}"
exit 1
;;
esac
echo -e "${GREEN}Detected: $PRETTY_NAME${NC}"
else
echo -e "${RED}[ERROR] Cannot detect distribution${NC}"
exit 1
fi
# Update system packages
echo "[2/8] Updating system packages..."
if [[ "$PKG_MGR" == "apt" ]]; then
apt update && apt upgrade -y
else
$PKG_INSTALL update -y
fi
# Install Apache and required packages
echo "[3/8] Installing Apache and required modules..."
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL apache2 certbot python3-certbot-apache
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_balancer
a2enmod lbmethod_byrequests
a2enmod ssl
a2enmod headers
a2enmod proxy_hcheck
a2enmod rewrite
else
$PKG_INSTALL httpd mod_ssl certbot python3-certbot-apache
fi
# Enable and start Apache
echo "[4/8] Starting Apache service..."
systemctl enable --now "$APACHE_SERVICE"
# Configure firewall
echo "[5/8] Configuring firewall..."
if command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
elif command -v ufw >/dev/null 2>&1; then
ufw allow 80/tcp
ufw allow 443/tcp
fi
# Create virtual host configuration
echo "[6/8] Creating virtual host configuration..."
CONF_FILE="${SITES_DIR}/lb-${DOMAIN}.conf"
cat > "$CONF_FILE" << EOF
<VirtualHost *:80>
ServerName $DOMAIN
ServerAlias www.$DOMAIN
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
<VirtualHost *:443>
ServerName $DOMAIN
ServerAlias www.$DOMAIN
SSLEngine on
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
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"
ProxyPreserveHost On
ProxyRequests Off
ProxyHCMethod GET
ProxyHCUri /health
ProxyHCInterval 10
ProxyPass /balancer-manager !
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
<Proxy balancer://mycluster>
BalancerMember http://$BACKEND1 route=app1 hcmethod=GET hcuri=/health
BalancerMember http://$BACKEND2 route=app2 hcmethod=GET hcuri=/health
ProxySet lbmethod=byrequests
ProxySet hcmethod=GET
ProxySet hcuri=/health
ProxySet hcinterval=10
ProxySet retry=5
</Proxy>
<Location "/balancer-manager">
SetHandler balancer-manager
Require ip 127.0.0.1
Require local
</Location>
ErrorLog ${APACHE_LOG_DIR}/loadbalancer_error.log
CustomLog ${APACHE_LOG_DIR}/loadbalancer_access.log combined
</VirtualHost>
EOF
# Set proper permissions
chmod 644 "$CONF_FILE"
chown root:root "$CONF_FILE"
# Enable site (for Debian/Ubuntu)
if [[ "$PKG_MGR" == "apt" ]]; then
a2ensite "lb-${DOMAIN}.conf"
fi
# Restart Apache to load configuration
echo "[7/8] Restarting Apache..."
systemctl restart "$APACHE_SERVICE"
# Verify configuration
echo "[8/8] Verifying configuration..."
if systemctl is-active --quiet "$APACHE_SERVICE"; then
echo -e "${GREEN}✓ Apache is running${NC}"
else
echo -e "${RED}✗ Apache failed to start${NC}"
exit 1
fi
if "$APACHE_SERVICE" -t 2>/dev/null || httpd -t 2>/dev/null; then
echo -e "${GREEN}✓ Apache configuration syntax is valid${NC}"
else
echo -e "${RED}✗ Apache configuration has syntax errors${NC}"
exit 1
fi
echo -e "${GREEN}Setup completed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Point your domain DNS to this server's IP address"
echo "2. Run: certbot --apache -d $DOMAIN -d www.$DOMAIN"
echo "3. Access balancer manager at: https://$DOMAIN/balancer-manager"
echo "4. Ensure backend servers respond to /health endpoint"
Review the script before running. Execute with: bash install.sh