TLS-RPT Reports: How SMTP TLS Reporting Works (RFC 8460, with JSON example)
SMTP STARTTLS is opportunistic. When a TLS handshake fails or downgrades to plaintext between two mail servers, the sending side sees the bounce and the receiving side sees nothing. A TLS-RPT report (RFC 8460) is the only standard channel that flips that asymmetry: senders email you a daily JSON aggregate of every TLS session attempted against your MX hosts and tell you exactly which ones broke.
That visibility matters because the policies that enforce TLS are still rare. Across DMARCguard’s February 2026 scan of 5.5 million domains, only 0.3% publish an MTA-STS policy and 0.0% publish a DANE TLSA record. URIports’ January 2026 Top 1M survey caps MTA-STS at 0.7% globally — and because TLS-RPT is almost always deployed alongside MTA-STS, that 0.7% is the practical ceiling for TLS-RPT adoption too. (DMARCguard’s scanner doesn’t yet record _smtp._tls TXT presence directly; we cite URIports’ Top 1M survey (~0.7%) as the closest public proxy.) The few operators who do enforce policies depend on TLS-RPT to know when their policy is breaking real mail. Read the TLS-RPT protocol fundamentals for the full primer, or keep going for the things every other guide skips.
This post walks through a real annotated JSON dump, the eleven IANA result types each paired with a fix, the deployment order operators actually back, and provider-specific setup for Google Workspace, Microsoft 365, Postfix, and Exim.
What is a TLS-RPT report?
A TLS-RPT report is a once-per-UTC-day JSON aggregate that a sending MTA emails (or HTTPS-POSTs) to a recipient domain. It summarises how many SMTP sessions opened — or failed to open — TLS against that domain’s MX hosts and records which MTA-STS or DANE policy was applied to each one. The reporting cadence and shape are fixed: per RFC 8460 §4.1, one report per (sender, recipient, policy) tuple per UTC day, with up to four hours of randomised delay.
The protocol was standardised in September 2018 by authors at Yahoo, Google, and Comcast as the visibility companion to MTA-STS (RFC 8461) and DANE-for-SMTP (RFC 7672). MTA-STS and DANE tell senders to enforce TLS; TLS-RPT tells you when that enforcement broke something.
Every report contains the same top-level shape:
organization-name— who sent the report (e.g. Google Inc., Microsoft Corporation).date-range— the 24-hour window covered, in RFC 3339 timestamps.contact-info— an addr-spec mailbox a human can reply to.report-id— a sender-defined unique identifier.policies[]— an array of{policy, summary, failure-details[]}objects, one per evaluated policy. The RFC mandates an array even for a single policy.
Confirmed senders today are Google, Microsoft, Comcast, Mail.ru, and Mimecast (URIports, August 2024). Yahoo and Apple are claimed by some vendor blogs but unconfirmed by any public JSON dump as of April 2026 — assume they may emit reports, but don’t depend on it.
An annotated TLS-RPT JSON report (RFC 8460 §4)
The single most useful thing a TLS-RPT guide can do is paste a real report and walk every field. This one is a Google → krvtz.net aggregate adapted from Paweł Krawczyk’s tlsrpt-rs fixtures, with the recipient domain sanitised to example.com and a validation-failure block grafted in from a separate Google → acme.com sample so both the heartbeat and failure paths are exercised.
{
"contact-info": "smtp-tls-reporting@google.com",
"date-range": {
"end-datetime": "2024-09-18T23:59:59Z",
"start-datetime": "2024-09-18T00:00:00Z"
},
"organization-name": "Google Inc.",
"policies": [
{
"failure-details": [
{
"failed-session-count": 2,
"receiving-ip": "173.212.201.41",
"receiving-mx-hostname": "mail.example.com",
"result-type": "validation-failure",
"sending-mta-ip": "209.85.222.201"
},
{
"failed-session-count": 1,
"receiving-ip": "173.212.201.41",
"receiving-mx-hostname": "mail.example.com",
"result-type": "validation-failure",
"sending-mta-ip": "209.85.208.176"
}
],
"policy": {
"mx-host": [
"mail.example.com"
],
"policy-domain": "example.com",
"policy-string": [
"version: STSv1",
"mode: testing",
"mx: mail.example.com",
"max_age: 86400"
],
"policy-type": "sts"
},
"summary": {
"total-failure-session-count": 3,
"total-successful-session-count": 47
}
}
],
"report-id": "2024-09-18T00:00:00Z_example.com"
} Block-by-block, here’s what each field means in the wild — and where vendors diverge from the spec.
organization-name is a required string. Google still emits "Google Inc." even though the legal entity has been Google LLC since 2017. Microsoft emits "Microsoft Corporation". Mail.ru emits the bare "Mail.ru". Reference parsers (parsedmarc, mox, tlsrpt-rs) all reject reports lacking this field.
date-range.start-datetime / end-datetime are RFC 3339 strings. There’s a quiet bug here: Google and Microsoft emit T00:00:00Z → T23:59:59Z, while Mail.ru emits T00:00:00Z → next-day T00:00:00Z. Computed report duration differs by one second across senders, and no parser normalises it.
contact-info is an addr-spec — Google uses smtp-tls-reporting@google.com, Microsoft uses tlsrpt-noreply@microsoft.com. Per RFC 8460 §5.6, this value is allowed to differ from the SMTP envelope-from of the report email itself. Don’t assume they match.
report-id has three real formats coexisting in production:
| Format | Sender | Example |
|---|---|---|
<ISO-8601>_<policy-domain> | 2024-09-18T00:00:00Z_krvtz.net | |
<FILETIME>+<policy-domain> | Microsoft | 133765181884122747+krvtz.net |
<UUID>@<reporter-domain> | Mail.ru | c96d67df-0440-57f7-6e96-c83824d0fdf2@mail.ru |
A parser that assumes a single regex will quietly drop two-thirds of senders.
policies[] is required to be an array, even for a single policy. Multi-policy reports — one entry for MTA-STS, one for DANE TLSA — are documented in the RFC and demonstrably emitted by Postfix 3.10+ to BSI TR-03108 recipients, but no public capture of one exists as of April 2026.
Inside each policy:
policy.policy-typeis an enum:sts,tlsa, orno-policy-found. The latter is what you see when only_smtp._tlsis published with no MTA-STS — Google heartbeats from low-MTA-STS recipients carry it.policy.policy-string[]is required to be an array of strings. Mailhardener’s KB shows a scalar form ("version: STSv1 mode: enforce …") which violates the schema; parsedmarc silently drops scalar values.policy.policy-domainis your domain, verbatim, no trailing dot.summary.total-successful-session-countis the daily heartbeat counter. Microsoft requires at least two messages per UTC 24-hour window before generating any report at all — low-volume domains see Microsoft as a non-emitter.summary.total-failure-session-countis the daily failure counter. No parser checks that this matches the sum offailed-session-countacrossfailure-details[], so vendor inconsistencies pass through silently.failure-details[]is omitted entirely on clean days. When present, each entry carries aresult-type(the IANA enum we’ll dissect in the next section), asending-mta-ip, an optionalreceiving-mx-hostnameandreceiving-ip, a requiredfailed-session-count, and an optionalfailure-reason-code.
There’s one last footgun worth flagging. The RFC 8460 prose calls a field additional-info-uri; the schema example in §4.4 spells it additional-information. mox follows the schema spelling, parsedmarc follows the prose spelling, and parsedmarc silently drops the schema-spelled key. DMARCguard’s parser handles both. If your aggregator only reads one, you’re losing data — quietly.
A clean Google heartbeat and a Microsoft sts-policy-fetch-error report look very different on the wire. The diff is worth seeing side-by-side:
Want this annotation rendered automatically against your own report? Paste a sample into DMARCguard’s TLS Report Analyzer and every field is labelled inline.
Every TLS-RPT result type and how to fix it
The IANA “STARTTLS Validation Result Types” registry has held exactly eleven values since 2018-09-28 — frozen, despite RFC 8460 §4.3 explicitly anticipating growth. Recovering signal from a TLS-RPT report means knowing which fault each value points at and what to change. The registry itself is the canonical source: IANA STARTTLS Validation Result Types.
Every cell below uses the verbatim hyphenated string so you can grep your own reports.
result-type | What it means | Most likely cause | Sender or receiver fix |
|---|---|---|---|
starttls-not-supported | Receiver MX answered EHLO but didn’t advertise STARTTLS, or returned 5xx. | TLS not configured; middlebox stripping the 250-STARTTLS line. | Postfix: smtpd_tls_security_level = may + smtpd_tls_chain_files. Exim: tls_advertise_hosts = * + tls_certificate. Disable SMTP fixup on ASA. |
certificate-host-mismatch | Cert SAN/CN doesn’t cover the MX hostname. | Web cert installed on the MX, or shared-IP cert. | Re-issue with every MX FQDN in SAN. For Exim multi-MX with SNI, use tls_certificate = ${if eq{$tls_in_sni}{...}{...}}. |
certificate-expired | Server presented a cert past its notAfter. | Renewal automation broken or didn’t reload the MTA. | certbot renew --deploy-hook 'systemctl reload postfix'. Postfix caches smtpd_tls_chain_files at startup — reload is required. |
certificate-not-trusted | Sender couldn’t build a chain to a trusted root. | Leaf-only cert served; missing intermediate; CA not in trust. | Serve full chain via smtpd_tls_chain_files = .../privkey-fullchain.pem. Exim: point tls_certificate at fullchain.pem, not cert.pem. |
validation-failure | RFC 8460 §4.3 catch-all for anything not above. | TLS-version mismatch, cipher mismatch, mid-handshake reset. | Inspect failure-reason-code. Modernise to TLS 1.2+: Postfix smtpd_tls_protocols = >=TLSv1.2. Update ca-certificates. |
tlsa-invalid | TLSA RR present but no records validate against the served cert. | Let’s Encrypt key rotated without pre-publishing new TLSA. | Publish 3 1 1 SPKI SHA-256 at _25._tcp.<mx-host>. Pre-publish before rotation; use certbot renew --reuse-key. |
dnssec-invalid | Validating resolver returned SERVFAIL/bogus on MX, A/AAAA, or TLSA. | Expired RRSIG; missing DS at registrar; broken NSEC3. | Run a validating local resolver (unbound: validate: yes). Re-sign with ECDSAP256SHA256. Push DS at the registrar. |
dane-required | Sender mandates DANE; no usable DNSSEC-secure TLSA was found. | TLSA published at _25._tcp.<domain> (wrong) instead of MX. | Publish TLSA at _25._tcp.<mx-host> for every MX. Publish DS at registrar. (Postfix dane-only; Exim hosts_require_dane.) |
sts-policy-fetch-error | Couldn’t GET https://mta-sts.<domain>/.well-known/mta-sts.txt. | TCP timeout; 4xx/5xx; forbidden redirect; cert expired. | nginx: location = /.well-known/mta-sts.txt { default_type text/plain; return 200 "..."; }. Bump id= after every policy edit. |
sts-policy-invalid | Policy fetched but failed RFC 8461 §3.2 parse. | UTF-8 BOM; CRLF; missing version: STSv1; invalid mode:. | LF newlines; no BOM; version: STSv1 first; mode: testing|enforce|none; mx: patterns; max_age: 0–31557600. |
sts-webpki-invalid | mta-sts.<domain> HTTPS host failed PKIX validation. | Self-signed cert; expired cert; no SAN for the hostname. | Publicly-trusted cert (Let’s Encrypt etc.) with SAN mta-sts.<domain>. Automated renewal with reload hook on a host that’s rarely visited. |
Five of these eleven values have no public verbatim JSON example as of April 2026: starttls-not-supported, certificate-expired, tlsa-invalid, dane-required, sts-policy-invalid, and sts-webpki-invalid. Their existence is documented at the log-line level (Postfix’s TLSRPT_README shows the exact mapping) but no operator has shared the corresponding aggregate JSON publicly. That’s a real gap in the corpus, not a comment on rarity in the wild.
A few cross-cutting notes the per-row fixes don’t fit. Let’s Encrypt’s OCSP responders shut down on 6 August 2025; legacy ACME configs that still set OCSP-Must-Staple have been silently surfacing as certificate-expired and validation-failure in TLS-RPT ever since. Postfix’s TLSRPT client (Postfix 3.10) emits at most one final TLS handshake status per SMTP connection and documents an average ~12-hour reporting lag, so Postfix-generated counts aren’t directly comparable to Microsoft- or Google-generated counts. Microsoft retries TLSA lookups every 15 minutes for the first 30 minutes and then hourly for 24 hours before issuing an NDR — DNSSEC rollover hiccups can be retried-through but still emit failed-session-count entries.
For the DANE-flavoured rows (tlsa-invalid, dnssec-invalid, dane-required), the DANE TLSA validation primer covers the operational discipline you need. For the MTA-STS rows (sts-policy-fetch-error, sts-policy-invalid, sts-webpki-invalid), MTA-STS explains the policy file. Two companion posts go deeper: What is DANE? (deeper walkthrough) and MTA-STS example walkthrough.
Pairing TLS-RPT with MTA-STS and DANE
A common mental model treats TLS-RPT as “phase zero” — turn on monitoring first, then layer policies on top. That model is wrong. TLS-RPT is not a freestanding monitoring phase. It only produces useful signal when paired with at least one TLS-enforcement policy; an MTA-STS policy in mode: testing is the minimum.
The deployment order operators actually back is consistent across primary sources:
- Publish
_smtp._tlsTXT and_mta-stsTXT inmode: testingtogether as a paired deployment. TLS-RPT is the report channel for the testing-mode policy and is functionally useless without it. - Monitor TLS-RPT for two to four weeks. security.gov.uk sets the floor at two weeks; NCSC-UK recommends one month.
- Promote MTA-STS to
mode: enforce. Bump theid=value in the_mta-stsTXT so caching senders re-fetch. - Add DANE/TLSA only if DNSSEC is operationally trusted. TLSA at
_25._tcp.<mx-host>, with DS at the registrar.
One major standards body actively dissents from this order. Germany’s BSI TR-03108 v2.0 mandates DANE for both inbound and outbound (TR-03108-05/06-M), mandates sending and receiving TLS-RPT (TR-03108-09/10-M), and only recommends MTA-STS (TR-03108-07/08-R). DANE has no testing mode by design, so a BSI-compliant deployment ships enforcement and reporting simultaneously. If you’re certifying for the BSI IT Security Label, that’s the order you follow; otherwise the NCSC + security.gov.uk staged ramp is the safer default.
Why does the testing window matter? In October 2020, a Mail-in-a-Box operator published an MTA-STS policy in mode: enforce with a stale id= value. Google sent clean TLS-RPT reports daily. Microsoft Exchange Online silently rejected mail for over a week with LED=450 4.4.317 — and Microsoft sent zero TLS-RPT reports during the entire incident. The lesson: monitoring is necessary but not sufficient. TLS-RPT report coverage is uneven across senders, and a non-emitter can break delivery without ever appearing in your dashboard.
Generate the records once you’re ready. Check your TLS-RPT record confirms the TXT syntax and walks every rua= URI for reachability. Generate a TLS-RPT record builds a valid record from a checklist.
TLS-RPT for Google Workspace and Microsoft 365
The two largest email platforms are also the two largest TLS-RPT report senders. Each has its own setup quirks.
Google Workspace
Inbound TLS-RPT for hosted Workspace domains is a one-record DNS change. Publish _smtp._tls.<your-domain> IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@<your-domain>" at your DNS provider — not in the Admin Console — and Gmail’s outbound senders honour it on day one. Reports arrive in standard RFC 8460 JSON, gzipped, daily.
Where to terminate the rua mailbox matters more than people expect. A dedicated Google Group with external-sender allowed is the cleanest pattern; alternatively, route to a third-party aggregator first (we’ll come back to ordering in a moment). For Gmail’s MTA-STS validator, navigate Admin Console → Apps > Google Workspace > Gmail > Compliance > MTA-STS > Validate.
Outbound TLS-RPT emission needs no Workspace toggle. Gmail emits daily aggregate reports for any domain that publishes a _smtp._tls TXT record — there’s no allowlist, no opt-in, no quota beyond standard rate limits. If you publish the record, you’ll start receiving Google reports within 48 hours.
Microsoft 365 / Exchange Online
Microsoft 365 is where TLS-RPT setup gets opinionated. Two gotchas dominate.
The first-rua bug. Microsoft delivers TLS-RPT reports to only the first rua= URI in your TXT record (URIports, August 2024). The common pattern of listing your own mailbox first and a third-party aggregator second loses all Microsoft data on the second endpoint. URIports’ recommendation, which we second: list your aggregator first.
The volume threshold. Microsoft requires at least two messages per UTC 24-hour window before generating any report at all. Low-volume domains see Microsoft as a non-emitter — silence isn’t success, it’s the threshold not being met.
For the rua mailbox itself, avoid Defender-quarantined shared mailboxes (aggregate reports are a known false-positive trigger for some quarantine policies). The preferred pattern is a no-quarantine distribution list to a parser-aware mailbox.
Microsoft does not host customer MTA-STS policies for Exchange Online. Per Microsoft Learn, customers must configure their own STS policy hosting. The recommended pattern is an Azure Static Web App on mta-sts.<domain> with a custom-domain managed cert, paired with the _mta-sts TXT record and the TLS-RPT TXT.
For the broader sender-side picture on these two platforms, Google + Yahoo sender requirements and Microsoft DMARC enforcement cover the DMARC and SPF angles. TLS-RPT is the third leg of the stool.
TLS-RPT for Postfix and Exim
Self-hosted MTAs split sharply on TLS-RPT support: Postfix has gone all-in, Exim has not.
Postfix 3.10+ ships native TLS-RPT support via libtlsrpt. Wire it into main.cf:
smtp_tlsrpt_enable = yes
smtp_tlsrpt_socket_name = /var/run/tlsrpt.sock Then run sys4’s tlsrpt-collectd and tlsrpt-reporter daemons. They populate /etc/tlsrpt/reportd.cfg with organization_name, contact_info, and sender_address — the values that Postfix-emitted reports will carry in their organization-name and contact-info fields. Reports are aggregated to SQLite, generated against the RFC 8460 schema, and dispatched via SMTP or HTTPS POST. The full reference is Postfix TLSRPT_README.
For Postfix < 3.10 or operators not adopting libtlsrpt, TLS-RPT consumption (parsing reports about your own MX) is well-supported by parsedmarc. TLS-RPT emission (generating reports about other people’s MX you delivered to) is not.
Exim is, as a TLS-RPT emitter, a dead-end. The Arch Wiki states it bluntly: “Exim only supports DANE and not MTA-STS or TLS-RPT.” Exim’s source carries no TLS-RPT reporter, and as of April 2026 there’s no roadmap commitment. Operators who need to emit TLS-RPT from an Exim site front it with a Postfix 3.10+ relay or accept the gap.
While we’re correcting the Arch Wiki: that page calls TLS-RPT reports “XML formatted.” They aren’t. TLS-RPT reports are gzipped JSON conforming to RFC 8460 §4 — the only related XML is DMARC’s aggregate-feedback schema, which is a different protocol entirely.
How DMARCguard delivers TLS-RPT reports
DMARCguard ingests mailto: rua endpoints, gunzips the attachment, parses the RFC 8460 schema, validates each result-type against the IANA registry, and renders the failure details with sender-IP attribution and remediation guidance linked inline. Every per-result-type cell is one click away from the corresponding fix, with the IANA-verbatim hyphenated string preserved so what you see in the dashboard matches what dig and jq show against the raw report.
What separates this from generic parsedmarc / Mailhardener / PowerDMARC dashboards: the field-name schism is handled. DMARCguard reads both additional-information (schema-faithful) and additional-info-uri (prose-faithful) so reports from mox-based senders don’t disappear into a silent drop the way they do in parsedmarc. The Microsoft first-rua bug is surfaced as a setup warning rather than a missing-data mystery. And the sts-policy-invalid body diff between testing and enforce policies is rendered side-by-side, not buried in raw text.
If you want to see TLS-RPT working without a signup: check your TLS-RPT record, generate a TLS-RPT record, or paste a sample into the TLS Report Analyzer. All three are free. Monitoring all seven core protocols on the free tier (nine on Pro) is the next step when you’re ready.
Frequently asked questions
What is a TLS-RPT report?
A TLS-RPT report is a once-per-UTC-day JSON aggregate that a sending mail server emails to a recipient domain, summarising every SMTP TLS session attempted that day. Defined in RFC 8460, it lists successes, failures, the MTA-STS or DANE policy applied, and an IANA result type for each fault.
How do I check my TLS-RPT record?
Run dig +short TXT _smtp._tls.<your-domain> to see the published policy. For deeper validation, paste the domain into DMARCguard’s TLS-RPT checker — it parses the TXT syntax, walks every rua= URI, and confirms the reporting mailbox is reachable so senders won’t silently drop reports.
How is a TLS-RPT report delivered?
Reports are gzipped JSON delivered over mailto: (DKIM-signed, content type application/tlsrpt+gzip) or HTTPS POST. RFC 8460 §3 requires delivery even when TLS to the report mailbox fails — so reports may arrive in cleartext when the very condition they describe blocks encryption.
Do I need TLS-RPT if I already have MTA-STS or DANE?
Yes. TLS-RPT is the only feedback channel for an MTA-STS policy in mode: testing and the only way to know a DANE rollover hasn’t broken inbound mail. Without it, an enforce-mode policy can silently bounce real mail — exactly what happened to a Mail-in-a-Box operator with Outlook in October 2020.
Why am I not getting Microsoft TLS-RPT reports?
Two known causes. Microsoft delivers reports to only the FIRST rua= URI in your TXT record, so a third-party aggregator listed second receives nothing. Microsoft also requires at least two messages per 24-hour UTC window before generating any report at all (URIports, August 2024).
What does sts-policy-fetch-error mean in a TLS-RPT report?
The sender saw a valid _mta-sts TXT record but couldn’t fetch https://mta-sts.<domain>/.well-known/mta-sts.txt — a TCP timeout, a 4xx/5xx response, or a forbidden redirect. Fix it by serving plain text with no redirects, then bump the policy id= so caching senders re-fetch.
How often are TLS-RPT reports sent?
Once per UTC day per sender, recipient, and policy tuple, per RFC 8460 §4.1, with up to four hours of randomised delay. Postfix 3.10’s reporter documents an additional ~12-hour processing lag, so a report covering Monday usually arrives mid-Tuesday.
Conclusion
A TLS-RPT report is daily JSON visibility for SMTP TLS — the only standard channel that tells a recipient domain when senders broke or downgraded the encrypted handshake. Every report distills to the eleven IANA result types covered above, and recovering signal means matching each verbatim string to the right fix on the right side of the conversation. Deploy paired with MTA-STS in mode: testing, monitor for two to four weeks, and only then promote — and remember that Microsoft’s first-rua bug and the two-message volume threshold are the two most common reasons operators think they have no Microsoft data.
Want to start? Check your TLS-RPT record free with DMARCguard’s TLS-RPT checker — no signup required. Or drop a sample into the TLS Report Analyzer and watch the IANA-token annotation render automatically. For the protocol fundamentals, the TLS-RPT primer is the next read.