Publish an MTA-STS policy, starting in testing mode on Cloudflare
Why it matters
Without MTA-STS, servers sending mail to you cannot insist on an encrypted connection, so an attacker on the network can downgrade it to plaintext. MTA-STS tells senders to require TLS to your mail servers, and it is rolled out in testing mode first so nothing is lost if it is misconfigured.
Staged change. Do this in stages and check each one before the next, so a mistake does not break your site or your mail.
The fix
- Check every MX host supports TLS with a certificate valid for its own hostname. Then host the policy file. The simplest option in Cloudflare is a Worker with a custom domain: create a Worker with the code in the snippet, then in the Worker open Settings > Domains & Routes > Add > Custom Domain and enter mta-sts.yourdomain. Cloudflare creates the DNS record and certificate for it.
- If a record named mta-sts already exists in DNS > Records (for example a CNAME to a web host), delete it first; a Worker Custom Domain cannot be created on a name that already has a DNS record.
- Alternatively point mta-sts.yourdomain at any HTTPS web host: in DNS > Records add an A, AAAA or CNAME record named mta-sts. For this web record only, proxied (orange cloud) is fine, because it is an ordinary HTTPS site; the policy must still answer 200 with text/plain and no redirect.
- Start with mode: testing and max_age: 86400. Replace the mx lines in the snippet with your own MX host names, written exactly as they appear in your MX records, and never copy host names from an example.
- After the policy URL works, in DNS > Records add the TXT record: Name _mta-sts, Content v=STSv1; id=2026100501. TXT records are DNS only. Change the id whenever you change the policy.
- Add the TLS-RPT record, read the reports for a few weeks, then switch to mode: enforce and change the id.
text
// Cloudflare Worker serving the MTA-STS policy // Replace the mx lines with your own MX host names before deploying const POLICY = [ "version: STSv1", "mode: testing", "mx: YOUR-MX-HOST.example", "mx: *.YOUR-MX-DOMAIN.example", "max_age: 86400", "" ].join("\r\n"); export default { async fetch(request) { const url = new URL(request.url); if (url.pathname !== "/.well-known/mta-sts.txt") { return new Response("Not found", { status: 404 }); } return new Response(POLICY, { headers: { "content-type": "text/plain; charset=utf-8" } }); } }; // DNS > Records, TXT record, added after the policy URL works // Name: _mta-sts.example.com // Content: v=STSv1; id=2026100501
The examples use example.com. Replace it with your own domain.
Related
- Reported by the MTA-STS and TLS-RPT check
- Nothing to do for MTA-STS on a domain that accepts no mail
- Fix the MTA-STS TXT record
- Make the MTA-STS policy file reachable
- Fix the certificate on the MTA-STS policy host
- Fix the MTA-STS policy file
- Switch MTA-STS from testing to enforce
- Turn MTA-STS back on
- Make the MTA-STS policy cover every MX host
- Raise the MTA-STS max_age
- Publish a TLS-RPT record
- Fix the TLS-RPT record
Scan your domain to see whether this applies to you.