Apache httpd configuration is notoriously verbose and error-prone. Getting VirtualHost configs, mod_rewrite rules, SSL settings, and security headers right requires memorizing obscure directives. AI tools dramatically reduce the lookup time — but accuracy varies. This guide tests Claude, GPT-4, and Copilot on real Apache configuration tasks.
Task 1: HTTPS VirtualHost with HTTP Redirect
Prompt: “Write an Apache httpd config for example.com that redirects HTTP to HTTPS, serves from /var/www/example.com, enables PHP-FPM via proxy, and sets modern TLS settings.”
Claude’s output:
# /etc/apache2/sites-available/example.com.conf
# HTTP → HTTPS redirect
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
# Redirect all HTTP traffic to HTTPS
RewriteEngine On
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
# HTTPS VirtualHost
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public
# SSL Configuration
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# Modern TLS — TLS 1.2+ only, strong ciphers
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder Off
SSLSessionTickets Off
# PHP-FPM via Unix socket proxy
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>
# Security headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Directory configuration
<Directory /var/www/example.com/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Deny access to hidden files
<FilesMatch "^\.">
Require all denied
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
Claude includes PHP-FPM via Unix socket (faster than TCP), correct HSTS with preload, disables directory listing, and uses the SSLHonorCipherOrder Off setting required for TLS 1.3 forward secrecy. GPT-4 produces similar output but sometimes uses ProxyPassMatch instead of the SetHandler approach, which is less efficient.
Task 2: mod_rewrite for SPA Routing
Single-page applications need all routes to serve index.html:
Prompt: “Write Apache mod_rewrite rules for a React SPA at /var/www/app. Serve index.html for all routes except actual files and API calls under /api/.”
Claude’s .htaccess:
# /var/www/app/public/.htaccess
Options -MultiViews
RewriteEngine On
RewriteBase /
# Don't rewrite actual files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Don't rewrite API calls
RewriteCond %{REQUEST_URI} ^/api/ [NC]
RewriteRule ^ - [L]
# Serve index.html for everything else (SPA routing)
RewriteRule ^ index.html [L]
GPT-4’s version often includes RewriteRule ^ /index.html [L] with the leading slash, which causes an infinite redirect loop in some Apache configurations. Claude correctly omits the leading slash in the target.
Task 3: Reverse Proxy with Load Balancing
Prompt: “Configure Apache as a reverse proxy load balancer for 3 Node.js backend servers (ports 3001, 3002, 3003). Use leastconn balancing, health checks, and sticky sessions.”
Claude:
# /etc/apache2/sites-available/loadbalancer.conf
# Load balancing modules required:
# a2enmod proxy proxy_http proxy_balancer lbmethod_byleastconn headers
<VirtualHost *:443>
ServerName api.example.com
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/api.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.example.com/privkey.pem
# Define the load balancer cluster
<Proxy "balancer://nodejs_cluster">
BalancerMember http://127.0.0.1:3001 route=node1
BalancerMember http://127.0.0.1:3002 route=node2
BalancerMember http://127.0.0.1:3003 route=node3
# Least connections balancing
ProxySet lbmethod=byleastconn
# Health check: probe every 5s, mark down after 2 failures
ProxySet nofailover=Off
BalancerMember http://127.0.0.1:3001 status=+H ping=5
BalancerMember http://127.0.0.1:3002 status=+H ping=5
BalancerMember http://127.0.0.1:3003 status=+H ping=5
</Proxy>
# Sticky sessions via cookie
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
ProxyPass / balancer://nodejs_cluster/ stickysession=ROUTEID|.routeid nofailover=Off
ProxyPassReverse / balancer://nodejs_cluster/
# Pass real client IP
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
ErrorLog ${APACHE_LOG_DIR}/lb-error.log
CustomLog ${APACHE_LOG_DIR}/lb-access.log combined
</VirtualHost>
Task 4: Security Headers and CSP
Prompt: “Generate Apache security headers for a banking application. Include strict CSP, HSTS, and disable all unsafe methods.”
Claude:
# Security headers module — requires: a2enmod headers
<IfModule mod_headers.c>
# Strict Transport Security (2 years + preload)
Header always set Strict-Transport-Security \
"max-age=63072000; includeSubDomains; preload"
# Content Security Policy — strict mode for financial app
Header always set Content-Security-Policy \
"default-src 'none'; \
script-src 'self' 'nonce-{NONCE}'; \
style-src 'self' 'nonce-{NONCE}'; \
img-src 'self' data: https:; \
font-src 'self'; \
connect-src 'self' https://api.example.com; \
frame-ancestors 'none'; \
form-action 'self'; \
base-uri 'self'; \
upgrade-insecure-requests"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Referrer-Policy "no-referrer"
Header always set Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Resource-Policy "same-origin"
# Remove server information headers
Header always unset X-Powered-By
Header always unset Server
ServerTokens Prod
ServerSignature Off
</IfModule>
# Disable unsafe HTTP methods
<LimitExcept GET POST PUT PATCH DELETE OPTIONS HEAD>
Require all denied
</LimitExcept>
Claude notes that the CSP nonce requires server-side generation and suggests a PHP snippet or Apache module for dynamic nonce injection.
Task 5: Rate Limiting and IP-Based Access Control
A common need for APIs and admin panels: rate limiting by IP using mod_ratelimit and mod_evasive, plus granular IP-based access control.
Prompt: “Configure Apache to rate limit the /api/ endpoint to 100 req/s per IP, block known bad IPs, and allow the /admin/ path only from a specific subnet.”
Claude:
# Rate limiting — requires: a2enmod ratelimit
<Location /api/>
# Limit response rate to 100KB/s per connection (mod_ratelimit)
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 100
# mod_evasive settings (if installed)
DOSHashTableSize 3097
DOSPageCount 10
DOSSiteCount 100
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 60
</Location>
# IP allowlist for admin panel — only internal subnet + VPN
<Location /admin/>
Require ip 10.0.0.0/8
Require ip 192.168.1.0/24
Require ip 203.0.113.42 # ops workstation static IP
</Location>
# Blocklist specific IPs (load from file for large lists)
<RequireAll>
Require all granted
Require not ip 198.51.100.1
Require not ip 198.51.100.2
</RequireAll>
For large blocklists (thousands of IPs), Claude recommends using mod_authz_host with an external file or switching to fail2ban integration rather than inline Apache config, which would become unmanageable.
GPT-4 produces similar rate limiting config but sometimes uses the deprecated mod_bandwidth or forgets to mention the a2enmod commands needed to activate modules.
Task 6: .htaccess for WordPress Multisite
WordPress Multisite has specific rewrite requirements that differ from single-site WordPress. This is a common source of AI config errors.
Prompt: “Write .htaccess rules for WordPress Multisite in subdirectory mode.”
Claude’s output:
# WordPress Multisite — subdirectory mode
# /var/www/wordpress/.htaccess
# Protect wp-config.php
<Files wp-config.php>
Require all denied
</Files>
# Protect .htaccess itself
<Files .htaccess>
Require all denied
</Files>
Options -Indexes
# BEGIN WordPress Multisite
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
# Uploaded files — must come before the main WP rules
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
# Add trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
# Don't rewrite real files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite network subsites
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
# Route everything else through index.php
RewriteRule . index.php [L]
# END WordPress Multisite
GPT-4 often generates the single-site WordPress rules and misses the ms-files.php upload handling rule, which breaks media uploads on subsites.
Tool Comparison
| Task | Claude | GPT-4 | Copilot |
|---|---|---|---|
| VirtualHost SSL config | Excellent — correct TLS settings | Good | Weak |
| mod_rewrite for SPA | Correct (no leading slash bug) | Sometimes buggy | No |
| Load balancer config | Excellent — leastconn, health checks | Good | No |
| Security headers | Excellent — COEP/COOP included | Good | No |
| Rate limiting config | Strong — recommends fail2ban for scale | Good | No |
| WordPress Multisite | Includes ms-files.php rule | Misses multisite rules | No |
| .htaccess password protection | Strong | Strong | Moderate |
| mod_wsgi for Python | Strong | Strong | No |
Module Activation Cheat Sheet
One consistently useful pattern: Claude always includes the a2enmod commands needed to activate referenced modules. Both GPT-4 and Copilot sometimes skip this, leaving you with configs that silently do nothing because the module isn’t loaded.
# Enable all modules referenced in this guide
sudo a2enmod rewrite ssl headers proxy proxy_http proxy_balancer \
lbmethod_byleastconn ratelimit
# Verify modules are loaded
apache2ctl -M | sort
# Test config before reloading
apache2ctl configtest
# Reload without dropping connections
sudo systemctl reload apache2
Related Reading
- Best AI Tools for Writing Vagrant Configs
- Best AI Tools for Writing Caddy Configs
- AI Tools for Automated SSL/TLS Configuration
- AI Assistants for Writing Correct AWS IAM Policies
Built by theluckystrike — More at zovo.one