Missing or malformed Message-ID header
The message lacks a `Message-ID` header or contains one that does not conform to RFC 5322 syntax.
Receivers treat a missing Message-ID as a hallmark of bulk scripts and malware, so SpamAssassin fires MISSING_MID (+1.8) and large providers tag the message as low-trust before content filtering even runs. A malformed Message-ID also breaks threading, In-Reply-To chains, bounce correlation, and feedback loop reports, which prevents you from matching ARF complaints back to the original send. Over time the pattern suppresses inbox placement across the whole sending IP.
What causes it
- A homegrown SMTP client or script that writes the DATA payload directly and never emits a
Message-ID:line, assuming the MTA will add one (many MTAs no longer do on submission). - PHP
mail()or legacy mailers behind a relay that strips or rewrites headers, leaving the final version with noMessage-IDor one lacking the@domainright-hand side. - A
Message-IDbuilt from a non-unique source such asmd5(subject)or a static string, producing duplicates that receivers treat as malformed or replayed. - Mailing-list or marketing software that forwards a draft without regenerating the ID, so the same
Message-IDis reused across every recipient of the campaign. - A broken template that injects unescaped characters (spaces, CRLF, unbracketed values) into the header, violating the
angle-bracket requirement.
How to fix it
Verify what is actually on the wire
Send a test to a mailbox you control and inspect the raw source. The header must appear exactly once, with angle brackets and an @domain part.
grep -i '^message-id:' /var/mail/you
# Expected: Message-ID: <20261010T143022.ab12cd34@mail.example.com> Generate a compliant ID at message-build time
Build the ID yourself rather than hoping the MTA inserts one. Use a high-entropy left-hand side and your sending domain on the right so bounces and FBL reports stay correlatable.
# Python stdlib
from email.utils import make_msgid
msg['Message-ID'] = make_msgid(domain='mail.example.com')
# Node.js
msg.messageId = `<${crypto.randomUUID()}@mail.example.com>`; Force Postfix to stamp a Message-ID on submission
If you cannot change the application, let the submission MTA add one. Postfix will insert a header for any message that arrives without one when always_add_missing_headers is enabled.
postconf -e 'always_add_missing_headers = yes'
postfix reload Validate the header syntax in CI
Add a regex check to your outbound test suite so a malformed header fails the build instead of reaching production. The value must match per RFC 5322 section 3.6.4.
^Message-ID:\s<[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}>$ Fixed it? Run a fresh test to confirm.
Test my inbox →