MTA-STS for Microsoft 365 (Exchange Online): The protection.outlook.com MX Enumeration, External HTTPS Hosting for mta-sts.<domain>, the _mta-sts id-Token Coupling, TLS-RPT Pairing for Monitoring, the Testing-to-Enforce Burn-in Workflow, and Why There Is No Defender-Portal MTA-STS Panel
A focused operator-implementation walkthrough for MTA-STS on Microsoft 365 / Exchange Online tenants. The M365 MTA-STS deployment model is structurally different from M365 DKIM and SPF: configuration lives entirely outside the Microsoft administrative surface (no Defender portal panel, no PowerShell cmdlet, no Exchange admin centre pane), M365 provides the inbound MX endpoint (<tenant>-com.mail.protection.outlook.com) but does NOT host the policy file (operators must stand up external HTTPS hosting at the mta-sts.<domain> subdomain via Azure Static Web Apps, Cloudflare Pages, GitHub Pages, or any commodity HTTPS host), and Exchange Online acts as both a recipient (with M365 inbound MX) and a sender (honoring recipient MTA-STS policies on outbound TLS sessions from 2022 onward). Documents the four-step deployment workflow (publish _mta-sts TXT record, stand up external HTTPS hosting, serve the policy file at /.well-known/mta-sts.txt, publish TLS-RPT companion record), MX enumeration for standard / hybrid / third-party-gateway tenant shapes, the testing-mode burn-in discipline, ten common mistakes, and a per-symptom troubleshooting decision matrix. Differentiation from sibling guides: help_mta_sts_explained.php is the protocol primer; help_mta_sts_policy_syntax.php is the byte-level policy-file reference; help_mta_sts_vs_dane.php is the comparison view; this page is the M365-specific operator reference.
This reference is provided in English. RFC 8461 canonical syntax (v=STSv1, _mta-sts, mode: enforce, mx:) and Microsoft 365 hostnames (protection.outlook.com, onmicrosoft.com, security.microsoft.com), PowerShell cmdlets (Get-AcceptedDomain, Resolve-DnsName), and Microsoft Learn documentation labels (Exchange admin center, Defender portal, mail flow best practices) do not translate. We keep the body in the source language so technical terms remain consistent with the specifications and Microsoft Learn documentation.
TL;DR
- Microsoft 365 MTA-STS is operator-driven at the DNS and external HTTPS layers — there is no Defender portal panel, no PowerShell cmdlet, no Exchange admin centre pane. Configuration lives entirely outside the Microsoft administrative surface.
- Four-step deployment:
(1) publish the
_mta-sts.<your-domain>TXT record withv=STSv1; id=<token>; (2) stand up an HTTPS endpoint atmta-sts.<your-domain>(M365 does NOT host this); (3) serve the policy file at/.well-known/mta-sts.txtwithmode: testinginitially; (4) publish the TLS-RPT companion record at_smtp._tls.<your-domain>for monitoring. - The mx: directive
enumerates the exact MX hostnames that
deliver inbound mail to your tenant. For
a standard M365 tenant with no on-prem
hybrid this is typically
<tenant>-com.mail.protection.outlook.com(where<tenant>-comis the dashed transformation of your domain:example.com→example-com). - External HTTPS hosting is mandatory
for the policy file. Common hosts: Azure
Static Web Apps, Azure Front Door,
Cloudflare Pages, AWS S3+CloudFront,
GitHub Pages with custom domain, Netlify,
Vercel, or any commodity LAMP/NGINX host.
The certificate at
mta-sts.<your-domain>must validate against the public PKI. - Testing-mode burn-in:
publish with
mode: testing+max_age: 86400(24h) for 1-2 weeks while TLS-RPT reports populate. Oncests-policy-fetch-error,sts-policy-invalid,sts-webpki-invalid, andcertificate-host-mismatchare all absent for 7+ consecutive days, promote tomode: enforceand increasemax_ageto 604800 (one week) or higher. - Exchange Online is BOTH a
recipient (with M365 inbound MX) AND a
sender (honoring MTA-STS on outbound TLS
sessions). As a recipient, M365
provides the MX endpoint you enumerate in
the mx: directive. As a sender, M365 fetches
recipient MTA-STS policies and refuses to
deliver over plaintext when
mode: enforceis in effect. - The id-token bump rule:
every edit to the policy file MUST
coincide with a fresh
id=value in the_mta-stsTXT record. Senders use the id-token to detect when their cached policy is stale and should be re-fetched. - MTA-STS does not replace DMARC; the two protocols protect different layers (MTA-STS protects transport encryption, DMARC protects sender authentication). Pair with DMARC for Microsoft 365 for end-to-end anti-spoofing protection.
What this guide covers
A focused operator-implementation walkthrough
for MTA-STS (SMTP MTA Strict Transport Security,
RFC 8461) on Microsoft 365 / Exchange Online
tenants. Sibling pages cover the protocol
primer (MTA-STS
explained), the byte-level policy-file
reference (MTA-STS
policy file syntax), and the
MTA-STS-vs-DANE decision view
(MTA-STS
vs DANE for SMTP). THIS page is the
Microsoft-365-specific operator reference:
the protection.outlook.com MX-enumeration in
the mx: directive, the mandatory external
HTTPS hosting for the
mta-sts.<your-domain>
subdomain (M365 does NOT serve the policy
file natively), the absence of any Defender
portal MTA-STS configuration UI, the
TLS-RPT pairing for monitoring, and the
hybrid-tenant mx-cutover discipline.
The page is anchored against current
Microsoft 365 deployment realities: M365
tenants receive mail via
<tenant>-com.mail.protection.outlook.com
MX targets, Exchange Online honors recipient
MTA-STS policies when sending outbound mail
(from 2022 onward), and the Defender portal
has no MTA-STS configuration panel. Hardening
inbound SMTP transport against downgrade
attacks via MTA-STS is therefore entirely an
operator responsibility executed at the DNS
and external HTTPS layers.
How Microsoft 365 MTA-STS deployment works
M365 MTA-STS deployment is structurally different from M365 DKIM and SPF deployment in three ways:
1. Configuration lives entirely outside the M365 admin surface
Unlike DKIM (configured in the Defender portal
or via Set-DkimSigningConfig) and
SPF (verified in the Defender portal via the
domain authentication panel), MTA-STS has NO
Microsoft administrative interface. There is
no Set-MtaStsConfig cmdlet, no
Get-MtaStsSigningConfig, no
Defender portal toggle, and no Exchange admin
centre panel. Configuration is purely DNS
(the _mta-sts TXT record at your
authoritative nameserver) plus external HTTPS
(the policy file served at a non-Microsoft
web host).
2. M365 provides the MX endpoint, not the policy host
Microsoft 365 supplies the inbound MX target
that operators enumerate in the policy file
mx: directive. For a standard
tenant, this is
<tenant>-com.mail.protection.outlook.com
where the -com suffix is the
dashed transformation of your domain
(example.com →
example-com). Microsoft does NOT
host the policy file itself — Exchange Online
is a mail platform, not a web platform.
Operators must spin up a separate HTTPS host
for the
mta-sts.<your-domain>
subdomain.
3. Microsoft 365 is also an MTA-STS-honoring sender
Exchange Online's outbound mail platform
honors recipient MTA-STS policies. When
delivering mail to a recipient domain that has
published an MTA-STS policy in
mode: enforce, Exchange Online
fetches the policy, caches it for the policy's
max_age, and refuses to deliver
over plaintext or with an unauthenticated
certificate. Operators do not need to enable
this behaviour — it ships on by default in
current Exchange Online tenants. The dual role
(M365 as both recipient and sender) means
every M365 tenant deploys MTA-STS for two
independent reasons: to protect inbound mail
arriving at the tenant's MX, and to ensure
outbound mail to MTA-STS-emitting recipients
delivers correctly.
Prerequisites
- An active Microsoft 365 tenant with at
least one custom accepted domain (the
domain you want to protect with MTA-STS).
Verify with
Get-AcceptedDomainvia Exchange Online PowerShell. - DNS administration rights at your custom
domain's authoritative nameservers (to
publish the
_mta-stsTXT record, the_smtp._tlsTLS-RPT TXT record, and a CNAME or A/AAAA record at themta-stssubdomain that points at your chosen HTTPS host). - Access to an external HTTPS hosting
platform (Azure Static Web Apps, Azure
Front Door, Cloudflare Pages, AWS S3 +
CloudFront, GitHub Pages, Netlify, Vercel,
or any commodity LAMP/NGINX host). The
platform must support custom-hostname
HTTPS with a publicly-trusted certificate
for the
mta-sts.<your-domain>hostname. - A mailbox or webhook endpoint to receive
TLS-RPT reports (typically
tls-reports@<your-domain>on the M365 tenant itself, or a third-party TLS-RPT analytics provider like Hardenize, Postmark, or Valimail). - DMARC and SPF already published for the custom domain — see DMARC for Microsoft 365 and SPF for Microsoft 365. MTA-STS protects the transport layer; DMARC and SPF protect the authentication layer. All three together form the canonical M365 anti-spoofing posture.
Step 1: Publish the _mta-sts DNS TXT record
The _mta-sts TXT record signals
to senders that your domain publishes an
MTA-STS policy and supplies the id-token they
use to detect when their cached policy is
stale. Publish a single TXT record at the
_mta-sts label of your custom
domain:
; Replace example.com with your custom domain and 20260522T120000
; with a fresh unique token (typically a timestamp; any string of 1-32
; alphanumeric characters is valid per RFC 8461 §3.1):
_mta-sts.example.com. IN TXT "v=STSv1; id=20260522T120000"
Two tags only: v=STSv1
(case-sensitive uppercase, MUST appear first)
and id=<token> (1-32
alphanumeric characters, opaque to
senders, used as a cache-bust signal). The
record MUST live at the three-label name
_mta-sts.<your-domain>
(with a leading underscore on
_mta-sts). Publishing at
mta-sts.<your-domain>
(without the underscore) is the #1 silent-
record-ignoring drift class.
Verify publication with
dig or
Resolve-DnsName:
$ dig +short TXT _mta-sts.example.com
"v=STSv1; id=20260522T120000"
PS> Resolve-DnsName _mta-sts.example.com -Type TXT
Name Type TTL Section Strings
---- ---- --- ------- -------
_mta-sts.example.com TXT 3600 Answer {v=STSv1; id=20260522T120000}
The id-token discipline matters operationally:
every time you edit the policy file at
https://mta-sts.<your-domain>/.well-known/mta-sts.txt,
you MUST bump the id= value in
this TXT record. Senders cache the policy by
id-token; without the bump, they continue
serving stale policy from cache for up to the
max_age of the previous fetch.
Step 2: Stand up HTTPS hosting for mta-sts.<your-domain>
Microsoft 365 does NOT host the MTA-STS policy
file. Operators must provision an external
HTTPS endpoint at the
mta-sts subdomain of their
custom domain. The endpoint requirements per
RFC 8461 §3.3:
- HTTPS-only. Plaintext HTTP on port 80 is not honored; senders fetch via HTTPS on port 443 only.
- Publicly-trusted TLS certificate.
The certificate must validate against the
public PKI (Let's Encrypt, DigiCert,
GlobalSign, Sectigo, etc.) and its
Subject CN or SAN must include
mta-sts.<your-domain>. Self-signed certificates, private-CA certificates, and wildcard certificates that don't cover themta-stssubdomain all causests-webpki-invalidin TLS-RPT and silent policy rejection. - Content-Type
text/plainon the policy file response. Other Content-Type values (notablyapplication/octet-streamandtext/html) causests-policy-invalid. - HTTP/1.1 or HTTP/2. Both are honored; HTTP/3 is not yet universally supported on the sender side and should not be relied on as the sole transport.
Hosting options for an M365 tenant
The most common operator choices, in approximate order of M365-affinity:
- Azure Static Web Apps (free tier, autoprovisions HTTPS certificate via Let's Encrypt). Easiest for tenants already running Azure workloads.
- Azure Front Door (recommended if you already have an Azure Front Door instance fronting other web workloads — adds a routing rule rather than provisioning a new endpoint).
- Cloudflare Pages or Cloudflare Workers (free tier, supports custom-hostname HTTPS via Cloudflare's certificate authority — typical for tenants already proxying their primary domain through Cloudflare).
- AWS S3 + CloudFront (low cost, certificate via ACM). Common for organizations with existing AWS infrastructure.
- GitHub Pages with custom domain (auto-HTTPS via Let's Encrypt, free for public repositories). The simplest zero-cost option for small tenants.
- Netlify or Vercel (free tier, zero-config HTTPS).
- Any commodity LAMP/NGINX host you already operate (works fine, requires manual certificate provisioning via Let's Encrypt or your existing CA).
DNS for the mta-sts subdomain
Whichever host you choose, publish a CNAME
(or A/AAAA records, depending on the host's
DNS pointer requirements) at
mta-sts.<your-domain>
pointing at the host:
; Example: Azure Static Web App fronted at mywebapp.azurestaticapps.net
mta-sts.example.com. IN CNAME mywebapp.azurestaticapps.net.
; Example: Cloudflare Pages fronted at mypages.pages.dev
mta-sts.example.com. IN CNAME mypages.pages.dev.
; Example: GitHub Pages custom domain — A records to GitHub's IPs
mta-sts.example.com. IN A 185.199.108.153
mta-sts.example.com. IN A 185.199.109.153
mta-sts.example.com. IN A 185.199.110.153
mta-sts.example.com. IN A 185.199.111.153
Verify the HTTPS endpoint is reachable and serves a valid certificate:
$ curl -v https://mta-sts.example.com/.well-known/mta-sts.txt
* Trying 185.199.108.153:443...
* Connected to mta-sts.example.com
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* Server certificate:
* subject: CN=mta-sts.example.com
* start date: Mar 1 00:00:00 2026 GMT
* expire date: May 30 23:59:59 2026 GMT
* issuer: CN=Let's Encrypt R3, O=Let's Encrypt, C=US
* SSL certificate verify ok.
> GET /.well-known/mta-sts.txt HTTP/2
< HTTP/2 200
< content-type: text/plain
< content-length: 78
version: STSv1
mode: testing
mx: example-com.mail.protection.outlook.com
max_age: 86400
Step 3: Publish the policy file at /.well-known/mta-sts.txt
Serve the policy file at the path
/.well-known/mta-sts.txt on the
mta-sts.<your-domain>
HTTPS host. The file is plain text with one
key: value pair per line per
RFC 8461 §3.2. The four canonical keys:
version: STSv1
mode: testing
mx: example-com.mail.protection.outlook.com
max_age: 86400
Field-by-field:
version: STSv1- Case-sensitive uppercase
STSv1. MUST appear first. Senders reject the policy when version is missing, mis-cased (stsv1,STSV1), or unrecognised. mode:- One of
enforce,testing, ornone. Case-sensitive lowercase.testing: senders report failures via TLS-RPT but still deliver mail.enforce: senders refuse to deliver over plaintext or with an unauthenticated certificate.none: emergency-rollback signal — equivalent to having no policy. Standard rollout: publish withtestingfor 1-2 weeks, review TLS-RPT, promote toenforce. mx:- One per line per MX host that handles
inbound mail. For an M365 tenant, this is
typically
<tenant>-com.mail.protection.outlook.com— see the next section. Single-label wildcards (*.mail.protection.outlook.com) match exactly one label; multi-label wildcards (**.protection.outlook.com) are not supported by RFC 8461. max_age:- Cache lifetime in seconds.
86400(24h) is typical for initial testing-mode publication;604800(7 days) or2592000(30 days) for stable enforce-mode policies. RFC 8461 §3.2 capsmax_ageat31557600(one Julian year); senders MUST honor values up to that cap but MAY cap shorter (Google caps at 604800).
Worked example for a standard M365 tenant at
example.com in initial
testing-mode burn-in:
version: STSv1
mode: testing
mx: example-com.mail.protection.outlook.com
max_age: 86400
After 7-14 days with no TLS-RPT failures, promote to enforce mode with a longer max_age:
version: STSv1
mode: enforce
mx: example-com.mail.protection.outlook.com
max_age: 604800
Don't forget to bump the
id= value in the
_mta-sts TXT record every time
the policy file changes. Without the bump,
senders continue serving the cached policy
until their cached max_age expires.
Enumerating MX hosts in the mx: directive
The mx: directive is an
enumeration — only listed hostnames can
deliver mail under MTA-STS enforcement.
Mis-listing an MX is the #1 reason
legitimate inbound mail bounces after
promoting to mode: enforce.
Standard M365 tenant (no hybrid, no third-party gateway)
Run dig +short MX <your-domain>
to discover your tenant's MX target:
$ dig +short MX example.com
0 example-com.mail.protection.outlook.com.
The format is consistent across all
Microsoft 365 tenants:
<tenant>-com.mail.protection.outlook.com
where <tenant>-com is the
dashed transformation of your domain
(example.com →
example-com, dots replaced
with dashes). The policy file mx: line
enumerates exactly that hostname:
mx: example-com.mail.protection.outlook.com
Hybrid Exchange tenant (on-prem Edge + M365)
Tenants running Exchange Server on-premises with M365 hybrid mode often have a primary MX pointing at an on-prem Edge Transport server with a secondary or backup MX at the M365 endpoint. Enumerate both:
$ dig +short MX example.com
10 mail-edge.example.com.
20 example-com.mail.protection.outlook.com.
mx: mail-edge.example.com
mx: example-com.mail.protection.outlook.com
During hybrid cutover (migrating from on-prem
to M365 cloud-only or vice-versa), update
the mx: list BEFORE swapping MX records.
Senders with cached policies will continue
enforcing the old mx: list for up to
max_age seconds — if you swap
MX without updating the policy, mail to the
new MX target bounces.
Third-party security gateway in front of M365
Tenants behind Mimecast, Proofpoint, Barracuda, FortiMail, or other security gateways have MX pointing at the gateway's public hostnames rather than directly at M365:
$ dig +short MX example.com
10 us-smtp-inbound-1.mimecast.com.
10 us-smtp-inbound-2.mimecast.com.
mx: us-smtp-inbound-1.mimecast.com
mx: us-smtp-inbound-2.mimecast.com
The gateway forwards mail to the M365
endpoint internally — that hop is not
visible to external MTA-STS-honoring
senders, so the
protection.outlook.com
hostname should NOT appear in mx: when a
gateway is in front. Confirm the gateway
itself supports STARTTLS and a valid
publicly-trusted certificate before
promoting to enforce mode.
Multi-domain tenant
Each accepted domain in a multi-domain
M365 tenant needs its own MTA-STS policy
with its own mx: list. The policies do NOT
inherit across the tenant. A policy
published at
mta-sts.example.com does NOT
protect mail delivered to
example.co.uk in the same
tenant — operators must publish separate
_mta-sts.example.co.uk and
mta-sts.example.co.uk/.well-known/mta-sts.txt
pairs for each domain.
Step 4: TLS-RPT for monitoring
MTA-STS is operationally blind without
TLS-RPT (SMTP TLS Reporting, RFC 8460).
Senders report TLS handshake failures and
policy-fetch failures back to you via the
rua= mailbox declared in
_smtp._tls.<your-domain>.
Publish the companion record:
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
Senders honor TLS-RPT by submitting JSON
reports per RFC 8460 §4 once per day
summarising every TLS handshake outcome
against your domain. The report enumerates
summary.total-successful-session-count,
summary.total-failure-session-count,
and per-failure failure-details[]
entries with result-type values
from the 12-value catalogue (the
MTA-STS-specific values to watch:
sts-policy-fetch-error,
sts-policy-invalid,
sts-webpki-invalid,
certificate-host-mismatch,
certificate-expired,
starttls-not-supported).
Without TLS-RPT, the testing-mode burn-in workflow is impossible — operators have no visibility into whether senders successfully fetched the policy and validated the certificate. Always publish TLS-RPT BEFORE promoting MTA-STS to enforce mode. See TLS-RPT explained, TLS-RPT DNS record syntax, and TLS-RPT JSON report format for the byte-level references.
Receiving TLS-RPT reports on Microsoft 365
The simplest setup uses a dedicated mailbox
on the M365 tenant itself
(tls-reports@<your-domain>).
The mailbox receives JSON attachments with
filename
<reporter>!<policy-domain>!<unix-start>!<unix-end>.json[.gz]
per RFC 8460 §5.1. Configure the mailbox to
auto-forward to a TLS-RPT analytics
provider (Hardenize, Postmark, Valimail,
Skysnag) or build an in-house ingestion
pipeline via the
DMARC Analyzer API
that parses the JSON attachments into the
dmarc_tls_reports table.
Testing-mode burn-in workflow
The canonical testing-to-enforce migration discipline for an M365 tenant:
- Week 0. Publish the
_mta-stsTXT record, stand up HTTPS hosting, publish the policy file withmode: testingandmax_age: 86400. Publish TLS-RPT pointing attls-reports@<your-domain>. - Day 1-3. Confirm policy
is reachable via
curland the certificate validates. Confirm DNS records resolve viadig. - Day 7. Begin reading
TLS-RPT reports arriving from Google,
Microsoft 365 (other tenants), Yahoo,
ProtonMail, and other MTA-STS-honoring
senders. Watch for
sts-policy-fetch-error,sts-policy-invalid,sts-webpki-invalid, andcertificate-host-mismatchin thefailure-details[]array. - Week 2. If TLS-RPT
reports show all four MTA-STS-specific
failure types absent for 7+ consecutive
days AND the
total-successful-session-countis substantially greater than zero (so you know senders are actually attempting policy-validated delivery), promote tomode: enforceand increasemax_ageto 604800 (7 days). Bump theid=value in the_mta-stsTXT record. - Week 4. With enforce
mode stable, increase
max_ageto 2592000 (30 days) for longer cache-pinning. Bumpid=again. - Operational discipline.
Every future change to MX records, the
mta-sts.<your-domain> certificate, or
the mta-sts.<your-domain> HTTPS host
provider MUST be preceded by reverting
to
mode: testingfor 24-72 hours while the change burns in. The cached-policy enforcement window is precisely why MTA-STS is brittle to infrastructure changes: cached enforce policies survive on the sender side untilmax_ageexpires regardless of what you change on your end.
Emergency rollback (mode: none)
If a misconfiguration causes legitimate
inbound mail to bounce in enforce mode,
revert to mode: none and bump
the id-token. Senders fetch the new policy
on their next cache-bust signal (within
minutes of the TXT record TTL expiring) and
suspend enforcement. mode: none
is semantically equivalent to having no
policy — emergency-rollback only.
Ten common Microsoft 365 MTA-STS mistakes
- Assuming Microsoft hosts the
policy file. Exchange Online is
a mail platform, not a web platform.
Microsoft 365 does NOT serve content at
mta-sts.<your-domain>; operators must spin up an external HTTPS host. The single most common "where do I start" confusion. - Looking for an MTA-STS panel in the Defender portal. There is none. MTA-STS configuration is entirely DNS + external HTTPS, outside the Microsoft administrative surface. No PowerShell cmdlet, no Exchange admin centre pane.
- Publishing the policy file at
the bare apex domain instead of the
mta-sts subdomain. RFC 8461
§3.3 locks the hostname to
mta-sts.<domain>— publishing the file athttps://<your-domain>/.well-known/mta-sts.txtis silently ignored by senders. - Forgetting the leading
underscore on the TXT record.
The DNS record lives at
_mta-sts.<domain>(underscore prefix per RFC 8552 underscore-label convention). Publishing atmta-sts.<domain>(no underscore) confuses MTA-STS configuration with the HTTPS host configuration and produces silent record-ignoring. - Promoting to enforce before the
policy file is reachable. With
mode: enforcein effect, a broken HTTPS endpoint causessts-policy-fetch-erroron every sender and inbound mail bounces. Always confirm the endpoint is reachable viacurland the certificate validates BEFORE promoting to enforce. - Enumerating the wrong MX
hostname in the mx: directive.
For a standard M365 tenant the MX is
<tenant>-com.mail.protection.outlook.com; for a hybrid Exchange tenant both the on-prem MX and the M365 MX must be enumerated; for a third-party gateway only the gateway MX appears in the list. Mis-enumeration causes legitimate mail to bounce in enforce mode. - Forgetting to bump the
id-token after a policy edit.
Senders cache the policy by id-token;
without the bump, the cached policy
persists until
max_ageexpires regardless of what you change on your end. Every policy edit MUST be paired with anid=bump in the_mta-stsTXT record. - Using a wildcard certificate
that doesn't cover the mta-sts
subdomain. A
*.example.comwildcard coversmta-sts.example.com; aexample.combare-apex certificate does NOT. Operators reusing an existing apex certificate for the mta-sts hostname seests-webpki-invalidon every TLS-RPT report. - Skipping TLS-RPT. Without TLS-RPT, the testing-mode burn-in workflow is impossible — operators have no visibility into whether senders successfully fetched and validated the policy. Always publish TLS-RPT BEFORE the MTA-STS policy.
- Capital-letter mode value.
Mode: Enforcewith capital E (or any other capitalisation variant) fails the token match per RFC 8461 §3.2. The value MUST be lowercaseenforce,testing, ornone.
Troubleshooting decision matrix
Map the observed TLS-RPT failure type or operator symptom to the diagnostic next step:
- Symptom: TLS-RPT
reports show
sts-policy-fetch-error.
Diagnosis: Senders cannot fetch the policy file. Either the HTTPS endpoint is unreachable, the certificate is invalid, the Content-Type is wrong, or themta-stssubdomain DNS does not resolve.
Fix: Runcurl -v https://mta-sts.<your-domain>/.well-known/mta-sts.txt— confirm 200 OK withcontent-type: text/plainand a Let's Encrypt / DigiCert certificate chain. - Symptom: TLS-RPT
reports show
sts-policy-invalid.
Diagnosis: The policy file is reachable but does not parse. Possible causes: UTF-8 BOM at file head, comments (# lines) in the file, capital-letterMode: Enforce, missingversion:ormax_age:key, malformed line endings.
Fix: Re-save the policy file with a BOM-free editor, confirm only the four canonical keys appear, confirm all values are lowercase. See MTA-STS policy file syntax for the full byte-level reference. - Symptom: TLS-RPT
reports show
sts-webpki-invalidorcertificate-host-mismatch.
Diagnosis: The certificate atmta-sts.<your-domain>either does not validate against the public PKI (self-signed, private CA, missing intermediates) or its Subject CN / SAN does not includemta-sts.<your-domain>exactly.
Fix: Runopenssl s_client -connect mta-sts.<your-domain>:443 -servername mta-sts.<your-domain>— confirm the chain bundles intermediates and the end-entity certificate matches the hostname. Provision a fresh Let's Encrypt certificate specifically for the mta-sts subdomain. - Symptom: Mail bounces
with "TLS required but not available"
or "MTA-STS enforcement failure" after
promoting to enforce.
Diagnosis: The mx: directive enumerates a hostname that does not match the actual MX target, or the actual MX target does not advertise STARTTLS with a valid certificate.
Fix: Rundig +short MX <your-domain>— confirm every returned MX hostname appears in the mx: directive verbatim. Revert tomode: testingtemporarily if the bounce rate is impacting business. - Symptom: No TLS-RPT
reports arrive even after 7 days.
Diagnosis: Either the domain has insufficient inbound mail from MTA-STS-honoring senders (low-traffic domain), the_smtp._tlsTXT record is mis-published (missing leading underscore, wrong scheme prefix on rua=, missing version tag), or the rua= mailbox is unreachable.
Fix: Verifydig +short TXT _smtp._tls.<your-domain>returns"v=TLSRPTv1; rua=mailto:<mailbox>". See TLS-RPT DNS record syntax for the full byte-level reference.
Frequently asked questions
- How do I enable MTA-STS on Microsoft 365?
- Four steps. (1) Publish the MTA-STS DNS TXT record at _mta-sts.<your-domain> with value "v=STSv1; id=<unique-token>" (the id-token changes whenever the policy file is updated). (2) Stand up an HTTPS endpoint at mta-sts.<your-domain> (Microsoft 365 does NOT host the policy file — typical hosts are Azure Static Web Apps, Azure Front Door, Cloudflare Pages, AWS S3+CloudFront, or any commodity HTTPS host with a publicly-trusted certificate). (3) Serve the policy file at https://mta-sts.<your-domain>/.well-known/mta-sts.txt with Content-Type text/plain, containing version: STSv1, mode: testing (initially) or mode: enforce (once burned in), one mx: line per MX host that handles your inbound mail (for M365 tenants this is typically <tenant>-com.mail.protection.outlook.com where <tenant>-com is the dashed transformation of your domain), and max_age: 86400 (24 hours) initially, increasing to 604800 (one week) or higher once stable. (4) Publish a TLS-RPT record at _smtp._tls.<your-domain> with v=TLSRPTv1; rua=mailto:tls-reports@<your-domain> so senders report TLS handshake failures and policy-fetch failures back to you. Microsoft 365 has NO Defender-portal MTA-STS panel — all configuration is DNS + external HTTPS.
- What MX hosts should I list in my Microsoft 365 MTA-STS policy?
- The exact MX hostname(s) Microsoft published for your tenant's accepted domains. Run dig +short MX <your-domain> to see the canonical values. For a standard M365 tenant with no on-prem hybrid and no third-party security gateway, you will see one MX target with the shape <tenant>-com.mail.protection.outlook.com where <tenant>-com is the dashed transformation of your domain (example.com → example-com.mail.protection.outlook.com). List that exact hostname in the mx: directive of your MTA-STS policy file. For multi-domain tenants, each accepted domain needs its own MTA-STS policy with its own mx: list — the policies do NOT inherit across the tenant. For hybrid Exchange tenants routing some mail through an on-prem Edge Transport server, list both the on-prem MX and the M365 MX. For tenants behind a third-party security gateway (Mimecast, Proofpoint, Barracuda, Fortinet), list the gateway MX (mail.protection.com hostnames are replaced by the gateway's public hostnames). The mx: directive is an enumeration — only listed hostnames can deliver mail under MTA-STS enforcement.
- Where do I host the MTA-STS policy file for a Microsoft 365 tenant?
- Anywhere that can serve a static text file over HTTPS with a publicly-trusted certificate matching mta-sts.<your-domain>. Microsoft 365 does NOT host the file natively — Exchange Online is a mail platform, not a web platform; there is no SharePoint / OneDrive / Defender route to publish a /.well-known/mta-sts.txt file on the mta-sts subdomain. Operators commonly use: (a) Azure Static Web Apps (free tier, autoprovisions HTTPS), (b) Azure Front Door (if you already use Azure for other workloads), (c) Cloudflare Pages or Cloudflare Workers (free tier, supports custom-hostname HTTPS), (d) AWS S3 with CloudFront for the certificate front-end, (e) GitHub Pages with a custom domain (auto-HTTPS via Let's Encrypt), (f) Netlify or Vercel static hosting, (g) any commodity LAMP / NGINX host where you already run a website. The hostname must be exactly mta-sts.<your-domain> (not www.mta-sts or mta-sts1 — the convention is locked by RFC 8461). The certificate at that hostname must validate against the public PKI; a self-signed cert or a wildcard cert that doesn't cover mta-sts.<your-domain> causes sts-webpki-invalid in TLS-RPT reports and silent policy rejection.
- Why does my MTA-STS policy fetch fail with a certificate error for an M365 tenant?
- Most likely the certificate at mta-sts.<your-domain> does not match the hostname being fetched, has expired, or was issued by a CA outside the public PKI trust store. Diagnose with three commands. (1) openssl s_client -connect mta-sts.example.com:443 -servername mta-sts.example.com -showcerts </dev/null returns the served certificate chain — verify the Subject CN or SAN includes mta-sts.example.com (NOT just example.com — wildcards must be *.example.com explicitly, not example.com on its own). (2) curl -v https://mta-sts.example.com/.well-known/mta-sts.txt returns the served file — verify the HTTP/2 status is 200 OK and the Content-Type is text/plain. (3) curl -v --cacert /path/to/empty.pem https://mta-sts.example.com/.well-known/mta-sts.txt with an empty CA store fails — verify the certificate chain bundles intermediates (a common oversight: serving only the end-entity certificate without the intermediate chain so commodity clients without the intermediate cached in their TLS stack fail validation). Microsoft 365 itself does NOT supply or proxy the certificate for the mta-sts.<domain> subdomain — operators must obtain and serve it from the chosen HTTPS host.
- Does Microsoft 365 honor MTA-STS policies when sending mail outbound (does Exchange Online enforce MTA-STS as a sender)?
- Yes. Exchange Online (the M365 outbound platform) acts as an MTA-STS-honoring SENDER: when delivering mail outbound to a recipient domain that has published an MTA-STS policy (either at <domain> directly or at a recipient-side accepted domain), Exchange Online (a) fetches the recipient's policy at https://mta-sts.<recipient-domain>/.well-known/mta-sts.txt, (b) caches it for the policy's max_age, (c) enforces the policy on outbound TLS sessions for that domain (refuses to deliver over plaintext or with an unauthenticated certificate when mode: enforce is in effect), and (d) emits TLS-RPT reports back to the recipient's rua= mailbox detailing every TLS handshake failure or policy-fetch failure observed. This means recipients running Microsoft 365 receive MTA-STS-enforced delivery from any sender whose outbound system honors MTA-STS (Microsoft 365, Google Workspace, Yahoo, ProtonMail, and most modern commercial MTAs). Microsoft published the enforcement-side timeline starting from 2022; current Exchange Online tenants honor MTA-STS by default with no operator configuration.
- How do I verify my Microsoft 365 MTA-STS deployment is working?
- Five verification gates. (1) DNS: dig +short TXT _mta-sts.<your-domain> must return "v=STSv1; id=<token>". (2) HTTPS endpoint: curl -v https://mta-sts.<your-domain>/.well-known/mta-sts.txt must return HTTP/2 200 OK with Content-Type text/plain and the four canonical keys (version: STSv1, mode:, mx:, max_age:). (3) Certificate: openssl s_client -connect mta-sts.<your-domain>:443 -servername mta-sts.<your-domain> must return a chain whose end-entity certificate Subject CN or SAN includes mta-sts.<your-domain> and validates against the public PKI. (4) Policy file content: the mx: directive must enumerate every hostname that appears in dig +short MX <your-domain> — typically <tenant>-com.mail.protection.outlook.com for an M365 tenant. (5) End-to-end TLS-RPT: 24-72 hours after publication, the rua= mailbox at _smtp._tls.<your-domain> begins receiving JSON reports from major senders (Google, Microsoft 365, Yahoo) detailing policy-fetch success, TLS handshake outcomes, and failure-details for any handshake or policy-fetch errors. Empty or absent TLS-RPT reports means either (a) no senders have attempted delivery yet (low-traffic domain), or (b) the rua= mailbox is mis-published or the receiving endpoint refuses inbound mail.
- Can I use the Microsoft 365 Defender portal to configure MTA-STS?
- No. The Microsoft 365 Defender portal (security.microsoft.com) has panels for SPF, DKIM, DMARC, ARC, and Connection Filter policy, but it has NO MTA-STS panel. MTA-STS is a recipient-side hardening protocol: the recipient publishes a DNS TXT record at _mta-sts.<domain> and serves a policy file at https://mta-sts.<domain>/.well-known/mta-sts.txt, and senders honor that policy when delivering mail to the recipient. Microsoft 365 (the recipient platform) provides MX hosts (<tenant>-com.mail.protection.outlook.com) that operators enumerate in their policy mx: directive, but Microsoft does NOT publish the DNS record or host the policy file on the operator's behalf. All MTA-STS configuration for an M365 tenant is operator-driven at the DNS and HTTPS layers — there is no PowerShell cmdlet (no Set-MtaStsConfig), no Defender portal toggle, no Exchange admin center pane. The pragmatic workflow: publish the DNS record at your authoritative nameserver, stand up an HTTPS host for the policy file, observe via TLS-RPT — all entirely outside the Microsoft administrative surface.
Verify MTA-STS on your Microsoft 365 tenant now
DMARC Analyzer ingests TLS-RPT JSON reports
sent to your rua= mailbox at
_smtp._tls.<your-domain>
via includes/TlsRptParser.php
and surfaces per-sender policy-fetch
success, certificate-validation outcomes,
and STARTTLS handshake breakdown — catching
sts-policy-fetch-error,
sts-policy-invalid,
sts-webpki-invalid, and
certificate-host-mismatch
within hours of the first failure. Live DNS
verification for the
_mta-sts and
_smtp._tls records runs through
includes/DnsChecker.php; the
Email Header
Analyzer at
header_analyzer.php diagnoses
individual TLS handshake failures by
parsing the
Received: ... (using TLSv1.3 ...)
stack via
includes/EmailHeaderParser.php.