Learn
MTA-STS and TLS-RPT
SPF, DKIM and DMARC protect mail sent in your name. MTA-STS protects mail sent to you: it tells other servers to deliver only over TLS to your real mail servers, and TLS-RPT tells you when they can't.
By Ahmet Karaca · Updated · Checked against RFC 8461 (MTA-STS) and RFC 8460 (SMTP TLS Reporting) on
The problem: STARTTLS is optional
Servers that deliver mail to each other upgrade the connection to TLS with the STARTTLS command. That upgrade is opportunistic: if the receiving server doesn't advertise STARTTLS, or the certificate it shows doesn't check out, the sending server usually delivers the message anyway, in plaintext or over an unauthenticated connection. That keeps mail flowing, but it also means encryption depends on nobody interfering.
RFC 8461 describes the two attacks this leaves open. An attacker who can delete the "250 STARTTLS" line from the server's reply forces a downgrade to plaintext, even though both servers support TLS. An attacker who can spoof your MX record or reroute the connection can pose as your mail server, because without a policy the sender has no way to know which certificate to expect. RFC 8460 points out that the receiving server may never notice either attack, so you can't rely on your own logs to catch it.
MTA-STS (SMTP MTA Strict Transport Security) closes that gap by letting you publish a policy that says: my mail servers support TLS with a valid certificate, these are their names, and here is what to do when that check fails. TLS-RPT (SMTP TLS Reporting, RFC 8460) is its companion: sending servers send you a daily summary of how TLS went when they delivered to you, including the failures.
MTA-STS: a TXT record and a policy file
MTA-STS has two parts, and a sender needs both. The first is a TXT record at _mta-sts under your domain. It announces that a policy exists and carries its current version:
_mta-sts.example.com. TXT "v=STSv1; id=20261013120000Z"v=STSv1is required and must come first.id=is required: up to 32 letters and digits that uniquely identify this version of the policy. Senders compare it with theidthey cached last time, so any value works as long as it changes with every policy change. The RFC's own example uses a timestamp, but it implies no ordering between values.- If senders find more than one
v=STSv1record at that name, or one that doesn't parse, they treat your domain as having no MTA-STS policy.
The second part is the policy itself, a plain-text file served over HTTPS at a fixed address on the mta-sts host of your domain: https://mta-sts.example.com/.well-known/mta-sts.txt. It holds one key/value pair per line:
version: STSv1
mode: testing
mx: mx1.example.com
mx: mx2.example.com
max_age: 1296000versionmust beSTSv1.modeisnone,testingorenforce(see the table below).mxnames a host allowed to receive your mail, one line per host. It can be a full name such asmx1.example.comor a wildcard such as*.example.com, which matches exactly one label:mail.example.com, but notexample.comora.b.example.com. At least onemxline is required unless the mode isnone.max_ageis how long, in seconds, senders may cache the policy after fetching it. The maximum is 31557600 seconds (365.25 days), and the RFC expects values "in the range of weeks or greater".
| Mode | When TLS or the MX check fails | Used for |
|---|---|---|
none | Senders treat the domain as if it had no policy. | Removing MTA-STS cleanly. |
testing | Senders that support TLS-RPT report the failure, then may deliver anyway. | Rolling out and watching reports. |
enforce | Senders must not deliver to the failing host and try the next MX or retry later. | Protection once reports are clean. |
The certificates have to be valid
MTA-STS depends on ordinary web PKI certificates in two places. The HTTPS server at mta-sts.example.com must present a certificate valid for that name, unexpired and chaining to a root the sender trusts. The policy only counts when the server answers 200 OK: senders must not follow redirects. Every MX host named in the policy must then offer STARTTLS with a certificate that is unexpired, chains to a trusted root and lists the MX hostname in its subject alternative names. A self-signed certificate, or one issued for a different name, fails validation.
Change the id whenever the policy changes
Senders don't download the policy file on every message. They check the id in the TXT record and fetch the file again only when it differs from the one they cached. If you edit the file but leave the id alone, senders may keep using the old policy until its max_age runs out. The RFC also sets the order: update the policy file first, then the TXT record, so a sender who sees the new id never caches the old file by mistake.
Two scope rules are easy to miss. A policy covers only the exact domain it is published on: for mail to user@mail.example.com, senders look at mail.example.com and never fall back to example.com. And if a provider runs your mail, the RFC describes delegating the policy with a CNAME on _mta-sts, while the mta-sts host still needs a certificate for your own name.
TLS-RPT: reports on how delivery went
MTA-STS on its own is silent: a sender that refuses to deliver doesn't tell you. TLS-RPT gives you that feedback. You publish a TXT record at _smtp._tls under your domain:
_smtp._tls.example.com. TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"v=TLSRPTv1is required and comes first.rua=is where reports go. It acceptsmailto:andhttps:addresses, and a comma-separated list of several. Reports sent by mail must carry a valid DKIM signature from the reporting domain, and reports without one must be ignored.- As with MTA-STS, more than one
v=TLSRPTv1record at that name means no TLS-RPT at all.
A report is a JSON file that should be gzip-compressed and should cover one full UTC day. It names the organization that sent it, the date range, and for each policy it applied: the policy type (sts for MTA-STS, tlsa for DANE, or no-policy-found), the policy text, the count of successful and failed TLS sessions, and a detail entry for each kind of failure. A trimmed example in the RFC's format:
{
"organization-name": "Example Sender",
"date-range": {
"start-datetime": "2026-10-12T00:00:00Z",
"end-datetime": "2026-10-12T23:59:59Z"
},
"contact-info": "tlsrpt@sender.example.net",
"report-id": "2026-10-12-example.com",
"policies": [{
"policy": {
"policy-type": "sts",
"policy-string": ["version: STSv1", "mode: testing", "mx: mx1.example.com", "mx: mx2.example.com", "max_age: 1296000"],
"policy-domain": "example.com",
"mx-host": ["mx1.example.com", "mx2.example.com"]
},
"summary": {
"total-successful-session-count": 412,
"total-failure-session-count": 3
},
"failure-details": [{
"result-type": "certificate-expired",
"sending-mta-ip": "198.51.100.25",
"receiving-mx-hostname": "mx2.example.com",
"receiving-ip": "192.0.2.20",
"failed-session-count": 3
}]
}]
}The result-type tells you what went wrong. The RFC defines starttls-not-supported, certificate-host-mismatch, certificate-expired, certificate-not-trusted and validation-failure for the TLS negotiation, and sts-policy-fetch-error, sts-policy-invalid and sts-webpki-invalid for problems with the MTA-STS policy itself. The success count matters too: it shows that reporting works even on days without failures.
Rolling out: testing first, then enforce
The RFC describes testing as a soft deployment for finding policy failures, and its example policy uses it to collect reports without changing how messages are handled. That gives a safe order:
- List the hosts that receive your mail with the MX lookup, and make sure each one offers STARTTLS with a valid certificate for its own hostname.
- Publish the
_smtp._tlsrecord so reports start arriving. - Serve the policy file with
mode: testingand anmxline for every MX host, then publish the_mta-stsrecord. - Read the reports for a while. In testing mode, senders that support TLS-RPT report each failure but may still deliver, so a mistake costs you nothing but a report entry.
- When the failure counts are zero or explained, change the file to
mode: enforce, then change theidin the TXT record.
RFC 8461 doesn't give one recommended max_age. It caps the value at 31557600 seconds, expects it "typically be in the range of weeks or greater", and in its security section says to prefer values "as long as is practical", because an attacker who blocks the first policy lookup can still downgrade a sender with nothing cached. Its own examples use 604800 seconds (7 days) for an enforce policy and 1296000 seconds (15 days) for a testing one.
Keep in mind what a long max_age means once you enforce. Senders keep the cached policy until both the file and the TXT record change and the record's DNS TTL has passed, so when you move to a new mail provider, add the new MX hosts to the policy and change the id before you switch the MX records. To retire MTA-STS, the RFC suggests publishing mode: none with a short max_age (its example is 1 day, 86400 seconds), changing the id, and removing the record and file only after every older policy has expired.
What Patchkit covers
The MTA-STS checker reads the _mta-sts TXT record and validates it, fetches and parses the policy file, checks every one of your MX hosts against its mx lines, and reads the _smtp._tls record for TLS-RPT. What it can't do is open a connection to your MX hosts on port 25, so it doesn't test whether they actually offer STARTTLS or which certificate they present there. Your TLS-RPT reports show how real senders' connections went. Patchkit's report monitoring handles DMARC aggregate reports, not TLS-RPT reports, so point rua= in your TLS-RPT record at a mailbox or service that can read the JSON format above. To read a single report, open it in the TLS-RPT report analyzer, which reads it in your browser.
The MX lookup shows the hosts your mx lines must cover, and the DMARC checker covers the records that authenticate your outgoing mail, which MTA-STS leaves untouched.