Your subdomains are already in the public log: 84 seconds from certbot to the first external request

You set up a panel on a fresh subdomain — panel.example.com, unknown to any search engine and to any of your friends. You started certbot, got a green lock, and went to brew tea. After a minute and a half, the access log already contains other people’s GET /.env and GET /.git/config.

This is not a leak and not a hack. It is the normal operation of a mechanism that is built into HTTPS on purpose and cannot be disabled: Certificate Transparency. Any name for which a public CA has issued a certificate is at that moment in an open log — forever, accurate to the second of issuance.

For OSINT, this is one of the cheapest ways to map someone else’s infrastructure. For the entity that maintains that infrastructure, it is a constantly operating channel of disclosure that many discover as soon as it happens.

Info:

This material is useful if you have your own domain and at least one service over HTTPS: the Remnawave or Marzban panel, 3x-ui, Grafana, Vaultwarden, Immich, or a test stand. If you use someone else’s VPN and don’t own your own domains — this is more about how open-source intelligence gathering works.

Why the certificate has no “do not publish” mode

Certificate Transparency emerged as a response to the forgery of certificates by certificate authorities: if every issuance ends up in a public append-only log based on a Merkle tree, the domain owner can notice a rogue certificate issued in their name. The logic is sound, and it works exactly as intended.

The side effect is that the “public log” is public to everyone, not just to the domain owner. And you cannot bypass it: browsers stop trusting a certificate that isn’t in the logs.

Chrome’s policy requires that a certificate has at least two SCTs — signed attestations of inclusion in the log — for a lifetime up to 180 days, and at least three for longer lifetimes, with at least two SCTs coming from different log operators. Safari requires at least two SCTs. An SCT is a promise from the log to include the entry in the tree within the Maximum Merge Delay, usually 24 hours; in practice the entry appears in search within minutes.

Important:

Options like “issue me a certificate but do not write the name in the log” do not exist either for Let’s Encrypt or any other publicly trusted CA. The choice is not between logging and not logging, but between “which name you will disclose in the log” and “do you actually need a public certificate for this host.”

Chronology: 23 seconds to DNS, 84 to HTTPS

The speed at which a name from the log becomes traffic on your port was measured twice, independently and four years apart.

The Sheep group and colleagues (IMC 2018, TU München and partners) set up 11 honeypot subdomains with random 12-character names and released them to the world in the only possible way — through a CT log. The first DNS queries arrived in 73 seconds; major resolvers Google, 1&1, and Amazon visited all eleven names within 12 minutes. The first HTTP connection came within a range from 59 minutes to 19 days, most often in the first hour or two. One machine scanned 30 ports on honeypots.

The Plötz group from UCSB repeated the measurement in 2022 (EuroS&P 2023) — and the numbers condensed by an order of magnitude. Median time to first DNS query — 23 seconds. To first HTTPS request — 84 seconds.

The most illustrative part of their experiment lasted 200 days. The control group of hosts lived with self-signed certificates and did not appear in CT for 200 days; the experimental group received ordinary public certificates. For IPv6 the control group over 200 days collected exactly zero incoming packets. The experimental group collected about 2,700, from 959 unique addresses across 112 autonomous systems. For IPv4, CT-name requests came from 2,291 addresses across 267 AS.

The IPv6 part is key: the address space is too large to enumerate, which means the only source of knowledge about these hosts was the log.

What a bystander sees in five minutes

Monitors like crt.sh, Censys, and merklemap mirror all logs and allow searching over them. The basic query is plain HTTP:

curl -s 'https://crt.sh/?q=example.com&output=json' | jq -r '.[].name_value' | tr ';' '\n' | sort -u

The same search with a wildcard character % (in the URL — %25) yields all subdomains at once. If crt.sh responds with Unsupported use of '%', there remains direct access to its database, which the project keeps open:

psql -h crt.sh -p 5432 -U guest certwatch
Warning:

The crt.sh search syntax has changed: support for % has been narrowed at different times. Do not rely on it in scripts — check the response, and if it fails, switch to psql or another monitor. The content of the logs does not change because of this.

The scale of what’s there was assessed by Roberts and Levin (WPES 2019, University of Maryland) over the full set of certificates from Censys. From host names they extracted more than 130,000 user names on seven sites, 5,861 email addresses in subdomains of a single cloud database service, 77,825 domains for which relationships “supplier — client” can be reconstructed, and 25,346 unique subdomains of a single company, Discovery. In the sample, internal QA and staging servers, admin panels, and code names of unreleased products were regularly included.

One team alone saw their side of the picture:

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

Everything listed in the output has already been read by someone else, except you.

Four forks — and the price of each

Approach Name in log What you pay
Certificate per host All names Full infrastructure map exposed
One wildcard for a zone Only *.example.com DNS-01, one private key for the entire zone
REALITY (XTLS) Nothing Only a proxy, not web services
Your own CA, self-signed Nothing Manual root deployment to clients

Wildcard is the simplest step and exactly what Roberts and Levin recommend. A key limitation: it hides one left-level only. The name a.b.example.com under *.example.com does not fall under it, and for it you need a separate certificate — and that name goes into the log again. Let’s Encrypt wildcard issuance is exclusively via DNS-01: HTTP-01 and TLS-ALPN-01 are unsuitable for this.

certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cf.ini \
  -d 'example.com' -d '*.example.com'

The cost is one private key for the entire zone: if you compromise the host where it resides, you compromise the entire subdomain.

REALITY bypasses the problem entirely because it does not issue a certificate at all: the server borrows the TLS handshake of another site indicated in dest/target and presents the client with its certificate. Nothing goes into the CT log — there is simply nothing to log. This only works for proxy traffic; Grafana and Immich won’t hide that way.

Your own CA is suitable for internal services that you and three of your devices access. The root certificate must be manually installed on every client, but the names stay under your control.

The fourth option is to accept public exposure and close the perimeter. Then anything that does not match a known name should not even complete the handshake:

server {
    listen               443 ssl default_server;
    ssl_reject_handshake on;   # nginx 1.19.4 and newer
}

Plus authentication at the entry, not behind a login form: mTLS, basic-auth at the reverse-proxy level, or access only from a VPN subnet. The point is that 84 seconds is not a long time to set things up — by the moment of the first request, everything should already be in place.

Security:

Searching CT-logs is public data and legal in itself. It ceases to be legal the moment the found hosts are checked for liveliness and vulnerabilities without the owner’s explicit permission. The boundary lies between “looked at an open log” and “sent a packet to someone else’s server.”

Related on the forum: Hysteria2 inbound + wildcard TLS for Remnawave, Guide: OSINT for beginners, Guide: how Reality (XTLS) works.

Sources

Question:

Check your domain with crt.sh — how many names did you see there, and how many of them would you prefer not to publish? And if you have already switched to wildcard: how did you resolve the issue of a single private key for the entire zone?