Root Cause Diagnosis
Cloudflare’s authoritative nameservers apply record changes globally within seconds. What you see as “propagation delay” is downstream caching: recursive resolvers, operating systems, and browsers keep serving the old answer until its TTL expires. Gray-clouded (DNS-only) records expose the real origin value and use the TTL you set (Auto = 300 seconds), so any cache that stored the old answer holds it for up to that long. Proxied records hide this because resolvers only see Cloudflare’s static anycast IPs. Lowering the TTL after a change does not shorten entries that are already cached, because those resolvers only re-read the TTL when the cached entry expires.
Quick Fix
Confirm the change is live at the authoritative server, then flush your local cache:
# Query Cloudflare's assigned nameserver directly (bypasses all caches)
dig +short A sub.example.com @$(dig +short NS example.com | head -n1)
# Flush local resolver (Linux, systemd-resolved)
resolvectl flush-caches
If the authoritative answer is correct but your resolver still returns the old value, flush the public resolver you use (Step 4). ISP resolvers cannot be flushed by you; you can only wait out the remaining TTL.
Step-by-Step Resolution
1. Verify the authoritative answer
dig +short NS example.com
dig +noall +answer A sub.example.com @amy.ns.cloudflare.com
dig +noall +answer AAAA sub.example.com @amy.ns.cloudflare.com
Use the nameserver names from the first command. Check both A and AAAA. A stale AAAA record makes IPv6-capable clients hit the old host while IPv4 tests look correct. If the authoritative answer is wrong, the fix is in DNS > Records and not a cache issue.
2. Measure the remaining cache lifetime on public resolvers
for r in 1.1.1.1 8.8.8.8 9.9.9.9 208.67.222.222; do
echo "== $r"; dig @$r +noall +answer A sub.example.com
done
The second column is the remaining TTL in seconds. A value below your configured TTL means the entry is cached, and that number is the longest you have to wait on that resolver. Anycast resolvers run multiple cache nodes, so repeated queries can return different TTLs. Query several times.
3. Check for negative caching
If the subdomain returned NXDOMAIN or NODATA before you created the record, that failure is cached separately:
dig @1.1.1.1 +noall +authority A sub.example.com
The SOA TTL in the output is how long the “does not exist” answer is cached. It is usually 1800 seconds (verify in the output), which is longer than the default record TTL. The record will not appear on that resolver until the negative entry expires or is purged.
4. Purge public resolver caches
| Resolver | Purge tool |
|---|---|
| Cloudflare 1.1.1.1 | https://one.one.one.one/purge-cache/ |
| Google 8.8.8.8 | https://dns.google/cache |
| Cisco OpenDNS | https://cachecheck.opendns.com/ |
Enter the exact hostname and record type. Purge the A, AAAA, and CNAME types separately if you changed more than one.
5. Flush local caches
# Windows
ipconfig /flushdns
# macOS
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
# Linux (systemd-resolved)
resolvectl flush-caches
# Linux (nscd)
nscd -i hosts
# Linux (dnsmasq)
systemctl restart dnsmasq
Browsers keep their own cache. In Chrome or Edge, open chrome://net-internals/#dns and clear the host cache. If the browser uses DNS-over-HTTPS, its cache is independent of the OS.
6. Restart long-lived processes that resolved the name at startup
nginx -t && systemctl reload nginx
systemctl restart your-app.service
Nginx resolves a hostname in proxy_pass once at load time and does not re-resolve it. Many applications, connection pools, and JVM services do the same.
7. Lower the TTL before planned changes
Set the TTL at least one full old-TTL period before the cutover, then change the record. In the dashboard: DNS > Records > Edit > TTL. Or via the API, using a token with Zone > DNS > Edit permission:
# Find the record ID
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=sub.example.com" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq -r '.result[] | "\(.id) \(.type) \(.content) \(.ttl) proxied=\(.proxied)"'
# Set TTL to 60 seconds, DNS-only
curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
-H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" \
--data '{"ttl":60,"proxied":false}'
Cloudflare’s minimum TTL is 60 seconds on standard plans (30 seconds on Enterprise). After the cutover succeeds, raise the TTL back to reduce query load.
8. Confirm end to end
dig +noall +answer A sub.example.com @1.1.1.1
curl -sv --resolve sub.example.com:443:NEW_IP https://sub.example.com/ -o /dev/null
The --resolve flag tests the new origin directly and separates a DNS problem from an application problem.
Why This Fails for Some Users
- Lowering the TTL after the change. Entries cached under the old TTL are unaffected. The reduction only helps for lookups made after the cache expires.
- Switching a record from proxied to DNS-only (or back). Resolvers that cached the previous answer (Cloudflare edge IPs or your origin IP) keep it for that record’s TTL. A proxied record reports a TTL of 300 seconds.
- Resolvers that override TTLs. Some ISP and corporate resolvers enforce a minimum TTL (often 300 to 3600 seconds) or ignore short values. You cannot fix this from Cloudflare.
- Application-level caching. Chrome caches lookups for about one minute regardless of TTL. Java caches for 30 seconds by default, or indefinitely when a security manager is installed (
networkaddress.cache.ttl). Some proxy and load-balancer software caches for hours. - Split-horizon or local overrides. Check /etc/hosts, router-level DNS (dnsmasq, Pi-hole, Unbound), and VPN-pushed DNS. These answer without consulting Cloudflare at all.
- CNAME target caching. A CNAME chain is cached per link. Changing the target’s A record does not shorten the TTL already cached for the CNAME itself, and each record keeps its own TTL.
- Leftover records. An old A record left beside the new one causes round-robin answers that look like intermittent propagation. List all records for the name in DNS > Records.
- Nameserver change, not a record change. If you changed nameservers at the registrar, the delegation NS records at the TLD are commonly cached for up to 48 hours, and Cloudflare cannot shorten that.
- Partial (CNAME) setup. If the zone uses Cloudflare’s partial setup, authoritative DNS stays at the other provider. Records edited in Cloudflare are not the ones the internet resolves for the apex zone. Check with
dig NS example.com. - DNSSEC mismatch. After a nameserver or DNSSEC change, a wrong or missing DS record at the registrar causes validating resolvers (1.1.1.1, 8.8.8.8) to return
SERVFAIL, which looks like a propagation failure. Check withdig +dnssec A sub.example.com @1.1.1.1and compare the DS record shown in DNS > Settings with the registrar. - Anycast cache inconsistency. A large public resolver can return the new answer from one node and the old answer from another for the duration of the TTL. Re-test several times and purge before concluding a change failed.