Configure Apache reverse proxy with caching for microservices

Intermediate 25 min Jun 11, 2026 31 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up Apache HTTP Server as a reverse proxy with intelligent caching for microservices architectures. This tutorial covers mod_proxy, mod_cache configuration, cache policies, and monitoring for high-performance service delivery.

Prerequisites

  • Root or sudo access
  • Basic understanding of HTTP and web services
  • Microservices running on different ports

What this solves

A reverse proxy with caching reduces load on backend microservices by serving cached responses for repeated requests. Apache's mod_proxy and mod_cache modules provide flexible routing, load balancing, and intelligent caching that can dramatically improve response times and reduce resource usage across your service architecture.

Step-by-step configuration

Update system packages

Start by updating your package manager to ensure you have the latest security patches and package versions.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install Apache HTTP Server

Install Apache web server which includes the proxy and caching modules we need.

sudo apt install -y apache2 apache2-utils
sudo dnf install -y httpd httpd-tools

Enable required Apache modules

Enable the proxy, cache, and related modules needed for reverse proxy functionality with caching.

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
sudo a2enmod cache
sudo a2enmod cache_disk
sudo a2enmod headers
sudo a2enmod rewrite
# Modules are loaded via LoadModule directives in configuration

We'll configure these in the next steps

Create cache directory

Create a dedicated directory for Apache to store cached content with proper permissions.

sudo mkdir -p /var/cache/apache2/mod_cache_disk
sudo chown www-data:www-data /var/cache/apache2/mod_cache_disk
sudo chmod 755 /var/cache/apache2/mod_cache_disk
Never use chmod 777. It gives every user on the system full access to your files. The web server only needs write access to the cache directory, which we provide with proper ownership.

Configure main Apache settings

Configure the main Apache configuration with performance optimizations for proxy and caching workloads.

# Add these settings at the end of the file

Performance tuning for proxy workloads

MaxRequestWorkers 400 ThreadsPerChild 25 StartServers 3 MinSpareThreads 75 MaxSpareThreads 250

Proxy timeout settings

ProxyTimeout 300 ProxyPreserveHost On

Security headers

Header always set X-Frame-Options DENY Header always set X-Content-Type-Options nosniff Header always set Referrer-Policy strict-origin-when-cross-origin
# Add these settings at the end of the file

Load required modules

LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_balancer_module modules/mod_proxy_balancer.so LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so LoadModule cache_module modules/mod_cache.so LoadModule cache_disk_module modules/mod_cache_disk.so LoadModule headers_module modules/mod_headers.so LoadModule rewrite_module modules/mod_rewrite.so

Performance tuning for proxy workloads

MaxRequestWorkers 400 ThreadsPerChild 25 StartServers 3 MinSpareThreads 75 MaxSpareThreads 250

Proxy timeout settings

ProxyTimeout 300 ProxyPreserveHost On

Security headers

Header always set X-Frame-Options DENY Header always set X-Content-Type-Options nosniff Header always set Referrer-Policy strict-origin-when-cross-origin

Create virtual host configuration

Create a virtual host that configures reverse proxy with caching for your microservices.


    ServerName api.example.com
    DocumentRoot /var/www/html

    # Enable caching
    CacheEnable disk /
    CacheRoot /var/cache/apache2/mod_cache_disk
    CacheDirLevels 2
    CacheDirLength 1
    CacheDefaultExpire 3600
    CacheMaxExpire 86400
    CacheMinExpire 60
    CacheDetailHeader on
    CacheHeader on

    # Cache policies for different content types
    
        CacheDefaultExpire 604800
        ExpiresActive On
        ExpiresDefault "access plus 1 week"
        Header set Cache-Control "public, max-age=604800"
    

    
        CacheDefaultExpire 300
        Header set Cache-Control "public, max-age=300"
        # Don't cache POST/PUT/DELETE requests
        SetEnvIf Request_Method "^(POST|PUT|DELETE)$" no-cache
        CacheDisable env=no-cache
    

    # Microservice routing
    # User service
    ProxyPass /api/users http://localhost:3001/
    ProxyPassReverse /api/users http://localhost:3001/

    # Product service with load balancing
    ProxyPass /api/products balancer://products-cluster/
    ProxyPassReverse /api/products balancer://products-cluster/

    # Order service
    ProxyPass /api/orders http://localhost:3003/
    ProxyPassReverse /api/orders http://localhost:3003/

    # Health checks bypass cache
    
        CacheDisable on
        ProxyPass http://localhost:3000/health
        ProxyPassReverse http://localhost:3000/health
    

    # Configure balancer cluster for products
    
        BalancerMember http://localhost:3002 route=1
        BalancerMember http://localhost:3022 route=2
        ProxySet lbmethod=byrequests
        ProxySet hcmethod=GET
        ProxySet hcuri=/health
    

    # Logging
    LogLevel info
    ErrorLog ${APACHE_LOG_DIR}/microservices_error.log
    CustomLog ${APACHE_LOG_DIR}/microservices_access.log combined
    CustomLog ${APACHE_LOG_DIR}/microservices_cache.log \
        "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %{cache-status}e"

    # Security settings
    Header always set X-Proxy-Cache %{cache-status}e
    Header unset Server
    ServerTokens Prod

    ServerName api.example.com
    DocumentRoot /var/www/html

    # Enable caching
    CacheEnable disk /
    CacheRoot /var/cache/httpd/mod_cache_disk
    CacheDirLevels 2
    CacheDirLength 1
    CacheDefaultExpire 3600
    CacheMaxExpire 86400
    CacheMinExpire 60
    CacheDetailHeader on
    CacheHeader on

    # Cache policies for different content types
    
        CacheDefaultExpire 604800
        ExpiresActive On
        ExpiresDefault "access plus 1 week"
        Header set Cache-Control "public, max-age=604800"
    

    
        CacheDefaultExpire 300
        Header set Cache-Control "public, max-age=300"
        # Don't cache POST/PUT/DELETE requests
        SetEnvIf Request_Method "^(POST|PUT|DELETE)$" no-cache
        CacheDisable env=no-cache
    

    # Microservice routing
    # User service
    ProxyPass /api/users http://localhost:3001/
    ProxyPassReverse /api/users http://localhost:3001/

    # Product service with load balancing
    ProxyPass /api/products balancer://products-cluster/
    ProxyPassReverse /api/products balancer://products-cluster/

    # Order service
    ProxyPass /api/orders http://localhost:3003/
    ProxyPassReverse /api/orders http://localhost:3003/

    # Health checks bypass cache
    
        CacheDisable on
        ProxyPass http://localhost:3000/health
        ProxyPassReverse http://localhost:3000/health
    

    # Configure balancer cluster for products
    
        BalancerMember http://localhost:3002 route=1
        BalancerMember http://localhost:3022 route=2
        ProxySet lbmethod=byrequests
        ProxySet hcmethod=GET
        ProxySet hcuri=/health
    

    # Logging
    LogLevel info
    ErrorLog logs/microservices_error.log
    CustomLog logs/microservices_access.log combined
    CustomLog logs/microservices_cache.log \
        "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %{cache-status}e"

    # Security settings
    Header always set X-Proxy-Cache %{cache-status}e
    Header unset Server
    ServerTokens Prod

Create cache directory for Red Hat systems

On Red Hat-based systems, create the cache directory with SELinux context.

# Cache directory already created in previous step
sudo mkdir -p /var/cache/httpd/mod_cache_disk
sudo chown apache:apache /var/cache/httpd/mod_cache_disk
sudo chmod 755 /var/cache/httpd/mod_cache_disk
sudo setsebool -P httpd_can_network_connect 1
sudo semanage fcontext -a -t httpd_cache_t "/var/cache/httpd/mod_cache_disk(/.*)?" || true
sudo restorecon -R /var/cache/httpd/mod_cache_disk

Enable the virtual host

Enable the new virtual host configuration and disable the default site.

sudo a2ensite microservices-proxy.conf
sudo a2dissite 000-default.conf
# Configuration is automatically loaded from conf.d directory

Configure cache cleanup

Set up automatic cache cleanup to prevent disk space issues. Create a systemd timer to clean old cache files.

[Unit]
Description=Apache Cache Cleanup
After=network.target

[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/bin/find /var/cache/apache2/mod_cache_disk -type f -mtime +7 -delete
ExecStart=/usr/bin/find /var/cache/apache2/mod_cache_disk -type d -empty -delete
[Unit]
Description=Run Apache Cache Cleanup Daily
Requires=apache-cache-cleanup.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now apache-cache-cleanup.timer

Test configuration and start Apache

Test the Apache configuration for syntax errors and start the service.

sudo apache2ctl configtest
sudo systemctl enable --now apache2
sudo systemctl status apache2
sudo httpd -t
sudo systemctl enable --now httpd
sudo systemctl status httpd

Configure firewall rules

Open HTTP and HTTPS ports in the firewall for web traffic.

sudo ufw allow 'Apache Full'
sudo ufw reload
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Configure cache policies and optimization

Advanced cache configuration

Fine-tune cache behavior with advanced policies for different types of content and API responses.

# Cache size limits and memory settings
CacheMaxFileSize 10000000
CacheMinFileSize 1
CacheReadSize 102400
CacheReadTime 3600

Ignore certain headers that prevent caching

CacheIgnoreHeaders Set-Cookie CacheIgnoreQueryString Off CacheKeyBaseURL http://api.example.com

Cache based on Accept-Encoding for compressed content

CacheStorePrivate On CacheStoreNoStore Off

Advanced cache policies

CacheDefaultExpire 2592000 Header set Cache-Control "public, max-age=2592000, immutable" ExpiresActive On ExpiresDefault "access plus 30 days" CacheDefaultExpire 900 Header set Cache-Control "private, max-age=900" CacheStorePrivate On CacheDefaultExpire 1800 Header set Cache-Control "public, max-age=1800" # Cache different responses for different query parameters CacheIgnoreQueryString Off

Don't cache authentication endpoints

CacheDisable on Header set Cache-Control "no-store, no-cache, must-revalidate" Header set Pragma "no-cache"
# Cache size limits and memory settings
CacheMaxFileSize 10000000
CacheMinFileSize 1
CacheReadSize 102400
CacheReadTime 3600

Ignore certain headers that prevent caching

CacheIgnoreHeaders Set-Cookie CacheIgnoreQueryString Off CacheKeyBaseURL http://api.example.com

Cache based on Accept-Encoding for compressed content

CacheStorePrivate On CacheStoreNoStore Off

Advanced cache policies

CacheDefaultExpire 2592000 Header set Cache-Control "public, max-age=2592000, immutable" ExpiresActive On ExpiresDefault "access plus 30 days" CacheDefaultExpire 900 Header set Cache-Control "private, max-age=900" CacheStorePrivate On CacheDefaultExpire 1800 Header set Cache-Control "public, max-age=1800" # Cache different responses for different query parameters CacheIgnoreQueryString Off

Don't cache authentication endpoints

CacheDisable on Header set Cache-Control "no-store, no-cache, must-revalidate" Header set Pragma "no-cache"

Enable cache optimization

Enable the cache optimization configuration and reload Apache.

sudo a2enconf cache-optimization
sudo apache2ctl configtest
sudo systemctl reload apache2
sudo httpd -t
sudo systemctl reload httpd

Monitor and troubleshoot reverse proxy

Set up cache monitoring script

Create a monitoring script to track cache performance and hit rates.

#!/bin/bash

Apache cache monitoring script

CACHE_DIR="/var/cache/apache2/mod_cache_disk" LOG_FILE="/var/log/apache2/microservices_cache.log"

Cache disk usage

echo "Cache Directory Usage:" du -sh $CACHE_DIR echo ""

Cache files count

echo "Cache Files Count:" find $CACHE_DIR -type f | wc -l echo ""

Recent cache statistics from logs

echo "Cache Hit/Miss Statistics (last 1000 requests):" if [ -f "$LOG_FILE" ]; then tail -1000 $LOG_FILE | awk '{ if ($NF ~ /HIT/) hits++; else if ($NF ~ /MISS/) misses++; total++ } END { if (total > 0) { hit_rate = (hits/total)*100; printf "Hits: %d (%.1f%%)\n", hits, hit_rate; printf "Misses: %d (%.1f%%)\n", misses, 100-hit_rate; printf "Total: %d\n", total; } else { print "No cache data found in logs"; } }' else echo "Cache log file not found" fi

Top requested URLs being cached

echo "" echo "Top 10 Cached URLs:" if [ -f "$LOG_FILE" ]; then tail -1000 $LOG_FILE | awk '{print $7}' | sort | uniq -c | sort -nr | head -10 fi
sudo chmod +x /usr/local/bin/apache-cache-stats.sh

Configure log rotation

Set up log rotation to prevent cache and access logs from consuming too much disk space.

/var/log/apache2/microservices_*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 644 www-data adm
    postrotate
        if systemctl is-active apache2 > /dev/null; then
            systemctl reload apache2
        fi
    endscript
}

Create health check endpoint

Set up a simple health check that monitors both proxy and cache functionality.

 'healthy',
    'timestamp' => date('c'),
    'checks' => []
];

// Check if cache directory is writable
$cache_dir = '/var/cache/apache2/mod_cache_disk';
$health['checks']['cache_writable'] = is_writable($cache_dir);

// Check cache directory size
$cache_size = shell_exec("du -sb $cache_dir | cut -f1");
$health['checks']['cache_size_bytes'] = intval(trim($cache_size));
$health['checks']['cache_size_mb'] = round($cache_size / 1024 / 1024, 2);

// Check if any backend services are responding
$backends = [
    'users' => 'http://localhost:3001/health',
    'products' => 'http://localhost:3002/health',
    'orders' => 'http://localhost:3003/health'
];

foreach ($backends as $name => $url) {
    $context = stream_context_create([
        'http' => [
            'timeout' => 2,
            'ignore_errors' => true
        ]
    ]);
    
    $result = @file_get_contents($url, false, $context);
    $health['checks']['backend_' . $name] = $result !== false;
}

// Set overall status
foreach ($health['checks'] as $check => $status) {
    if (!$status && $check !== 'cache_size_bytes' && $check !== 'cache_size_mb') {
        $health['status'] = 'degraded';
        break;
    }
}

echo json_encode($health, JSON_PRETTY_PRINT);
?>
sudo chown www-data:www-data /var/www/html/proxy-health.php
sudo chmod 644 /var/www/html/proxy-health.php

Verify your setup

Test your Apache reverse proxy configuration and caching functionality.

# Check Apache status and configuration
sudo systemctl status apache2
sudo apache2ctl -S

Test basic proxy functionality

curl -H "Host: api.example.com" http://localhost/health

Test cache headers (run twice to see cache hit)

curl -I -H "Host: api.example.com" http://localhost/api/users curl -I -H "Host: api.example.com" http://localhost/api/users

Check cache statistics

sudo /usr/local/bin/apache-cache-stats.sh

View recent access logs

sudo tail -f /var/log/apache2/microservices_access.log
Note: The X-Proxy-Cache header in responses will show HIT, MISS, or other cache status values. A second request to the same endpoint should show HIT if caching is working correctly.

Common issues

SymptomCauseFix
502 Bad Gateway errorsBackend services not runningCheck backend service status with systemctl status service-name
Cache not working (always MISS)Backend sends no-cache headersAdd CacheIgnoreHeaders directive for specific headers
Permission denied on cache directoryWrong ownership or SELinux contextFix with chown www-data:www-data and SELinux contexts
Cache fills up disk spaceNo cache cleanup configuredEnsure cache cleanup timer is active: systemctl status apache-cache-cleanup.timer
Slow response timesInsufficient Apache workersIncrease MaxRequestWorkers in Apache configuration
Module not found errorsRequired modules not enabledEnable modules with a2enmod module-name on Debian/Ubuntu
Balancer not distributing loadHealth checks failingCheck backend health endpoints return 200 status
SSL termination issuesMissing SSL configurationConfigure SSL virtual host with certificates

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and performant across environments is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.