InboxGrade

TLS certificate hostname does not match HELO

The CN or SAN on the TLS certificate presented during STARTTLS does not match the hostname advertised in the SMTP `EHLO`/`HELO` command.

Impact — Medium impact

Receivers performing strict (DANE or MTA-STS enforce mode) TLS validation will refuse the session, bouncing or deferring mail with errors like TLSA validation failed or certificate name mismatch. Even lenient receivers downgrade the connection to opportunistic TLS, lose the DANE/MTA-STS enforcement signal, and may score the message higher for spam. Over time this breaks inbound replies, transactional delivery to Google Workspace and Microsoft 365 tenants with inbound gateways, and any receiver that logs X-TLS headers used by downstream filters.

What causes it

  1. The MTA's smtpd_banner/myhostname (Postfix) or server.name (Exim) sends an EHLO string that was never added to the SAN list on the certificate — most commonly a short internal hostname like mail01 while the cert covers mail.example.com.
  2. A shared-hosting or load-balancer front end presents the provider's default certificate (e.g. mail.hostingprovider.net) while the MTA announces the customer domain in HELO.
  3. The certificate was issued for the web vhost (example.com / www.example.com) and reused for SMTP, but HELO is set to mta1.example.com which is not on the SAN list.
  4. An expired or renewed certificate was deployed with a different SAN set, and the operator forgot to update HELO/PTR to match, or vice versa.
  5. A reverse proxy or SMTP relay (HAProxy, sslh, Mimecast, Proofpoint) terminates TLS with its own cert while the backend MTA still issues the original HELO.

How to fix it

Confirm the mismatch from an external host

Use openssl s_client with STARTTLS to capture the exact EHLO string and the cert SANs the receiver sees. Compare subject/subjectAltName against the 250- line in the banner.

openssl s_client -starttls smtp -crlf -connect mail.example.com:25 \
  -servername mail.example.com 2>/dev/null | \
  openssl x509 -noout -subject -ext subjectAltName

Align HELO with a name on the certificate

Pick one FQDN that appears on the cert's SAN list and set it as the MTA's HELO name. For Postfix, set myhostname and smtp_helo_name to that value; for Exim, set primary_hostname; for Sendmail, set confSMTP_HELO_NAME. The chosen name must also forward- and reverse-resolve to the sending IP.

# /etc/postfix/main.cf
myhostname = mail.example.com
smtp_helo_name = mail.example.com

# reload
postfix reload

Reissue the certificate with the correct SAN set

If you cannot change HELO (e.g. multi-tenant relay), reissue the cert so every HELO name is listed as a SAN. With Let's Encrypt, use certbot certonly with all SAN entries in one call so the deployed cert covers both the public MX name and any per-queue HELO variants.

certbot certonly --standalone \
  -d mail.example.com \
  -d mta1.example.com \
  -d mta2.example.com \
  --preferred-challenges http

Update DANE TLSA records after any cert change

If the domain publishes _25._tcp. TLSA, a new cert will fail DANE until the TLSA record is regenerated from the new key or cert. Rotate TLSA before swapping the cert, or use 3 1 1 (certificate SPKI) with a published next-key so there is no validation gap.

openssl x509 -in /etc/letsencrypt/live/mail.example.com/cert.pem \
  -noout -pubkey | openssl pkey -pubin -outform DER | \
  openssl dgst -sha256 -hex

# publish as:
# _25._tcp.mail.example.com. IN TLSA 3 1 1 <hex-digest>

Fixed it? Run a fresh test to confirm.

Test my inbox →

Related errors