CNAME (Canonical Name) records alias one hostname to another. They're the mechanism behind most third-party service integrations: your docs.example.com points to a Gitbook subdomain, status.example.com points to a Statuspage instance, app.example.com points to a Heroku or Vercel deployment. When those CNAME targets change — because a service migrated, an account was cancelled, or a deployment was removed — your subdomain either breaks or becomes vulnerable to takeover.
This guide covers how to check CNAME records, what happens when CNAME chains break, and how continuous CNAME record monitoring protects against service disruption and subdomain hijacking.
# Query CNAME for a specific subdomain
dig CNAME docs.example.com +short
# Follow the full resolution chain
dig docs.example.com +trace
# Check with a specific resolver
dig CNAME docs.example.com @1.1.1.1 +short
# nslookup equivalent
nslookup -type=CNAME docs.example.com
A CNAME lookup returns the target hostname the subdomain resolves to:
docs.example.com. 300 IN CNAME myorg.gitbook.io.
The number (300) is the TTL in seconds. A CNAME record must point to a hostname, never directly to an IP address. Resolvers follow the chain until reaching an A or AAAA record.
To check whether the CNAME target itself resolves:
# Check the full resolution chain — returns the final IP
dig docs.example.com A +short
# Check if the CNAME target resolves
dig $(dig CNAME docs.example.com +short) A +short
If the CNAME exists but its target returns NXDOMAIN (domain not found), the subdomain is dangling — and likely vulnerable to hijacking.
Subdomain hijacking occurs when a CNAME points to a service hostname that is no longer registered or provisioned to your organization. An attacker can claim the target hostname on the same service platform and serve content under your subdomain.
Classic scenario:
blog.example.com as a CNAME to yourcompany.ghost.ioyourcompany.ghost.io is releasedyourcompany.ghost.ioblog.example.com serves the attacker's content — under your domainThis attack is documented against GitHub Pages, Heroku, Netlify, Fastly, AWS S3, Azure, and dozens of other services. It's not theoretical: security researchers regularly find and report hijackable subdomains on well-known company domains.
dig CNAME subdomain.example.com +short — if it returns a target — then run dig <target> A +short. If the target returns NXDOMAIN or empty, the CNAME is dangling. If the target resolves but returns a 404 with a platform "claim this subdomain" message, the hijacking window is open.
Beyond hijacking, CNAME changes cause ordinary service disruptions when third-party targets change without DNS updates:
Documentation platforms, status pages, analytics tools, and other SaaS services sometimes change their subdomain structure during platform upgrades. When the target hostname changes and your CNAME still points to the old one, the service breaks silently.
# Old CNAME target (deprecated platform hostname)
docs.example.com → myorg.readme.io # no longer valid
# New target after platform migration (not updated in DNS)
docs.example.com → myorg.readme.io # still wrong, service returns 404
When a deployment on a PaaS platform is deleted, the subdomain that was assigned to it is released back to the platform's pool. Your CNAME still points to it. New deploys or free-tier accounts can claim the old name:
# Check if Heroku app subdomain still resolves
dig $(dig CNAME app.example.com +short) A +short
# If NXDOMAIN or 404 with "There's nothing here yet" — hijack risk
CNAMEs to CDN edge hostnames (Cloudflare, CloudFront, Fastly, Akamai) break when the CDN configuration is deleted or the distribution is disabled. The CNAME continues to resolve to the CDN hostname, but the CDN returns an error for the missing origin.
In cloud environments, CNAMEs to internal load balancers or service endpoints break when those resources are destroyed. Cloud load balancers typically have autogenerated hostnames that change on recreation.
Most DNS providers offer zone export. Use it to get a complete list of CNAMEs for your domain:
# If you have zone transfer access (less common)
dig AXFR example.com @your-nameserver
# Using AWS Route 53 CLI
aws route53 list-resource-record-sets --hosted-zone-id ZONEID \
| jq -r '.ResourceRecordSets[] | select(.Type=="CNAME") | "\(.Name) -> \(.ResourceRecords[0].Value)"'
# Using Cloudflare API
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/ZONEID/dns_records?type=CNAME" \
-H "Authorization: Bearer $CF_TOKEN" \
| jq -r '.result[] | "\(.name) -> \(.content)"'
For each CNAME, verify the target resolves and returns expected content:
#!/bin/bash
# check-cnames.sh — verify all CNAMEs resolve correctly
declare -A CNAMES=(
["docs.example.com"]="myorg.gitbook.io"
["status.example.com"]="myorg.statuspage.io"
["app.example.com"]="myapp.vercel.app"
["cdn.example.com"]="d1234.cloudfront.net"
)
for subdomain in "${!CNAMES[@]}"; do
expected="${CNAMES[$subdomain]}"
actual=$(dig CNAME "$subdomain" +short @1.1.1.1 | sed 's/\.$//')
target_resolves=$(dig "$actual" A +short @1.1.1.1)
if [ "$actual" != "$expected" ]; then
echo "CHANGED $subdomain"
echo " Expected: $expected"
echo " Actual: $actual"
elif [ -z "$target_resolves" ]; then
echo "DANGLING $subdomain -> $actual (target does not resolve)"
else
echo "OK $subdomain -> $actual"
fi
done
A change-detection script runs on a schedule, compares current CNAME values against a stored baseline, and alerts on any difference:
#!/bin/bash
# monitor-cnames.sh — detect CNAME changes and alert
DOMAIN="example.com"
SUBDOMAINS=("docs" "status" "app" "api" "cdn" "blog")
SNAPSHOT_DIR="/var/lib/dns-monitor"
ALERT_EMAIL="ops@example.com"
CHANGES=""
mkdir -p "$SNAPSHOT_DIR"
for sub in "${SUBDOMAINS[@]}"; do
fqdn="${sub}.${DOMAIN}"
snapshot_file="${SNAPSHOT_DIR}/cname-${sub}.${DOMAIN}.txt"
current=$(dig CNAME "$fqdn" +short @8.8.8.8 | sed 's/\.$//')
[ -z "$current" ] && current="(no CNAME)"
if [ ! -f "$snapshot_file" ]; then
echo "$current" > "$snapshot_file"
continue
fi
previous=$(cat "$snapshot_file")
if [ "$current" != "$previous" ]; then
CHANGES="${CHANGES}${fqdn}:\n Was: ${previous}\n Now: ${current}\n\n"
echo "$current" > "$snapshot_file"
fi
done
if [ -n "$CHANGES" ]; then
echo -e "CNAME changes detected for ${DOMAIN}:\n\n${CHANGES}" \
| mail -s "DNS ALERT: CNAME changes on ${DOMAIN}" "$ALERT_EMAIL"
fi
Add to cron for near-real-time monitoring:
*/5 * * * * /usr/local/bin/monitor-cnames.sh
A complete CNAME monitoring approach does more than detect value changes:
Look up current CNAME records for any subdomain instantly. See the target value, resolution chain, and whether the target resolves — no signup required.
Check CNAME records → Get change alerts →DNSWatch monitors CNAME records (and all other DNS record types) for your domains on a continuous schedule. When a CNAME value changes — planned migration or unauthorized modification — you get an immediate alert with the before and after values. It also detects dangling CNAMEs where the target has stopped resolving, so you catch subdomain hijacking risk before an attacker does.
No zone access required. No script to maintain. Monitoring runs from external infrastructure and checks what the internet sees — from multiple resolvers.
DNSWatch monitors all your DNS record types and alerts you immediately when anything changes — including subdomain hijacking risk.
Join the DNSWatch waitlist → Free DNS checker →