CANONICAL(5)
π CANONICAL(5) β the Postfix canonical table format, bestie edition π
okay so you found the canonical page, literally the Postfix canonical table format, and I'm so proud of you for reading docs. no cap. let's get into it π«Ά
β¨ NAME β¨
canonical β Postfix canonical table format. that's it, that's the name, iconic. πΈ
β¨ SYNOPSIS β¨
postmap /etc/postfix/canonical
postmap -q "string" /etc/postfix/canonical
postmap -q - /etc/postfix/canonical <inputfile
β¨ DESCRIPTION β¨
So the canonical(5) table is optional, and it's the one that says "when you see this address, actually make it that address" β for local AND non-local addresses. π« The mapping gets used by the cleanup(8) daemon, and it happens before mail gets stored into the queue. And the address mapping is recursive, iykyk, it keeps going.
Normally you write your canonical(5) table as a plain text file, and that file is the input to the postmap(1) command, which makes an indexed file so lookups are FAST. β‘
Run this:
postmap /etc/postfix/canonical
...to rebuild a default-type indexed file after you change the text file. Or run:
postmap type:/etc/postfix/canonical
...to specify an explicit type.
The default indexed file type is set by the default_database_type parameter. Depending on your platform it might be one of lmdb:, cdb:, hash:, or dbm: β and yes, without the trailing ':'. π
When the table comes from other means β think NIS, LDAP, or SQL β the same lookups are done, exactly like ordinary indexed files. Managing those databases? Not Postfix's problem, that's outside the scope of Postfix, so don't @ me.
Alternatively the table can be a regular-expression map where the patterns themselves are regular expressions, or you can point lookups at a TCP-based server. In those cases the lookups work a little differently, described down below under "REGULAR EXPRESSION TABLES" or "TCP-BASED TABLES". π¦
By default, the canonical(5) mapping hits both message header addresses (the ones that appear inside messages) and message envelope addresses (like the addresses used in SMTP protocol commands). Which ones get hit is controlled by the canonical_classes parameter. π
NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP clients only if the client matches thelocal_header_rewrite_clientsparameter, or if theremote_header_rewrite_domainconfiguration parameter specifies a non-empty value. To get the behavior from before Postfix 2.2, specify"local_header_rewrite_clients = static:all". π
Typically you'd use canonical(5) to swap login names for Firstname.Lastname, or to clean up addresses that some legacy mail system spat out. π
And pleaseeee do not confuse canonical(5) mapping with virtual alias support or local aliasing. If you want to change the destination but not the headers, use the virtual(5) or aliases(5) map instead. this is the vibe check, don't mix them up bestie π
β¨ CASE FOLDING β¨
The search string gets folded to lowercase before the database lookup. But as of Postfix 2.3, the search string is not case folded with database types like regexp: or pcre: β because those lookup fields can match both upper and lower case. π
β¨ TABLE FORMAT β¨
The input format for the postmap(1) command goes like this:
pattern addressβ whenpatternmatches a mail address, replace it by the correspondingaddress.- blank lines and comments β empty lines and whitespace-only lines are ignored, and so are lines whose first non-whitespace character is a
'#'. π - multi-line text β a logical line starts with non-whitespace text. A line that starts with whitespace continues a logical line.
β¨ TABLE SEARCH ORDER β¨
When you're looking up from indexed files like DB or DBM, or from networked tables like NIS, LDAP or SQL, every user@domain query produces a sequence of query patterns, like described below. π
Each query pattern gets sent to each specified lookup table before trying the next query pattern β until a match is found.
user@domain addressβ replaceuser@domainbyaddress. This form has the highest precedence. π₯
This is super useful for cleaning up addresses produced by legacy mail systems. It can also be used to produce Firstname.Lastname style addresses, but see below for a simpler solution. β¨
user addressβ replaceuser@sitebyaddresswhensiteis equal to$myorigin, whensiteis listed in$mydestination, or when it is listed in$inet_interfacesor$proxy_interfaces.
This form is useful for replacing login names by Firstname.Lastname. π
@domain addressβ replace other addresses indomainbyaddress. This form has the lowest precedence.
Note:@domainis a wild-card. π When this form is applied to recipient addresses, the Postfix SMTP server accepts mail for any recipient indomain, regardless of whether that recipient exists. This may turn your mail system into a backscatter source: Postfix first accepts mail for non-existent recipients and then tries to return that mail as "undeliverable" to the often forged sender address. π
To avoid backscatter with mail for a wild-card domain, replace the wild-card mapping with explicit 1:1 mappings, or add a reject_unverified_recipient restriction for that domain:
smtpd_recipient_restrictions =
...
reject_unauth_destination
check_recipient_access
inline:{example.com=reject_unverified_recipient}
unverified_recipient_reject_code = 550
In the above example, Postfix may contact a remote server if the recipient is rewritten to a remote address.
β¨ RESULT ADDRESS REWRITING β¨
The lookup result gets address rewriting applied to it:
- When the result has the form
@otherdomain, the result becomes the same user inotherdomain. π¦ - When
"append_at_myorigin=yes", append"@$myorigin"to addresses without"@domain". - When
"append_dot_mydomain=yes", append".$mydomain"to addresses without".domain".
β¨ ADDRESS EXTENSION β¨
When a mail address localpart contains the optional recipient delimiter (e.g., user+foo@domain), the lookup order becomes: user+foo@domain, user@domain, user+foo, user, and @domain. π―
The propagate_unmatched_extensions parameter controls whether an unmatched address extension (+foo) is propagated to the result of the table lookup. π«
β¨ REGULAR EXPRESSION TABLES β¨
This section is all about how the table lookups change when your table is given in the form of regular expressions. For a description of regular expression lookup table syntax, see regexp_table(5) or pcre_table(5). π
Each pattern is a regular expression applied to the entire address being looked up. So user@domain mail addresses are not broken up into their user and @domain constituent parts, and user+foo is not broken up into user and foo. nope, we're keeping it whole. π«Ά
Patterns are applied in the order as specified in the table, until a pattern is found that matches the search string. β¨
Results are the same as with indexed file lookups, with the extra feature that parenthesized substrings from the pattern can be interpolated as $1, $2 and so on. slay. π
β¨ TCP-BASED TABLES β¨
This section is all about how the table lookups change when lookups are directed to a TCP-based server. For a description of the TCP client/server lookup protocol, see tcp_table(5). Heads up: this feature is not available up to and including Postfix version 2.4. π
Each lookup operation uses the entire address once. So user@domain mail addresses are not broken up into their user and @domain constituent parts, and user+foo is not broken up into user and foo. π§
Results are the same as with indexed file lookups. β¨
β¨ BUGS β¨
The table format does not understand quoting conventions. π that's the bug. that's it.
β¨ CONFIGURATION PARAMETERS β¨
These are the main.cf parameters that are especially relevant. The text below is only a parameter summary. See postconf(5) for more details, including examples. π
canonical_classes(default:envelope_sender, envelope_recipient, header_sender, header_recipient) β what addresses are subject tocanonical_mapsaddress mapping. πcanonical_maps(default: empty) β optional address mapping lookup tables for message headers and envelopes.recipient_canonical_maps(default: empty) β optional address mapping lookup tables for envelope and header recipient addresses. πsender_canonical_maps(default: empty) β optional address mapping lookup tables for envelope and header sender addresses.propagate_unmatched_extensions(default:canonical, virtual) β what address lookup tables copy an address extension from the lookup key to the lookup result.
Other parameters of interest: π
inet_interfaces(default:all) β the local network interface addresses that this mail system receives mail on.local_header_rewrite_clients(default:permit_inet_interfaces) β rewrite or add message headers in mail from these clients, updating incomplete addresses with the domain name in$myoriginor$mydomain, and adding missing headers.proxy_interfaces(default: empty) β the remote network interface addresses that this mail system receives mail on by way of a proxy or network address translation unit.masquerade_classes(default:envelope_sender, header_sender, header_recipient) β what addresses are subject to address masquerading. πmasquerade_domains(default: empty) β optional list of domains whose subdomain structure will be stripped off in email addresses.masquerade_exceptions(default: empty) β optional list of user names that are not subjected to address masquerading, even when their addresses match$masquerade_domains.mydestination(default:$myhostname, localhost.$mydomain, localhost) β the list of domains that are delivered via the$local_transportmail delivery transport.myorigin(default:$myhostname) β the domain name that locally-posted mail appears to come from, and that locally posted mail is delivered to. π¦owner_request_special(default:yes) β enable special treatment forowner-listnameentries in thealiases(5)file, and don't splitowner-listnameandlistname-requestaddress localparts when therecipient_delimiteris set to"-".remote_header_rewrite_domain(default: empty) β rewrite or add message headers in mail from remote clients if theremote_header_rewrite_domainparameter value is non-empty, updating incomplete addresses with the domain specified in theremote_header_rewrite_domainparameter, and adding missing headers. π
β¨ SEE ALSO β¨
cleanup(8), canonicalize and enqueue mailpostmap(1), Postfix lookup table managerpostconf(5), configuration parametersvirtual(5), virtual aliasing
okay bestie that's everything for part 1, and trust me there is more coming, so don't go anywhere π
β¨ README FILES β¨
okay bestie, so the docs live somewhere on your system and you're like "where tho π₯Ί" β literally just ask postfix, she'll tell you:
- Run
postconf readme_directoryto find where the READMEs are chilling. - Run
postconf html_directoryto find the HTML version instead, if that's your vibe.
And then the two big ones you're gonna wanna read:
DATABASE_READMEβ the whole Postfix lookup table overview, like the β¨loreβ¨ of lookup tables.ADDRESS_REWRITING_READMEβ the address rewriting guide, aka how your addresses get their glow-up. π
π LICENSE π
The Secure Mailer license must be distributed with this software. No skips, no "ooo I'll add it later", no cap. It comes with the thing, periodt. π
π AUTHOR(S) π
shoutout to the main character who wrote all this, Wietse Venema π«Ά β here's his whole contact card situation, kept exactly as it's written:
- Wietse Venema IBM T.J. Watson Research P.O. Box 704 Yorktown Heights, NY 10598, USA
- Wietse Venema Google, Inc. 111 8th Avenue New York, NY 10011, USA
like. two addresses. two eras. iconique behavior honestly π
and that's a wrap on the page, babe π the footer down at the bottom is just our girl CANONICAL(5) holding it down, so whenever you see that lil tag you know exactly which page you're reading. stream responsibly, read the READMEs, and go rewrite those addresses πΈπ¦β¨
π¦ where this page comes from
- alpine 3.24: postfix-doc 3.11.7-r0 Β· IPL-1.0 EPL-2.0
- arch rolling: postfix 3.11.7-1 Β· EPL-2.0 OR IPL-1.0 AND BSD-4-Clause-UC
- nixos 26.05: postfix 3.11.7
- nixpkgs unstable: postfix 3.11.7
- ubuntu stonking: postfix 3.11.7-1ubuntu2
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms