Certificate Transparency

How to monitor your subdomains with Certificate Transparency

Every TLS certificate trusted by browsers is logged to an append-only public ledger called a Certificate Transparency log — within minutes of issuance. That means every subdomain of your domain that has ever gotten a cert is already public knowledge. Here is what that means for you, and how to stay on top of it.

What Certificate Transparency is

Certificate Transparency (CT) is a set of IETF standards (RFC 6962 and RFC 9162) that require publicly-trusted Certificate Authorities to submit every issued certificate to one or more independent, append-only logs before it is trusted by browsers. The logs are Merkle-tree structures that make it mathematically provable that no certificate was silently inserted or removed after the fact.

The upshot: there is no such thing as a "private" certificate from a publicly-trusted CA. The moment DigiCert, Let's Encrypt, or any other browser-trusted CA signs a cert for staging.yourcompany.com, that hostname appears in at least two CT logs, and within minutes it is indexed and queryable by anyone on the internet.

How crt.sh works

crt.sh is a free public CT log aggregator run by Sectigo (formerly Comodo CA). It polls all recognized CT logs and indexes the resulting certificates into a PostgreSQL database with a public web and JSON API. You can use it right now without any credentials.

To see all certificates ever issued for subdomains of example.com, query the JSON endpoint directly:

curl -s "https://crt.sh/?q=%25.example.com&output=json"   | jq '.[].name_value' | sort -u

The %25 is a URL-encoded wildcard (%). The name_value field contains the Subject Alternative Names from the certificate — every hostname the cert covers. The response is a JSON array of objects, each representing one certificate. Relevant fields include:

  • name_value — the hostnames (SANs) the cert covers, newline-separated
  • not_after — the cert's expiry timestamp in UTC (ISO 8601)
  • issuer_name — which CA signed it
  • entry_timestamp — when it was logged to CT

A complete query including expiry data looks like:

curl -s "https://crt.sh/?q=%25.example.com&output=json"   | jq '.[] | {name: .name_value, expires: .not_after}'

Why a forgotten subdomain becomes discoverable the moment it gets a cert

Consider the lifecycle of a typical self-hosted project. You spin up a staging environment: staging.yourapp.com. You point Caddy at it, Caddy auto-provisions a Let's Encrypt cert via ACME, and the ACME CA logs that cert to CT within 60 seconds. The subdomain is now indexed by crt.sh, Censys, and every other CT monitor on the internet — before you have even finished deploying the code.

You later shut down the staging environment but forget to remove the DNS record or the Caddy site block. The cert expires, but the CT entry persists forever (CT logs are append-only). If you later update the staging config and Caddy renews the cert, the hostname resurfaces in CT with a fresh not_after. Anyone running automated CT monitoring against your domain — including a competitor or a vulnerability researcher — sees it immediately.

The same applies to:

  • Internal tools inadvertently given public subdomains (kibana.yourapp.com)
  • Developer experiments left running (test-2023.yourapp.com)
  • Acquired or migrated services that were never fully decommissioned
  • Subdomains created by third-party SaaS integrations on your behalf

The baseline-diffing approach

A one-time CT query tells you your current state. Continuous monitoring tells you when that state changes. The pattern is simple:

  1. Query crt.sh for all SANs under your apex domain; deduplicate and store the result.
  2. On the next scheduled query, compare the new result against the stored baseline.
  3. Any hostname in the new result but not in the baseline is a new subdomain — alert immediately.
  4. Update the baseline; repeat on a schedule.

This is exactly what Perimeter does on every scan cycle. On the Solo plan, scans run daily; on the Agency plan, every 6 hours. New CT entries usually appear within minutes of cert issuance, so 6-hourly scans keep the gap tight for domains where latency matters.

The alert includes the new hostname, the issuing CA, and the not_after date from the CT record — so you know immediately whether this is a short-lived cert (90-day Let's Encrypt, probably an ACME auto-provision) or a longer-lived cert from a paid CA.

What to do when a new subdomain appears

When an alert fires for a hostname you don't recognize:

  1. Check who requested the cert. The issuer_name field tells you the CA. Let's Encrypt means an ACME client on a server you control provisioned it automatically. A commercial CA means someone explicitly purchased and installed the cert.
  2. Look up the DNS record. Does staging.yourapp.com actually resolve to an IP you recognize? If it points to a third party's infrastructure, that is a higher-priority investigation. See also: DNS drift monitoring.
  3. Check your Caddy or nginx configuration. Look for site blocks you may have forgotten. On a Caddy setup, cat /etc/caddy/Caddyfile or check any conf.d/ includes.
  4. If it is legitimate, add it to your known baseline so future alerts focus on genuinely new discoveries.
  5. If it is unknown or unauthorized, treat it as a potential compromise of your DNS or certificate provisioning pipeline and investigate accordingly.

CAA records: limiting who can issue certs for your domain

While CT monitoring shows you what certs have been issued, CAA DNS records let you restrict which CAs may issue certs for your domain in the future. A CAA record like:

yourapp.com. CAA 0 issue "letsencrypt.org"

tells every other CA that they must refuse to issue a cert for yourapp.com or any subdomain (unless a more-specific subdomain record overrides it). CT monitoring and CAA records are complementary: CAA narrows the attack surface; CT monitoring audits it.

Perimeter's DNS drift monitoring will alert you if your CAA records change unexpectedly — for instance, if an attacker who has gained access to your DNS removes the CAA restriction before requesting a fraudulent cert.

Running a free CT scan right now

Perimeter's free one-shot report queries crt.sh for every cert ever issued to subdomains of your domain, resolves each hostname's DNS, and returns the full picture — no account, no credit card, just your domain. The free report runs the same query described above and presents it in a readable format. Continuous monitoring and baseline diffing are the paid feature.

Frequently asked

How quickly does a new subdomain appear in crt.sh after a cert is issued?

Most CA submissions are logged within 60 seconds of issuance. crt.sh indexes new CT log entries on a continuous polling cycle, so a new hostname is typically searchable within 5 to 15 minutes of the cert being issued. On the Agency plan, Perimeter scans every 6 hours, so the maximum delay from issuance to alert is around 6 hours in the worst case.

Do CT logs record certs for private CAs or self-signed certs?

No. Only certificates submitted by publicly-trusted Certificate Authorities are required to appear in CT logs. Internal certs signed by your own private CA, or self-signed certs, are not logged. If you use a private CA for internal services, those hostnames will not appear in crt.sh regardless of the subdomain.

What is the not_after field?

not_after is the X.509 certificate field that records the expiry timestamp — the moment after which the cert is no longer considered valid. It is stored as a UTC datetime in the CT log entry. Perimeter reads this from the CT record directly (no active connection to your server needed) to power the cert-expiry alerting.

Can I use crt.sh to monitor a competitor's subdomains?

The CT data itself is public, and querying crt.sh for any domain is legal (the data is intentionally public). Perimeter is designed for monitoring domains you own and have verified. Perimeter does not support monitoring domains you do not control.

Does Perimeter connect to the subdomains it discovers?

No. Perimeter reads CT log data from crt.sh and DNS records from public resolvers. It does not connect to, port-scan, or probe any discovered hostname. The only active outbound connection to a customer host is a TLS handshake to a verified, monitored hostname to read the live cert's not_after field — and even that requires prior domain ownership verification.

See what the internet already knows about your domain

Free one-shot report — no signup, no credit card. Built entirely on public data.

Run the free report