Certificate Renewal Deployment Gap Gate
Pricing
from $10.00 / 1,000 run starteds
Certificate Renewal Deployment Gap Gate
Find the renewal that did not reach every server. For each hostname the Actor resolves every A and AAAA address, opens a TLS handshake to each address with the hostname as SNI, and compares the certificate that each address serves with the newest certific
Pricing
from $10.00 / 1,000 run starteds
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Find the renewal that did not reach every server.
Automatic renewal works until it does not. A new certificate is issued, one server in the pool takes it, and one does not. The name still answers, the monitoring still says "days to expiry: 60", and one address out of four keeps serving the old certificate until it expires.
This Actor is not an expiry checker. It answers one question for each hostname: is the certificate that every address serves the newest certificate that the CA issued for this name?
How it works
For each hostname:
- It resolves the A and AAAA records over DNS-over-HTTPS, so it sees the whole address pool and not one cached address.
- It opens one TLS handshake to each address, with the hostname in the SNI extension, and reads the certificate that this one address serves.
- It reads the Certificate Transparency logs for the exact name and takes the newest certificate that is still valid.
- It compares the two and gives one verdict.
Verdicts
| Verdict | Meaning |
|---|---|
current | Every address serves the same certificate, and the CT logs hold nothing newer. |
renewed-not-deployed | The CT logs hold a newer certificate than one or more addresses serve. The renewal happened and did not reach every server. |
mixed-across-ips | The addresses behind the name serve different certificates. |
renewal-overdue | The certificate is past the renewal point and the CT logs hold no newer certificate. The renewal did not happen. |
unreachable | The name did not resolve, every address was refused, or no address completed a handshake. |
renewed-not-deployed wins over mixed-across-ips, and mixed-across-ips
wins over renewal-overdue. The verdict_reasons field lists every condition
that holds, so a hostname that is both mixed and behind CT shows both.
The comparison always uses the oldest certificate that any address serves. One laggard address is the whole failure that this Actor reports.
Certificate Transparency lag
A CT log shows a certificate minutes to hours after the CA issues it. A
certificate that a server already serves and the log does not show yet is
therefore treated as current, never as renewal-overdue. A difference below
60 seconds does not count as newer. When both CT sources fail, the run reports
ct_status: failed, gives no renewed-not-deployed and no renewal-overdue
verdict, and still reports mixed-across-ips.
Input
| Field | Type | Default | Description |
|---|---|---|---|
hostnames | array | ["apify.com", "iana.org"] | 1 to 300 fully qualified hostnames. Add a port with a colon, for example api.example.com:8443. The default port is 443. An IP address is refused. |
max_ips_per_hostname | integer | 4 | 1 to 8. How many addresses of the name get a handshake. |
renewal_point_fraction | number | 0.33 | A certificate is due for renewal when less than this fraction of its lifetime is left. |
alert_webhook_url | string | empty | One public HTTPS URL. One JSON POST goes out when a run finds a gap. |
request_timeout_seconds | integer | 15 | Timeout for one DNS query, one handshake, one CT query and the POST. |
concurrency | integer | 4 | How many hostnames the run checks at the same time. |
max_run_seconds | integer | 240 | Wall-clock deadline for the list. |
Every field has a default, so a run with an empty input works and checks the two demo hosts. The Actor needs no API key and no secret.
It reads three public sources: Cloudflare and Google for DNS-over-HTTPS, crt.sh for the CT history, and Cert Spotter when crt.sh does not answer. Cert Spotter sends no serial number, so a fallback comparison uses the validity dates.
Output
One record for each hostname
| Field | Description |
|---|---|
target, hostname, port | The name that this record describes. |
verdict, is_gap, verdict_reasons | The verdict, whether it is billed as a gap, and every condition that holds. |
reason_code | OK, DNS_EMPTY, DNS_FAIL, BLOCKED_TARGET, HANDSHAKE_FAIL, RUN_DEADLINE, CHARGE_LIMIT or INVALID_HOSTNAME. |
ip_addresses_checked, ip_addresses_reachable | The addresses behind the name, and the ones that answered. |
certificates | One entry for each address: ip_address, fingerprint_sha256, serial_number, not_before, not_after, issuer, san_names, name_match, handshake_ms, error. |
renewal_gap_ip_addresses | The addresses that serve a certificate older than the newest one in CT. |
deployed_fingerprints, distinct_certificates | The SHA-256 fingerprints in the pool. More than one means mixed-across-ips. |
oldest_deployed_* | The fingerprint, serial, dates and issuer of the laggard certificate. |
remaining_lifetime_fraction, days_to_expiry, renewal_due | Where the laggard certificate stands against the renewal point. |
ct_status, ct_source, ct_entries_matched | Whether the CT lookup worked, and which source answered. |
ct_serial_number, ct_not_before, ct_not_after, ct_issuer | The newest valid certificate for the exact name in the CT logs. |
One summary record
gate_pass, hostnames_in_input, hostnames_checked, renewal_gaps,
verdict_counts, gap_hostnames, unreachable_hostnames and alert_status.
gate_pass is true only when every hostname has the current verdict. A gap
and an unreachable host both turn it off.
A failed gate, an unreachable host and an input mistake are results, not
failures. The run always ends succeeded, and the verdict is in the dataset
and in the run status message. Wire gate_pass into your pipeline.
Pricing: pay per event
| Event | Price | When the Actor charges it |
|---|---|---|
run-started | USD 0.01 | Once for each run, before the first query. |
hostname-checked | USD 0.004 | Once for each hostname that the run resolved, handshaked and compared with the CT logs. An unreachable hostname is charged here, because the work was done. A hostname that the input got wrong, and one that the run deadline or the charge limit cut off, are not charged. |
renewal-gap-flagged | USD 0.02 | Once for each hostname with a renewed-not-deployed, mixed-across-ips or renewal-overdue verdict. An unreachable hostname is never a gap. |
The default maximum total charge is USD 3 for each run. When a run reaches that
limit it checks fewer hostnames; a hostname that it did not check gets the
CHARGE_LIMIT reason code, and a gap that the limit did not cover is counted in
gaps_not_charged and still reported.
Schedule it
Run it on a daily Apify schedule. Automatic renewal runs on a cycle and each cycle can miss one server. From 2026 the CA/Browser Forum cuts the maximum certificate lifetime in steps, so there are more renewal cycles each year and more chances for one of them to go half way.
Limits
- 1 to 300 hostnames for each run, and 1 to 8 addresses for each hostname.
- At most 64 KB from one DNS answer and 8 MB from one CT answer.
- The run stops at the
max_run_secondsdeadline. - Loopback, private, link-local and reserved addresses are never contacted, in the hostname list, in the DNS answers and in the webhook URL.
- Plain HTTPS and plain TLS. No browser, no proxy, no key, no stored state.