Deploying MTA-STS and TLS-RPT to enforce and monitor encrypted inbound SMTP
STARTTLS is opportunistic — an on-path attacker can strip it and your mail crosses the wire in cleartext. MTA-STS makes TLS mandatory for inbound mail and TLS-RPT tells you when sessions fail. Here's the controlled none-to-testing-to-enforce rollout, the exact DNS and HTTPS artifacts, and how to read a real TLS-RPT report.
EvilMail TeamJuly 17, 202610 min read
Your inbound mail is encrypted right up until the moment someone decides it shouldn't be. SMTP's STARTTLS is advertised in the plaintext part of the session, before any encryption exists to protect it. An active on-path attacker — a compromised router, a hostile hotspot, a BGP hijack — rewrites the receiving server's capability line from 250-STARTTLS to 250-XXXXXXXA, the sending MTA sees no TLS option, and it delivers your mail — headers, body, any credentials — in the clear. No error. No bounce. The sender thinks it did the right thing.
It gets worse, because even when TLS *is* negotiated, SMTP TLS is unauthenticated by default. A sending server will happily complete a handshake against a self-signed certificate, an expired one, or a cert whose name has nothing to do with your MX host. There is no browser-style padlock refusing to load. The design assumption in RFC 3207 was "some encryption is better than none," and for two decades that was the ceiling.
MTA-STS (RFC 8461) raises the ceiling. It lets you, the receiving domain, publish a policy that says: for mail addressed to me, TLS is mandatory, and the certificate must validate and match these MX hosts — if it doesn't, don't deliver; defer and retry instead. TLS-RPT (RFC 8460) is the other half you cannot skip: a daily feedback channel where sending organizations report how many TLS sessions to your domain succeeded and, crucially, how many failed and why. MTA-STS without TLS-RPT is enforcement with a blindfold on. And flipping straight to enforce without first sitting in testing mode and reading a week of those reports is exactly how operators black-hole their own inbound mail.
Be precise about scope — this is where people burn a weekend. MTA-STS and TLS-RPT protect mail arriving at your domain: the connection between a sender and your inbound MX. They are orthogonal to SPF, DKIM and DMARC, which authenticate your outbound identity so other people can trust mail claiming to be from you. If someone tells you MTA-STS "helps your DMARC," they've conflated two different problems. This is transport security for your inbox, not identity for your outbox.
There are two competing standards for the job. DANE (RFC 7672) pins certificates via TLSA records in DNS, but it hard-depends on DNSSEC across your whole zone — and if your registrar or DNS host doesn't do DNSSEC cleanly, DANE is off the table. MTA-STS carries its trust over HTTPS with the public web PKI instead, so it works without DNSSEC. That's why it won in practice: Google, Microsoft and Yahoo all fetch and honor MTA-STS policies and all emit TLS-RPT reports as of 2026. If your inbound mail comes from the real world, having those three enforce on your behalf is most of the value.
The three artifacts that must agree
MTA-STS is three moving parts, and enforcement failures are almost always because two of them disagree:
A DNS TXT record at _mta-sts.<domain> that announces a policy exists and carries a version id.
An HTTPS-served policy file at https://mta-sts.<domain>/.well-known/mta-sts.txt, presented over 443 with a publicly-trusted certificate.
The MX hosts named in that policy, whose presented TLS certificates must cover their own hostnames.
The load-bearing detail: the mx patterns in the policy are matched against the certificate the MX host presents, not merely against the MX resource record. If your MX is mx1.evilmail.pro but that host serves a cert whose SAN only lists mail.evilmail.pro, a strict sender sees a host mismatch and, in enforce mode, refuses to deliver. This single mismatch is the number-one cause of self-inflicted enforcement outages. Sort your MX certificate SANs out *before* you touch anything else.
Publishing the record and the policy file
The announce record. The id must change on every policy update, because senders cache the policy keyed by that id — bump it or your change is invisible for up to max_age. Timestamp form is the convention; it's alphanumeric, max 32 characters.
dns
_mta-sts.evilmail.pro. IN TXT "v=STSv1; id=20260704T120000;"
The policy file itself. CRLF line endings are recommended, and the mode during your first deploy is testing, never enforce:
A few things that bite people. The wildcard *.evilmail.pro matches exactly one label — it covers mx1.evilmail.pro but not a.b.evilmail.pro. max_age is in seconds; the RFC permits up to 31557600 (a year), but during rollout keep it at 604800 (one week) so mistakes age out fast. Serve the file with Content-Type: text/plain — some validators and senders reject text/html. And the certificate on the mta-sts. host is an ordinary web cert (Let's Encrypt is perfect); it is a completely separate cert from the ones on your MX hosts.
An nginx location block on the mta-sts.evilmail.pro vhost is all the serving you need:
nginx
location = /.well-known/mta-sts.txt {
default_type text/plain;
alias /var/www/mta-sts/mta-sts.txt;
}
Start in testing, then read the reports
Testing mode is the whole point of a safe rollout. In mode: testing, a policy failure is reported but the mail still delivers — you discover the broken MX cert or the 500-ing well-known endpoint from the reports, not from an angry user whose mail vanished. To get those reports you publish the TLS-RPT record:
dns
_smtp._tls.evilmail.pro. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]"
rua can also point at an HTTPS endpoint (rua=https://...) if you'd rather ingest reports programmatically, but a mailbox you actually read is fine to start. Reporting is a daily aggregate: each sending organization posts one gzipped JSON report per day, filename <sending-domain>!<policy-domain>!<start-unix>!<end-unix>.json.gz. Gmail and Microsoft will start landing reports within a day of the record going live.
Reading a TLS-RPT report
Here's a trimmed real report body, the shape you'll actually get:
Read it top-down. policy-type: sts confirms the sender found and applied your MTA-STS policy (you may also see tlsa or no-policy-found). The two counts in summary are your headline metric — 8123 clean sessions, 14 failures. Then failure-details[].result-type tells you *why*, and each value maps to a specific fix:
sts-policy-fetch-error — the sender couldn't retrieve your policy file. Broken HTTPS, expired web cert, or a 404/500 on the mta-sts. host. Fix the endpoint.
sts-webpki-invalid / sts-policy-invalid — the policy fetched but the cert didn't chain to a public root, or the file body is malformed. Check Content-Type and CRLF endings.
certificate-host-mismatch — the MX cert's CN/SAN doesn't cover the MX hostname the sender connected to. The classic one: fix the SANs on that specific receiving-mx-hostname.
certificate-not-trusted / certificate-expired — self-signed or lapsed cert on the MX. Renew or re-issue.
starttls-not-supported — the MX didn't offer STARTTLS at all, or it was stripped. If it's persistent from one sender/IP, suspect an on-path device — precisely the threat MTA-STS exists to catch.
validation-failure — a general TLS negotiation failure; correlate by sending-mta-ip.
The green light to enforce is roughly this: one to two weeks of reports where total-failure-session-count sits at zero or a negligible, explained residue (a single misconfigured sender you've already identified). Don't chase literally zero forever — chase *no failures you can't explain*.
Flipping to enforce, and the rollback that actually works
To enforce, change mode: enforce in the policy file and bump the `id` in the TXT record. Both, always — the id bump is what tells cached senders to re-fetch.
Now internalize the cache behavior, because it dictates your rollback plan. Senders honor max_age, so a policy they fetched during testing lingers in their cache for up to a week regardless of what you change. That cuts both ways: enforcement doesn't propagate instantly, and neither does turning it off. If enforce mode starts black-holing legitimate mail, the fast kill switch is not deleting the TXT record — a deleted record leaves the cached enforcing policy live until it expires. Instead, bump the id and serve a policy with mode: none. Senders see the new id, re-fetch, get none, and stand down. That's why a conservative max_age of 604800 during rollout is worth the slightly more frequent refresh traffic: your worst-case blast radius is one week, and your rollback is a two-line edit that senders pick up on their next fetch.
Verify end to end
Before and after every state change, confirm all three artifacts with your own eyes:
bash
# 1. Announce record present, id as expected
dig +short TXT _mta-sts.evilmail.pro
# 2. Reporting record present
dig +short TXT _smtp._tls.evilmail.pro
# 3. Policy file: expect HTTP 200 and Content-Type: text/plain
curl -sI https://mta-sts.evilmail.pro/.well-known/mta-sts.txt
# 4. Inspect the cert your MX actually presents — SAN must cover the MX hostname
openssl s_client -starttls smtp -connect mx1.evilmail.pro:25 \
-servername mx1.evilmail.pro 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName
That last command is the one that catches certificate-host-mismatch before a sender does. The subjectAltName output must list every MX hostname named in your policy. Cross-check with a third-party validator too — Hardenize, MECSA, or Google's own Postmaster/CheckMX tooling — since they fetch from the outside exactly as an enforcing sender would.
Rollout checklist
MX certificate SANs cover every MX hostname in the policy. Verify with openssl s_client per host.
mta-sts. subdomain live with a valid, publicly-trusted web cert on 443.
Policy file returns 200 and Content-Type: text/plain at the .well-known path.
_mta-sts TXT published with a unique id.
_smtp._tls TXT published, rua pointing at a mailbox you genuinely monitor.
Deploy mode: testing. Confirm reports start arriving within ~24h.
Watch TLS-RPT for 1–2 weeks. Drive total-failure-session-count to zero-or-explained.
Flip mode: enforce, bump the id. Keep watching the reports.
Rollback ready: to disable fast, bump id and serve mode: none — never rely on deleting the TXT record.
The rule of thumb, and the only sentence worth memorizing: never enforce a policy you haven't first watched fail in testing. Testing mode plus a week of TLS-RPT is the difference between hardening your inbound mail and quietly bouncing it.