The problem with opportunistic TLS
SMTP between servers uses STARTTLS: the receiving server offers encryption, the sender takes it. If the offer is missing, because someone stripped it from the connection or because the certificate is broken, most senders deliver anyway, unencrypted, and nobody notices. The certificate is usually not validated either.
Two standards fix this for inbound mail to your domain:
- MTA-STS (RFC 8461) publishes a policy: "my mail servers are these, they support TLS with a valid certificate, do not deliver otherwise."
- TLS-RPT (RFC 8460) asks sending servers to send you a daily report of TLS problems they had when delivering to you.
DANE does the same as MTA-STS through DNSSEC and TLSA records. It is stronger, but needs DNSSEC on your domain. MTA-STS works with any DNS and is the practical first step. Exchange Online validates both MTA-STS and DANE for outbound mail, so your own outgoing mail already benefits wherever recipients publish a policy.
Step 1: TLS-RPT first, it costs nothing
Publish a reporting address before you change anything else:
_smtp._tls.example.com TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
Reports arrive as compressed JSON, one per sending organisation per day. They tell you how many sessions succeeded and which failed with which error, such as an expired certificate or a host name mismatch. Use a dedicated mailbox or a reporting service. DMARC Geeks shows whether the TLS-RPT and MTA-STS records of a domain are present.
Step 2: the policy file
MTA-STS has two parts: a small text file served over HTTPS, and a DNS record that says it exists. The file lives at a fixed address:
https://mta-sts.example.com/.well-known/mta-sts.txt
version: STSv1
mode: testing
mx: *.mail.protection.outlook.com
max_age: 86400
mode:testingmeans senders report failures but still deliver;enforcemeans they refuse to deliver when TLS or the certificate fails;nonewithdraws the policy.mx: one line per allowed MX host name, wildcards for one label allowed. List exactly the host names your MX records use. For Exchange Online that is usually a name likeexample-com.mail.protection.outlook.com, which the wildcard above covers. If a gateway sits in front of Exchange Online, list the gateway's MX hosts instead.max_age: how long senders cache the policy, in seconds. Short while testing, a week or more once enforced.
The host mta-sts.example.com needs a valid certificate from a public CA, and the file has to be served directly, without redirects, as plain text. Separate the lines with CRLF to be safe. Any small web server, a static site service or the web server you already run will do.
Step 3: the DNS record
_mta-sts.example.com TXT "v=STSv1; id=20261013T1200"
The id is a version marker. Senders only fetch the policy file again when it changes, so change the id every time you change the file. A timestamp makes that easy to remember.
Check both parts:
Resolve-DnsName "_mta-sts.example.com" -Type TXT | Select-Object -ExpandProperty Strings
Resolve-DnsName "_smtp._tls.example.com" -Type TXT | Select-Object -ExpandProperty Strings
(Invoke-WebRequest "https://mta-sts.example.com/.well-known/mta-sts.txt" -MaximumRedirection 0).Content
Resolve-DnsName "example.com" -Type MX | Select-Object NameExchange, Preference
Compare the MX names with the mx: lines. A mismatch here is the most common reason enforced policies block mail.
Step 4: testing, then enforce
Stay in testing for two to four weeks and read the TLS reports. Failures you will typically see:
- Host name mismatch: an MX host that is not listed in the policy, often a backup MX nobody remembered.
- Certificate problems: an expired or self-signed certificate on an on-premises gateway.
- Policy fetch failures: a redirect, a wrong content type or a certificate problem on the
mta-stsweb host.
When the reports are clean, switch the file to mode: enforce, raise max_age (for example to 604800, one week) and change the id in DNS.
Changing MX later
Enforce mode makes MX changes a planned operation. Before you switch to a new gateway or a new MX host: add the new host name to the policy, change the id, wait at least the old max_age so senders have the new policy, and only then change the MX records. Remove the old host name afterwards. Skipping the wait means senders with a cached policy refuse to deliver to the new host.
Parked domains
A domain that receives no mail does not need MTA-STS. It needs a null MX (MX 0 .), v=spf1 -all and DMARC p=reject.
Common mistakes
- Publishing the DNS record before the policy file is reachable, so senders log fetch errors from day one
- Changing the policy file but not the
id, so senders keep the old policy untilmax_ageruns out - Forgetting a backup MX or a second gateway in the
mx:lines - Letting the certificate of the
mta-stshost expire: in enforce mode, senders that cannot refresh the policy keep using the cached one until it expires, and then fall back to delivery without a policy - Never reading the TLS reports, which turns a monitoring tool into decoration
Checklist
- TLS-RPT record published, reports arriving and read
- Policy file on
https://mta-sts.<domain>/.well-known/mta-sts.txt, valid certificate, no redirects mx:lines match every MX record, including backup MX and gateways_mta-stsTXT record with an id that changes with every policy change- Two to four weeks in testing with clean reports
- Enforce mode with a longer
max_age - MX changes documented as: policy first, wait
max_age, then DNS - Certificate of the
mta-stshost monitored for expiry