MTA-STS and TLS-RPT: enforce encrypted email delivery to your domain
MTA-STS (RFC 8461) lets your domain tell sending mail servers that they must deliver to your MX hosts over TLS with a valid certificate, and must not fall back to unencrypted delivery. You publish it with a _mta-sts TXT record and a short policy file at https://mta-sts.<your-domain>/.well-known/mta-sts.txt. TLS-RPT (RFC 8460) is its companion: a _smtp._tls TXT record that asks senders to send you daily JSON reports on TLS successes and failures. Start in testing mode, read the reports, fix what they show, then switch to enforce.
Updated October 29, 2026
Why SMTP needs MTA-STS
Server-to-server email uses opportunistic TLS. Your MX advertises STARTTLS in its reply to EHLO, and the sending server upgrades the connection if it can (RFC 3207). If the extension is missing or the handshake fails, most senders simply deliver in plaintext, and most don't check whether the certificate is valid or matches the hostname.
220 mx1.example.com ESMTP
EHLO out.sender.example.org
250-mx1.example.com
250-PIPELINING
250-SIZE 52428800
250-STARTTLS
250 8BITMIMEThat leaves two easy attacks: strip STARTTLS from the EHLO reply so the sender never tries TLS, or spoof DNS so the sender connects to a different server. The sender notices neither. MTA-STS closes both gaps. You publish, over HTTPS authenticated by an ordinary web certificate, a policy listing your legitimate MX hosts. A sender that supports MTA-STS then requires TLS, requires a certificate that is valid for the MX hostname, and in enforce mode refuses to deliver rather than downgrade.
How MTA-STS, TLS-RPT and DANE fit together
| Mechanism | Where it is published | What it does | Needs DNSSEC |
|---|---|---|---|
| MTA-STS (RFC 8461) | _mta-sts TXT record plus an HTTPS policy file | Tells senders which MX hosts are valid and whether to require verified TLS | No |
| TLS-RPT (RFC 8460) | _smtp._tls TXT record | Asks senders to report TLS successes and failures to you daily | No |
| DANE (RFC 7672) | TLSA records at _25._tcp.<mx-host> | Pins the MX certificate or its issuer in DNS | Yes |
For most domains, especially on hosted mailboxes, MTA-STS plus TLS-RPT is the practical setup because it needs only DNS records and a static web file. DANE is covered at the end of this guide. TLS-RPT works with either and is worth publishing on its own, since it shows you how senders experience TLS on your MX hosts.
Here is how a supporting sender uses the policy: it looks up _mta-sts.example.com, fetches and caches the policy file, looks up your MX records as usual, drops any MX host that doesn't match the policy's mx lines, and requires a valid certificate on the host it connects to. On later deliveries it rechecks the TXT record and fetches the file again only when the id has changed.
Before you start: check every MX host's TLS
MTA-STS only enforces what your MX hosts already do. Each host must advertise STARTTLS and present a certificate that is valid for its MX hostname, chains to a publicly trusted CA and has not expired. Self-signed certificates fail. List your hosts with the MX lookup, which also shows each host's IPv4 and IPv6 addresses, then test each one:
$ openssl s_client -starttls smtp -connect mx1.example.com:25 \
-servername mx1.example.com -verify_hostname mx1.example.com -brief </dev/null
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Verification: OK
Verified peername: mx1.example.comHosted providers such as Google Workspace and Microsoft 365 already serve valid certificates on their MX hosts, so the work there is publishing the policy correctly. If you run your own servers or a filtering gateway in front of the provider, this check is where most problems surface.
Step 1: publish the MTA-STS policy file
The policy is a plain text file served at exactly https://mta-sts.example.com/.well-known/mta-sts.txt, where example.com is the domain in your recipients' addresses. It contains one key: value pair per line.
version: STSv1
mode: testing
mx: mx1.example.com
mx: mx2.example.com
max_age: 86400| Field | Values | Notes |
|---|---|---|
version | STSv1 | Required, and must be the first line. |
mode | testing, enforce or none | testing reports failures but still delivers. enforce refuses delivery to hosts that fail. none is used to retire a policy. |
mx | A hostname or *. wildcard | One line per pattern. Every MX host you publish must match one of them. |
max_age | Seconds, up to 31557600 | How long senders may cache the policy. RFC 8461 expects weeks or more once you enforce. |
A wildcard matches exactly one label on the left. mx: *.example.net matches mx1.example.net but not example.net itself and not a.b.example.net. For Microsoft 365, the documented pattern is mx: *.mail.protection.outlook.com. For Google Workspace, list what you publish: mx: smtp.google.com for the newer single record, or mx: aspmx.l.google.com plus mx: *.aspmx.l.google.com to cover the five legacy hosts.
- The
mta-stshostname needs its own DNS record (an A record or a CNAME to your web host is fine here; the no-CNAME rule applies only to MX targets). - The HTTPS certificate must be valid for
mta-sts.example.comspecifically. A wildcard certificate for*.example.comcovers it. - The URL must return
200with the policy itself. Senders must not follow redirects, so anhttptohttpsorwwwredirect on that exact path breaks the fetch. - Serve it as
text/plain. RFC 8461 tells senders to check the media type. - Any static host works: object storage behind a CDN, a small serverless function, or a static site with a custom domain. Single-page app hosts often return their
index.htmlwith a200for unknown paths, so confirm the body is the policy.
Step 2: publish the _mta-sts TXT record
The TXT record tells senders a policy exists and which version it is. Publish it at _mta-sts under the recipient domain:
_mta-sts.example.com. 3600 IN TXT "v=STSv1; id=20261029T0900"v=STSv1must come first.idis 1 to 32 letters and digits. A timestamp works well because it is unique and tells you when the policy last changed.- Change the
idevery time you change the policy file. Senders with a cached policy only refetch the file when theiddiffers. - Publish exactly one record starting with
v=STSv1. If senders find more than one, they treat the domain as having no policy.
MTA-STS is discovered per recipient domain. If people receive mail at @support.example.com, that name needs its own _mta-sts.support.example.com record and a policy at https://mta-sts.support.example.com/.well-known/mta-sts.txt.
Step 3: publish a TLS-RPT record
TLS-RPT is a single TXT record at _smtp._tls. The rua tag says where reports go: a mailto: address, an https: endpoint that accepts posted JSON, or both separated by a comma.
_smtp._tls.example.com. 3600 IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"Publish it before or with the MTA-STS policy so you have data from the start of testing. Use a mailbox that accepts compressed attachments, because reports usually arrive as gzipped JSON. If you already process DMARC aggregate reports (how to read DMARC reports), sending TLS reports to the same pipeline keeps everything in one place.
Testing vs enforce: a safe rollout
- Confirm STARTTLS and a valid certificate on every MX host, as shown above.
- Publish the TLS-RPT record.
- Publish the policy in
mode: testingwith a shortmax_agesuch as86400, then the_mta-stsTXT record. - Collect reports for at least two weeks. Look for failures on any host, especially backups that rarely receive traffic.
- Fix what the reports show and test the affected hosts directly.
- Switch the file to
mode: enforcewith a moderatemax_agesuch as604800(one week), and publish a newid. - Once enforce has run cleanly, raise
max_ageto several weeks or more, again with a newid.
Testing mode is there to surface certificate and MX mismatches without blocking mail. A quiet report period is not proof every fallback host was exercised, though. A secondary MX with a broken certificate chain only shows up in reports when senders actually reach it, so test infrequently used hosts directly before you enforce.
In enforce mode, a supporting sender that cannot validate an MX host skips it and tries the others. If none passes, it queues the message, retries, and eventually bounces it if nothing changes. When a sender reports such a delay, paste the notice into the bounce analyzer to see which MX and error it hit, or read how to read a bounce message. A short max_age early on limits how long a mistake stays cached.
Verify the setup end to end
Check both DNS records and the exact policy URL. A DNS-only check cannot tell you whether the policy can be fetched and parsed.
$ dig +short TXT _mta-sts.example.com
"v=STSv1; id=20261029T0900"
$ dig +short TXT _smtp._tls.example.com
"v=TLSRPTv1; rua=mailto:tls-reports@example.com"
$ curl -sS -D - https://mta-sts.example.com/.well-known/mta-sts.txt
HTTP/2 200
content-type: text/plain
version: STSv1
mode: testing
mx: mx1.example.com
mx: mx2.example.com
max_age: 86400- Every host from
dig +short MX example.commatches anmxline in the policy. - The policy response is
200,text/plain, not a redirect, and the body is the policy rather than an HTML page. - The certificate on
mta-sts.example.comis valid with normal verification on (no-k). - Each MX host passes the
openssl s_clientcheck withVerification: OK.
Then send yourself mail from an outside account and check the receiving hop's Received header in the header analyzer. It should show ESMTPS and a TLS version.
Reading TLS-RPT reports
Supporting senders, Google among them, send one report per day covering a UTC day. By email it arrives with a subject such as Report Domain: example.com Submitter: sender.example Report-ID: <...> and a gzipped JSON attachment. Inside, each policy the sender applied has a summary and, if anything failed, failure details.
{
"organization-name": "Sender Example Inc.",
"date-range": {
"start-datetime": "2026-10-27T00:00:00Z",
"end-datetime": "2026-10-27T23:59:59Z"
},
"contact-info": "smtp-tls-reporting@sender.example",
"report-id": "2026-10-27T00:00:00Z_example.com",
"policies": [{
"policy": {
"policy-type": "sts",
"policy-string": ["version: STSv1", "mode: testing",
"mx: mx1.example.com", "mx: mx2.example.com", "max_age: 86400"],
"policy-domain": "example.com"
},
"summary": {
"total-successful-session-count": 4210,
"total-failure-session-count": 37
},
"failure-details": [{
"result-type": "certificate-expired",
"sending-mta-ip": "198.51.100.7",
"receiving-mx-hostname": "mx2.example.com",
"receiving-ip": "203.0.113.26",
"failed-session-count": 37
}]
}]
}policy-type is sts, tlsa (DANE) or no-policy-found. policy-string shows the policy the sender actually used, which matters during changes because it may be an older cached version. Group failures by receiving-mx-hostname and result-type to find the specific endpoint and fault.
| result-type | What failed | Where to fix it |
|---|---|---|
starttls-not-supported | The MX did not offer STARTTLS | SMTP server or gateway config |
certificate-host-mismatch | Certificate does not cover the MX hostname | SMTP certificate |
certificate-expired | SMTP certificate past its expiry | SMTP certificate renewal on that host |
certificate-not-trusted | Chain doesn't reach a trusted CA (often self-signed or missing intermediate) | SMTP certificate chain |
validation-failure | Other validation problem | Read failure-reason-code or additional-information if present |
sts-policy-fetch-error | Sender could not fetch the policy file | Web host for mta-sts. (404, redirect, timeout) |
sts-policy-invalid | Policy fetched but invalid, or MX not covered | Policy file syntax and mx lines |
sts-webpki-invalid | Policy host's HTTPS certificate failed validation | Web certificate for mta-sts. |
tlsa-invalid, dnssec-invalid, dane-required | DANE problems | TLSA records or DNSSEC chain |
Keep certificate failures and policy failures apart: they live on different systems, often owned by different teams. One report can contain both a policy fetch error and an expired SMTP certificate, and renewing the web certificate does nothing for the mail server. Reports also describe what particular senders saw at a particular time, so compare their timestamps with your change log before matching them to today's live checks. If you use HealthCheck Email for DMARC report monitoring, it can receive and parse TLS-RPT reports in the same place.
Policy not found or can't be fetched
The most common broken setup has a correct TXT record and no working policy file. Discovery succeeds, retrieval fails, and reports show sts-policy-fetch-error. A working homepage on the mta-sts host proves nothing; only the exact well-known URL counts.
- The file was never deployed, or the deploy dropped dot-folders such as
.well-known. - The host returns an HTML error page or an app shell with status
200, which senders fail to parse (sts-policy-invalid). - A redirect sits in front of the file:
httptohttps, apex towww, or a trailing-slash rule. - No DNS record exists for
mta-sts.example.com, or it points at a host that doesn't serve that name.
The web certificate is a separate failure point from the SMTP certificate. A valid MX certificate does not help if the certificate on mta-sts.example.com has expired, doesn't include that name, or isn't served because the host lacks an SNI binding for it. Reports show this as sts-webpki-invalid. Check it directly, and never declare the policy healthy with certificate verification turned off:
$ openssl s_client -connect mta-sts.example.com:443 -servername mta-sts.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -enddate -ext subjectAltNameWhen a fetch fails, a sender with a valid cached policy keeps using it, and a sender with nothing cached proceeds as if no policy exists. So a broken policy host usually weakens protection quietly instead of blocking mail, which is why the reports matter.
MX certificate and STARTTLS failures
Hostname mismatch: a server can complete a TLS handshake while presenting a certificate for a different name. If DNS routes senders to mx.example.com and the server presents a certificate only for panel.example.com, encryption works but identity verification fails, and an enforcing sender won't deliver. The name that matters is the MX hostname the sender connected to, not the domain in the From address.
$ openssl s_client -starttls smtp -connect mx.example.com:25 \
-servername mx.example.com -verify_hostname mx.example.com -brief </dev/null
Verification error: hostname mismatchExpired certificates: check every advertised endpoint, not one. A common pattern is a primary MX that renews automatically and a backup that still holds last year's certificate, so failures appear only when senders reach the backup. Equal-preference peers behind separate addresses can drift the same way, and a load balancer's main address can hide an expired certificate on one node. After deploying the renewal, watch queues and the next TLS reports for recovery. How mail behaves depends on each sender's policy, so an expired certificate rarely fails everything identically.
STARTTLS not advertised: if EHLO no longer lists STARTTLS, the session cannot be upgraded and reports show starttls-not-supported. Upgrades to SMTP proxies and filtering gateways are the usual cause: the gateway's listener has TLS disabled while the backend behind it still supports it, and outside senders only ever see the gateway. Compare the EHLO reply on each MX and network path before suspecting interception; one missing capability can also come from configuration or a transient fault.
Changing MX hosts or the policy: caching and transitions
An enforcing policy blocks delivery to any MX host it doesn't cover, so MX changes and policy changes must be coordinated. The complication is caching: senders keep a policy for up to max_age and only refetch when they see a new id. Deleting the TXT record does not cancel a cached policy either; senders keep applying it until it expires. During a change, different senders evaluate your MX against different policy versions.
- Adding an MX host: put it in the policy file, publish a new
id, then change MX records. - Removing an MX host: change MX records first, keep the old host listed until at least one
max_agehas passed, then remove it with a newid. - Editing the file without changing the
idis the most common mistake: cached senders have no reason to refetch, so the new MX line may be ignored for days. - A new
idis a signal, not a guarantee. Senders check it when they next deliver to you, so lightly used senders refresh later.
| When | Action | Why |
|---|---|---|
| Day 0 | Add mx: mx.newhost.example to the policy and publish a new id | Senders learn the new host before it receives mail |
Day 0 + max_age | Switch MX records to the new host | Every cache that held the old policy has expired |
| Later | Remove the old mx line and publish a new id | The old host no longer receives mail |
| Throughout | Watch TLS-RPT | Confirms senders validate the new host |
If you can't wait a full max_age, switch to mode: testing with a new id first, wait for senders to pick it up, make the MX change, then return to enforce with another new id. Save every policy version with its id and publication time; when a report shows sts-policy-invalid for a host you just added, its policy-string tells you the sender was still on the old version. The email DNS change checklist and MX records explained cover the MX side of a migration.
To retire MTA-STS, publish mode: none with a new id and keep serving it for at least the previous max_age before deleting the file and TXT record. Senders that see none drop the old policy.
TLS-RPT reports aren't arriving
A TLS-RPT record requests reports; it doesn't guarantee them. Not every sender produces reports, and a low-volume domain may only hear from a few large providers. Before assuming nobody reports, check the plumbing:
- Resolve
_smtp._tls.example.comand check the version (v=TLSRPTv1) and theruasyntax, including themailto:orhttps:prefix. - Confirm the reporting mailbox still exists. A renamed or deleted address silently drops every report while mail flow continues.
- Make sure the mailbox or spam filter accepts gzipped JSON attachments, and that an
httpscollector accepts the posted format. - Compare with earlier periods: which organizations reported before, and has inbound volume from them changed?
No reports does not mean no TLS failures. Keep the direct openssl checks on every MX host and the policy URL alongside passive reporting.
DANE and TLSA records with DNSSEC
DANE for SMTP (RFC 7672) takes the other route: instead of a web-hosted policy, you publish TLSA records that pin each MX host's certificate or its issuing CA, and senders trust them because DNSSEC signs them. Both the recipient domain's MX answer and the TLSA records must validate under DNSSEC.
_25._tcp.mx1.example.com. IN TLSA 3 1 1 8f2c...e41a
# SHA-256 of the certificate's public key, for a 3 1 1 record
$ openssl x509 -in mx1.pem -noout -pubkey | openssl pkey -pubin -outform DER \
| openssl dgst -sha256A TLSA record without a valid DNSSEC chain gives no DANE assurance at all. A frequent failure: after a DNS provider migration, a stale DS record at the registrar no longer matches the new signing keys, so validating resolvers return SERVFAIL. The TLSA bytes still match the certificate, but the trust chain is broken and DANE senders defer mail. Validation outcomes come in three kinds: secure (signed and valid), insecure (provably unsigned) and bogus (signed but failing). Only secure supports DANE, and bogus is the one that stops mail.
- Check validation with a validating resolver:
dig +dnssec TLSA _25._tcp.mx1.example.comand look for theadflag, or usedelv. - Compare
dig +short DS example.comwith the zone'sDNSKEYrecords when you see SERVFAIL. - Fix the DNSSEC chain through the DNS operator; changing TLSA data does not repair it.
- On certificate renewal, publish the new TLSA record alongside the old one before you deploy the new key. A
3 1 1record only survives renewal if the key stays the same.
You can publish DANE and MTA-STS together, and TLS-RPT reports on both (policy-type: tlsa). Because DANE ties certificate and DNS changes together, make sure whoever renews certificates and whoever runs DNSSEC coordinate.
Sources and further reading
- RFC 8461 — SMTP MTA Strict Transport Security (MTA-STS) — Policy discovery, file format, caching, mx matching and enforcement.
- RFC 8460 — SMTP TLS Reporting — The _smtp._tls record, report JSON schema and result types.
- RFC 7672 — SMTP Security via Opportunistic DANE TLS — TLSA records for SMTP and their DNSSEC requirements.
- RFC 3207 — SMTP Service Extension for Secure SMTP over TLS — The STARTTLS extension that MTA-STS hardens.
- Google Workspace Admin Help — About MTA-STS and TLS reporting
- Microsoft Learn — Enhancing mail flow with MTA-STS