Skip to content

MTA-STS and TLS-RPT for small businesses

How MTA-STS makes sure email to a domain arrives encrypted, how TLS-RPT reports problems, and how to switch both on safely.

By Doug Hall · Published · Updated

In short
  • Email between servers is usually encrypted, but by default a sender will fall back to plain text if encryption fails.
  • MTA-STS tells senders to insist on encryption for your domain. TLS-RPT asks them to report when it fails.
  • Start MTA-STS in testing mode, read the TLS reports, then switch to enforce.

This guide is for agencies and IT providers who look after a client's domain and want email sent to it to arrive encrypted, every time. It covers what to have in place first, how to roll the policy out without losing mail, how to read the reports, and how the hosted version in Domain Health Hub works.

Why it matters

When one mail server hands a message to another, it offers to encrypt the connection. That encryption is opportunistic: if the offer is refused, or someone in the middle strips it out, most senders deliver the message in plain text rather than not at all, and nobody is told. For a client who emails contracts, payroll or medical details, that's a quiet gap.

MTA-STS (RFC 8461) lets the domain say: don't fall back, only deliver to these servers, and only over a verified encrypted connection. TLS-RPT (RFC 8460) asks senders to report, once a day, how many connections succeeded and why any failed. Think of it this way: DMARC protects what goes out under the domain's name; MTA-STS protects what comes in.

What you need before you start

Unlike SPF or DMARC, MTA-STS is not only a DNS record. Senders fetch the policy over HTTPS, so four things must be in place:

  • A DNS record for mta-sts.<domain>: an A, AAAA or CNAME pointing at a web server you control.
  • A certificate valid for exactly that name, from a public certificate authority. A wildcard certificate for *.<domain> covers it; the certificate on the bare domain or www does not.
  • The policy served at https://mta-sts.<domain>/.well-known/mta-sts.txt as text/plain, with a 200 response and no redirect. Senders are told not to follow redirects, so a host that bounces everything to www breaks it.
  • The domain's MX hosts, as they appear in DNS, to list in the policy.

If the client's website host can't serve one file on a new subdomain with its own certificate, and many shared hosts can't, skip to hosted MTA-STS, which does that part for you.

The record and the policy file

It takes two parts: a DNS record that says a policy exists, and the policy file itself.

_mta-sts.example.com TXT
v=STSv1; id=20261013a

The id is 1 to 32 letters and digits, nothing else. Senders re-fetch the policy when the id changes or their cached copy expires, so change it every time the file changes. A date with a letter after it works well.

https://mta-sts.example.com/.well-known/mta-sts.txt
version: STSv1
mode: testing
mx: *.mail.protection.outlook.com
max_age: 604800
version
Always STSv1.
mode
testing (report problems, deliver anyway), enforce (refuse to deliver to an unlisted host or over an unverified connection) or none (forget the policy).
mx
One line per mail server, matching the hostnames in the MX records. A wildcard covers only the left-most label: *.mail.protection.outlook.com matches example-com.mail.protection.outlook.com but not a.b.mail.protection.outlook.com.
max_age
How long senders keep the policy, in seconds, up to 31557600 (a year). 604800, a week, is a sensible start; raise it once you're in enforce.

The example lists Microsoft 365 with a wildcard rather than the exact example-com.mail.protection.outlook.com form, so that if Microsoft changes the host in front of the pattern the policy still matches. For other providers, copy the exact hosts from the MX records.

Testing first, then enforce

  1. Publish TLS-RPT first, so you can see what happens from the start. The record is in the next section.
  2. Put the policy file up in testing mode, listing every MX host, and check it with the MTA-STS checker. It fetches the file exactly as a sender would and compares the mx lines with the live MX records.
  3. Publish the _mta-sts record. Senders start fetching the policy, and the TLS reports start to measure failures against it.
  4. Read the reports for two to four weeks. When they show no failures from senders the client cares about, change mode to enforce and change the id. A domain left in testing gets no protection; see moving out of testing mode.
  5. Keep max_age modest until you trust the set-up, then raise it.
  6. When the mail provider changes, add the new hosts to the policy and change the id before the MX records move, and keep the old hosts for at least max_age. Get this backwards and senders in enforce refuse the new servers until their cached policy expires.

TLS-RPT: asking senders to report

TLS-RPT is a single TXT record asking senders to send a daily report of successful and failed encrypted deliveries:

_smtp._tls.example.com TXT
v=TLSRPTv1; rua=mailto:tls-reports@example.com

The rua accepts mailto: addresses and https: endpoints, and more than one, separated by commas. By email, each report arrives as a gzip-compressed JSON attachment; over HTTPS it's posted as JSON. TLS-RPT works on its own: senders report even before there's an MTA-STS policy, so it's a harmless first step. The TLS-RPT checker confirms the record is well formed. Like DMARC reports, the files are meant for software; Domain Health Hub receives TLS reports at the same address as DMARC reports and shows them side by side.

How to read a TLS report

Here is a shortened report for a made-up domain, with one policy and one kind of failure. The addresses are reserved documentation ranges.

mail-provider.example!example.co.uk!1791158400!1791244799.json
{
  "organization-name": "Mail Provider Example",
  "date-range": {
    "start-datetime": "2026-10-12T00:00:00Z",
    "end-datetime": "2026-10-12T23:59:59Z"
  },
  "contact-info": "tls-reports@mail-provider.example",
  "report-id": "2026-10-12T00:00:00Z_example.co.uk",
  "policies": [
    {
      "policy": {
        "policy-type": "sts",
        "policy-string": [
          "version: STSv1",
          "mode: testing",
          "mx: *.mail.protection.outlook.com",
          "max_age: 604800"
        ],
        "policy-domain": "example.co.uk"
      },
      "summary": {
        "total-successful-session-count": 5420,
        "total-failure-session-count": 3
      },
      "failure-details": [
        {
          "result-type": "certificate-host-mismatch",
          "sending-mta-ip": "192.0.2.25",
          "receiving-mx-hostname": "mail.old-host.example",
          "receiving-ip": "198.51.100.7",
          "failed-session-count": 3
        }
      ]
    }
  ]
}
organization-name
The sender that wrote the report, usually a large mailbox provider.
date-range
The day the report covers, in UTC.
policies
One entry per policy the sender saw for the domain: sts for MTA-STS, tlsa for DANE, or no-policy-found.
policy-string
The policy exactly as the sender read it. After a change, this tells you which version each sender had.
summary
total-successful-session-count and total-failure-session-count: the two numbers to look at first.
failure-details
One entry per kind of failure: the result-type, the sending server, the mail server it tried to reach and how many sessions failed.

A healthy domain shows a large successful count and at most a handful of failures. Failures from several senders that all name the same receiving host mean the policy or that server needs attention.

The failure types you'll see

result-typeWhat it meansWhat to do
starttls-not-supportedThe mail server didn't offer encryption at allCheck that host's configuration, or remove it from the MX records
certificate-host-mismatchThe certificate on the mail server doesn't match its nameCommon after a provider rename, or a self-signed certificate on a legacy host
certificate-expiredThe mail server's certificate has lapsedRenew it; most providers do this for you
certificate-not-trustedNot from a public authority, or the chain is incompleteInstall the full chain, or use a public certificate
validation-failureSome other TLS problemRead failure-reason-code in the report for the detail
sts-policy-fetch-errorThe sender couldn't get mta-sts.txtCheck the web server, the certificate on mta-sts.<domain> and that nothing redirects; see policy file unreachable
sts-policy-invalidThe file was fetched but didn't parseCheck the version line, the mode and the line breaks; see fixing an invalid policy
sts-webpki-invalidThe certificate on mta-sts.<domain> itself failedRenew or replace it with one that covers that exact name

A result-type starting sts- is about the policy; the others are about the mail servers. In testing mode none of them stops delivery; in enforce, the certificate and policy ones do, which is the point of testing first. The three DANE types only appear if the domain publishes DANE records, which few small businesses do.

Hosted MTA-STS and TLS-RPT

Domain Health Hub hosts both, on every plan. Our worker serves the policy file at https://mta-sts.<domain>/.well-known/mta-sts.txt with a Let's Encrypt certificate it obtains the first time a sender asks, so the client needs no web server and no certificate of their own. The set-up is three records at the client's DNS, where <token> is a code unique to the domain that the domain page shows you:

Record at the client's DNSTypePoints at
_smtp._tls.<domain>CNAME<token>._smtp._tls.hosted.domainhealthhub.com
mta-sts.<domain>CNAME<token>.mta-sts.hosted.domainhealthhub.com
_mta-sts.<domain>CNAME<token>._mta-sts.hosted.domainhealthhub.com

The order matters. The mta-sts CNAME goes first: once it resolves, we fetch the policy over HTTPS as a sender would, and only then does the domain page show the _mta-sts record, so a policy is never announced before it can be read. On Cloudflare the CNAME must be DNS only (grey cloud), not proxied, or the certificate can't be issued; and if the DNS is at Cloudflare, one click in Cloudflare's own dashboard adds all three records for you.

From then on the mode, mx lines and max_age are changed from the dashboard, not at the client's DNS provider: start in testing, and switch to enforce from the same screen once the TLS reports are clean. Those reports are shown next to the domain's DMARC reports, and the JSON is discarded after parsing. See how hosted records work.

Doing this for clients? Domain Health Hub checks every client domain each day and puts the results in a monthly report card with your logo. Start a free trial; no card needed.

Questions

Does MTA-STS protect email the client sends?

No. It protects mail coming in to the domain. Mail the client sends is protected by the receiving domain's own MTA-STS policy.

Why does MTA-STS need a website?

The policy is fetched over HTTPS from mta-sts.<domain>, so a valid certificate proves it really came from the domain owner. Hosted MTA-STS in Domain Health Hub serves the file and certificate for you.

What happens if the policy file goes offline?

Senders keep using the copy they cached until its max_age runs out, and report the fetch failure in their TLS reports. Once the cached copy expires with nothing to replace it, they fall back to ordinary opportunistic encryption, so mail still flows.

Watch every client's domains, every day.

Reports read for you, and a monthly report card your clients will understand.

Start 28-day free trial