Skip to content
Control Plane Labs

How to find every subdomain with CT logs

Certificate Transparency logs are the most complete public record of a company's hostnames. How to query them, clean the output, and run it as a standing control.

Ben Ennis

Published July 27, 2026

Asset inventory is the security control everybody agrees with and nobody finishes. The list of hostnames an organisation actually operates is always longer than the list anyone can produce from memory, and the gap is where incidents live. CISA made this explicit for federal agencies in BOD 23-01, which mandates automated asset discovery on a seven-day cadence.

Certificate Transparency is the cheapest way to close most of that gap. It is public, authoritative, free, and read-only.

Scope note before anything else: run this against domains you own or have written authorisation to assess. Reading a public log is not an intrusion, but acting on what you find — resolving, connecting, testing — is where authorisation starts to matter. Nothing in this article involves touching a target system.

Why CT is a better source than a wordlist

Traditional subdomain discovery brute-forces a wordlist against DNS. It is noisy, slow, and only finds names someone thought to put in a wordlist. CT inverts the problem: instead of guessing names, you read the list of names a CA has already certified.

The reason this works is a hard requirement, not a convention. Chrome has enforced its CT policy since April 2018 — a publicly-trusted certificate without evidence of inclusion in qualifying logs is rejected outright. Apple enforces the same in Safari. So a certificate that works in a browser is a certificate that is in a public log, and the log entry contains every hostname on it via the Subject Alternative Name extension defined in RFC 5280 §4.2.1.6.

The result is a list of names that were real enough that someone requested a certificate for them. That is a much higher-signal set than a wordlist.

What you actually query

Three practical options, in increasing order of effort.

A CT search front-end. crt.sh, operated by Sectigo, indexes the logs and exposes a JSON endpoint. The CT lookup tool here does the same thing with a cleaner output shape.

curl -s 'https://crt.sh/?q=%25.example.com&output=json' \
  | jq -r '.[].name_value' \
  | tr '[:upper:]' '[:lower:]' \
  | tr '\n' '\n' | sed 's/^\*\.//' \
  | sort -u

The % is a SQL wildcard, so %.example.com matches every subdomain. The sed strips the leading *. from wildcard entries, and sort -u collapses the enormous amount of duplication you are about to see.

The logs directly. Every RFC 6962 log exposes a get-entries endpoint (RFC 6962 §4.6) that returns raw DER certificates by index. This is authoritative and completely impractical for a single-domain question — you are downloading the entire web’s certificates to find yours. It is the right approach only if you are building a monitor.

Tile-based static logs. The newer Static CT API, formerly the Sunlight API, replaces the dynamic read path with immutable, cacheable “tiles” of hashes served as static assets. Monitors fetch tiles in parallel and compute proofs locally. Submission still uses the RFC 6962 add-chain endpoints, so nothing changes for CAs. If you are writing a log follower in 2026, write it against the tiled API.

Cleaning the output

Raw CT output is dirty in specific, predictable ways.

Pre-certificates double everything. CAs submit a poisoned pre-certificate to the log before issuing the real one, so most hostnames appear at least twice per issuance. Deduplicate on hostname, not on log entry.

Renewals multiply it further. A 90-day certificate renewed for three years is a dozen entries for the same name. That gets worse from here: the maximum validity for a publicly-trusted certificate dropped to 200 days in March 2026 under CA/Browser Forum ballot SC081v3, falling to 100 days in 2027 and 47 days in 2029. Let’s Encrypt’s 160-hour shortlived profile is generally available now. Shorter certificates mean strictly more log entries per hostname per year, and any deduplication that was optional before is mandatory now.

Wildcards hide the interesting names. A *.internal.example.com entry tells you the subtree exists but names nothing in it. Treat it as a lead, not a result.

Expired is not the same as gone. A certificate that expired in 2022 still tells you the hostname existed. Keep it in the inventory with a date, because forgotten hosts are the ones that matter. Note that CT says nothing about revocation — that lives in CRLs, and since Let’s Encrypt shut down its OCSP responders in August 2025, increasingly only in CRLs.

The issuer column is the second half of the data

Most people extract hostnames and throw the rest away. The issuer field is worth keeping, because the distribution of CAs across your own domains is a cheap governance signal.

An organisation with one CA and one renewal cadence has central certificate management. An organisation with four CAs, three overlapping cadences, and a handful of certificates issued by a CA nobody recognises has teams buying their own. That last category is worth a conversation before it becomes an expiry incident at 03:00.

It also matters for chain debugging. Let’s Encrypt rotated to a new “Generation Y” hierarchy in 2026 and now selects an intermediate at random for each issuance specifically to discourage intermediate pinning. If you see YE1, YE2, YR1, and YR2 scattered across your own certificates, that is correct behaviour and not four different problems.

curl -s 'https://crt.sh/?q=%25.example.com&output=json' \
  | jq -r '.[] | "\(.issuer_name)"' \
  | sed 's/.*O=\([^,]*\).*/\1/' \
  | sort | uniq -c | sort -rn

A CA in that list you cannot account for is the misissuance case CT exists to surface. Before escalating, check the obvious explanations first: a CDN or load balancer provider issuing on your behalf, a SaaS vendor terminating TLS on a CNAMEd subdomain, or a marketing platform that provisioned a certificate when someone pointed a record at it. Those account for nearly every surprise. What remains after that is a genuine lead, and the CA’s problem-reporting address is the place to take it.

What CT will not find

CT is a record of certificate issuance, not of infrastructure. It misses anything that never got a publicly-trusted certificate: hosts behind an internal CA, plain-HTTP services, names covered only by a wildcard, and anything fronted by a CDN certificate that lists the CDN’s names instead of the customer’s.

It also tells you nothing about whether a name resolves or a service is running. Those require DNS and a connection respectively — the DNS lookup tool is the usual next step, and that step is the one where authorisation matters.

Running it as a standing control

The one-off query is the least valuable use. The valuable version is a diff.

Query CT for your domains nightly, normalise to a sorted set of hostnames, and compare against yesterday. A new hostname is one of three things: a legitimate deployment nobody told you about, a shadow-IT project, or a certificate issued for your domain by a CA you do not use. The third case is the one CT was designed to catch, and it is why the OWASP Web Security Testing Guide lists CT review as a standard information-gathering step.

Keep the output boring. A daily “no change” is what a working control looks like, and it makes the day something does change impossible to miss.

The corollary is worth internalising if you operate domains: every certificate you request publishes its hostnames permanently, to an append-only log you cannot edit. Name hosts accordingly. checkout-v2-staging is fine. acme-corp-acquisition-diligence is a press release.

Frequently asked questions

Is querying CT logs legal?+
Reading a public append-only log is not unauthorised access — the data is published deliberately, and the logs exist to be audited. What can cross a line is what you do next: resolving, connecting to, or testing the hosts you discover. Run discovery against domains you own or are contractually authorised to assess.
Why does the same hostname appear twenty times?+
Pre-certificates and renewals. CAs log a pre-certificate before issuing the real one, so every issuance produces at least two entries, and every renewal produces another pair. Deduplicate on the hostname string, and keep the earliest and latest dates if you care about lifecycle.
Can a company remove its subdomains from CT logs?+
No. CT logs are append-only Merkle trees; that is the entire security property. The only remediation for a hostname you did not want published is to revoke the certificate and rename the underlying resource. Requesting deletion is not a thing that exists.
Does CT find internal hostnames?+
Only if a publicly-trusted CA issued a certificate for them, which happens more often than people expect — *.corp.example.com and jira.internal.example.com routinely show up because someone wanted a green padlock. Hosts served by a private CA or by HashiCorp Vault PKI will not appear.
How current are CT logs?+
Effectively real-time. The Signed Certificate Timestamp is embedded in the certificate before the CA hands it over, so a hostname is usually queryable within seconds of issuance — well before the service it belongs to is in production.

Tags: #certificate-transparency, #asset-inventory, #security, #dns