Skip to content

Delivery status notifications

When a message cannot reach a recipient, the sender needs to know, because otherwise the message is silently lost. A delivery status notification (DSN, RFC 3464) is the message that carries this news. A mail server sends it to the envelope sender of a message to report what happened to the message. It can report three outcomes: that the message could not be delivered to a recipient, that its delivery is delayed and still being retried, or, when the sender asked for it, that it was delivered. Users know these notifications as bounces.

Stalwart sends DSNs for the messages in its queue. This page describes when it sends them, how senders control them, who they come from and how to suppress them. It also covers the other direction: how Stalwart uses DKIM2 to check the DSNs it receives.

Stalwart checks after each delivery attempt whether the sender should learn about any recipient. It then sends one DSN per message, which reports every recipient whose status the sender should learn. A recipient is reported in one of three kinds of notification:

NotificationReported when
FailureThe recipient failed permanently, because the receiving server refused it or the message expired in the queue.
DelayThe recipient is still being retried and a delay notification is due according to the recipient’s schedule.
SuccessThe recipient was delivered, and the sender requested a success notification.

Each recipient is reported once for each kind. The subject of the DSN reflects its content, for example Failed to deliver message, Warning: Delay in message delivery, Successfully delivered message or Partially delivered message.

The DSN itself is a multipart/report message (RFC 6522), a format that combines a part for people with parts for software. It contains the following:

  • A readable explanation.
  • A machine-readable delivery status for each recipient.
  • The headers of the original message, up to 4 KiB, whatever the RET parameter requests.

The DSN carries Auto-Submitted: auto-generated, and the remote server’s reply for each failed recipient.

No DSN is sent for a message whose envelope sender is empty (<>). DSNs themselves are sent with that empty sender, so this rule ensures that two servers never exchange bounces about bounces.

Senders differ in which notifications they want for a message. The SMTP DSN extension (RFC 3461) lets the sender of a message control its notifications with parameters on the SMTP commands. These parameters are available only when the extension is offered to the client, which by default is to authenticated sessions only (EHLO stage):

ParameterCommandEffect
NOTIFYRCPT TOWhich notifications to send for the recipient: NEVER, or any of SUCCESS, FAILURE and DELAY. Without the parameter, failure and delay notifications are sent.
ORCPTRCPT TOThe original address of the recipient, reported in the Original-Recipient field.
ENVIDMAIL FROMAn identifier of the message chosen by the sender, reported in the Original-Envelope-Id field.
RETMAIL FROMFULL or HDRS. Stalwart returns only the headers in its own DSNs, and passes the parameter on to the next server.

The parameters do not stop at Stalwart. When the next server also supports the DSN extension, Stalwart passes the parameters on to it, so that the final server can report to the original sender.

A DSN is a message like any other, so it needs a sender and, to be trusted by receivers, a DKIM signature. These are set on the DsnReportSettings singleton (found in the WebUI under Settings › MTA › Reports › DSN). Each field is an expression evaluated with the queue sender variables, so the values can depend on the message being reported:

The default sender address and signing domain are built from system('report_domain'). This expression returns the report domain shared by all reports, or the default domain unless the report domain is set.

The Reporting-MTA field of each DSN, which names the server that produced it, is the outboundReportSubmitter of the server. The following object body sends DSNs from a postmaster address of the organisation’s domain:

{
"fromName": {"else": "'Mail Delivery System'"},
"fromAddress": {"else": "'[email protected]'"},
"dkimSignDomain": {"else": "'example.org'"}
}

Some recipients should never cause a notification, whatever the sender requested. A system Sieve script at the RCPT TO stage can override the NOTIFY parameter of a recipient by setting envelope.notify. The following script suppresses notifications for messages sent to any mailer-daemon address:

require ["variables", "envelope"];
if envelope :matches "to" "mailer-daemon@*" {
set "envelope.notify" "NEVER";
}

Try this script in Sievepad

The script takes effect once it is selected with the script field of MtaStageRcpt.

Forged DSNs are a common way to deliver unwanted mail. A server cannot tell from a DSN alone whether it really sent the message being reported, so a spammer can disguise a message as a bounce of mail the recipient never sent. DKIM2 closes this gap, because a DKIM2-signed DSN returns the signed original, and the original sender can check its own signature on it.

Stalwart applies this check when three conditions hold:

  • A DSN signed with DKIM2 arrives.
  • dkimVerify is enabled, as it is with its default of relaxed.
  • The message that the DSN returns is also signed with DKIM2.

Stalwart then verifies that both chains of signatures are valid and that the DSN reports a message this server sent. A DSN that fails these checks is refused with 550 5.7.1 DSN rejected due to DKIM2 verification failure.

A check can also fail to complete because a public key lookup failed temporarily. The outcome then depends on the mode of dkimVerify. When it is strict, the DSN is refused with 451 4.7.5 Unable to verify DKIM2 DSN, try again later. In relaxed mode, the DSN is accepted, and the result is logged as smtp.dkim2-fail.

Each refusal is logged as smtp.dkim2-dsn-rejected, with the reason in its details. The exact conditions are listed in DKIM2.

The DSNs that Stalwart sends are new messages, so they carry no DKIM2 chain of their own, and their DKIM2 signature starts a chain at i=1. They are signed with DKIM2 only when their From header field is Authentic for the signing domain. Otherwise they are sent without DKIM2 header fields (Reports and notifications).