2026年11月12日

2026年11月12日

Send WordPress Email with Postfix: Direct, SMTP Relay, SPF/DKIM/DMARC

This article is a localized edition of the Japanese original. Read the Japanese original

WordPress sends email such as contact form notifications and password resets with PHP's mail() function (which calls the server's sendmail command internally). If you manage your own server, such as a VPS, Postfix is a common choice for the mail server that receives those messages and sends them on.

This article shows how to configure Postfix as a send-only server so that WordPress mail gets delivered. The examples use Ubuntu 24.04 / Debian 12 as of October 2026, and differences on RHEL-family systems (AlmaLinux, Rocky Linux) are noted along the way. The domain is example.com and the server's IP address is the documentation address 192.0.2.10.

There are two ways to send

There are two main ways for Postfix to send mail to the outside world. Pick one and configure it.

Method How it works When it fits
A. Direct delivery Delivers straight to the recipient's mail server (Gmail and so on) on port 25 Outbound port 25 is allowed and you can set reverse DNS (PTR)
B. Relay through an external SMTP service Hands mail to a sending service such as Amazon SES or SendGrid, with authentication, on port 587 or similar Port 25 is blocked, or you want the service to manage deliverability

In the cloud, outbound traffic on port 25 is sometimes restricted by default (for example, Amazon EC2 restricts it by default and lifts it on request; Google Cloud does not allow outbound port 25). Check your provider's official documentation, and choose B if you cannot use it.

Don't mix A and B, for example by leaving the relay host empty (relayhost blank) while enabling SMTP authentication (SASL). Use authentication only with B.

Install Postfix

# Ubuntu / Debian
sudo apt update
sudo apt install postfix mailutils

On the configuration screen during installation, choose "Internet Site" for A or "Internet with smarthost" for B, and for "System mail name" enter the domain you want after the @ in addresses (for example example.com). You will edit main.cf directly later, so you can change these choices afterwards.

# RHEL family
sudo dnf install postfix s-nail
sudo systemctl enable --now postfix

Check that it is running and which version you have:

sudo postfix status
postconf mail_version

Common main.cf settings

Open /etc/postfix/main.cf and check or change the following for a send-only setup. You can also set values with sudo postconf -e "parameter = value".

# This server's host name (FQDN). Match the DNS A record and reverse DNS to this
myhostname = mail.example.com

# Domain used after the @ in sender addresses
myorigin = example.com

# Send-only, so don't accept connections from outside
inet_interfaces = loopback-only

# Destinations this server receives for itself. Do not include example.com
mydestination = $myhostname, localhost.$mydomain, localhost

# When sending, encrypt with TLS if the other side supports it
smtp_tls_security_level = may

If you put example.com in mydestination, mail to info@example.com is treated as local and delivered on this server, so it never reaches a mailbox hosted elsewhere, such as Google Workspace. If your domain's mail is received by another service, leave example.com out as shown above.

If the server has an IPv6 address but you have not set IPv6 reverse DNS, setting inet_protocols = ipv4 to send only over IPv4 helps avoid rejections by receiving servers.

After changing the settings, check the syntax and apply them:

sudo postfix check
sudo systemctl reload postfix

A. Direct delivery

Set up DNS and reverse DNS

With direct delivery, receiving servers verify the sending server, so set up these records:

Where to set it Record Example
Your domain's DNS A record for mail.example.com 192.0.2.10
Your VPS provider's control panel Reverse DNS (PTR) for 192.0.2.10 mail.example.com
Your domain's DNS TXT (SPF) for example.com v=spf1 ip4:192.0.2.10 ~all

Reverse DNS (PTR) is set by the VPS provider, which owns the IP address. Google's sender guidelines also require that the sending IP's forward and reverse DNS match.

A domain can have only one SPF record. If you already have SPF set up, for example for Google Workspace, don't add a new record; add ip4:192.0.2.10 to the existing one (for example v=spf1 include:_spf.google.com ip4:192.0.2.10 ~all). The trailing ~all means "mail from anywhere else is suspicious," and -all means "mail from anywhere else is not authorized."

dig +short TXT example.com
dig +short -x 192.0.2.10

Sign with OpenDKIM

DKIM attaches a digital signature to outgoing mail, which the receiver verifies with a public key in DNS. Connect OpenDKIM to Postfix as a milter.

sudo apt install opendkim opendkim-tools

# Create the key (selector "mail", 2048 bits)
sudo mkdir -p /etc/opendkim/keys/example.com
sudo opendkim-genkey -b 2048 -d example.com -s mail -D /etc/opendkim/keys/example.com
sudo chown -R opendkim:opendkim /etc/opendkim/keys

Set the following in /etc/opendkim.conf (edit lines that already exist):

Domain                  example.com
Selector                mail
KeyFile                 /etc/opendkim/keys/example.com/mail.private
Canonicalization        relaxed/simple
Mode                    sv
Socket                  inet:8891@localhost

Postfix on Debian / Ubuntu runs some processes inside a chroot (/var/spool/postfix), so OpenDKIM's default UNIX socket (under /run/opendkim/) may not be visible to Postfix. The example above connects over TCP (port 8891 on localhost), which avoids that problem. If /etc/default/opendkim has an active SOCKET= line, set it to the same value.

Configure the milter on the Postfix side:

sudo postconf -e "smtpd_milters = inet:localhost:8891"
sudo postconf -e "non_smtpd_milters = \$smtpd_milters"
sudo postconf -e "milter_default_action = accept"
sudo systemctl restart opendkim
sudo systemctl reload postfix

non_smtpd_milters applies to mail handed over through the sendmail command, as WordPress does. Without it, WordPress mail is not signed.

The public key is written to mail.txt in DNS format:

sudo cat /etc/opendkim/keys/example.com/mail.txt

Create a TXT record named mail._domainkey.example.com and use everything from v=DKIM1; through p=... inside the parentheses as its value (join the parts split into several "..." strings in the file into one value; some DNS control panels accept them split). After adding it, confirm that the key matches DNS:

sudo opendkim-testkey -d example.com -s mail -vvv
# → "key OK" means success ("key not secure" also appears if you don't use DNSSEC)

Set up DMARC

DMARC is a record that tells receivers how you want them to handle mail, based on the SPF and DKIM results. Start with p=none (take no action, just receive reports), and after confirming that all legitimate mail passes, tighten it to quarantine or reject.

Name Type Example value
_dmarc.example.com TXT v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com

B. Relay through an external SMTP service

If you use a sending service such as Amazon SES or SendGrid, finish domain authentication in the service's dashboard (DNS records for DKIM and so on) and note the SMTP host, port, user name and password. These values differ by service, so follow each service's official documentation.

Install the SASL authentication components:

# Ubuntu / Debian
sudo apt install libsasl2-modules
# RHEL family
sudo dnf install cyrus-sasl-plain

Add the following to main.cf. Wrapping the host in [ ] makes Postfix connect to that host name directly instead of looking up MX records.

relayhost = [smtp.example.net]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt

With smtp_tls_security_level = encrypt, Postfix does not send if it cannot encrypt with TLS. This keeps your password from being sent in plain text.

Create the credentials file. Write the destination on the first line the same way as relayhost.

# /etc/postfix/sasl_passwd
[smtp.example.net]:587    username:password
sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo systemctl reload postfix

Make the sasl_passwd.db file created by postmap readable only by root, like the original file. Register SPF, DKIM and DMARC in DNS as the sending service instructs (you normally don't need to run OpenDKIM on your own server).

Send from WordPress

WordPress's default sending method (PHP's mail()) hands mail to Postfix through the sendmail command, so as long as Postfix is running, it sends with no extra configuration.

By default the sender is wordpress@your-site-domain. To send from an address on the domain where you set up SPF and DKIM, set the sender as needed:

<?php
// Put this in your theme's functions.php or a custom plugin
add_filter( 'wp_mail_from', function ( $from ) {
	return 'noreply@example.com';
} );
add_filter( 'wp_mail_from_name', function ( $name ) {
	return 'Example Site';
} );

If WP-CLI is available, you can test sending from the server:

wp eval 'var_dump( wp_mail( "you@example.net", "Test", "Hello from WordPress" ) );' --path=/var/www/html

bool(true) means the message was handed to Postfix; check the log to see whether it reached the recipient.

Verify delivery

# Log (Ubuntu / Debian). On RHEL-family systems, /var/log/maillog or journalctl -u postfix
sudo tail -f /var/log/mail.log

# Messages waiting to be sent
mailq

# Retry queued messages right away
sudo postqueue -f

In the log, status=sent means the recipient's server accepted the message, status=deferred means a temporary failure awaiting retry, and status=bounced means it could not be delivered and was returned. The reason from the remote server is written in parentheses on deferred and bounced lines.

After sending to a Gmail address, open "Show original" from the received message's menu and check that SPF, DKIM and DMARC show PASS. The Authentication-Results: header shows the same results.

If you use an external test service (such as mail-tester.com), send to the dedicated address shown on its site and check the results there.

Notes

  • Google's sender guidelines require all senders to set up SPF or DKIM, have matching forward and reverse DNS for the sending IP, and send over TLS; senders with a high daily volume also need DMARC. The requirements can change, so check Google's latest guidance.
  • Because of inet_interfaces = loopback-only, this server does not accept mail from outside. Use addresses for replies and error mail on a domain that another mail service receives (a domain not in mydestination).
  • For high-volume sending, such as member sign-ups or newsletters, a sending service (B) lets you hand off volume limits and deliverability management better than direct delivery.

Related articles

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲