uCheckeruChecker
11 min read

TLS-RPT: how to find out your mail is traveling unencrypted

SPF, DKIM, DMARC are configured. Messages are getting through. Everything looks fine? Almost. There is one question most people skip: is traffic between servers actually encrypted? And how would you know if it is not? TLS-RPT is the reporting protocol that tells you exactly where encryption breaks down.


What TLS-RPT is and why it matters

TLS-RPT (Transport Layer Security Reporting) is the mechanism described in RFC 8460. The idea is simple: receiving servers send you daily reports on whether TLS connections succeeded when delivering your mail. If a recipient's server tried STARTTLS but something went wrong — an expired certificate, a hostname mismatch, an unsupported protocol version — you get a report with the details.

Without TLS-RPT you are flying blind. Mail can travel in plaintext between servers and you will never know. Your sending server's logs show only its own side: it attempted STARTTLS. What happened at the receiving end? Silence. TLS-RPT closes that gap.

TLS-RPT is especially useful alongside MTA-STS (RFC 8461), which tells sending servers to require TLS when delivering to your domain and refuse plaintext fallback. MTA-STS sets the rule; TLS-RPT shows who is failing to meet it. One without the other is half the picture.

How it works: from DNS record to JSON report

The mechanism has three parts. First, a DNS record you publish. Second, sending servers that read that record and know where to send reports. Third, the reports themselves in JSON format, arriving once a day.

When Gmail or Outlook sends mail to your domain, it checks DNS for a _smtp._tls.yourdomain.com record. If it finds one, it notes the reporting address. At the end of the day it compiles statistics for all delivery attempts to your domain and sends a summary report: successful TLS connections, failed attempts, failure reasons, all in one file.

Setting up the DNS record

TLS-RPT requires a single TXT record in DNS. The format is minimal: a protocol version and an address for the reports.

Reports by email

_smtp._tls.yourdomain.com.  IN  TXT  "v=TLSRPTv1; rua=mailto:tlsrpt@yourdomain.com"

Breaking it down:

  • _smtp._tls is the fixed prefix. Two underscores, in that order. Do not confuse it with _dmarc — the format differs.
  • v=TLSRPTv1 is the version. There is only one version.
  • rua=mailto: is the aggregate report address. Works the same as rua in DMARC.

Reports via HTTPS

To avoid flooding a mailbox with JSON attachments, point reports to an HTTPS endpoint instead. Sending servers will POST the report directly.

_smtp._tls.yourdomain.com.  IN  TXT  "v=TLSRPTv1; rua=https://tlsrpt.yourdomain.com/report"

You can specify both, separated by a comma:

_smtp._tls.yourdomain.com.  IN  TXT  "v=TLSRPTv1; rua=mailto:tlsrpt@yourdomain.com,https://tlsrpt.yourdomain.com/report"

The HTTPS option is better for automation: the endpoint receives JSON directly, no MIME attachments to unpack. For getting started, email is simpler — no infrastructure required.

Verifying the record

dig TXT _smtp._tls.yourdomain.com +short

You should get a string starting with v=TLSRPTv1. If nothing comes back, check the record name (it must be _smtp._tls, not _tls._smtp) and wait for DNS propagation.

JSON report structure

Reports arrive as a gzip archive (when emailed) or a JSON POST body (when sent to an HTTPS endpoint). The structure is standardized. Here is a realistic example where most connections succeeded but a few did not:

{
  "organization-name": "Google Inc.",
  "date-range": {
    "start-datetime": "2026-05-14T00:00:00Z",
    "end-datetime": "2026-05-14T23:59:59Z"
  },
  "contact-info": "smtp-tls-reporting@google.com",
  "report-id": "2026-05-14T00:00:00Z_yourdomain.com",
  "policies": [
    {
      "policy": {
        "policy-type": "sts",
        "policy-string": [
          "version: STSv1",
          "mode: enforce",
          "mx: mail.yourdomain.com",
          "max_age: 604800"
        ],
        "policy-domain": "yourdomain.com"
      },
      "summary": {
        "total-successful-session-count": 1285,
        "total-failure-session-count": 3
      },
      "failure-details": [
        {
          "result-type": "certificate-expired",
          "sending-mta-ip": "209.85.220.41",
          "receiving-mx-hostname": "mail.yourdomain.com",
          "receiving-ip": "203.0.113.10",
          "failed-session-count": 2
        },
        {
          "result-type": "starttls-not-supported",
          "sending-mta-ip": "209.85.220.42",
          "receiving-mx-hostname": "backup.yourdomain.com",
          "receiving-ip": "203.0.113.11",
          "failed-session-count": 1
        }
      ]
    }
  ]
}

Key fields to understand:

  • organization-name is who sent the report. Google, Microsoft, Yahoo, Mail.ru — major providers support TLS-RPT.
  • policy-type is the policy type. Usually sts (MTA-STS) or no-policy-found when MTA-STS is not configured.
  • total-successful-session-count is the number of successful TLS connections for the period. Ideally this is the only nonzero number.
  • total-failure-session-count is failed attempts. Any value above zero is worth investigating.
  • result-type is the failure category. The most common ones: certificate-expired, certificate-host-mismatch, starttls-not-supported, validation-failure.

Error types and what to do about them

Each result-type points to a specific problem. Here is the full list from RFC 8460 translated into practical terms:

starttls-not-supported. The receiving server does not support STARTTLS. If this is your primary MX, enable TLS immediately. Backup MX servers need encryption too — attackers know that secondary servers are often configured more loosely.

certificate-expired. The most common error. The certificate on your MX server is past its validity date. The fix is obvious: renew it. Set up auto-renewal with Let's Encrypt or equivalent. Certbot with cron takes a few minutes to configure and eliminates this class of failure permanently.

certificate-host-mismatch. The hostname in the certificate does not match the MX record. Classic case: MX points to mail.yourdomain.com, but the certificate was issued for yourdomain.com. Add a SAN (Subject Alternative Name) or issue a separate certificate for the MX hostname.

certificate-not-trusted. The certificate is not from a trusted CA. Self-signed certificates on a production server are not a cost-saving measure — they mean no encryption for strict senders.

validation-failure. A general TLS validation error. This can relate to an unsupported protocol version (TLS 1.0 and 1.1 are now considered deprecated), weak cipher suites, or a broken certificate chain. Check your configuration with openssl s_client -connect mail.yourdomain.com:25 -starttls smtp.

TLS-RPT + MTA-STS: the complete picture

TLS-RPT works without MTA-STS — you will receive failure reports either way. But the combination gives you the full picture. MTA-STS tells senders to require TLS and not fall back to plaintext. TLS-RPT shows who failed to meet that requirement and why.

Setting up MTA-STS requires two things: a DNS record and a policy file reachable over HTTPS.

MTA-STS DNS record

_mta-sts.yourdomain.com.  IN  TXT  "v=STSv1; id=20260515001"

The id field is an arbitrary string. Change it every time you update the policy — sending servers cache the policy and check the id to decide whether to re-fetch the file.

Policy file

The file must be served at https://mta-sts.yourdomain.com/.well-known/mta-sts.txt. HTTPS, that exact path.

version: STSv1
mode: enforce
mx: mail.yourdomain.com
mx: backup.yourdomain.com
max_age: 604800
  • mode: testing — start here. Senders report failures via TLS-RPT but still deliver regardless.
  • mode: enforce — switch to this after testing. Mail without TLS is rejected.
  • max_age is the policy cache duration in seconds. 604800 is one week, a reasonable minimum.
MTA-STS in testing mode combined with TLS-RPT is a safe starting point. You get real TLS failure data without risking any delivery disruption. After two or three weeks of clean reports, switch to enforce.

Monitoring in practice

Getting reports is only half the job. You need to read them and act on them. Three approaches, from basic to serious:

1. Manual inspection

Reports arrive as gzip attachments. Unpack, read the JSON. For a domain sending a few thousand messages per day this is fine. Quick check from the terminal:

# Unpack and find failures
gunzip -c report.json.gz | python3 -m json.tool | grep -A5 "failure-details"

# With jq — more compact
gunzip -c report.json.gz | jq '.policies[]."failure-details"[]?'

2. Aggregation script

For heavier volumes, automate the parsing. A short Python script that reads all reports from a folder and prints a summary:

import json, gzip, glob, collections

failures = collections.Counter()

for path in glob.glob("reports/*.json.gz"):
    with gzip.open(path, "rt") as f:
        report = json.load(f)
    for policy in report.get("policies", []):
        for detail in policy.get("failure-details", []):
            key = detail["result-type"]
            count = detail["failed-session-count"]
            failures[key] += count

print("TLS failure summary:")
for error, count in failures.most_common():
    print(f"  {error}: {count}")

3. Third-party services

If you would rather not write parsers, there are ready-made options. Report-URI, Postmark, and URIports accept TLS-RPT (and DMARC), parse the reports, visualize them, and send alerts. Point your rua at their address and watch a dashboard instead of raw JSON. For most domains with moderate traffic, this is the practical choice.

Common setup mistakes

  • Wrong record name order. Correct: _smtp._tls. Wrong: _tls._smtp. This trips people up because the intuitive order feels reversed. The RFC specifies it that way and there is no getting around it.
  • Reporting mailbox filling up. TLS-RPT from major providers can generate dozens of messages per day on a busy domain. Use a dedicated mailbox or switch to an HTTPS endpoint.
  • No MTA-STS but expecting failure-details. Without MTA-STS, reports still arrive but with policy-type: no-policy-found. Less detail — there is no specific policy violation to report against.
  • Forgetting the backup MX. Primary server: fresh certificate, TLS 1.3, correct ciphers. Backup MX: TLS 1.0, self-signed certificate. When the primary is unavailable, mail routes through the backup and TLS-RPT shows a flood of errors.
  • Stale id in MTA-STS after updating the policy. Senders cache the policy. If you changed the file but did not update the id in DNS, servers keep using the old version until max_age expires.

Who sends TLS-RPT reports

Not all providers report equally. As of 2026: Google sends reports reliably and with good detail. Microsoft (Outlook, Hotmail) also sends them, though the format occasionally differs in minor ways. Yahoo supports TLS-RPT. Mail.ru and Yandex send reports with less granularity. Small hosting providers and self-hosted corporate servers vary — some report, most do not.

In practice, 80 to 90% of inbound traffic for a typical domain comes from the major providers. Their reports are enough to catch systemic TLS issues.

Setup checklist: 15 minutes

  1. Confirm all MX servers (including backup) have valid TLS certificates. Check with: openssl s_client -connect mail.yourdomain.com:25 -starttls smtp.
  2. Add the TLS-RPT DNS record: _smtp._tls.yourdomain.com TXT "v=TLSRPTv1; rua=mailto:tlsrpt@yourdomain.com".
  3. Create the MTA-STS policy file at https://mta-sts.yourdomain.com/.well-known/mta-sts.txt with mode: testing.
  4. Add the MTA-STS DNS record: _mta-sts.yourdomain.com TXT "v=STSv1; id=20260515001".
  5. Wait 24 to 48 hours. The first reports will start arriving.
  6. Review failure-details. Fix certificate and TLS configuration issues.
  7. Once failure-count is consistently zero, switch MTA-STS to mode: enforce and update the id in DNS.

Why TLS monitoring is worth the effort

Encrypting email traffic is not just a checkbox. Opportunistic STARTTLS — where servers encrypt if they can but do not insist — is vulnerable to downgrade attacks. An attacker sitting between your server and the recipient can suppress the STARTTLS advertisement, and the message goes in plaintext. MTA-STS plus TLS-RPT addresses this: you declare that TLS is mandatory and get notified when it fails.

For domains sending financial notifications, medical information, or legal documents, this is not optional. EU regulators under GDPR require adequate technical measures when transmitting personal data. TLS monitoring is one such measure.

There is also a deliverability angle. Google's sender guidelines specifically call for TLS 1.2 or higher on receiving servers. It is not a dominant ranking factor, but combined with SPF, DKIM, DMARC, and a clean list, it fills out the picture of a responsible sender.

Authentication without a clean list is only half the job

TLS-RPT, MTA-STS, SPF, DKIM, DMARC are the infrastructure layer. They protect the channel. But if your list has dead addresses, spam traps, and nonexistent domains, perfect authentication will not save your deliverability. Bounce rate climbs, reputation drops, mail starts landing in spam.

Once TLS-RPT is in place, check the list. Run your list through uChecker and in a few minutes you will see invalid addresses, risky contacts, and potential traps.

TLS-RPTTLS monitoringemail encryptionTLS reportsMTA-STSSTARTTLSdeliverabilityDNS records