Resources / Transport Security
TLS-RPT (RFC 8460): SMTP TLS reporting explained
TLS-RPT is the feedback channel for SMTP transport security. Senders mail you a daily JSON summary of how many TLS sessions succeeded or failed against your domain and why. This page covers the _smtp._tls TXT record, the report schema, every failure result-type, and how to use reports to roll out MTA-STS and DANE safely - per RFC 8460.
Last checked: June 22, 2026
SMTP TLS Reporting (TLS-RPT, RFC 8460, September 2018) solves a specific problem with enforced transport security: when MTA-STS or DANE blocks a delivery, the failure happens at the sender, not at you. A misconfigured certificate, a stripped 250 STARTTLS, or a botched TLSA record all look the same from your side - mail simply does not arrive, or arrives later. As RFC 8460 puts it, “because such ‘downgrade attacks’ are not necessarily apparent to the receiving MTA, this document defines a mechanism for sending domains to report on failures at multiple stages of the MTA-to-MTA conversation.”
The 60-second version
- Publish one DNS record: a TXT record at
_smtp._tls.<domain>with arua(reporting address). - Cooperating senders mail (or POST) you a daily JSON report - one per UTC day - summarising TLS sessions to your MX hosts.
- Each report carries a success count (a heartbeat that reporting works) and, for failures, a
result-typethat names the cause: STARTTLS missing, certificate expired, MTA-STS policy invalid, DNSSEC invalid, and so on. - TLS-RPT itself enforces nothing. It is pure observability - the instrument you watch while rolling out MTA-STS and DANE.
- Deploy it first, before turning any policy to enforcing.
The record
; mailto: destination
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tlsrpt@example.com"
; or an HTTPS endpoint
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=https://reporting.example.com/v1/tlsrpt"
The record lives at the label _smtp._tls under the Policy Domain - for example.com, that is _smtp._tls.example.com. It has two fields:
| Field | Required | Value |
|---|---|---|
v | Yes | TLSRPTv1 |
rua | Yes | A comma-separated list of mailto: and/or https: destinations for reports |
Parsing follows the same discipline as the other records: senders discard any _smtp._tls record not starting v=TLSRPTv1;, and “if the number of resulting records is not one, senders MUST assume the recipient domain does not implement TLSRPT.” You can list more than one rua; a reporter may try each, and the report is considered delivered once any endpoint accepts it.
How reports arrive
Reports can come two ways:
- Email (
mailto:). The report is amultipart/report; report-type="tlsrpt"message containing a human-readabletext/plainpart and a machine-readableapplication/tlsrpt+jsonpart (usually gzipped asapplication/tlsrpt+gzip). It carries two headers,TLS-Report-DomainandTLS-Report-Submitter, and must be DKIM-signed by the reporting domain. Crucially, emailed reports “MUST NOT use the ‘l=’ attribute” (so nothing can be appended without breaking the signature) and SHOULD declare the DKIM service types=tlsrpt. - HTTPS (
https:). The report isPOSTed to the URI; success is an HTTP200or201. Report submitters “MAY ignore certificate validation errors when submitting reports via HTTPS POST” - because a misconfigured SMTP server often means a misconfigured HTTPS server, and the report needs to get through anyway.
A defining rule: when reports are emailed, the sender “MUST deliver reports despite any TLS-related failures” for that report’s own delivery - otherwise a TLS problem would suppress the very report that describes it.
Other delivery facts from RFC 8460:
- A report SHOULD cover a full UTC day (
00:00–24:00) and be sent after a delay (the spec illustrates a random delay of up to four hours) to avoid thundering-herd load. - The report SHOULD be gzip-compressed; a commonly observed receiver limit is ten megabytes.
- On a delivery failure the sender SHOULD retry for up to 24 hours, ideally with exponential backoff. Reports are optional, so non-delivery is tolerated.
- The recommended filename is
sender!policy-domain!begin-timestamp!end-timestamp!unique-id.json.gz.
The JSON report schema
Reports are Internet JSON (I-JSON, RFC 7493). The shape is small and stable:
{
"organization-name": "Company-X",
"date-range": {
"start-datetime": "2026-06-01T00:00:00Z",
"end-datetime": "2026-06-01T23:59:59Z"
},
"contact-info": "sts-reporting@company-x.example",
"report-id": "5065427c-23d3-47ca-b6e0-946ea0e8c4be",
"policies": [{
"policy": {
"policy-type": "sts",
"policy-string": ["version: STSv1", "mode: testing",
"mx: *.mail.company-y.example", "max_age: 86400"],
"policy-domain": "company-y.example",
"mx-host": "*.mail.company-y.example"
},
"summary": {
"total-successful-session-count": 5326,
"total-failure-session-count": 303
},
"failure-details": [{
"result-type": "certificate-expired",
"sending-mta-ip": "2001:db8:abcd:0012::1",
"receiving-mx-hostname": "mx1.mail.company-y.example",
"failed-session-count": 100
}, {
"result-type": "starttls-not-supported",
"sending-mta-ip": "2001:db8:abcd:0013::1",
"receiving-mx-hostname": "mx2.mail.company-y.example",
"receiving-ip": "203.0.113.56",
"failed-session-count": 200
}]
}]
}
The key fields:
| Field | What it tells you |
|---|---|
organization-name, contact-info, report-id | Who sent the report and a unique id to deduplicate |
date-range | The reporting window (a full UTC day) |
policies[].policy.policy-type | Which policy was applied: sts, tlsa, or no-policy-found |
policies[].policy.policy-string | The literal policy seen (the MTA-STS lines, or the TLSA records) |
policies[].policy.policy-domain, mx-host | The domain and MX the policy applies to |
summary.total-successful-session-count | The heartbeat - sessions that negotiated policy-compliant TLS |
summary.total-failure-session-count | Sessions that failed |
failure-details[] | One entry per failure type, with result-type, sending IP, receiving MX, and a failed-session-count |
Each failure-details entry can also carry receiving-ip, receiving-mx-helo, an additional-information URI, and a free-text failure-reason-code (for example an OpenSSL error string). Note that “the failure types are non-exclusive” - one session hitting several errors can appear under several types, so the failure counts are not a clean partition of the failure total.
The failure result-types
This is the part you actually act on. RFC 8460 defines the initial set in three groups.
Negotiation failures (the TLS handshake or certificate itself):
result-type | Meaning |
|---|---|
starttls-not-supported | The MX did not offer STARTTLS at all |
certificate-host-mismatch | The certificate did not match any identity required by the policy (no matching SAN) |
certificate-expired | The presented certificate has expired |
certificate-not-trusted | Untrusted/unknown CA, chain errors, name-constraint failures, and similar |
validation-failure | A general failure that does not fit a category above (use failure-reason-code for detail) |
DANE-specific policy failures:
result-type | Meaning |
|---|---|
tlsa-invalid | None of the TLSA records in the RRset validated against the certificate |
dnssec-invalid | No valid (DNSSEC-validated) records were returned by the resolver |
dane-required | The sender requires DANE for this destination but no DNSSEC-validated TLSA record was present |
MTA-STS-specific policy failures:
result-type | Meaning |
|---|---|
sts-policy-fetch-error | The MTA-STS policy could not be fetched (for example, the policy host was unreachable) |
sts-policy-invalid | The overall MTA-STS policy failed validation |
sts-webpki-invalid | The MTA-STS policy could not be authenticated via PKIX (Web PKI) |
One deliberate omission: transient errors like TCP timeouts and too-busy networks “are not required to be reported,” so the reports stay focused on TLS and policy problems rather than ordinary network noise.
How to read a report
A practical reading order:
- Check the heartbeat. A healthy
total-successful-session-countwith zero or near-zero failures means senders are negotiating TLS with you as expected. - Triage by
result-type.certificate-expiredorcertificate-host-mismatchis your own certificate or MX config.sts-webpki-invalidorsts-policy-fetch-erroris an MTA-STS problem (a bad cert on themta-stshost, or an unreachable policy endpoint).dnssec-invalidortlsa-invalidis a DANE/DNSSEC problem. - Correlate with the IPs and MX hosts.
sending-mta-ipplusreceiving-mx-hostnametell you whether a problem is one sender, one of your MXes, or everyone. - Watch for the attack signature. A sudden cluster of
starttls-not-supportedagainst an MX that normally offers STARTTLS, especially from many senders at once, is the fingerprint of an active downgrade attempt - exactly what RFC 8460 was written to make visible.
Why TLS-RPT and DNSSEC go together
There is a chicken-and-egg risk: an adversary who can downgrade your TLS can often also suppress the _smtp._tls TXT lookup, hiding the evidence of their own attack. RFC 8460 therefore advises that “administrators are thus encouraged to deploy TLSRPT TXT records with a large TTL … or to deploy DNSSEC on the deploying zone.” A signed zone makes the reporting record itself tamper-evident.
Common confusion
- “TLS-RPT encrypts or enforces something.” It does neither. It only reports. Enforcement is MTA-STS and DANE; TLS-RPT is the dashboard.
- “No report means everything is fine.” No report can also mean no sender supports TLS-RPT, or your record is wrong, or the report was suppressed. Confirm you are receiving reports from at least the large providers before you trust the silence.
- “Reports are real-time.” They are daily aggregates, intentionally delayed by hours. TLS-RPT is for trend and incident detection, not live alerting.
- “A
certificate-expiredreport is a sender problem.” It is almost always your certificate (or your MX’s) that expired - the report is describing what senders saw when they connected to you.
What this means for you, and what Egressif does
We treat TLS-RPT as the prerequisite for any enforced transport security, not an afterthought. On the domains we operate we publish the _smtp._tls record before switching MTA-STS to enforce or turning on DANE, so a misconfiguration shows up as a failure report instead of as quietly delayed mail. We monitor the result-type mix the same way we watch DMARC aggregate reports: a rise in certificate-expired is a renewal we missed, a burst of sts-webpki-invalid points at the mta-sts host’s certificate, and a spike in starttls-not-supported from many senders is treated as a possible downgrade attack until proven otherwise. We keep the record on a signed zone where the domain is DNSSEC-enabled, so the reporting channel cannot be quietly cut off by the same attacker it is meant to expose.
Related references
- Email encryption in transit: STARTTLS, MTA-STS, DANE SMTP started as a cleartext protocol, and the TLS that secures it today is opportunistic and unauthenticated by default. This page explains STARTTLS, the downgrade and stripping attacks it cannot stop on its own, and how MTA-STS and DANE turn best-effort encryption into enforced, authenticated delivery.
- MTA-STS (RFC 8461): policy, records, and modes MTA-STS lets a domain demand authenticated TLS for inbound mail using DNS plus an HTTPS-served policy, without DNSSEC. This page covers the _mta-sts TXT record, the well-known policy file, the three modes, max_age caching, and exactly how a sending MTA validates and applies a policy - all per RFC 8461.
- DANE for email (RFC 7672): TLSA records and DNSSEC DANE uses DNSSEC-signed TLSA records to tell senders exactly which certificate or key your MX must present, making SMTP TLS downgrade-resistant. This page covers the TLSA record format, the hard DNSSEC dependency, the certificate usages to publish, and a DANE vs MTA-STS comparison so you know when each applies - per RFC 7672, 6698, and 7671.
Tell us what you run today.
Domains, rough volume, current providers, and what hurts. You will get a straight answer on fit, and a real number, in one conversation.