Back to Blog

Certificate Transparency Went Static. Your get-entries Monitor Is Already Dead.

RFC 6962 CT logs are shutting down and the new Static CT logs are flat files in object storage. If your monitoring still polls get-entries, it stopped seeing new certificates months ago. Here's what changed and how to migrate.

CertGuard TeamSSL Security Experts10 min read
Two dark server cabinets behind mesh doors, laced with orange and teal patch cables and dotted with small green and amber status lights

The endpoint your monitor polled is gone

If you wrote a Certificate Transparency monitor in the last five years, it almost certainly does one thing in a loop: call get-sth to learn how big the log is, then call get-entries to pull the new certificates. That code has a problem. The logs it talks to are being switched off.

Let's Encrypt took its RFC 6962 logs read-only on November 30, 2025 and shut them down completely on February 28, 2026. Not deprecated with a warning banner. Off. Any worker still pointed at those old endpoints got HTTP errors, then eventually connection refused, and unless someone was watching the logs of the log-watcher, it failed quietly. That is the worst kind of monitoring failure: the dashboard is green because nothing is coming in, and nothing coming in looks exactly like nothing happening.

We have written before about running CT monitoring at scale, and the Go worker in that post is built around get-entries. It was correct when we published it. It is now a museum piece. Here is what replaced it.

A log is a pile of files now

RFC 6962 treats a CT log as a service. You ask it questions over a REST API and it computes answers: give me entries 40000 to 40255, give me the current signed tree head, give me an inclusion proof. Every one of those is a live query against a running server, and every one costs the operator CPU and bandwidth on demand.

The replacement, specified at c2sp.org/static-ct-api and formerly called the Sunlight API, throws that model out. A Static CT log is a set of flat files sitting in object storage. Nobody computes anything when you read it. You fetch files, and a CDN in front of the bucket does most of the work.

The files are called tiles. The Merkle tree that backs the log gets sliced into chunks of 256 hashes. A full tile is exactly 256 hashes wide, 8,192 bytes, and once written it never changes. The certificates themselves live in data tiles, also 256 entries each. There is a single small file called checkpoint, which is the signed tree head: the root hash, the tree size, and the log's signature over both. That checkpoint is the one thing that updates, and it is tiny.

So the whole log is: one mutable pointer, plus an ever-growing stack of immutable 8 KB files. That is the entire idea.

Reading one is a handful of GETs

The submission side barely changed. CAs still add-chain to write certificates, and the log still hands back an SCT. If you run a CA or an ACME client, your issuance path is fine. It is the read side, the monitoring side, that got rewritten.

Old world, ask the server to do work:

# how big is the log right now?
curl "https://oak.example/ct/v1/get-sth"

# give me a page of entries
curl "https://oak.example/ct/v1/get-entries?start=40000&end=40255"

New world, fetch files and cache them until the heat death of the universe:

# the only thing that moves: the signed tree head
curl "https://log.example/checkpoint"

# data tile number 156, holding entries 39936..40191
curl "https://log.example/tile/data/156"

To tail a log you read the checkpoint, note the tree size, and pull whatever data tiles you do not have yet. Tile indexes above 999 get split into three-digit path segments with an x prefix on the leading groups, so entry tile 1,234,067 lives at tile/data/x001/x234/067. Odd looking, but it keeps any single directory in the bucket from holding millions of objects.

One trap worth naming. The newest tile is usually partial, fewer than 256 entries, and it is served at a .p/<width> suffix. Partial tiles change as the log grows. Full tiles never do. So you cache full tiles forever and you refuse to cache partial ones, otherwise you will serve yourself stale data and wonder why the last few certificates keep flickering.

Why operators wanted this

CT logs are punishing to run. Under RFC 6962 the get-entries endpoint is a live database read that anyone on the internet can hammer, and monitors did hammer it, constantly, because tailing a log means asking for the newest entries over and over. Logs fell over. Logs got rate limited. Logs quietly dropped monitors they did not like. The reflections Let's Encrypt published after a year on Sunlight get into the operational numbers, and the short version is that static files behind object storage are close to rate-limit free and cost a fraction of the old design.

There is a latency win too. RFC 6962 permits a Maximum Merge Delay of up to 24 hours, meaning a certificate can legally sit unlogged for a full day after issuance. Static logs merge far faster than that in practice, often within a minute or two, because appending a tile is cheap. For anyone hunting misissuance or phishing certs, that shrinks the window where a bad certificate exists but has not shown up anywhere you can see it.

The Hacker News thread on running a CT log is a good read if you want the operator's-eye view of why the old model became untenable.

What this breaks in your setup

If you own any of this code, go find it before it finds you.

Your log list is now mixed. Some logs still speak RFC 6962, some speak Static CT, and Google's Chrome CT log list marks which is which. A monitor that assumes one API will silently skip half the ecosystem. You either run both client paths or you drop coverage, and dropping coverage in CT monitoring means missing exactly the certificate you built the system to catch.

Your batch logic is wrong. Code that asks for 256 entries starting at an arbitrary offset does not map onto tiles, which are fixed 256-entry blocks at fixed boundaries. You do not choose the ranges anymore. The tiling does.

Your caching assumptions invert. Under the old API almost nothing was safely cacheable. Under the new one almost everything is, except the checkpoint and partial tiles. Teams that bolt a naive cache in front of a Static CT client and forget to exclude partials end up with a monitor that is permanently a few certificates behind.

Your inclusion-proof code changes shape. Proofs are no longer an endpoint you call. You reconstruct them from the tree tiles yourself, or you lean on a client library that does it for you. The phishing detection workflow still works the same way at the business level, watch the logs for lookalike domains, but the plumbing underneath that watch loop is different.

Migrating without a flag day

You do not need a big-bang rewrite, and you should not attempt one. Run both worlds side by side while the old logs finish dying.

Start by not writing the tile math yourself. Use a maintained Static CT client. The Sunlight project ships Go tooling, and the certificate-transparency-go ecosystem has been growing static support. The parsing of the certificate out of a data tile is the same X.509 you already handle, so your enrichment, your domain matching, and your alerting rules carry straight over. Only the fetch layer moves.

Then get the reader right:

1. GET /checkpoint, verify the log's signature on it
2. read the tree size from the checkpoint
3. for every full data tile you have not seen, fetch and cache it forever
4. fetch the trailing partial tile, use it, cache nothing
5. sleep, repeat from step 1

Keep your certificate baseline exactly as it was. If you already diff new CT entries against an inventory of what you expect, none of that logic is affected. This migration is a transport change, not a redesign. The failure mode to fear is not a crash. It is a monitor that keeps running, keeps its dashboard green, and stops seeing new certificates because it is politely retrying a log that no longer exists.

The merge delay got shorter, and that helps

Faster merges are not just an operator nicety. If you use CT logs as an early-warning system, and you should, the gap between issuance and visibility is your blind spot. A 24-hour worst case under the old rules meant an attacker with a misissued certificate had up to a day of quiet. Static logs closing that to minutes tightens the loop for defenders.

It does not eliminate the gap. A certificate can still be used before it appears anywhere, which is why CT monitoring is one layer and not the whole wall. CAA records, tight domain validation, and SCT enforcement in browsers all still matter. But the layer got noticeably sharper this year, for free, and you inherit that just by moving your reader to the new logs.

Where the rest of the ecosystem sits

Let's Encrypt now runs its production logs, Sycamore and Willow, on Sunlight. Cloudflare runs Static CT logs of its own. Chrome's CT log policy accepts static-ct-api logs alongside RFC 6962, so the browsers that make CT mandatory are already treating the new logs as first-class.

The timing is not an accident. Certificate lifetimes are dropping hard, and the 47-day maximum arriving by 2029 means every certificate gets reissued far more often. More reissuance means more CT entries, and the old dynamic-query logs were never going to carry that load cheaply. Static logs are how the ecosystem is bracing for the volume. So if you run CT monitoring in production, you are not migrating because it is trendy. You are migrating because the thing you were polling turned into a bucket of files, and the sooner your reader knows that, the sooner your green dashboard means something again.

Frequently asked questions

Do I need to change anything on the certificate issuance side? No. Static CT only changes how logs are read, not how certificates are submitted. Your ACME client and your CA still use add-chain and still receive SCTs the same way, so issuance and stapling are untouched.

Are all RFC 6962 logs shutting down, or just Let's Encrypt's? Let's Encrypt has retired its own RFC 6962 logs, with read-only on November 30, 2025 and shutdown on February 28, 2026. Other operators are on their own timelines, and some logs still run the old API today. Read Chrome's CT log list to see which logs are still live and which API each one speaks, and support both while the transition finishes.

How do inclusion proofs work if there is no proof endpoint? You rebuild them from the Merkle tree tiles instead of calling a get-proof API. The tiles at levels above zero hold the interior tree hashes you need, and a Static CT client library will assemble the proof from those tiles for you rather than making you do the hashing by hand.

Will crt.sh and the tools I already use keep working? Higher-level services that sit on top of the log ecosystem are adapting their own ingestion, so most of them keep working from your side without changes. The break only bites you if you consume raw logs directly with your own get-entries code, which is the case this article is about.

Related posts

Never miss an expiring certificate again

CertGuard watches your SSL certificates and domains, and alerts you long before anything expires. Free for your first five domains.

Start Free