Incident Postmortem — Apex Vhost Fallthrough
What happened
A client's apex domain briefly served a different, unrelated client's website on the shared MainDomains redirect server.
Root cause
- A new vhost was created for the affected domain by copying an existing
client's
.conffile and editing the domain-specific lines. - The final
Redirect permanent / https://www.<domain>/line was accidentally omitted during the edit. - Because
sites-enabled/loads alphabetically and no explicit catch-all existed, the alphabetically-first vhost was Apache's implicit default for unmatched requests. The new domain's redirect logic being incomplete meant its traffic wasn't confidently routed to itself, and the affected window exposed the fallthrough to that default vhost — a different client's site.
Contributing factors
- Copying a live client's vhost as a template — brings along that client's cert paths and redirect target, easy to miss one line.
- No catch-all/default-deny vhost existed — any config gap had a real, unrelated client's site as its fallback instead of a safe error.
- No automated post-deploy check to verify a new domain actually redirects to itself before considering onboarding "done."
Fix applied
- Added
000-catchall.conf— loads first for both*:80and*:443, returns403 Require all deniedfor any unmatched Host/SNI. Confirmed viaapache2ctl -Sas the default server ahead of all client vhosts. - Verified with
curl --resolveacross all existing client domains — all redirect correctly to their ownwww.subdomain. - Verified an unmapped test domain now returns
403instead of another client's content. - Replaced the "copy an existing client's vhost" workflow with a clean domain-only template + certbot-generated SSL block — see Apache Config Templates.
Verification evidence
client-a.com.pk -> 301 Location: https://www.client-a.com.pk/
client-b.com.pk -> 301 Location: https://www.client-b.com.pk/
client-c.co.za -> 301 Location: https://www.client-c.co.za/
client-d.com.sa -> 301 Location: https://www.client-d.com.sa/
client-e.com -> 301 Location: https://www.client-e.com/
client-f.et -> 301 Location: https://www.client-f.et/
client-promo-site.com -> 301 Location: https://www.client-promo-site.com/
unmapped-test-domain.com -> 403
Follow-up items identified
- A cert-path copy/paste mismatch was also found on an unrelated existing vhost (using another domain's Let's Encrypt cert path) — same root cause class, not yet triggered. To be corrected using the same template + certbot approach.
- Long-term architecture options evaluated to reduce reliance on the shared server entirely — see Static-IP Options Compared.
Prevention going forward
See the Onboarding Checklist — every new apex domain must go through this before being considered live.