Type example.com. into your browser — with the dot on the end — and, most of the time, you’ll land on the same page as example.com. The dot looks like a typo. It isn’t. That trailing dot is the single most technically correct way to write a domain name, and the fact that it usually does nothing visible is the setup for one of the most persistent, low-grade sources of bugs in networked software.

Here’s the uncomfortable part: DNS knows exactly what the trailing dot means and has since 1987. Almost every layer stacked on top of DNS — TLS, HTTP, cookies, HSTS — either disagrees or never got the memo. Those disagreements aren’t academic. They’ve produced real CVEs, and a new one landed in 2026.

The dot is the root

Start with what a domain name actually is. www.example.com is a path through a tree, read right to left: the com zone, delegated to by the root; example under it; www under that. The tree has a top, and the top has a name too — except its name is nothing. The empty string. A zero-length label.

RFC 1034, from November 1987, put it plainly: “Since a complete domain name ends with the root label, this leads to a printed form which ends in a dot.” Write out every label including the root, separate them with dots, and the last label — the root — is empty, so the name ends in a dot with nothing after it. www.example.com. The trailing dot isn’t decoration. It’s the root label, made visible.

RFC 1035 gives the two forms their names. In its description of zone files, names that end in a dot “are called absolute, and are taken as complete.” Names without the dot are relative, and something has to complete them — there, by appending the zone’s origin — before they mean anything. Resolvers apply the same idea to the names you type. So example.com. is the whole truth. example.com is, strictly, an abbreviation the resolver is trusted to finish.

That distinction sounds pedantic until you watch it change where your traffic goes.

Where the abbreviation bites: resolv.conf

On a Unix box, /etc/resolv.conf can carry a search list — domains the resolver appends to relative names before giving up. Set search corp.example.com and ask for wiki, and the resolver tries wiki.corp.example.com first. Convenient.

The knob that decides when it does this is ndots, default 1. The rule: if a name contains fewer dots than ndots, try the search list before trying the name as-is. With the default, a bare wiki (zero dots) gets suffixed; wiki.internal (one dot) is tried on its own first.

Now the trap. A name with a trailing dot is absolute, so the search list is skipped entirely — the resolver looks it up exactly as written and nowhere else. A name without the dot is fair game for suffixing. Most of the time you don’t notice, because the bare name resolves and the search list is never consulted. But when it doesn’t resolve cleanly, or when ndots is cranked up, the difference is the difference between one query and several — and occasionally between reaching the right host and reaching a completely different one.

Kubernetes made this famous. Pods ship with ndots:5 by default, so an in-cluster name like api.prod (one dot, under five) gets the full search list appended and walked before the bare name is tried. Every external lookup a busy pod makes can turn into a fan of failed suffixed queries before the real one goes out. The standard fix is exactly the thing this whole post is about: put a trailing dot on names you know are absolute — api.example.com. — and the resolver stops guessing. The dot that “does nothing” is a measurable latency fix.

Above DNS, the consensus falls apart

So DNS is clear: the dot is the root, absolute versus relative is a real distinction, done. The problem is that no layer above DNS agreed to honor it, and each one improvised.

TLS carries the server’s name in the SNI extension so the server knows which certificate to present. Per the spec, SNI is a byte string that must not include a trailing dot. So at the TLS layer, example.com. and example.com are supposed to be the same string — the dot gets stripped before it goes on the wire. Except clients don’t agree on who strips it. curl strips the trailing dot from SNI; popular browsers send it as typed. Go has a long-open issue (#63117): its TLS server strictly rejects an SNI name with a trailing dot, so a browser that sends one fails the handshake outright. Same name, same server, different answer depending on which client you used.

HTTP has the same split in the Host header. Does example.com. and example.com count as one virtual host or two? Depends on the server and how it was configured. Some vhosts match the dotted form; some 404 it.

Cookies got one explicit rule and one silence. RFC 6265 says a Domain attribute that ends in a dot makes the user agent ignore the attribute entirely. But its definition of a canonicalized host says nothing about trailing dots, so whether a cookie for example.com belongs to example.com. is left to each implementation. And when implementations improvise, this stops being trivia.

The dot that broke security

When two layers disagree about whether two spellings are the same name, you get a security bug, because a check performed under one spelling doesn’t cover the other.

curl learned this twice. In 2022, CVE-2022-30115 — an HSTS bypass. HSTS is the mechanism that says “for this host, never speak plain HTTP again.” curl stored the HSTS flag under the exact hostname. Store it for example.com, then request example.com., and the lookup missed — curl didn’t think it had an HSTS entry for that “different” name, and happily downgraded to HTTP. Or store it with the dot and request without. Either way, the trailing dot slipped past the very protection meant to be absolute. The bug was introduced in curl 7.82.0 and patched in 7.83.1, shipped the same day the advisory went out.

Then 2026 produced a nastier one: CVE-2026-8924, the “trailing dot domain super cookie.” The Public Suffix List is what stops a site from setting a cookie for co.uk or com — a cookie scoped to a public suffix would be readable across every site under it, which is exactly the cross-site leak the PSL exists to prevent. A PSL-aware curl correctly rejects Domain=co.uk. But it accepted Domain=co.uk. — same suffix, one dot on the end — because the trailing-dot form didn’t match the entries in the list it was checking against. A malicious server could set a cookie scoped to a public suffix and have curl send it to unrelated third-party domains under that suffix. The PSL check was real; the trailing dot walked around it. It affected curl from 7.46.0 all the way through 8.20.0 — more than a decade of releases — and was fixed in 8.21.0 in June 2026. (Note that RFC 6265 already says a Domain attribute with a trailing dot should be ignored outright.)

Notice the shape both bugs share. Nobody wrote broken logic. Someone wrote a correct check — “is HSTS set for this host,” “is this domain a public suffix” — and the trailing dot created a second spelling of the same name that the check didn’t recognize. The vulnerability lives in the gap between two layers’ definitions of “same.”

Why this keeps happening

You could ask why we don’t just pick one rule and enforce it everywhere. The honest answer is that we’re forty years deep. The dot-as-root is baked into DNS at the protocol level and can’t change. Every layer above it made an independent, reasonable-at-the-time decision about whether to normalize the dot away, and those decisions are now frozen into deployed clients, servers, and libraries that all have to keep interoperating. There is no central authority that can retrofit consistency onto TLS stacks, HTTP servers, cookie jars, and HSTS caches at once.

So the trailing dot survives as a kind of permanent seam in the system — usually invisible, occasionally a performance win if you know to use it, and every so often a security hole when two components on either side of the seam disagree about what they’re looking at. It’s a small thing. That’s exactly why it keeps getting missed.

The practical takeaways are short. If you write software that compares, caches, or security-checks hostnames, normalize the trailing dot first, before any comparison, and do it the same way everywhere — one canonical form, applied consistently, is the entire fix. If you run infrastructure and care about lookup latency, a trailing dot on names you know are absolute is free and effective. And if you ever see example.com. in a URL and assume it’s a mistake — it’s the opposite. It’s the only form that’s telling you the whole name.