A Dangling CNAME Is a Subdomain Takeover Waiting to Happen — and DNS Still Resolves Fine
You spun up docs.example.com on a hosted docs service two years ago, pointed a CNAME at example.hosteddocs.io, and later moved the docs somewhere else. You cancelled the hosted account. You did not delete the CNAME, because deleting DNS records is scary and the subdomain was not in use anyway.
That CNAME is now a liability, and the reason it is dangerous is the reason it is easy to miss: it still resolves. dig docs.example.com returns an answer. Your uptime monitor, if you even still have one on that host, either 404s quietly or was removed. Nothing is red. And example.hosteddocs.io is now an unclaimed name on a platform where anyone can register it — which means anyone can register it, serve content from your subdomain, read cookies scoped to *.example.com, and pass your domain's reputation to a phishing page. That is a subdomain takeover, and the DNS record that enables it looks perfectly healthy from every angle that checks whether it resolves.
The general shape is bigger than takeovers. The dangerous DNS events are not outages. They are silent edits to a delegation — a CNAME that gets dropped, added, or quietly repointed — and they are invisible precisely because the thing they break is not "does it resolve" but "does it still resolve to what you meant."
"It Still Resolves" Is the Wrong Question
An outage announces itself. A subdomain that stops resolving generates errors, tickets, a Slack thread. You find out.
A delegation drift does the opposite. Consider the failure the product literature never dramatizes because it is too boring to picture: a client's IT department migrates nameservers over a weekend, rebuilds the zone from a records export that was three months stale, and silently omits the CNAME that pointed the marketing site at WP Engine or Pantheon. Monday morning the site is serving from a parked page or a default vhost. It resolves. It returns 200. The CDN's own status page is green. Nobody who runs an uptime check on "is the hostname up" sees anything, because the hostname is up — it is just up as the wrong thing.
So the question a DNS monitor should answer is not "does docs.example.com resolve" — DNS almost always resolves, that is what it is built to do. The question is "is docs.example.com still delegated to the target I published, or did that record change while I wasn't looking?" Resolution success is not safety. The record can resolve flawlessly to a target that was deleted, retargeted, or handed to someone else.
Alert on the First Hop. Never on the Chain.
Here is where building this correctly gets interesting, and where most naive implementations go wrong in the opposite direction — by alerting on too much.
When you resolve a CNAME'd hostname, you do not get one hop. You get the whole chain the resolver followed. A real example:
www.microsoft.com
→ www.microsoft.com-c-3.edgekey.net
→ e13678.dscb.akamaiedge.net
→ (A records)
Three hops. It is tempting to record the full chain and alert whenever any of it changes. Do that and you have built a pager that goes off constantly for reasons that have nothing to do with you. Look at who owns each hop:
www.microsoft.com → www.microsoft.com-c-3.edgekey.netis the record in Microsoft's own zone. This is the delegation they published and control. If this changes, something meaningful happened....edgekey.net → ...akamaiedge.netis Akamai's edge-mapping name. Akamai reshuffles which edge cluster answers based on load, geography, and time of day. It is theirs to change and it changes all the time.
Alerting on the second kind of hop means paging the customer for their CDN's internal routing decisions — a signal that fires several times a day, is never actionable, and trains everyone to mute the channel. And a muted channel is worthless the day the first hop — the one that matters — actually changes.
So the rule is: key every alerting decision on the first hop, the delegation target published in your own zone, and record the rest of the chain only as evidence. The full chain is useful for a human debugging a problem; it is poison as an alert trigger. The distinction is not a simplification — it is the difference between a monitor people trust and one they turn off.
"Removed", "Added", "Retargeted", and "I Didn't Look"
Once you are comparing this sweep's delegation to last sweep's, there are four things that can be true, and the fourth is the one that bites:
unchanged— same target as before. The overwhelmingly common case; say nothing.added— there was no CNAME before, there is one now. The hostname started being delegated.removed— there was a CNAME, now there is none (the host resolves directly, or not at all).retargeted— the CNAME now points somewhere different. This is the takeover-adjacent one: your delegation moved.
And then the trap: "the hostname publishes no CNAME" and "I did not measure the CNAME" are different facts, and a monitor that conflates them lies on its first run. If your stored row predates the feature, or a probe failed, the delegation field is absent — not null. Read "absent" as "no CNAME" and the first sweep after you ship the feature reports every CNAME'd host in your fleet as a brand-new added delegation, because it is comparing a real observation against a blank it misread as "there was nothing here." A day-one flood of false "your DNS changed" alerts is how a good feature gets disabled in week one. The row has to distinguish measured-and-empty from not-measured, and only ever compute a change between two real observations.
The Best Part: This Costs Nothing to Watch
The reason this kind of monitoring is often skipped is an assumed cost: surely watching the delegation means an extra DNS query per host per interval, or a second stored row, doubling your largest table. It does not, and the reason is a small, satisfying fact about DoH.
When you query a CNAME'd name for its address records over DNS-over-HTTPS, the resolver returns the CNAME hops in the same answer section, ahead of the addresses:
GET /dns-query?name=www.microsoft.com&type=1
Answer: [
{ name: "www.microsoft.com", type: 5, data: "www.microsoft.com-c-3.edgekey.net." },
{ name: "www.microsoft.com-c-3.edgekey.net", type: 5, data: "e13678.dscb.akamaiedge.net." },
...A records...
]
The delegation chain is already in the response you are making anyway. A lot of resolvers-and-fetchers filter those type-5 records out to get at the addresses — which means the answer to "did my delegation change" has been arriving on every single sweep, and being thrown away, for as long as the monitor has existed. Reading it instead of discarding it costs zero extra queries, zero extra rows, and zero extra sweeps. The one thing to normalize is rendering: DoH hands the owner name back without a trailing dot and the rdata with one, so compare them lowercased and dot-stripped or a chain will look like it changed on every sweep when only its punctuation moved.
Merlonix reads the CNAME delegation chain out of the DNS answer it already fetches on every monitoring interval, and alerts on a change to the first hop — the record in your own zone — while recording the full downstream chain as evidence and never paging you for a CDN's internal reshuffles. It tells added from removed from retargeted, and it distinguishes a measured "this host publishes no CNAME" from "not observed yet," so it does not cry wolf on its first sweep. The delegation edit that precedes a broken client site during a nameserver migration — and the one that leaves a subdomain dangling toward a target someone else can now claim — is exactly the silent change it exists to catch, because your uptime check never will: the record still resolves.
It sits on the same surface as the rest of what fails quietly in DNS and TLS — DNSSEC, your CAA policy governing who can issue certificates, and the difference between domain-registration expiry and certificate expiry. You can check a domain's live DNS, TLS, and delegation right now without signing up, and the free tools hub has the rest. A CNAME you set once and forgot is not inert. It is a standing decision about who your subdomain trusts — and the only way to know it still says what you meant is to watch the record, not the resolution.