Domains & DNS

DNS TTL: Why You Lower It Before You Change Anything

Every DNS migration guide says “lower your TTL first.” Almost none of them explain what TTL actually does during the change, which is why so many people lower it ten minutes before the cutover and wonder why the old value is still being served hours later.

TTL is the single lever you control. It has three properties that decide how well any change goes, and two of them are not intuitive.

What TTL controls, and what it does not

TTL is a 32-bit field on a DNS record, defined in RFC 1035, that tells a resolver how long it may keep the answer without asking again.

The important property is what happens when it decrements. A resolver does not restart the clock when you change the record upstream. It serves the value it has, for the remainder of the TTL it originally received. So:

  • You set TTL to 3600 (one hour) and change the record at 10:00.
  • A resolver that fetched the old value at 09:30 keeps serving it until 10:30.
  • Anyone asking in that window gets the old destination.

This is why “lower the TTL” means lower it days before, so that the shorter value has itself propagated everywhere before you need it to matter.

The one you have already been bitten by: your own cache

There is a second cache in the path and it is on your machine, in your operating system, or in your browser. A platform’s own documentation acknowledges it directly — noting that “your local DNS cache may take longer to update.”

So the first troubleshooting step for “my change is not working” is not “check the DNS” but “check whether you are looking at a cached answer”:

# Ask a specific public resolver, bypassing your local cache
dig @1.1.1.1 example.com A +short
dig @8.8.8.8 example.com A +short

# See the remaining TTL on the answer you receive
dig @1.1.1.1 example.com A

In the dig output, the second column of each answer record is the remaining TTL — not the value you configured. If it reads 2,400 when you set 3,600, you are looking at a cached copy that has been alive for twenty minutes. That is normal, and it is the most useful diagnostic in DNS work.

Negative answers are cached too, with a different TTL

Here is the fact that most guides omit, and it is the one that produces mysterious failures.

When a resolver asks for a record that does not exist, the failure is cached. This is standardised in RFC 2308, and the details matter:

  • A NXDOMAIN response (the name does not exist) is cached against the name and class.
  • A NODATA response (the name exists, the type does not) is cached against the name, type and class.
  • The negative TTL is not the record’s TTL. It is the smaller of the SOA record’s MINIMUM field and the SOA record’s own TTL.
  • When a resolver serves a cached negative answer, it must return the SOA with the TTL decremented by the time it has been stored.
  • A negative response without an SOA record should not be cached at all.

Two consequences for a migration.

A brief failure becomes a lasting failure. If a record is missing or misconfigured for a few minutes during your change, resolvers that asked during that window cache the negative answer for hours afterwards. You fix the record, and the failure persists — for some visitors, not all, which is the worst kind of bug.

Your SOA MINIMUM is a lever. It is usually set by the DNS provider and rarely looked at. If you are planning a change, it is worth knowing what it is, because it bounds how long a transient failure will haunt you.

RFC 2308 suggests keeping negative caching within a range of one to three hours and notes that “exceeding one day” is problematic. It also caps failure responses at five minutes. None of that is binding on every resolver — it is guidance, and real resolvers choose their own bounds.

Your TTL setting may be ignored entirely

This is the practical surprise. On Cloudflare, all proxied records have a TTL of Auto, set to 300 seconds, and that value cannot be edited.

If your record sits behind the orange cloud, the TTL field in the dashboard is not what is being used. Cloudflare is answering with its own 300-second value, regardless of what you would like it to be.

The documented TTL options apply to unproxied records: a range from 30 seconds (Enterprise) or 60 seconds (other plans) up to one day, with Auto resolving to 300 seconds.

So the preparation step — “lower the TTL before the change” — means different things depending on whether the record is proxied:

Record stateEffective TTLCan you lower it?
Proxied (orange cloud)300s, Auto, fixedNo
Unproxied, set manuallyWhatever you chooseYes, down to 60s

On a proxied record you already have a five-minute TTL and cannot go lower, which is good news for cutover speed. It is also a ceiling: if you needed a longer TTL for a stable record, proxied records will not give you one.

There is a caveat about how far the proxied TTL gets you: the record’s TTL governs caching of the answer, but a routing change at the platform level — where mail is delivered, for example — is documented as potentially taking up to 24 hours to propagate. Not everything in the DNS path honours the number you set.

The upper bound, and a rule that is not what it looks like

RFC 2181 sets the maximum TTL at 2^31−1 seconds. The interesting part is what happens above it: values received with the most significant bit set should be treated “as if the entire value received was zero.”

Zero, not the maximum. A TTL that overflows does not clamp to the largest allowed value — it collapses to the smallest. And the same RFC states that implementations are always free to impose their own upper bound and treat anything larger as that bound.

The practical reading: do not use very large TTLs. A resolver is permitted to ignore your intent, and the failure mode of an overflowing value is a zero TTL, which is the opposite of what a long TTL was for.

The preparation routine

For any DNS change you care about:

  1. Check the record’s status. Proxied records already have a 300-second TTL. Unproxied records need lowering manually.
  2. Lower unproxied TTLs to the minimum you can, and wait at least one full original TTL before making the change. If the TTL was a day, that means waiting a day.
  3. Note the SOA MINIMUM if you might have a period where the record does not resolve. That value is how long a transient failure will be remembered.
  4. Change the record, then verify against public resolvers rather than your own machine.
  5. Watch for the negative-cache tail. If anything briefly failed, expect residual failures past the point where your own tests pass.
  6. Restore the TTL to something sensible afterwards — a few hours for stable records.

When to skip all of this

If the record you are changing is not used by anything that has to resolve, lower the TTL to 60 seconds, change it, and move on. The ceremony exists for records where a window of unavailability matters — the A record your site resolves through, the MX record your mail depends on, the CNAME a verification check is watching.

For everything else, the useful skill is not the routine. It is knowing which records are load-bearing, and applying the routine only to those.


Written by TestedHost. Every recommendation on this site comes from running the setup described, on a live deployment — not from a vendor spec sheet. Spotted something out of date? Tell us.