MTA-STS
Hedwig implements MTA-STS (RFC 8461) on the sending side. When delivering outbound email, Hedwig automatically looks up recipient domain TLS policies and enforces them — preventing STARTTLS downgrade attacks and MX impersonation.
MTA-STS is always active. No configuration is needed. If a recipient domain doesn’t publish a policy, delivery proceeds normally.
How it works
When Hedwig delivers mail to a recipient domain (e.g. gmail.com):
- DNS lookup — checks for a
_mta-sts.gmail.comTXT record - Policy fetch — downloads the policy from
https://mta-sts.gmail.com/.well-known/mta-sts.txt - MX validation — verifies that each MX server matches the policy’s allowed patterns
- TLS enforcement — ensures the connection uses valid, PKIX-authenticated TLS
Policy modes
Recipient domains publish their policy in one of three modes:
| Mode | Behavior |
|---|---|
| enforce | MX servers that don’t match the policy are skipped. If all MXes fail, the email is deferred (never bounced). |
| testing | Validation failures are logged as warnings but delivery proceeds normally. |
| none | No enforcement — the domain is opting out of MTA-STS. |
Caching
Policies are cached in memory to avoid repeated DNS and HTTPS lookups:
- Cache respects each policy’s
max_age(TTL set by the recipient domain) - A background task refreshes cached policies every 24 hours
- If a fresh policy can’t be fetched, the cached version is used (per RFC requirement)
- Failed fetches trigger a 5-minute cooldown before retrying
Major providers with MTA-STS
Most major email providers publish MTA-STS policies:
| Provider | Mode | max_age |
|---|---|---|
| Gmail | enforce | 1 day |
| Outlook / Hotmail | enforce | 7 days |
| Microsoft | enforce | 7 days |
| ProtonMail | enforce | 7 days |
| Yahoo | testing | 1 day |
| Fastmail | testing | 1 day |
Metrics
When metrics are enabled, Hedwig exports MTA-STS counters:
hedwig_mta_sts_policy_fetch_total{result}— policy fetch attempts (success,failure,cached)hedwig_mta_sts_enforcement_total{mode,result}— enforcement decisions (pass,fail) by modehedwig_mta_sts_cache_size— number of cached policies
Verify it’s working
Check the logs for MTA-STS activity:
# Policy fetched and cached
INFO cached MTA-STS policy domain=gmail.com mode=enforce max_age=86400
# Enforce mode: MX validated
DEBUG MTA-STS policy found domain=gmail.com mode=enforce
# Testing mode: would-be failure logged
WARN MTA-STS testing: MX host does not match policy domain=example.com mx=bad.example.com
Or query the Prometheus metrics:
curl -s http://localhost:9090/metrics | grep mta_sts