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 with v=STSv1; id=<token>; (2) stand up an HTTPS endpoint at mta-sts.<your-domain> (M365 does NOT host this); (3) serve the policy file at /.well-known/mta-sts.txt with mode: testing initially; (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>-com is 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. Once sts-policy-fetch-error, sts-policy-invalid, sts-webpki-invalid, and certificate-host-mismatch are all absent for 7+ consecutive days, promote to mode: enforce and increase max_age to 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: enforce is in effect.
  • The id-token bump rule: every edit to the policy file MUST coincide with a fresh id= value in the _mta-sts TXT 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-AcceptedDomain via Exchange Online PowerShell.
  • DNS administration rights at your custom domain's authoritative nameservers (to publish the _mta-sts TXT record, the _smtp._tls TLS-RPT TXT record, and a CNAME or A/AAAA record at the mta-sts subdomain 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 the mta-sts subdomain all cause sts-webpki-invalid in TLS-RPT and silent policy rejection.
  • Content-Type text/plain on the policy file response. Other Content-Type values (notably application/octet-stream and text/html) cause sts-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:

  1. Azure Static Web Apps (free tier, autoprovisions HTTPS certificate via Let's Encrypt). Easiest for tenants already running Azure workloads.
  2. 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).
  3. 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).
  4. AWS S3 + CloudFront (low cost, certificate via ACM). Common for organizations with existing AWS infrastructure.
  5. GitHub Pages with custom domain (auto-HTTPS via Let's Encrypt, free for public repositories). The simplest zero-cost option for small tenants.
  6. Netlify or Vercel (free tier, zero-config HTTPS).
  7. 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, or none. 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 with testing for 1-2 weeks, review TLS-RPT, promote to enforce.
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) or 2592000 (30 days) for stable enforce-mode policies. RFC 8461 §3.2 caps max_age at 31557600 (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:

  1. Week 0. Publish the _mta-sts TXT record, stand up HTTPS hosting, publish the policy file with mode: testing and max_age: 86400. Publish TLS-RPT pointing at tls-reports@<your-domain>.
  2. Day 1-3. Confirm policy is reachable via curl and the certificate validates. Confirm DNS records resolve via dig.
  3. 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, and certificate-host-mismatch in the failure-details[] array.
  4. Week 2. If TLS-RPT reports show all four MTA-STS-specific failure types absent for 7+ consecutive days AND the total-successful-session-count is substantially greater than zero (so you know senders are actually attempting policy-validated delivery), promote to mode: enforce and increase max_age to 604800 (7 days). Bump the id= value in the _mta-sts TXT record.
  5. Week 4. With enforce mode stable, increase max_age to 2592000 (30 days) for longer cache-pinning. Bump id= again.
  6. 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: testing for 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 until max_age expires 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

  1. 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.
  2. 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.
  3. 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 at https://<your-domain>/.well-known/mta-sts.txt is silently ignored by senders.
  4. 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 at mta-sts.<domain> (no underscore) confuses MTA-STS configuration with the HTTPS host configuration and produces silent record-ignoring.
  5. Promoting to enforce before the policy file is reachable. With mode: enforce in effect, a broken HTTPS endpoint causes sts-policy-fetch-error on every sender and inbound mail bounces. Always confirm the endpoint is reachable via curl and the certificate validates BEFORE promoting to enforce.
  6. 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.
  7. 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_age expires regardless of what you change on your end. Every policy edit MUST be paired with an id= bump in the _mta-sts TXT record.
  8. Using a wildcard certificate that doesn't cover the mta-sts subdomain. A *.example.com wildcard covers mta-sts.example.com; a example.com bare-apex certificate does NOT. Operators reusing an existing apex certificate for the mta-sts hostname see sts-webpki-invalid on every TLS-RPT report.
  9. 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.
  10. Capital-letter mode value. Mode: Enforce with capital E (or any other capitalisation variant) fails the token match per RFC 8461 §3.2. The value MUST be lowercase enforce, testing, or none.

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 the mta-sts subdomain DNS does not resolve.
    Fix: Run curl -v https://mta-sts.<your-domain>/.well-known/mta-sts.txt — confirm 200 OK with content-type: text/plain and 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-letter Mode: Enforce, missing version: or max_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-invalid or certificate-host-mismatch.
    Diagnosis: The certificate at mta-sts.<your-domain> either does not validate against the public PKI (self-signed, private CA, missing intermediates) or its Subject CN / SAN does not include mta-sts.<your-domain> exactly.
    Fix: Run openssl 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: Run dig +short MX <your-domain> — confirm every returned MX hostname appears in the mx: directive verbatim. Revert to mode: testing temporarily 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._tls TXT record is mis-published (missing leading underscore, wrong scheme prefix on rua=, missing version tag), or the rua= mailbox is unreachable.
    Fix: Verify dig +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.

Sign up — Free plan Check my Microsoft 365 MTA-STS setup