Skip to content

Routes and Relays

Christopher Holloway edited this page Oct 21, 2026 · 12 revisions

Routes and Relays

This page began as a chapter of the 6.2.10 manual and has been corrected against the 6.2.28 source tree. The Control Panel's pages are grouped differently now, so the paths below use today's groups; the TLS ports 465, 993 and 995 exist only after you create them on the TCP/IP ports page (a fresh install seeds 25, 587, 110 and 143); and everything added since 6.2.10 is in Changes-Since-6210. Where a value here disagrees with the Settings Reference, which is generated from the code, the reference is right. What 6.3.4 (27 September 2026, schema 6104), adds - remote domain policies, outbound shaping and IP warm-up, gateway encryption, restricted delivery, journaling, the send API, the two-family bind address and mail in to a webhook - is in §14.2a to §14.2f, labelled since 6.3.4; the page was brought up to 6.3.4 on 26 September 2026. Brought up on 5 October 2026 to 6.3.6 (6 October 2026, schema 6108), for what 6.3.6 changes on this page. 6.3.7, the latest release (17 October 2026, schema 6108), is not yet reflected here.

There are four ways this server can decide where an outbound message goes, and they are tried in a fixed order. Almost every "it went to the wrong server" question is answered by knowing that order, so it comes first.

flowchart TD
    M(["A message is ready to leave. Recipients are grouped by domain"]) --> RULE{"Did a global RULE apply the Send using route action?"}
    RULE -->|yes| FIXED(["Every recipient goes to that route's target host. Nothing else is consulted"])
    RULE -->|no| R{"Is there a ROUTE whose domain matches the RECIPIENT domain? Wildcards allowed"}
    R -->|yes| RT(["The route's host, port, connection security and credentials"])
    R -->|no| D{"Does the SENDER's local domain have a relay host of its own?"}
    D -->|yes| DR(["That domain's relay host, port, security and credentials. Port 0 means 25"])
    D -->|no| G{"Is a server-wide SMTP relayer configured?"}
    G -->|yes| GR(["The relayer host, port, security and credentials. Port 0 means 25"])
    G -->|no| MX(["Ordinary MX lookup for the recipient domain, then A or AAAA for each MX host"])
    RT --> MERGE
    DR --> MERGE
    GR --> MERGE
    MX --> MERGE["Targets that turn out to be identical are merged, so one relay gets ONE connection carrying every recipient"]
Loading

Read from the code in SMTP/ServerTargetResolver.cpp. Three things in that picture are worth saying in words:

  • A route is a statement about the destination and beats everything. A per-domain relay is a statement about the sender. The server-wide relayer is the fallback for mail nothing else has an opinion about.
  • A bounce never picks up a per-domain relay. A bounce has no envelope sender, so there is no sending domain to consult, and it belongs to the server rather than to the domain whose message failed (GetSenderDomain_).
  • Recipients are merged by target. A message for four domains that all relay through one smart host opens one connection, not four.

14.1 Sending through a smart host

If your ISP blocks outbound port 25, or your IP has poor reputation, relay outbound mail through a smart host — your ISP's server, or a service such as SendGrid, Mailgun or Amazon SES.

Configure it in Mail flow & delivery → Delivery of e-mail → Smart host.

The "Delivery of e-mail" page on its Delivery tab, subtitled "Outbound delivery, retries and the smart host.", showing Number of delivery retries, Minutes between retries 60, "Maximum MX hosts to try (0 = all)" 15, and the "Use PIPELINING when the receiving server offers it" and "Use CHUNKING (BDAT) when the receiving server offers it" switches; the bind-to-IP box, "Outbound delivery security (after MX lookup)" and "Add Delivered-To header" follow below the fold

Five tabs: Delivery, Smart host, Feedback, Reputation and Mailing lists. The Smart host tab holds the smart-host settings described here and the card for OAuth2 against Microsoft 365; the Delivery tab holds the outbound PIPELINING, CHUNKING and hard-linked local copies.

Setting COM name Default What it does What happens when it is wrong
Smart host name (empty = direct delivery) SMTPRelayer empty The smart host. Empty means "use MX lookup" A host that does not resolve produces the bounce text "The host specified as SMTP relay server could not be found" with enhanced code 4.3.5 — a temporary failure, so the message is retried rather than destroyed
Port SMTPRelayerPort 25 0 is treated as 25 A wrong port looks like a connection timeout in the SMTP log
Connection security SMTPRelayerConnectionSecurity None None, SSL/TLS, STARTTLS optional, STARTTLS required Required against a host that does not offer STARTTLS fails every message permanently - Server does not support STARTTLS. (5.7.0) - and returns it to its sender; 6.3.6 leaves this as it was (SMTPClientConnection.cpp answers 500/5.7.0 when the connection security is STARTTLS (required) and no TLS policy asked for TLS). Only TLS that a rule, a remote domain policy, or the destination's TLSA records or MTA-STS policy asked for is deferred (450 4.7.0, with a sentence naming it; since 6.3.6 for a host that offers no STARTTLS too)
The smart host requires authentication SMTPRelayerRequiresAuthentication off Off against a host that requires it produces 530/535 in the SMTP log and the mail stays queued
User name / password SMTPRelayerUsername, SetSMTPRelayerPassword empty
Number of delivery retries SMTPNoOfTries 4 How many times a temporarily failed message is retried 0 means one attempt and then a bounce
Minutes between retries SMTPMinutesBetweenTry 60
Maximum MX hosts to try MaxNumberOfMXHosts 15 Truncates a long MX list. 0 = all Set too low against a domain with many MX hosts and delivery gives up early
Outbound delivery security SMTPConnectionSecurity STARTTLS (optional) Applies to MX-resolved delivery, not to the relayer Required makes every receiver that lacks STARTTLS undeliverable

The first five rows live on the Smart host tab; the last four are on Delivery, because they govern all outbound mail rather than the smart host alone.

Several smart hosts (failover)

Separate the host names with a vertical bar to list more than one relay:

smtp1.example.net|smtp2.example.net

ExternalDelivery::ResolveRecipientServers_ splits on |, resolves each name in turn, and appends every address each name resolves to. The result is one flat, ordered list of candidate hosts, tried top to bottom.

flowchart LR
    A["SMTPRelayer holds two host names separated by a vertical bar"] --> B["Split on the bar"]
    B --> C["Resolve smtp1.example.net to 198.51.100.7 and 198.51.100.8"]
    B --> D["Resolve smtp2.example.net to 203.0.113.9"]
    C --> E["One ordered candidate list: 198.51.100.7, then 198.51.100.8, then 203.0.113.9"]
    D --> E
    E --> F{"Try the next candidate"}
    F -->|connected and accepted| G(["Delivered"])
    F -->|unreachable or a 4xx| F
    F -->|a 5xx permanent rejection| H(["Delivery FAILED. The next host is NOT tried"])
    F -->|list exhausted| I(["Queued for retry"])
Loading
Detail Behaviour
A single name with several A records Already gives you failover — every address is tried before moving on. You do not need the bar for that
Shared settings All hosts in the list share the port, connection security and credentials configured alongside them, so they must accept the same authentication
A permanent rejection (5xx) Treated as a real delivery failure and not retried against the next host. Trying the next one would deliver the message twice if the first server had in fact accepted it
MXTriesFactor INI, default 0 = off. When set, the number of hosts tried in one pass is capped at (retries so far + 1) × MXTriesFactor, so a message with a long candidate list does not hold a delivery thread. The log says "Limiting to MXTriesFactored value of N"

Worked example: relaying everything through an ISP

Goal: this server receives its own mail directly, but all outbound mail leaves through smtp.isp.example on port 587 with STARTTLS and a user name.

  1. Mail flow & delivery → Delivery of e-mail → Smart host
  2. Smart host name: smtp.isp.example
  3. Port: 587
  4. Connection security: STARTTLS (required) — required, not optional, so a downgrade cannot silently send your credentials in clear
  5. Tick The smart host requires authentication, fill in the user name and password
  6. Save changes

Send one message and read Monitoring & troubleshooting → Live logs. The line that proves the relayer was used is:

"SMTPDeliverer - Message 41: Relaying to host smtp.isp.example."

If you instead see the message go to an MX host, the relayer field is empty, or a route or a per-domain relay took precedence — check them in the order at the top of this page.

Per-domain relay: when each domain has its own provider

The Relay tab of the domain's dialog (Accounts & domains → Domains, the pencil button on the domain's row) sets where mail from that domain leaves through. It sits between a route and the server-wide relayer in specificity, and it is for one shape of installation: a server hosting several independent domains where each one has bought its own delivery provider.

Leave the host empty and the domain has no opinion, which is byte-for-byte the behaviour before the setting existed. Verified by test/RegressionTests/SMTP/PerDomainRelay.cs — ADomainRelayIsUsedInsteadOfTheServerWideRelayer, ADomainWithoutARelayStillUsesTheServerWideRelayer and ARouteOutranksTheSendingDomainsRelay.

14.2 Routes

A route overrides delivery for a specific recipient domain — "mail for partner.example.com goes to this specific server, not wherever DNS says."

Mail flow & delivery → Routes: type the domain and target host, press Add, then Properties… for everything else.

The Routes page: a table with Domain, Target host, Port, Retries and Authentication columns, empty - "No routes, so every domain's mail is delivered the usual way: to its MX hosts, or through the smart host set on the Delivery of e-mail page." with an Add a route button - under Properties… and Delete selected, and an "Add route" panel with Domain, Target SMTP host and Port 25

Used for internal domains, split delivery during a migration, or a partner with a private link.

The route editor, tab by tab

Tab Field Default What it does What happens when it is wrong
General Domain name — The recipient domain this route covers. Wildcards are matched, so *.partner.example works (Routes::GetItemByNameWithWildcardMatch). Unique per route A wildcard broader than you meant silently captures other domains. The first matching route in name order wins
Target SMTP host — Where the mail is sent. A name or a literal IP address. The host1|host2 failover of §14.1 applies here too A host that does not resolve gives the same 4.3.5 deferral as a bad relayer
Target SMTP port 25 when added from the Routes page
Description empty Free text
Delivery Number of delivery retries 3 when added from the Routes page Replaces the server-wide SMTPNoOfTries for recipients on this route Note the arithmetic: the delivery path applies the route's value as tries − 1 (ExternalDelivery.cpp:1038), so a route set to 3 schedules 2 further attempts
Minutes between retries 10 when added from the Routes page Replaces SMTPMinutesBetweenTry for those recipients Where one message has recipients on several routes, the largest retry count and the largest interval win
Addresses Deliver to all addresses on On: every address at the domain is routed. Off: only the addresses listed here Off with an incomplete list is the single most common route mistake — see the table below
The address list empty Used only when Deliver to all addresses is off
Security Connection security None None, SSL/TLS, STARTTLS optional, STARTTLS required
Authentication Target server requires authentication, user name, password off Credentials for the route's target
Treat sender domain as local off A sender at this domain counts as local for the relay and authentication matrix in Your First Domain and Mailbox §6.4 On, this lets a machine send as that domain under the local-sender rules. Turn it on only for a domain you actually control
Treat recipient domain as local off A recipient at this domain counts as local for the same matrix On, unauthenticated senders may send to it from the Internet range without authenticating, because external → local needs no auth by default

What a route does to an unknown recipient

This is the behaviour that surprises people, and the regression suite pins both halves of it (test/RegressionTests/SMTP/Routes.cs):

Route setting Domain has a postmaster (catch-all)? Address is in the route's list? Result Test
Deliver to all addresses — — Routed RoutesShouldSupportWildcardDomain
Specific addresses only no no 550 Recipient not in route list. RecipientNotInListShouldReturnError
Specific addresses only yes no Delivered to the catch-all, not routed RecipientNotInListButDomainHasCatchAll_EmailShouldEndUpInCatchAll
Specific addresses only yes yes Routed. The catch-all does not intercept it RecipientInRouteAndDomainHasCatchAll_EmailShouldNotGoToCatchAll

Other behaviour the suite fixes in place: a route beats the SMTP relayer (RoutesShouldHaveHigherPrioThanSMTPRelay), several recipients on one route share one connection (RoutesShouldConsolidateRecipients), an alias may forward into a route (SendMessageToAliasForwardToRoute), and a route target may be a literal IP address (ShouldBePossibleToSendToRouteWithTargetIPAddress).

Routes also make ETRN work. When a remote site issues ETRN <domain> and a route matches that domain, the messages held for it are released for immediate delivery (SMTPConnection.cpp:3990-4020). That is the classic dial-up/on-demand arrangement, and it is the reason a route can be a holding destination rather than only a redirect.

14.2a Remote domain policies: what this server will do for one remote domain

Since 6.3.4 (schema 6045). 6.3.3 has no per-domain TLS requirement, size or rate limit, or recipient callout.

A route says where mail for a domain goes. A remote domain policy says what this server will and will not do when it talks to one domain it does not host — a partner, a bank, a provider — however that domain's server was found. A domain may have both, and a policy applies to a routed delivery exactly as it applies to an MX one.

Mail flow & delivery → Remote domains, beside Routes, edits them; so do the Deck's Remote domains view, /api/v1/remote-domains (see APIs Reference §4) and COM Settings.RemoteDomainPolicies. All four write the same rows, and a saved policy is in force for the next message without a restart. It is server-administrator only: an API key restricted to named domains is refused, because a policy names somebody else's domain.

Field Default What it does
Domain — A name (bank.example) or a pattern (*.example, *). One record per name or pattern
Active on An inactive record is skipped, and the next most specific active one governs
Outbound TLS none encrypted (STARTTLS must succeed, certificate not judged), verified (the certificate must chain and name the host), dane (it must match a DNSSEC-validated TLSA record) — see Encryption and Certificates §9.6a
Require TLS on mail from this domain off An unencrypted, unauthenticated session whose MAIL FROM is at the domain is answered 530 5.7.0
Largest message (KB) 0, no limit A larger message is never attempted: no connection, and an immediate bounce with 5.3.4 naming the policy and both sizes
Simultaneous connections 0, unlimited Beyond it the delivery is deferred with 4.4.5 and retried on the usual schedule; the count covers every domain the pattern matches
Messages a minute 0, unlimited Beyond it, deferred with 4.4.5; independent of MaxOutboundPerDestinationPerMinute, and both apply
Automatic replies may go to this domain on Off: no account or domain out-of-office reply is sent to an address there
Mail may be forwarded to this domain on Off: an account's forward there, and a Sieve redirect, are not made; the message still reaches the mailbox
Verify a recipient with the domain's own server off The backup-MX recipient callout, below
Server to ask, port empty (the domain's MX hosts), 25
How long to wait 10 s At most 60; one deadline over the whole verification session
How long an answer is remembered 60 min An answer that gave no verdict is remembered for one minute at most
Verifications per minute to one remote 10 Beyond it, recipients are accepted without asking

Which record governs. The most specific active one: an exact name beats any pattern, a longer pattern beats a shorter one, and switching off the record for one domain beneath a * that requires TLS removes an exception rather than exempting that domain from the rule. GET /api/v1/remote-domains/effective?domain=mail.bank.example (or PolicyForDomain over COM) answers which record a delivery will use, before a message has to find out. When one connection carries recipients of several domains, the strictest combination applies: the highest TLS requirement and the smallest of each non-zero limit.

A refusal is a deferral. A delivery a TLS requirement cannot satisfy is never sent in the clear: the recipient gets 4.7.0, the message stays queued and is retried, and the log line — and the bounce, if the retries run out — names the rule:

Delivery deferred: the remote domain policy for bank.example requires TLS with a
certificate that verifies to this destination, and it could not be established.
The message has NOT been sent in the clear.

Inbound TLS has two exemptions: an authenticated submission, and a connection from a configured incoming relay (§14.3) — behind a gateway, the hop the policy is about happened before that machine, so require TLS on the gateway. The envelope sender is whatever the connecting server claims: this is a transport rule for honest partners, not anti-spoofing.

Since 6.3.4 a policy can also encrypt mail to the domain at this server, to the partner's own OpenPGP key or S/MIME certificate - gateway encryption, with its own key, method and fallback fields on the same record; Encryption and Certificates §9.6b has it.

The recipient callout, for a domain this server is a backup MX for

A backup MX accepts mail for every address at the domain it backs up, because the list of addresses is on the primary; while the primary is down, mail for addresses that do not exist is accepted, queued, refused later and bounced to a sender who, in a dictionary attack, is somebody else. With the callout on, this server asks the primary at RCPT TO time: EHLO, MAIL FROM:<>, RCPT TO:<address>, QUIT, with no message sent, to the configured server or else the domain's MX hosts in preference order — skipping any address and port this server itself listens on, and trying at most two.

The primary answers This server
2xx to RCPT TO accepts the recipient
5xx to RCPT TO refuses it: 550 5.1.1 (the enhanced code only in an ESMTP session)
anything else — 4xx, a refused null sender, no greeting, a timeout, no host to ask accepts, exactly as without the policy

It asks only for an unauthenticated session, only after the relay decision, the DNS blacklists and greylisting have had their say, and never for a domain this server hosts. Two cautions, both from the operator's document: a callout can enumerate a third party's addresses, so turn it on only for a domain you back up, by agreement with whoever runs the primary; and some primaries refuse the null sender or accept every RCPT TO, against which verification gives no verdict — look for gave no verdict in the log after turning it on. When a primary that was refusing wrongly has been fixed, forget the remembered answers with the Deck's button, POST /api/v1/remote-domains/verification-cache/clear or RemoteDomainPolicies.ClearVerificationCache().

What is not here: Exchange's per-domain message format (rich text, character sets) — this server sends a message as it was written; a per-policy SMTP port or credential, which is a route's job; and a callout to a primary that is this server's own database, which the ordinary recipient check already answers.

Tested, and in 6.3.4. The two regression fixtures (API/RestApiRemoteDomains.cs, SMTP/RemoteDomainPolicyEnforcement.cs) are part of the Windows regression suite. Policies are carried by the built-in backup with Back up settings (Backup and Restore §15.3), and a change to one is recorded by the audit trail as a remote domain policy (Monitoring and Health).

14.2b Slowing down for a destination, and IP warm-up (since 6.3.4)

Outbound Shaping is the whole account; what bears on routing:

  • A back-off window, on by default. A destination that answers a slow-down reply - a 421, or a 4xx whose code or words mean "too much, not now" (never 4.7.1, which is greylisting, and never a 5xx) - gets a window of OutboundBackoffMinutes (2) in which nothing is sent to it; one delivery then probes it and the same answer doubles the window, up to OutboundBackoffMaxMinutes (60). Other destinations are untouched. A relay or a smart host is one destination: one slow-down reply from it holds everything routed through it for the window, which is what it asked for. OutboundBackoffMinutes=0 turns it off.
  • Connection ceilings, off by default. MaxOutboundConnectionsPerDestination is the per-destination session ceiling for domains no remote domain policy names (a policy's own max_connections still wins), and MaxOutboundConnectionsPerTenant keeps one hosted domain - or all relayed mail together, as the tenant (external) - from holding every delivery thread.
  • A deferral this server chose is not counted as an attempt. The back-off, the ceilings, the policy's rate and MaxOutboundPerDestinationPerMinute defer with 4.4.5 and leave the message's tries untouched, until it has been queued longer than its retry schedule spans.
  • IP warm-up: a schedule for a new outbound address - the one SMTPDeliveryBindToIP or a rule's bind to address binds - holding it to a daily ceiling of recipients per large provider (decided from the MX host, not the recipient's domain) along a fixed thirty-, sixty- or fifteen-day plan. Nothing is held until a schedule is made.

The windows and counts are on GET /api/v1/queue/shaping and the Deck's queue page.

14.2c Restricted delivery (since 6.3.4)

Schema 6084. Accounts, or a whole domain, that may only exchange mail with named domains - Google Workspace's restrict delivery, for a school or a regulated desk. The domain names the domains (Domain.RestrictedDomains, the domain dialog's Restricted delivery tab: partner.example, or .example for the domain and everything beneath it), each account follows it, is restricted, or is exempt (Account.RestrictedDelivery), and the one decision is enforced at RCPT TO in both directions with 550 5.7.1 per recipient, at delivery for a list member or an alias target, and on the account's forwards and out-of-office replies. Bounces, the postmaster and the domain's own aliases always get through; other domains this server hosts must be named. It is not a route: it decides whether mail may go, not where.

14.2d Journaling and dual delivery (since 6.3.4)

Schema 6075. A copy of a domain's mail, or of one account's, sent to an address - as the Exchange-style envelope report (the envelope sender, subject, Message-Id and one line per envelope recipient, with the original attached byte for byte), or, Google's dual delivery, as the message itself with one header added. Per rule: a domain or one account; mail arriving, mail sent over an authenticated session, or both; and where the copy goes, local or anywhere else, through the ordinary queue. It is evaluated once per accepted message, so a retry never makes a second copy, and it does not loop. Server administrator only, on Compliance → Journaling & dual delivery or /api/v1/journal-rules. It is not an archive, not a forward and not retention: it sends mail elsewhere and keeps nothing.

14.2e Two more ways out (since 6.3.4)

  • The send API. An application can send through POST /api/v1/send under an API key of its own, whose role holds send_through_api and which names one sender - an address, or any address of a hosted domain - within per-key ceilings a minute and a day and the sender account's own sending quota, DKIM-signed as the domain's mail is. GET /api/v1/send/{queue_id} answers that key what became of each address, from the message trace (which is off by default; without it a message that has left the queue reads unknown). It is a submission path beside SMTP with an app password, and every route in this chapter applies to what it sends.
  • An outbound bind address per family (GitHub issue 276). SMTPDeliveryBindToIP may name one address, or one of each family separated by a comma; the one of the recipient host's family is bound, and an address of the other family is no longer a reason delivery to that host fails.

14.2f Mail in, to a webhook (since 6.3.4)

Schema 6100. A route (in this chapter's sense) sends a domain's mail to another mail server. A webhook route sends an address's mail - or a whole hosted domain's - to an application: each message that arrives for it is posted to a URL, parsed, as multipart/form-data in SendGrid's inbound-parse field names or as one JSON document, signed with HMAC-SHA256 and retried on the outbound webhooks' schedule. What SendGrid and Mailgun call inbound parse; what a helpdesk wants from support@.

  • It is asked before everything in §14.1. A routed address is accepted at RCPT TO whether or not an account, an alias, a list or a route has it; with deliver off the webhook takes the mail instead of them, with deliver on it posts the message and delivers it as well.
  • Where an endpoint may be. Plain http only to this machine; a link-local or cloud metadata address never; an API key, or any credential but the server administrator's, names a public address only - checked when the route is saved and again at every connection. No redirect is followed; a route held to public addresses goes through HttpProxy when it is set (6.3.6), asking the proxy for a tunnel to the address the server checked and never to the name, an administrator's route directly.
  • When the endpoint never accepts it, the route keeps the message to be downloaded or retried (on_failure keep, the default), or returns it to its sender (bounce).
  • Off until a route is made, over REST (/api/v1/inbound-webhooks) or on the Control Deck's Inbound webhooks view, under the permission manage_inbound_webhooks, which only the server administrator's role holds. There is no COM member, no Control Panel page and no setting of its own, and the routes are not in the configuration backup.

Mail In to a Webhook is the whole of it.

14.3 Incoming relays

Mail flow & delivery → Incoming relays lists servers that legitimately forward mail to you — a front-end spam filter, a load balancer, a hosted gateway.

The Incoming relays page, subtitled "Upstream gateways (spam filters, load balancers) whose IP addresses should not count as the connecting client in anti-spam host checks", with Edit… and Delete selected above the list, an "Add relay" panel taking Name, Lower IP and Upper IP, and the Front-end proxies (PROXY protocol and XCLIENT) card below it

Each entry is a name and an IP range (lower and upper address). Listing a relay changes exactly two things, and knowing which two is the whole point of the feature:

What changes Where Effect
When spam protection runs SMTPConnection::InitializeSpamProtectionType_ A connection from a listed relay is switched from pre-transmission to post-transmission testing. Checks that judge the connecting IP — DNSBL, PTR, HELO — cannot usefully run against your own front end, so they are deferred until the message body has arrived and the real origin can be read from it
Which IP counts as the origin MessageUtilities::RetrieveOriginatingAddress The Received chain is walked from the top, and the first address that is not a listed relay becomes the originating IP. That address is what SPF, DMARC and ARC are then evaluated against
flowchart TD
    A["Received chain, newest first"] --> B["Received: from gw2.example.net [198.51.100.8]"]
    B --> C{"Is 198.51.100.8 a listed incoming relay?"}
    C -->|yes| D["Received: from gw1.example.net [198.51.100.7]"]
    D --> E{"Is 198.51.100.7 a listed incoming relay?"}
    E -->|yes| F["Received: from mail.sender.example [203.0.113.44]"]
    F --> G{"Is 203.0.113.44 a listed incoming relay?"}
    G -->|no| H(["203.0.113.44 is the originating IP. SPF, DMARC and ARC are evaluated against it"])
    C -->|no| I(["198.51.100.8 is the originating IP"])
    E -->|no| J(["198.51.100.7 is the originating IP"])
Loading
Symptom Cause
Every message is scored as though it came from your own filter; SPF always fails The front end is not listed as an incoming relay
A DNSBL hit on your gateway's address blocks all mail The same
Adding a relay stopped spam being rejected during the SMTP conversation Expected. Testing moved to post-transmission, which is what lets the real origin be evaluated. Quarantine and scoring still apply
Listing a range too wide An attacker inside it can insert a forged Received header and choose which IP you evaluate. Keep the range to the exact gateway addresses

PROXY protocol and XCLIENT

If the front end can pass the real client address itself, that is better than reconstructing it from headers. hMailServer accepts two mechanisms, both off by default and both honoured only from the addresses you list:

Setting Type Default What it does
SMTPProxyProtocolEnabled bool 0 HAProxy PROXY v1/v2 on the SMTP listener, read before TLS and before the banner
SMTPProxyProtocolTrustedIPs comma-separated IP or CIDR list empty The real TCP peers allowed to send a PROXY header. Empty means nobody
SMTPXClientEnabled bool 0 The Postfix XCLIENT verb
SMTPXClientTrustedIPs comma-separated IP or CIDR list empty Peers allowed to use it

Both are edited in Bin\hMailServer.INI and are listed in the Settings Reference - up to 6.3.3. Since 6.3.4 that stops being true once the server has stored them: from schema 6042 the [Settings] keys live in the database, a change made in the file to a stored key is reported as HM5804 and not used, its line put back where the service may write the file (under NT SERVICE\hMailServer, to which the file is read-only, the edit stays in the file - The Service Account), and the way to change them is the Control Panel, PUT /api/v1/settings/ini/{name} or hmctl ini set - and a save applies when it is saved, as the field under each says.

A listed incoming relay changes two more things since 6.3.4. A remote domain policy's inbound TLS requirement (§14.2a) is not applied to a connection from it, and a domain's external-sender tag (see Stopping Spam §10.7a) treats mail it hands over as coming from inside — so a domain behind a filtering gateway listed here sees nothing tagged at all. XCLIENT is advertised in the EHLO response only to a trusted peer — an untrusted one never learns the verb exists, and gets 550 if it tries anyway. The trust decision is always made against the real TCP peer address, never against anything the peer claims.

14.4 Working out which one applied

When mail goes somewhere unexpected, answer these in order:

Question Where to look
Is the address, or its domain, routed to a webhook? (since 6.3.4) The Deck's Inbound webhooks view, or GET /api/v1/inbound-webhooks; a route that only posts means nothing reached a mailbox (§14.2f)
Did a rule force a route? Mail flow & delivery → Rules — an action of Send using route overrides every other decision for the whole message
Is there a route matching the recipient domain, including by wildcard? Mail flow & delivery → Routes
Does the sending domain have a relay host? The domain's dialog (Accounts & domains → Domains, the pencil button), Relay tab
Is a server-wide relayer set? Mail flow & delivery → Delivery of e-mail → Smart host
Did a remote domain policy defer, refuse or limit it? (since 6.3.4) Mail flow & delivery → Remote domains, or GET /api/v1/remote-domains/effective?domain=<domain>; the deferral text names the policy (§14.2a)
Is the destination in a back-off window, a ceiling or a warm-up hold? (since 6.3.4) GET /api/v1/queue/shaping, the Deck's queue page; the deferral says by this server's own shaping (§14.2b)
Is the sender or the recipient under restricted delivery? (since 6.3.4) The domain's Restricted delivery tab; the refusal is 550 5.7.1 naming the address (§14.2c)
What does DNS actually say? Monitoring & troubleshooting → MX query — check DNS before blaming the server
What did the server do? Monitoring & troubleshooting → Live logs. Relaying to host X. means a fixed target was used; its absence means MX lookup

The bounce text names which of the two it was:

Bounce text Enhanced code Meaning
"The host specified as SMTP relay server could not be found." 4.3.5 A fixed target — relayer, per-domain relay or route — does not resolve. This is your configuration
"No mail servers appear to exists for the recipient's address." 4.1.2 DNS answered, and the recipient's domain publishes nowhere to deliver mail
"Unable to find the recipient's email server. The DNS query has failed." 4.4.4 The lookup itself failed. Check the resolver — Connections & protocols → DNS resolver

All three are class 4, deliberately, even where the condition looks permanent: the message is retried, and a 5.x.x code paired with a retry would tell the sending software the address is dead while this server is still trying it.

See also Diagnosing Stalled Mail for the case where nothing bounces and nothing moves.


Home — full index

Start here

1. Install and run

2. Secure it

3. Operate it

4. Extend it

5. Contribute to it

Look it up — from any journey


About this wiki

Clone this wiki locally