Skip to main content
by Meysam Azad
12 min read

“MTA-STS Policy Is Missing: STSFetchResult.NONE” — What It Means and How to Fix It

You ran a status check — most likely Mail-in-a-Box’s System Status Checks — and one line came back with a red ✖: MTA-STS policy is missing: STSFetchResult.NONE. Mail still sends. Mail still arrives. So what is actually broken?

Short version: nothing is down, but a protection layer you intended to have is not there. This guide decodes the STSFetchResult.NONE token — where the string comes from and what the enum means — then walks a three-stage diagnosis with copy-paste dig, curl, and openssl commands: DNS TXT record, HTTPS policy fetch, TLS certificate chain. If you first want the protocol background, start with what MTA-STS does and why; this post stays on the error.

What does “MTA-STS policy is missing: STSFetchResult.NONE” mean?

It means the checker queried DNS and HTTPS for your MTA-STS policy and found nothing usable — no _mta-sts.<domain> TXT record, no fetchable policy file, or both. The fix is to publish the TXT record and serve https://mta-sts.<domain>/.well-known/mta-sts.txt over a valid certificate with a direct HTTP 200 response.

The token itself is worth decoding, because no other guide does. STSFetchResult is a Python enum from the open-source postfix-mta-sts-resolver library by Vladislav Yarmak. Mail-in-a-Box imports that library in management/status_checks.py and prints its return value verbatim — output.print_error(f"MTA-STS policy is missing: {valid}") — which is how a Python enum’s repr ends up in your admin panel. The library’s GitHub Issue #60 shows the runtime values: NONE = 0 (no policy found at all) and FETCH_ERROR = 2 (a policy was found but could not be fetched or validated). A working policy returns VALID.

Only two tools emit this exact string: Mail-in-a-Box and the library’s own mta-sts-query CLI. MXToolbox, Hardenize, and Google’s Workspace checker report the same condition in their own wording. So if you are seeing stsfetchresult.none, you are almost certainly looking at Mail-in-a-Box status checks.

One thing this error does not mean: an outage.

Key finding

Fail open RFC 8461: when no policy is fetchable, senders "MUST continue with delivery as though the domain has not implemented MTA-STS"

Source: RFC 8461, IETF (2018)

Your mail is not blocked. Senders fall back to opportunistic TLS. What you are missing is downgrade protection — the guarantee that supporting senders refuse to deliver over a stripped or plaintext connection. That is worth fixing, on your schedule, one gate at a time.

Three-stage MTA-STS diagnosis: DNS TXT record, HTTPS policy fetch, TLS certificate chain
The three-gate diagnosis cascade: each gate must pass before the next one matters. Fix the first failing gate.

Stage 1 — Is the _mta-sts DNS TXT record resolving?

Start at DNS, because every later stage is meaningless until this one passes. A sending server discovers your policy through a single TXT record — the _mta-sts TXT record — and if that record does not resolve, the resolver returns NONE without ever attempting the HTTPS fetch.

Check the _mta-sts TXT record bash
# Query the MTA-STS discovery record
dig +short txt _mta-sts.example.com

# Expected: exactly one record in this shape (empty output = record missing)
"v=STSv1; id=20260728120000"

# Real-world reference (gmail.com, verified 2026-07-28)
dig +short txt _mta-sts.gmail.com
"v=STSv1; id=20190429T010101;"

You want exactly one v=STSv1; id=... answer. If you get nothing, work through these branches — ordered most common first, and each one is a documented incident, not a hypothetical:

  • The record was never published to your real DNS. After a Mail-in-a-Box install or upgrade, users of external DNS (Cloudflare, registrar DNS) found the box generated the record internally but never wrote it to the authoritative zone. This is the root cause in the 2020 forum thread (6960) that still ranks for this error. Add _mta-sts.yourdomain.com IN TXT "v=STSv1; id=<timestamp>" at your provider.
  • Propagation lag. Mail-in-a-Box zones use a 1,800-second TTL; a freshly added record can take that long to appear everywhere. Cross-check several public resolvers before concluding it is missing.
  • A wildcard TXT record is shadowing the answer. One admin in the same thread traced the failure to a *.<domain> TXT record that stopped the _mta-sts query from being answered. Remove the wildcard or re-add it after the MTA-STS records.
  • Stale id after a policy edit. The record exists but senders keep a cached copy. Microsoft’s MTA-STS documentation requires the id value to change to a new unique string on every policy update so senders re-fetch — Google’s checker enforces a 1–32 alphanumeric id as well.

One library quirk to know: a local DNS resolver error on the box also returns NONE (Issue #60), so an external validator can pass while your own status check still errors. When in doubt, run the full discovery chain from outside with our MTA-STS checker — it walks DNS, fetch, and certificate in one pass.

Gate passed when: the TXT record resolves from public resolvers. Now the fetch.

Stage 2 — Does the HTTPS policy fetch return a direct 200?

The second gate is the policy file itself. A sending MTA fetches https://mta-sts.<domain>/.well-known/mta-sts.txt — the mta-sts.txt well-known path — and it is far stricter about the response than a browser is. Test it the way an MTA sees it. The first status line of the response must read HTTP/2 200 — if you see a 301 or 302 instead, that redirect is not a detour on the way to the policy; it is the failure itself:

Fetch the policy like a sending MTA bash
# Fetch the policy exactly like a sending MTA: no redirects, headers visible
curl -sS -D - --max-time 10 \
  https://mta-sts.example.com/.well-known/mta-sts.txt -o /dev/null

# Pass criteria — all three, on the FIRST response:
#   HTTP/2 200            <- a 301/302/307 is a hard failure (RFC 8461 §3.3)
#   content-type: text/plain
#   (no HTML challenge page in the body)

# Example healthy output:
#   HTTP/2 200
#   content-type: text/plain

# Example failing output (site-wide redirect caught the mta-sts host):
#   HTTP/2 301                              <- the redirect IS the failure
#   location: https://www.example.com/

Why the no-redirect rule matters enough to check headers:

Key finding

200 only RFC 8461 §3.3: HTTP 3xx redirects MUST NOT be followed and HTTP caching MUST NOT be used — any status other than a direct 200 fails the fetch

Source: RFC 8461 §3.3, IETF (2018)

This single rule explains the most confusing symptom in the wild: the policy opens fine in your browser, yet the checker still reports STSFetchResult.NONE. Browsers follow redirects silently; sending MTAs must not. None of the top-ranking guides for this error mention it.

Failure branches at this gate, in order of how often they show up, each from a documented case:

  • 404 or no host to connect to. Wrong path, or no web server (and no A/AAAA records) on the subdomain — the single largest bucket in the URIports survey below. The path is exact: /.well-known/mta-sts.txt.
  • Cloudflare proxying or Browser Integrity Check. With the record proxied (orange cloud), the policy fetch fails unless it is served from a Worker; and in one documented bounce, Browser Integrity Check served a challenge page to the sending MTA’s libwww-perl User-Agent instead of the policy. Unproxy the record, or add a WAF rule that skips security features for that hostname.
  • A site-wide redirect catches the mta-sts host. A Cloudflare community poster redirected their whole site to another domain; the blanket rule also applied to mta-sts.<domain>, so every MTA fetch died on the 301. Exclude the mta-sts host from redirects.
  • Wrong Content-Type. An F5 BIG-IP team found Microsoft rejected their policy until the response sent text/plain. Serve the file as plain text, not HTML or octet-stream.

These are not exotic edge cases — they are the mainstream failure modes among domains that publish MTA-STS:

Key finding

34% + 25% of misconfigured published policies: missing A/AAAA records for the policy host (34%) and HTTPS certificate problems (25%)

Source: URIports MTA-STS survey (January 2026)

Once the fetch returns a direct 200, sanity-check the body: version, mode, mx, and max_age, in that shape. A full worked policy with hosting options lives in our working MTA-STS policy example, and the MTA-STS policy generator produces a correct file from your MX hosts. This post stays on diagnosis.

Gate passed when: direct 200, text/plain, sane body. One gate left.

Stage 3 — Is the TLS certificate on the policy host valid?

The final gate is the certificate the policy host presents. The fetch happens over HTTPS, and RFC 8461 requires a CA-trusted certificate that covers mta-sts.<domain> — an MTA-STS certificate problem fails the fetch even when DNS and the file are perfect. Inspect what the host actually serves:

Verify the mta-sts certificate bash
# Inspect the certificate the policy host actually serves
echo | openssl s_client -connect mta-sts.example.com:443 \
  -servername mta-sts.example.com 2>/dev/null \
  | openssl x509 -noout -dates -subject -ext subjectAltName

# Pass criteria:
#   notAfter is in the future (not expired)
#   subjectAltName contains DNS:mta-sts.example.com — literally, not a wildcard
#     for a different level
#   s_client's "Verify return code" is 0 (ok) — run without the x509 pipe to see
#     chain errors such as "unable to get local issuer certificate"

# Example healthy output:
#   notBefore=Jun  1 00:00:00 2026 GMT
#   notAfter=Aug 30 23:59:59 2026 GMT       <- in the future: not expired
#   subject=CN=mta-sts.example.com
#   X509v3 Subject Alternative Name:
#       DNS:mta-sts.example.com             <- SAN literally covers the host

# Example failing output (cert never provisioned for the subdomain):
#   notAfter=Mar 12 23:59:59 2026 GMT       <- already past: renewal lapsed
#   subject=CN=example.com
#   X509v3 Subject Alternative Name:
#       DNS:example.com, DNS:www.example.com   <- no mta-sts entry: SAN miss

Three checks, in order of how often they fail:

  1. SAN coverage. The Subject Alternative Name list must literally contain mta-sts.<domain>. This is the dominant failure mode by far:
Key finding

94.5% of TLS-handshake failures on self-managed MTA-STS policy hosts stem from Common Name / Subject Alternative Name mismatches

Source: Ashiq, Fiebig & Chung, ACM IMC 2025
  1. Expiry. notAfter must be in the future.
  2. Chain. The full chain must reach a trusted root — no self-signed leaf, no missing intermediate.

Both patterns show up repeatedly in Mail-in-a-Box reports. In the 2020 upgrade thread, the certificate for the mta-sts. subdomain was never provisioned — openssl showed the SAN entries missing — and the community-confirmed fix was to provision the Let’s Encrypt certificate in the admin console, then re-run sudo mailinabox so the records regenerate. In a 2025 thread (16160), the error appeared suddenly across all domains because certificates had silently stopped renewing; the main site still worked, and the error cleared on its own once renewal succeeded. If your policy host’s certificate is managed separately from your main site’s, check its renewal explicitly.

It was working before — why did it break?

A deployment that passed all three gates can regress without anyone touching the policy. These are the drift modes setup guides skip:

  • Certificate renewal lapsed. The most common “sudden” cause, per the 2025 Mail-in-a-Box case above. Nothing changed — a renewal quietly failed.
  • Stale id with a long max_age. You edited the policy but never bumped the DNS id, so senders keep the old cached policy until max_age expires — up to the RFC maximum of roughly a year. Broken cache, correct file.
  • MX drift. You migrated mail providers and the policy’s mx: lines no longer match your live MX records.
  • Provider constraints. Microsoft does not support CNAME’d MX hosts with MTA-STS — the MX name must resolve to A/AAAA records (Microsoft Tech Community, “Introducing MTA-STS for Exchange Online”). Google Workspace validates your records but does not host the policy file for you — you still need a web server for the mta-sts subdomain (Google Workspace Help, answer 9276387). mailcow serves the policy dynamically since its 2025-09 release, so there is no static .well-known file to edit.

If you are in enforce mode and need to stop the bleeding while you fix a gate, roll back safely — revert the mode and invalidate sender caches in the same change:

Enforce-mode rollback: testing + id bump text
# 1. Edit the policy file back to testing mode
version: STSv1
mode: testing
mx: mail.example.com
max_age: 86400

# 2. Bump the id in DNS so senders discard the cached enforce policy
_mta-sts.example.com. IN TXT "v=STSv1; id=20260728130000"

# 3. Verify the rollback landed: allow one DNS TTL to pass, then re-run the
#    checker (status checks or the three-stage commands above) and confirm
#    the new id and mode: testing come back

And build yourself an early-warning system: senders that fail to fetch your policy tell you about it in TLS-RPT reporting with machine-readable result codes like sts-policy-fetch-error and certificate-expired. A domain with TLS-RPT enabled finds out about a lapsed certificate from reports — not from a status check weeks later.

FAQ

Is my email being blocked when I see STSFetchResult.NONE?

No. RFC 8461 requires senders to deliver as though MTA-STS were not implemented when no policy can be fetched, and Gmail and Exchange Online both fall back to opportunistic TLS. You lose downgrade protection, not delivery.

Which tool produces the “STSFetchResult.NONE” error message?

Mail-in-a-Box’s System Status Checks, which print the enum returned by the postfix-mta-sts-resolver library verbatim. No mainstream online checker — MXToolbox, Hardenize, Google’s Workspace checker — emits this string; they report the same condition in their own wording.

Why does my policy load in a browser but still fail?

RFC 8461 §3.3 forbids senders from following HTTP 3xx redirects, and browsers follow them silently. A site-wide redirect, a proxied Cloudflare record, or a bot-challenge page returns something other than a direct 200 to the sending server, so the fetch fails.

How long until the error clears after I fix it?

Once the TXT record resolves publicly and the policy returns a direct HTTP 200 over a valid certificate, the next status-check run flips to VALID. Allow for DNS TTL propagation, and bump the policy id so senders discard cached copies.

Should I start in testing or enforce mode?

Start with mode: testing and TLS-RPT enabled — the NCSC’s explicit recommendation — then move to enforce and increment the id once reports show no failures. Jumping straight to enforce is the one path that can block inbound mail.

Fix the one gate that fails

“MTA-STS policy is missing: STSFetchResult.NONE” is a Mail-in-a-Box status check printing a library enum, and the enum means one thing: no policy could be fetched. The diagnosis is a strict cascade — dig the _mta-sts TXT record, curl the policy for a direct 200, openssl the certificate chain — and in every documented incident the failure sat at exactly one gate. Fix that gate; the later ones are meaningless until the earlier ones pass.

The error will come back the same way it appeared: a renewal lapses, an MX drifts, an id goes stale. DMARCguard monitors MTA-STS alongside DMARC, SPF, DKIM, and TLS-RPT and tells you which gate broke before senders notice.