Mail between servers is usually encrypted, but nothing guarantees it. The sending server connects to your MX on port 25, sees STARTTLS offered, and upgrades - unless something in the path removes that offer, in which case it shrugs and sends in plain text. It also does not check that the certificate it is shown belongs to your server. That is opportunistic TLS: it stops passive eavesdropping and nothing else. Three standards close the gap. MTA-STS publishes a policy over HTTPS that says "my mail servers are these names, and they always do valid TLS". DANE publishes the certificate's fingerprint in DNSSEC-signed DNS. TLS-RPT asks senders to report back when TLS to you fails. For most domains the right first step is TLS-RPT plus MTA-STS in testing mode, moving to enforce after a few weeks of clean reports; DANE is worth it if your DNS host does DNSSEC properly.
How STARTTLS works between mail servers, and where it fails#
An SMTP session between two servers starts in plain text. The receiving server lists its capabilities in reply to EHLO, and if STARTTLS is among them, the sender can ask to upgrade:
S: 220 mail.example.com ESMTPC: EHLO mx.sender.netS: 250-mail.example.comS: 250-SIZE 52428800S: 250-STARTTLSS: 250 ENHANCEDSTATUSCODESC: STARTTLSS: 220 2.0.0 Ready to start TLSEverything after that is encrypted. The weaknesses are in the two decisions the sender makes:
- Whether to upgrade. The capability list travels in plain text. An attacker in the path - or a badly behaved firewall that "inspects" SMTP, which is a real thing - can strip the
250-STARTTLSline. The sender sees a server that does not offer TLS and delivers in clear text, because refusing would mean refusing mail to the large number of servers that genuinely do not support it. - Whether to trust the certificate. Even with TLS, the sender has no reliable way to know which name the certificate should carry. The
MXrecord came from unauthenticated DNS, so an attacker who can forge DNS can point the sender at their own server with their own valid certificate. Most senders therefore accept any certificate, self-signed and expired ones included.
Opportunistic TLS is still worth having - it defeats bulk passive collection - but it does nothing against an active attacker. MTA-STS and DANE each solve both problems, by giving the sender an authenticated statement, published in advance, of what to expect.
MTA-STS: a policy published over HTTPS#
MTA-STS (RFC 8461) has two parts: a DNS TXT record that says a policy exists, and the policy itself, fetched over HTTPS from a fixed address. Because HTTPS uses the ordinary web certificate system, the policy is authenticated even though the DNS lookup is not.
The sender looks up _mta-sts.example.com, finds an id, fetches https://mta-sts.example.com/.well-known/mta-sts.txt, and caches the policy for max_age seconds. From then on, every delivery to your domain must go to an MX host matching the policy, over TLS, with a certificate that is valid for that host name and chains to a public certificate authority. In enforce mode a sender that cannot meet those conditions does not deliver; the message waits in its queue and is retried.
The cache is what makes it resistant to attack. An attacker who blocks the policy fetch on a later delivery achieves nothing, because the sender still has the policy from last time. The first contact is the weak moment, which is why max_age is normally long.
Publishing an MTA-STS policy#
Three things are needed: the policy file, a web server that serves it with a valid certificate at mta-sts.<your domain>, and the TXT record.
The policy file:
version: STSv1mode: testingmx: mail.example.commax_age: 86400| Key | Values | What it means |
|---|---|---|
version | STSv1 | The only version |
mode | testing, enforce, none | Report only, require, or withdraw the policy |
mx | A host name, or *.example.com | Repeat the line for each MX host |
max_age | Seconds, up to 31557600 | How long senders cache the policy |
The TXT record:
v=STSv1; id=20261008T120000The id is any string of up to 32 letters and digits. Senders compare it with the one they cached, and refetch the policy when it changes, so a timestamp is the convenient choice. Every time you edit the policy file, change the id.
Rules that the specification makes strict, and which account for most broken deployments:
- The policy host must have a valid certificate for
mta-sts.example.com, issued by a public authority. Self-signed will not do; this is the whole basis of trust. - No redirects. Senders must not follow an HTTP redirect when fetching the policy. A web server that redirects
/.well-known/...to another path or host breaks MTA-STS silently. - Serve it as `text/plain` with a
200response. - Every `mx` line must match the names in your
MXrecords, and the certificate on each mail server must be valid for the name theMXpoints at.
Start with mode: testing and a short max_age such as a day. In testing mode senders deliver whatever happens, but report failures through TLS-RPT. After two or three weeks of reports showing nothing but successes, switch to enforce, raise max_age to a week or more (604800), and change the id.
TLS-RPT: finding out when TLS fails#
TLS-RPT (RFC 8460) is one more TXT record. It asks senders to send you a daily summary of their TLS connections to your domain:
v=TLSRPTv1; rua=mailto:tls-reports@example.comLarge senders, including Google and Microsoft, send these reports. Each is a gzipped JSON document covering one day: the policy they applied (MTA-STS, DANE or none), counts of successful sessions, and counts of failures by type. The failure types are specific enough to act on:
| Result type | Usual cause |
|---|---|
starttls-not-supported | The server did not offer STARTTLS, or something stripped it |
certificate-expired | Renewal failed |
certificate-host-mismatch | Certificate does not cover the MX name |
validation-failure | Chain incomplete or not trusted |
sts-policy-fetch-error | The policy URL failed, redirected or has a bad certificate |
sts-webpki-invalid | The policy host's certificate is invalid |
tlsa-invalid / dane-required | DANE record wrong or unusable |
Publish TLS-RPT before anything else, even without MTA-STS. With no policy, the reports still tell you how many senders reached you over TLS, which is a useful baseline, and they cost nothing. Use a dedicated mailbox for them - they arrive daily from every large provider - and either read the JSON or feed it into one of the free report viewers.
DANE: certificates pinned in DNSSEC#
DANE for SMTP (RFC 7672) takes a different route to the same goal. Instead of an HTTPS policy, you publish a TLSA record that states which certificate or key your mail server presents, and the record is trusted because the zone is signed with DNSSEC.
_25._tcp.mail.example.com. 3600 IN TLSA 3 1 1 ( 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d )The three numbers are usage, selector and matching type. 3 1 1 - "this exact end-entity key, by its SHA-256 hash" - is the common and recommended form for mail: it does not depend on any certificate authority at all, and a self-signed certificate is acceptable. The hash is of the certificate's public key, which you can compute from the certificate file:
$ openssl x509 -in cert.pem -noout -pubkey \ | openssl pkey -pubin -outform DER \ | openssl dgst -sha256DANE has two hard requirements, and they decide whether it is for you:
- The zone holding your `MX` host names must be DNSSEC-signed, and so must the zone of your domain for the
MXlookup to be trusted. If your DNS host does not offer DNSSEC, or you are not confident managing it, stop here. A broken DNSSEC chain does worse than disable DANE - validating resolvers stop resolving the domain at all. - Key rollover has to be planned. Because the record pins the key, renewing the certificate with a new key without updating the record first makes every DANE-validating sender refuse delivery. Either reuse the key on renewal (most ACME clients have an option for it), or publish the new key's hash alongside the old one, wait out the record's TTL, then switch the certificate and remove the old hash.
DANE is validated by Postfix and Exim when configured to, by Stalwart for outbound mail, and by Microsoft's Exchange Online, among others. Gmail relies on MTA-STS instead. That split is why many operators publish both.
MTA-STS or DANE
| MTA-STS | DANE | |
|---|---|---|
| Trust comes from | Public web certificates | DNSSEC |
| Needs | A web host for the policy | A DNSSEC-signed zone |
| First contact protected | No, relies on caching | Yes |
| Certificate | Must be publicly trusted | Any, including self-signed |
| Honoured by | Google, Microsoft and others | Microsoft, many self-hosted servers |
| Main failure mode | Policy host or certificate breaks | Key changed without the record |
If you can only do one, MTA-STS is the easier and more widely honoured choice for a small domain. If your DNS host makes DNSSEC a checkbox and you are disciplined about key rollover, add DANE. They coexist without conflict, and TLS-RPT reports on both.
The sending side: your server and other domains#
Everything above protects mail sent to you. Your own server, when it sends, makes the same decisions about other domains. Stalwart checks MTA-STS policies and DANE records when delivering outbound mail, and both are configurable per delivery strategy in the web admin. The defaults treat them as optional: a policy that is published and valid is applied, but a domain whose policy cannot be fetched or validated still receives mail. Setting either to "require" globally would make your server refuse delivery to the many domains that publish neither, which is not what you want. Leave the defaults unless you have a specific partner domain where encryption must be guaranteed, and handle that with a rule for that domain.
Doing this with a RE:NODE mail server#
The Mail Server line runs Stalwart, and the DNS records here - TXT for MTA-STS and TLS-RPT, TLSA for DANE - are yours to add at whoever hosts your DNS, the same as the MX, SPF, DKIM and DMARC records. Support forwards port 25 to the server so it can receive mail from the internet, and that port is what senders test when they apply your policy, so make sure the certificate presented there covers the name your MX points at before publishing anything.
The MTA-STS policy file needs a web host with a valid certificate on mta-sts.example.com. Any static host will do. On a web hosting plan, point an A record for mta-sts.example.com at the address shown for the proxy slot and the certificate is issued and renewed automatically; the policy is one text file in .well-known. Check that nothing in the site's configuration redirects that path.
An order of work that does not lose mail#
These records sit on top of the ones every mail domain needs, so check the foundation first. SPF, DKIM and DMARC decide whether your mail is believed; MX records decide where mail goes. TLS policy only describes how it gets there, and a policy written against a wrong MX is worse than none.
- Fix the certificate on port 25. It must be valid, in date, and cover every name your
MXrecords point at. Until this is true, every later step reports failures. - Publish TLS-RPT. Collect a week of reports with no policy at all, to see how senders currently reach you.
- Publish MTA-STS in testing mode with a one-day
max_age. Read two to three weeks of reports. - Switch to enforce, raise
max_age, change theid. Keep reading reports; a renewal that fails in three months will show up there first. - Add DANE only if your DNS is DNSSEC-signed and you have a written plan for key rollover.
The habit is the same one that makes a DMARC rollout safe - publish in reporting mode, read what comes back, then enforce - and DMARC reports and policy rollout describes it in more detail. Aggregate reports are the only feedback you get from other people's mail servers, and they are worth the dedicated mailbox they need.
Testing your setup#
From any machine with dig, curl and openssl:
$ dig +short TXT _mta-sts.example.com$ curl -sv https://mta-sts.example.com/.well-known/mta-sts.txt$ dig +short TXT _smtp._tls.example.com$ dig +short MX example.com$ openssl s_client -connect mail.example.com:25 -starttls smtp \ -servername mail.example.com </dev/null | openssl x509 -noout -subject -dates$ dig +dnssec +short TLSA _25._tcp.mail.example.comCheck that the policy fetch returns 200 with no redirect, that the mx lines match the MX answer, that the certificate on port 25 names the MX host and is in date, and - for DANE - that the TLSA answer matches the key hash and the response is validated. Your domain and its certificate covers certificate renewal, which is where most of these setups eventually break.
FAQ#
Is my mail encrypted without any of this?
Usually, yes. Most large providers offer and use STARTTLS, so most mail travels encrypted. What you lack without MTA-STS or DANE is any guarantee: an attacker able to interfere with the connection or with DNS can force plain text or impersonate your server, and the sender will not notice.
Does MTA-STS encrypt mail I send to other people?
No. Your policy tells other servers how to deliver to you. Whether your outbound mail is protected depends on the recipient's domain publishing a policy and your server honouring it. Stalwart applies other domains' MTA-STS and DANE records when it sends.
What happens if my policy is wrong in enforce mode?
Senders that honour it refuse to deliver and keep the mail queued, typically for several days, before bouncing it to the original sender. Nothing reaches you and nothing tells you, apart from TLS-RPT reports. That is why testing mode comes first and why a policy change needs a new id.
Do I need DNSSEC for MTA-STS?
No. MTA-STS was designed for domains without DNSSEC; its trust comes from the HTTPS certificate on the policy host. DANE is the one that requires DNSSEC.
How long should max_age be?
A day while testing, so mistakes expire quickly. Once in enforce mode and stable, a week to a month is common, and longer gives better protection against an attacker blocking the policy fetch. Remember that a long max_age also means a long wait when you change mail servers.




Comments
Completely anonymous: no account, no email, no cookie. We store the name you type, the text and the time - nothing else. Links are limited and markup is not rendered.