qasim@wiki:~$ ▍

Recommended Apache Config Templates

1. Catch-all vhost (000-catchall.conf)

Must load first — the 000- prefix guarantees this alphabetically.

# Catches any Host/SNI that doesn't match a real vhost — must load FIRST
<VirtualHost *:80>
    ServerName catchall.invalid
    <Location />
        Require all denied
    </Location>
    ErrorDocument 403 "Not found"
</VirtualHost>

<VirtualHost *:443>
    ServerName catchall.invalid
    SSLEngine on
    SSLStrictSNIVHostCheck On
    SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
    SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
    <Location />
        Require all denied
    </Location>
    ErrorDocument 403 "Not found"
</VirtualHost>
sudo a2ensite 000-catchall.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

2. New-domain template (_template.conf) — HTTP only, no cert paths

<VirtualHost *:80>
    ServerName __DOMAIN__
    ServerAlias www.__DOMAIN__

    RedirectMatch 301 ^/(.*)$ https://www.__DOMAIN__/$1
    RewriteEngine on
    RewriteCond %{SERVER_NAME} =www.__DOMAIN__ [OR]
    RewriteCond %{SERVER_NAME} =__DOMAIN__
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Deliberately has no :443 block — that gets generated by certbot in step 4 of the workflow below, removing cert-path copy/paste as a failure mode.

3. Scaffolding script (/opt/scripts/new-apex-vhost.sh)

#!/bin/bash
set -e
DOMAIN="$1"
if [ -z "$DOMAIN" ]; then
  echo "Usage: $0 domain.com"; exit 1
fi

CONF="/etc/apache2/sites-available/${DOMAIN}.conf"
sed "s/__DOMAIN__/${DOMAIN}/g" /etc/apache2/sites-available/_template.conf > "$CONF"

a2ensite "${DOMAIN}.conf"
apache2ctl configtest && systemctl reload apache2

echo "HTTP vhost created and enabled for $DOMAIN."
echo "Next: confirm DNS A record points here, then run:"
echo "  sudo certbot --apache -d ${DOMAIN} -d www.${DOMAIN}"

4. Onboarding workflow (see also: Onboarding Checklist)

  1. sudo /opt/scripts/new-apex-vhost.sh <domain> — HTTP-only vhost from the clean template.
  2. Client adds apex A record → this server's static IP.
  3. Confirm DNS propagation: dig +short <domain>
  4. Confirm port-80 vhost is matching (not falling to catchall): curl -s -o /dev/null -w "%{http_code}\n" --resolve <domain>:80:127.0.0.1 http://<domain>/
  5. Issue cert — certbot writes the :443 block with correct paths: sudo certbot --apache -d <domain> -d www.<domain>
  6. Run the full --resolve verification loop from Apache Best Practices to confirm the redirect chain end-to-end over HTTPS.

Until step 5, HTTPS requests to the new domain hit 000-catchall and get a safe 403 — not another client's site.

← Back to the DNS series index