Apache Basics & When to Use What

Why This Matters

Apache HTTP Server (commonly just "Apache") has been around since 1995. It was the dominant web server for over two decades and still powers roughly a quarter of all websites. Many enterprise applications, hosting control panels (cPanel, Plesk), and PHP applications (WordPress, Drupal, Magento) are built with Apache in mind.

Even if your new projects use Nginx, you will inevitably encounter Apache in production environments, inherited infrastructure, or specific situations where Apache is the better choice. Understanding Apache is not optional -- it is a professional requirement.

This chapter covers Apache's architecture, configuration, key modules, and provides clear guidance on when to choose Apache versus Nginx.


Try This Right Now

On a Debian/Ubuntu system:

$ sudo apt update && sudo apt install -y apache2
$ sudo systemctl start apache2
$ curl -I http://localhost

You should see:

HTTP/1.1 200 OK
Server: Apache/2.4.58 (Ubuntu)
Content-Type: text/html; charset=UTF-8
...

Distro Note: On RHEL/CentOS/Fedora, the package is called httpd, not apache2:

$ sudo dnf install -y httpd
$ sudo systemctl start httpd

The service name, binary name, and configuration paths all differ between Debian and RHEL families. We will cover both.


Apache vs Nginx: When to Use Each

Before diving into Apache's internals, let us address the question you are already asking.

┌──────────────────────────────────────────────────────────────┐
│                   Apache vs Nginx                             │
├────────────────────┬─────────────────────────────────────────┤
│                    │  Apache              │  Nginx            │
├────────────────────┼──────────────────────┼───────────────────┤
│ Architecture       │ Process/thread-based │ Event-driven      │
│ Config model       │ Distributed (.htaccess)│ Centralized     │
│ Module loading     │ Dynamic (at runtime) │ Mostly compile-time│
│ PHP integration    │ mod_php (embedded)   │ PHP-FPM (external)│
│ Static files       │ Good                 │ Excellent         │
│ Reverse proxy      │ Good (mod_proxy)     │ Excellent         │
│ Concurrency        │ Good (event MPM)     │ Excellent         │
│ Memory per conn    │ Higher               │ Lower             │
│ .htaccess support  │ Yes                  │ No                │
│ Shared hosting     │ Better (per-dir conf)│ Not suited        │
│ Learning curve     │ Gentle               │ Moderate          │
└────────────────────┴──────────────────────┴───────────────────┘

Choose Apache when:

  • You need .htaccess files (shared hosting, user-controlled directories)
  • You are running PHP with mod_php and want simplicity
  • You need dynamic module loading without recompiling
  • You are maintaining existing Apache infrastructure
  • You need per-directory configuration overrides

Choose Nginx when:

  • You need maximum performance for static files and reverse proxying
  • You are handling a large number of concurrent connections
  • You want a simpler, centralized configuration model
  • You are building microservice architectures with many backends
  • Memory efficiency matters (containers, cloud instances)

The most common pattern in production: Nginx in front as a reverse proxy, with Apache behind running PHP or legacy applications. You get the best of both worlds.


Apache Architecture: MPM Models

Apache uses Multi-Processing Modules (MPMs) to determine how it handles connections. Understanding MPMs is key to understanding Apache's behavior and performance.

prefork MPM

┌──────────────────────────────────────────────────────────┐
│                   prefork MPM                             │
│                                                          │
│  ┌──────────────┐                                        │
│  │ Master Process│                                       │
│  └──────┬───────┘                                        │
│         │                                                │
│    ┌────┼────────────────────────┐                       │
│    │    │         │              │                        │
│  ┌─┴─┐ ┌┴──┐ ┌───┴┐ ┌────┐ ┌───┴┐                      │
│  │P1 │ │P2 │ │ P3 │ │ P4 │ │ P5 │   One process per     │
│  │1  │ │1  │ │ 1  │ │ 1  │ │ 1  │   connection.          │
│  │req│ │req│ │ req│ │ req│ │ req│   No threads.          │
│  └───┘ └───┘ └────┘ └────┘ └────┘   Safe for non-       │
│                                       thread-safe libs.   │
└──────────────────────────────────────────────────────────┘
  • Creates a separate process for each connection
  • Maximum compatibility (safe for non-thread-safe PHP modules)
  • Highest memory usage (each process is a full copy)
  • Use case: Legacy PHP applications with non-thread-safe extensions

worker MPM

┌──────────────────────────────────────────────────────────┐
│                   worker MPM                              │
│                                                          │
│  ┌──────────────┐                                        │
│  │ Master Process│                                       │
│  └──────┬───────┘                                        │
│         │                                                │
│    ┌────┼──────────┐                                     │
│    │    │          │                                      │
│  ┌─┴──────┐ ┌─────┴───┐                                 │
│  │Process 1│ │Process 2│    Each process has multiple     │
│  │ T1  T2 │ │ T1  T2  │    threads. Each thread handles  │
│  │ T3  T4 │ │ T3  T4  │    one connection.               │
│  │ T5  T6 │ │ T5  T6  │    Better memory usage than      │
│  └────────┘ └─────────┘    prefork.                      │
└──────────────────────────────────────────────────────────┘
  • Each process spawns multiple threads
  • Each thread handles one connection
  • Less memory than prefork (threads share process memory)
  • Use case: High-traffic sites with thread-safe applications
┌──────────────────────────────────────────────────────────┐
│                   event MPM                               │
│                                                          │
│  Like worker MPM, but with a dedicated listener thread.   │
│  Keep-alive connections don't tie up worker threads.       │
│                                                          │
│  ┌──────────┐                                            │
│  │Process 1 │  Listener thread handles idle keep-alive   │
│  │ Listener │  connections. Worker threads only handle    │
│  │ W1 W2 W3│  active requests. Much more efficient.      │
│  │ W4 W5 W6│                                             │
│  └──────────┘                                            │
└──────────────────────────────────────────────────────────┘
  • Improves on worker by handling keep-alive connections asynchronously
  • A listener thread manages idle connections without consuming a worker thread
  • Default on modern Apache installations
  • Use case: General purpose, the best choice for most new deployments

Hands-On: Check Your MPM

# Debian/Ubuntu
$ apachectl -V | grep MPM
Server MPM:     event

# Or check the loaded modules
$ apachectl -M | grep mpm
 mpm_event_module (shared)

# RHEL/CentOS/Fedora
$ httpd -V | grep MPM
Server MPM:     event

Switching MPMs (Debian/Ubuntu)

# Disable current MPM, enable a different one
$ sudo a2dismod mpm_event
$ sudo a2enmod mpm_prefork
$ sudo systemctl restart apache2

Safety Warning: Switching MPMs requires a restart (not just reload) and will briefly drop all connections. Do this during a maintenance window.


Configuration File Structure

Debian/Ubuntu Layout

/etc/apache2/
├── apache2.conf            # Main config
├── ports.conf              # Listen directives (ports 80, 443)
├── envvars                 # Environment variables (user, group, paths)
├── sites-available/        # All site configs
│   ├── 000-default.conf    # Default HTTP site
│   └── default-ssl.conf    # Default HTTPS site template
├── sites-enabled/          # Symlinks to active sites
│   └── 000-default.conf -> ../sites-available/000-default.conf
├── mods-available/         # All available modules
├── mods-enabled/           # Symlinks to active modules
├── conf-available/         # Additional config fragments
└── conf-enabled/           # Active config fragments

RHEL/CentOS/Fedora Layout

/etc/httpd/
├── conf/
│   └── httpd.conf          # Main config (everything in one file)
├── conf.d/                 # Additional configs (*.conf auto-loaded)
│   ├── ssl.conf            # SSL/TLS configuration
│   └── welcome.conf        # Default welcome page
├── conf.modules.d/         # Module loading configs
│   ├── 00-base.conf
│   ├── 00-ssl.conf
│   └── ...
└── logs -> /var/log/httpd   # Log symlink

Distro Note: The Debian layout is more modular (separate dirs for sites, mods, confs). The RHEL layout is flatter (mostly in httpd.conf and conf.d/). Both achieve the same result.


VirtualHost Configuration

VirtualHosts are Apache's equivalent of Nginx's server blocks.

Basic VirtualHost

$ sudo nano /etc/apache2/sites-available/mysite.conf
<VirtualHost *:80>
    ServerName mysite.example.com
    ServerAlias www.mysite.example.com
    DocumentRoot /var/www/mysite

    <Directory /var/www/mysite>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/mysite-error.log
    CustomLog ${APACHE_LOG_DIR}/mysite-access.log combined
</VirtualHost>

Enable it:

# Debian/Ubuntu
$ sudo a2ensite mysite.conf
$ sudo systemctl reload apache2

# RHEL (just drop the file in conf.d/)
$ sudo cp mysite.conf /etc/httpd/conf.d/
$ sudo systemctl reload httpd

Hands-On: Create a VirtualHost

# Create document root and content
$ sudo mkdir -p /var/www/mysite
$ echo '<h1>Apache says hello!</h1>' | sudo tee /var/www/mysite/index.html
$ sudo chown -R www-data:www-data /var/www/mysite

# Create VirtualHost config
$ sudo tee /etc/apache2/sites-available/mysite.conf > /dev/null << 'EOF'
<VirtualHost *:80>
    ServerName mysite.example.com
    DocumentRoot /var/www/mysite

    <Directory /var/www/mysite>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/mysite-error.log
    CustomLog ${APACHE_LOG_DIR}/mysite-access.log combined
</VirtualHost>
EOF

# Enable and reload
$ sudo a2ensite mysite.conf
$ sudo apache2ctl configtest      # Test config (like nginx -t)
Syntax OK
$ sudo systemctl reload apache2

# Test
$ curl -H "Host: mysite.example.com" http://localhost
<h1>Apache says hello!</h1>

Disabling Sites

# Debian/Ubuntu
$ sudo a2dissite mysite.conf
$ sudo systemctl reload apache2

Think About It: Both Apache and Nginx use the Host header to route requests to virtual hosts. What happens if you do not set a ServerName in your VirtualHost? (Answer: Apache will use the first VirtualHost it finds as the default, similar to Nginx's default_server.)


The .htaccess File

This is Apache's killer feature that Nginx does not have. .htaccess files allow per-directory configuration without editing the main config or reloading Apache.

How .htaccess Works

When Apache receives a request for /var/www/mysite/blog/post.html, it checks for .htaccess files in every directory along the path:

/var/www/.htaccess            (if exists, apply it)
/var/www/mysite/.htaccess     (if exists, apply it -- overrides parent)
/var/www/mysite/blog/.htaccess (if exists, apply it -- overrides parent)

Enabling .htaccess

.htaccess processing is controlled by AllowOverride:

<Directory /var/www/mysite>
    AllowOverride All          # Allow .htaccess to override everything
    Require all granted
</Directory>

AllowOverride options:

  • None -- .htaccess files are completely ignored (best for performance)
  • All -- .htaccess can override any directive
  • FileInfo -- allows MIME types, redirects, rewriting
  • AuthConfig -- allows authentication directives
  • Indexes -- allows directory index settings

Common .htaccess Uses

# /var/www/mysite/.htaccess

# Redirect HTTP to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# Custom error pages
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html

# Password protection
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user

# Block access to sensitive files
<FilesMatch "\.(env|git|htpasswd|log)$">
    Require all denied
</FilesMatch>

# Cache static assets
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
</IfModule>

Safety Warning: Every .htaccess file causes a filesystem stat on every request for every directory in the path. On high-traffic sites, this has measurable performance impact. In production, prefer putting directives in the main config and setting AllowOverride None.


mod_rewrite: URL Rewriting

mod_rewrite is one of Apache's most powerful and most confusing modules. It rewrites URLs based on regular expressions.

Enabling mod_rewrite

$ sudo a2enmod rewrite
$ sudo systemctl restart apache2

Common Rewrite Rules

# In VirtualHost or .htaccess

RewriteEngine On

# Redirect old URLs to new ones
RewriteRule ^/old-page$ /new-page [R=301,L]

# Pretty URLs: /products/42 -> /product.php?id=42
RewriteRule ^/products/([0-9]+)$ /product.php?id=$1 [L,QSA]

# Remove trailing slashes
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [R=301,L]

# Front controller pattern (like WordPress, Laravel)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /index.php [L]

Rewrite flags:

  • [L] -- Last rule, stop processing
  • [R=301] -- External redirect with status code
  • [QSA] -- Append query string to the rewritten URL
  • [NC] -- Case-insensitive matching
  • [F] -- Return 403 Forbidden

Hands-On: Set Up URL Rewriting

# Enable mod_rewrite
$ sudo a2enmod rewrite
$ sudo systemctl restart apache2

# Create a test site
$ sudo mkdir -p /var/www/rewrite-demo
$ echo "Main page" | sudo tee /var/www/rewrite-demo/index.html
$ echo "User profile page" | sudo tee /var/www/rewrite-demo/user.php

# Create .htaccess with rewrite rules
$ sudo tee /var/www/rewrite-demo/.htaccess > /dev/null << 'EOF'
RewriteEngine On
# /users/alice -> /user.php?name=alice
RewriteRule ^users/([a-z]+)$ user.php?name=$1 [L,QSA]
EOF

# Update VirtualHost to allow overrides
# (ensure AllowOverride All in the Directory block)

# Test
$ curl "http://localhost/users/alice"
# Should serve user.php with name=alice

Enabling and Disabling Modules

Apache's module system is one of its greatest strengths. Modules are loaded dynamically -- no recompilation needed.

Debian/Ubuntu: a2enmod / a2dismod

# List available modules
$ ls /etc/apache2/mods-available/ | head -20

# List enabled modules
$ apachectl -M

# Enable a module
$ sudo a2enmod ssl
$ sudo a2enmod headers
$ sudo a2enmod proxy
$ sudo a2enmod proxy_http

# Disable a module
$ sudo a2dismod autoindex

# Always restart after changing modules
$ sudo systemctl restart apache2

RHEL/CentOS/Fedora

On RHEL, modules are managed through config files in /etc/httpd/conf.modules.d/:

# List loaded modules
$ httpd -M

# Modules are loaded via LoadModule directives
$ cat /etc/httpd/conf.modules.d/00-base.conf
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule unixd_module modules/mod_unixd.so
...

# To disable a module, comment out its LoadModule line
$ sudo sed -i 's/^LoadModule autoindex/# LoadModule autoindex/' /etc/httpd/conf.modules.d/00-base.conf
$ sudo systemctl restart httpd

Essential Modules

ModulePurpose
mod_sslHTTPS/TLS support
mod_rewriteURL rewriting
mod_headersCustom HTTP headers
mod_proxyReverse proxy functionality
mod_proxy_httpHTTP backend proxying
mod_proxy_wstunnelWebSocket proxying
mod_deflateResponse compression (gzip)
mod_expiresCache control headers
mod_auth_basicHTTP Basic authentication
mod_securityWeb Application Firewall (WAF)

Basic Authentication

Apache has built-in support for HTTP Basic Auth:

# Create a password file
$ sudo htpasswd -c /etc/apache2/.htpasswd admin
New password: ****
Re-type new password: ****
Adding password for user admin

# Add another user (-c creates the file; omit for subsequent users)
$ sudo htpasswd /etc/apache2/.htpasswd developer

In VirtualHost config:

<VirtualHost *:80>
    ServerName internal.example.com
    DocumentRoot /var/www/internal

    <Directory /var/www/internal>
        AuthType Basic
        AuthName "Internal Access Only"
        AuthUserFile /etc/apache2/.htpasswd
        Require valid-user
    </Directory>
</VirtualHost>

Or in .htaccess:

AuthType Basic
AuthName "Restricted"
AuthUserFile /etc/apache2/.htpasswd
Require user admin

Test it:

# Without credentials (401 Unauthorized)
$ curl -I http://internal.example.com
HTTP/1.1 401 Unauthorized

# With credentials
$ curl -u admin:password http://internal.example.com

Apache as a Reverse Proxy

Apache can function as a reverse proxy using mod_proxy:

# Enable required modules
$ sudo a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests
$ sudo systemctl restart apache2

Simple Reverse Proxy

<VirtualHost *:80>
    ServerName api.example.com

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    ErrorLog ${APACHE_LOG_DIR}/api-error.log
    CustomLog ${APACHE_LOG_DIR}/api-access.log combined
</VirtualHost>
  • ProxyPreserveHost On -- forward the original Host header to the backend
  • ProxyPass -- forward requests to the backend
  • ProxyPassReverse -- rewrite Location headers in responses so redirects work correctly

Load Balancing with mod_proxy_balancer

<VirtualHost *:80>
    ServerName app.example.com

    <Proxy "balancer://app_cluster">
        BalancerMember http://10.0.1.10:3000
        BalancerMember http://10.0.1.11:3000
        BalancerMember http://10.0.1.12:3000
        ProxySet lbmethod=byrequests      # Round-robin
    </Proxy>

    ProxyPreserveHost On
    ProxyPass / balancer://app_cluster/
    ProxyPassReverse / balancer://app_cluster/
</VirtualHost>

Load balancing methods:

  • byrequests -- round-robin by request count
  • bytraffic -- distribute by bytes transferred
  • bybusyness -- send to least busy worker
  • heartbeat -- use heartbeat monitoring

Hands-On: Complete Apache Setup

Let us set up a realistic Apache configuration:

# 1. Install Apache and enable essential modules
$ sudo apt update && sudo apt install -y apache2
$ sudo a2enmod rewrite ssl headers proxy proxy_http
$ sudo systemctl restart apache2

# 2. Create a site with URL rewriting and security headers
$ sudo mkdir -p /var/www/production
$ echo '<h1>Production Site</h1>' | sudo tee /var/www/production/index.html
$ sudo chown -R www-data:www-data /var/www/production

# 3. Create the VirtualHost
$ sudo tee /etc/apache2/sites-available/production.conf > /dev/null << 'CONF'
<VirtualHost *:80>
    ServerName www.example.com
    DocumentRoot /var/www/production

    <Directory /var/www/production>
        AllowOverride None
        Require all granted

        # Rewrite rules (in config, not .htaccess, for performance)
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule ^ /index.html [L]
    </Directory>

    # Security headers
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-XSS-Protection "1; mode=block"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Hide Apache version
    ServerSignature Off

    # Block hidden files
    <FilesMatch "^\.">
        Require all denied
    </FilesMatch>

    # Logging
    ErrorLog ${APACHE_LOG_DIR}/production-error.log
    CustomLog ${APACHE_LOG_DIR}/production-access.log combined
</VirtualHost>
CONF

# 4. Enable the site, disable the default
$ sudo a2ensite production.conf
$ sudo a2dissite 000-default.conf
$ sudo apache2ctl configtest
$ sudo systemctl reload apache2

# 5. Test
$ curl -I http://localhost
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff

Debug This

After enabling mod_rewrite and adding rewrite rules in .htaccess, your URLs are not being rewritten. All requests return the literal file path.

Debugging steps:

# 1. Is mod_rewrite loaded?
$ apachectl -M | grep rewrite
 rewrite_module (shared)
# If this is empty, run: sudo a2enmod rewrite && sudo systemctl restart apache2

# 2. Is AllowOverride set correctly?
$ grep -A3 "Directory /var/www" /etc/apache2/sites-enabled/mysite.conf
# If AllowOverride is "None", .htaccess is being ignored.
# Change to "AllowOverride All" (or "AllowOverride FileInfo")

# 3. Is RewriteEngine On in .htaccess?
$ cat /var/www/mysite/.htaccess
# Forgetting "RewriteEngine On" is the #1 mistake

# 4. Check Apache error log for rewrite issues
$ sudo tail -20 /var/log/apache2/error.log

# 5. Enable rewrite logging (temporarily, for debugging)
# In VirtualHost:
# LogLevel alert rewrite:trace3
$ sudo systemctl reload apache2
# Now check error.log for detailed rewrite processing

Common causes:

  • AllowOverride None in the VirtualHost (most common)
  • mod_rewrite not enabled
  • Missing RewriteEngine On in .htaccess
  • .htaccess file has wrong permissions (Apache cannot read it)

Configuration Testing

Always test Apache configuration before reloading:

# Debian/Ubuntu
$ sudo apache2ctl configtest
Syntax OK

# Or
$ sudo apachectl -t
Syntax OK

# RHEL/CentOS
$ sudo httpd -t
Syntax OK

For a verbose dump of the entire parsed configuration:

# Show all virtual hosts
$ sudo apachectl -S
VirtualHost configuration:
*:80                   www.example.com (/etc/apache2/sites-enabled/production.conf:1)

# Show all loaded modules
$ sudo apachectl -M

# Show compiled-in modules
$ sudo apachectl -l

What Just Happened?

┌──────────────────────────────────────────────────────────────┐
│                     Chapter 46 Recap                          │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  Apache HTTP Server uses MPM models for concurrency:          │
│  - prefork: one process per connection (legacy)               │
│  - worker: threads within processes                           │
│  - event: async keep-alive handling (recommended)             │
│                                                              │
│  Key concepts:                                                │
│  - VirtualHost = virtual host (like Nginx server blocks)      │
│  - .htaccess = per-directory config (Apache's unique feature) │
│  - mod_rewrite = powerful URL rewriting                       │
│  - a2enmod/a2dismod = enable/disable modules (Debian)         │
│                                                              │
│  Apache vs Nginx:                                             │
│  - Apache: .htaccess, mod_php, dynamic modules, shared hosting│
│  - Nginx: performance, memory efficiency, reverse proxy       │
│  - Best pattern: Nginx in front, Apache behind for PHP/legacy │
│                                                              │
│  Config:  /etc/apache2/ (Debian)  /etc/httpd/ (RHEL)         │
│  Logs:    /var/log/apache2/       /var/log/httpd/             │
│  Test:    apache2ctl configtest   httpd -t                    │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Try This

Exercise 1: MPM Comparison

Switch between event and prefork MPMs. Use ps aux | grep apache to compare the process model. With prefork, start ab -n 100 -c 10 http://localhost/ (Apache Bench) and watch processes spawn.

$ sudo apt install -y apache2-utils
$ ab -n 1000 -c 50 http://localhost/

Exercise 2: .htaccess Mastery

Create a site with these .htaccess rules:

  • Password-protect the /admin/ directory
  • Set up a custom 404 page
  • Redirect /old-blog to /blog with a 301
  • Block requests with empty User-Agent headers

Exercise 3: Reverse Proxy

Set up Apache as a reverse proxy for a Python (python3 -m http.server 8888) or Node.js backend. Verify that ProxyPreserveHost correctly passes the Host header.

Exercise 4: Module Exploration

Enable mod_status to get a real-time Apache status page:

<Location "/server-status">
    SetHandler server-status
    Require ip 127.0.0.1
</Location>

Visit http://localhost/server-status and explore the information provided.

Bonus Challenge

Set up Nginx in front of Apache. Nginx handles static files and proxies PHP requests to Apache. Apache runs mod_php to process the PHP. This is the "Nginx + Apache" pattern used by many high-traffic WordPress sites.


What Comes Next

Both Nginx and Apache can do load balancing, but they are web servers first and load balancers second. In the next chapter, we cover HAProxy -- a tool that is a load balancer first and foremost, with features that neither Nginx nor Apache can match.