Watching only the apex: how ignoring subdomains turns into a six-figure incident.
Your uptime checks are on example.com. Your SSL monitor is on example.com. Your DMARC alerts fire on example.com. Meanwhile api., staging. and blog. are running unmonitored — and that is exactly where attackers live. This piece walks the four concrete ways apex-only monitoring costs organizations real money, shows what a complete subdomain inventory looks like, and gives you a checklist you can run in one afternoon to close the gap.
Why apex-only monitoring breaks down
The mental model behind apex-only monitoring is that a domain is one thing you watch. The reality is that a registered domain is a root, and every subdomain under it is its own host with its own posture.
- Different IPs per host Every subdomain can resolve to a different IP and sit behind a different CDN, origin, or cloud account.
- Different certificates per host Each subdomain carries its own SSL certificate with its own issuer, renewal automation, and expiry date.
- Different reputation per host IP-based blacklists (Spamhaus PBL, Barracuda, SORBS) list by IP. mail. can be listed while apex stays clean.
- Different DNS and headers per host TXT records, CORS configuration, security-header sets, and DMARC policy all vary per subdomain.
- Different owners per host Marketing, engineering, ops, and third-party vendors each provision hosts independently. Central inventory rarely exists.
Four concrete ways this costs money
Every pattern here has landed in disclosure programs, post-mortems, or news cycles in the last three years.
- Subdomain takeover A dangling CNAME points at an abandoned S3 bucket, Heroku app, Azure Blob, GitHub Pages site, or SaaS trial. Attacker re-registers the target and serves attacker content from your subdomain. First-party cookies leak, phishing pages look legitimate.
- Expired certificate on an unmonitored host Apex renews on schedule because it is automated. api-v2.example.com was a one-off Terraform stack whose ACM auto-renewal quietly failed six weeks ago. A partner integration goes down at 2am.
- DMARC bypass via unenrolled subdomain You set p=reject on the apex and stopped there. sp= was never set. Every subdomain inherits no policy and an attacker sends look-alike email from newsletter.example.com. Your DMARC dashboard reports 100 percent aligned because fraudulent messages never touch your infrastructure.
- Blacklist listing on a subdomain mail server Apex serves marketing behind Cloudflare — clean, unlisted. mail.example.com resolves to a self-hosted Postfix instance whose IP quietly ended up on Spamhaus PBL. Delivery to Gmail tanks. Sales attributes the quiet week to seasonality.
How subdomain takeover actually works
The single most costly pattern in the list. The mechanic is dumb-simple, the impact is enormous.
- Step 1 — Provision You provision a cloud resource: S3 bucket, Heroku app, Azure Blob, GitHub Pages site, Netlify project, Vercel deployment, Zendesk portal, HelpScout Docs site.
- Step 2 — Point DNS You add a CNAME: support.example.com to yourorg.zendesk.com.
- Step 3 — Forget Six months later you cancel the plan or delete the resource. Nobody remembers the CNAME.
- Step 4 — Attacker enumerates Attacker reads the same Certificate Transparency logs you should be watching, finds the dangling CNAME, and registers a new resource under the same name on the same platform.
- Step 5 — Weaponize support.example.com now serves attacker-controlled content on your brand. Cookies scoped to .example.com are readable. CSP self allowances are exploitable. Partner integrations that trust your subdomain are compromised.
When the expiring-cert alert fires on the wrong host
Every serious SSL monitor watches the apex certificate. Very few watch every subdomain. This creates a specific failure mode: the alert you built does not fire because the outage is on a host you never told the monitor to watch.
- Cloudflare fronts apex, origin serves subdomain example.com is proxied through Cloudflare with a Universal SSL cert that rotates automatically. api.example.com is served directly from EC2 with a Let's Encrypt cert. Renewal silently starts failing after an nginx location reorder. Apex monitor sees a healthy Cloudflare cert forever.
- Multi-cloud, multi-issuer Half your subdomains are on AWS ACM (13 months, no notification unless EventBridge is set up). The other half are on Let's Encrypt (90 days, requires a working renewal cron). One will fail. Apex-only monitoring will not tell you which.
- The fix Enumerate every subdomain. Subscribe your monitor to every subdomain that terminates its own TLS. Set the alert threshold to 30 days out per host. The hard part is step one — the enumeration.
DMARC, sp=, and the subdomain-spoofing gap
Email people already know about sp=, but the error is common enough to worth stating clearly.
- p= applies to the apex p=reject applies to messages whose RFC 5322 From is exactly example.com. That is what the tag means. Nothing more.
- sp= applies to subdomains sp= applies to messages whose From is any subdomain of the apex — invoice.example.com, mail.example.com, secure-payments-2026.example.com.
- Missing sp= means no policy When you publish v=DMARC1; p=reject; and stop, mailbox providers apply reject to apex From only. Messages from subdomains fall through to whatever the mailbox provider decides — usually deliver with a soft warning at worst. That is a bypass.
- The fix Publish sp=reject alongside p=reject. Force strict policy on every subdomain by default. If a legitimate subdomain needs a different policy (marketing sends via SendGrid from mail.example.com), publish an override on that subdomain explicitly.
How to actually find every subdomain
Subdomain discovery is a solved problem. Every modern tool draws from the same corpus.
- Certificate Transparency logs Every publicly-trusted certificate issued since 2018 is logged in append-only CT logs, mandated by browsers. Every SAN on every cert is a hostname someone at your org (or a vendor of yours) provisioned. crt.sh exposes this as a free JSON API. Passive, no target-side load.
- DNS brute-force against a common-prefix wordlist Top 40 prefixes (www, api, mail, staging, admin, dev, blog, dashboard, vpn) catch about 80 percent of real-world hosts. Extended top-200 wordlists sweep the long tail — infra (ns1, mx2), ops (grafana, sentry, jenkins), SaaS defaults (crm, billing, hr).
- Passive DNS data Commercial services (SecurityTrails, VirusTotal, DNSDB) carry historical resolutions from real user queries. Catches subdomains that never issued a cert and are not in your brute-force wordlist. Useful for the last 5-10 percent.
- Combining sources CT plus brute-force catches almost every subdomain that matters. CLI tools (Sublist3r, Subfinder, Amass, Assetfinder) combine them differently. Hosted tools add liveness enrichment (DNS + HTTP + TLS per host) and takeover-risk cross-check in the same call.
Monitoring that scales with your subdomains
Finding subdomains is one problem. Monitoring every one continuously is a different problem — and where DIY setups fall apart.
- Per-host cert expiry alerts Each subdomain that terminates its own TLS needs its own 30-day-out alert. Cloudflare-fronted hosts inherit the front-door cert; origin-terminated hosts do not.
- Per-host header audits CSP, HSTS, X-Frame-Options vary per subdomain. Apex-level audit misses drift on staging and legacy hosts.
- Per-host blacklist checks IP-based reputation is per-IP, not per-domain. mail. and marketing. and api. all need independent checks.
- New-subdomain alerts The load-bearing signal. Attacker registers a lookalike CNAME, gets a certificate (necessarily logged to CT), sets up harvest page, blasts links. If you watched CT you saw the cert minutes after the fact.
- Platform capability DomainScan's platform tier triggers subdomain discovery on onboarding, re-runs enumeration every 24 hours, and fires NEW_SUBDOMAIN alerts the moment a host appears that was not there yesterday — including CT-only hosts you did not provision.
A checklist for this week
Every item here is finishable inside two hours if you already know your DNS.
- Enumerate Point a subdomain finder at every apex your org owns. Include the -shop.com, -corp.io and other spelling variants marketing registered five years ago.
- Triage the dead ones Hosts that do not resolve today but appear in CT are old infrastructure. Verify the CNAME is dead-dead. If the target matches a cloud fingerprint and does not resolve, that is a takeover-risk row. Remove the CNAME the same day.
- Inventory the live ones Group by ownership. Every subdomain has an owner — team, vendor, CDN. Write it down.
- Verify TLS per host Every live subdomain that terminates its own TLS needs its own 30-day-out alert. Cloudflare-fronted hosts inherit; origin-terminated hosts do not.
- Publish sp=reject If your DMARC record is p=reject; sp=none; or has no sp= at all, you have a subdomain-spoofing gap. Fix it.
- Set up new-subdomain alerts Whether you build it (poll crt.sh nightly and diff) or use a platform, cert-appeared-for-hostname-I-did-not-provision is one of the highest-value security signals.
- Repeat monthly for the first quarter First pass catches the biggest gaps. Second and third catch what the first missed. After 90 days you have a real inventory.
Common questions
- How is this different from enterprise EASM like Randori or CyCognito? Enterprise EASM is broader scope (cloud metadata scraping, port scanning, config drift) and orders of magnitude more expensive. Right tool for a security organization with a dedicated program. If your team is one person part-time and you just need to stop losing time to expired certs on subdomains you forgot existed, subdomain finder plus per-host monitoring is 90 percent of the value for 5 percent of the cost.
- Do I need to authenticate to enumerate my own subdomains? No. Certificate Transparency is public by design — browsers verify certs against public logs. crt.sh is a free interface. DNS brute-force sends one query per candidate hostname and the target sees only the queries that actually resolve.
- What if my subdomain uses a wildcard cert? Wildcards mean the cert itself does not tell CT what hostnames are in use. Passive CT still catches subdomains that get their own non-wildcard certs (most modern infra does), and DNS brute-force catches the rest.
- How often do subdomains actually change? Active engineering orgs: multiple new subdomains a week from staging, feature branches, review apps, preview deploys. Stable orgs: one or two a month from marketing campaigns and SaaS onboarding. Either way the delta grows month over month unless something is actively closing the gap.
- Is crt.sh always reliable? Occasionally not. Under load crt.sh times out or returns partial results. Any serious tool should fall back to DNS brute-force when passive CT fails, and should not cache empty results.