Email security

MTA-STS and TLS-RPT: enforcing encrypted mail transport, step by step

Mail servers encrypt with TLS when they can, and send in plain text when they cannot. An attacker who can interfere with the connection decides which of the two happens. MTA-STS takes that choice away.

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: testing means senders report failures but still deliver; enforce means they refuse to deliver when TLS or the certificate fails; none withdraws 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 like example-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-sts web 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 until max_age runs out
  • Forgetting a backup MX or a second gateway in the mx: lines
  • Letting the certificate of the mta-sts host 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-sts TXT 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-sts host monitored for expiry

Hiring for Microsoft 365, identity or email security in Singapore?

Permanent role, contract or a first conversation: write to me. I answer personally.