MTA-STS: the email TLS cousin nobody talks about
DMARC, SPF and DKIM authenticate the sender. MTA-STS authenticates the transport.
It's the policy that says "when delivering mail to example.com, you MUST
use TLS, and you MUST validate the cert chain. If you can't, drop the message rather
than fall back to plaintext."
Without MTA-STS, an attacker on the network path can downgrade the SMTP connection to plaintext (STRIPTLS attacks documented since 2015) and read every word. With MTA-STS, the sending MTA refuses to deliver in cleartext.
The two pieces
1. The DNS record
_mta-sts.example.com. IN TXT "v=STSv1; id=20260429T120000;"
The id is opaque — it just has to change whenever you update the policy
file. Convention: timestamp.
2. The policy file (HTTPS)
# Served at https://mta-sts.example.com/.well-known/mta-sts.txt
# Content-Type: text/plain
version: STSv1
mode: enforce
mx: mail.example.com
mx: *.googlemail.com
max_age: 604800
| Field | Meaning |
|---|---|
mode | enforce (drop on TLS failure), testing (TLSRPT only), none (disabled) |
mx | Allowed MX targets. Wildcards OK. Sender's MX list MUST match. |
max_age | How long sender caches the policy (seconds). Recommended: 86400-604800. |
The HTTPS-served policy file
The dependency on HTTPS for policy distribution is the catch. You need a separate
subdomain mta-sts.example.com with a valid TLS cert. Most setups handle this
by adding a virtual host in the same nginx/Apache that serves the apex.
# nginx
server {
listen 443 ssl;
server_name mta-sts.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location = /.well-known/mta-sts.txt {
default_type text/plain;
return 200 "version: STSv1\nmode: enforce\nmx: mail.example.com\nmax_age: 604800\n";
}
location / { return 404; }
}
Alternative: serve a static file at /var/www/mta-sts/.well-known/mta-sts.txt
if you prefer file-based config.
The rollout dance (don't skip)
- Phase 1 (week 1-2): mode=testing. Receivers will report TLS failures via TLSRPT but will NOT drop messages. You watch the reports.
- Phase 2 (week 2+): mode=enforce. Receivers drop on TLS failure. Hardened.
Skipping testing mode and going straight to enforce is the classic gun-meets-foot mistake. If your MX hostname doesn't match the policy (typo, forgotten cert renewal, mid-migration) your mail just stops arriving. Receivers don't bounce — they queue and retry, then eventually drop. Customers think you're ignoring them.
TLSRPT (the companion record)
TLSRPT is the reporting half. Receivers send daily aggregate reports of TLS delivery failures to the address you publish:
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
You'll get JSON reports listing connection attempts, outcomes, error codes. Critical for the testing phase — you can't roll out enforce blind.
How UnveilScan checks MTA-STS
Our mta_sts_policy checker (Extended profile):
- Resolves
_mta-sts.example.comTXT, parsesv=STSv1+id=. - Fetches
https://mta-sts.example.com/.well-known/mta-sts.txt. - Parses
version/mode/mx/max_age. - Cross-checks the policy
mxvalues against your actualMXDNS records (wildcard-aware per RFC 8461).
| Finding | Severity | Trigger |
|---|---|---|
| mta_sts_missing | LOW | No MTA-STS record at all |
| mta_sts_mode_none | LOW | Policy exists but mode=none |
| mta_sts_mode_testing | INFO | Policy in testing mode (transitional) |
| mta_sts_mx_mismatch | HIGH | Policy mx doesn't match DNS MX (silent breakage risk) |
| mta_sts_unreachable | MEDIUM | Policy URL returns 404/500/timeout |
Check your email transport policy
Extended scan reports MTA-STS + TLSRPT presence and policy alignment.
See pricing