Every WordPress email problem (the contact form that silently fails, the password reset that never arrives, the DMARC report full of authentication failures) traces back to the same delivery chain. Understanding that chain is the difference between fixing the problem and guessing at it.
This is the mental model. Not a protocol specification (Kaspar Etter’s
Email explained from first principles is the best single resource for that). Instead: the architecture of email as it applies to running a WordPress site. Enough depth to make the diagnostic and configuration decisions that the rest of nanoPost’s reference material assumes.
The short version: WordPress calls wp_mail(), which delegates to PHPMailer, which hands the message to a transport (the local MTA by default, an external relay if a mailer plugin is configured), which delivers it to the recipient’s mail server, which decides whether it reaches the inbox. Every configuration decision maps to one of those stages.
The delivery chain
An email sent from WordPress travels through five stages:
wp_mail()– the single PHP function through which all WordPress email passes. Defined inwp-includes/pluggable.php, called by WordPress core (password resets, admin notifications), WooCommerce (order confirmations), contact form plugins, and anything else that sends mail the WordPress way.- PHPMailer – the mail-composition library bundled with WordPress core since the 2.x releases (upgraded to PHPMailer 6 in WordPress 5.5).
wp_mail()delegates to PHPMailer to build the MIME message: headers, body, attachments, encoding. - The transport – how the composed message leaves the server. By default, PHPMailer calls PHP’s
mail()function, which hands the message to the local mail transfer agent (sendmail or Postfix). A mailer plugin replaces this step, routing through an external SMTP relay or API endpoint instead. - The relay’s outbound infrastructure – the external email service (Postmark, Brevo, SES, SMTP2GO, Mailgun, or whatever was configured in step 3) accepts the message, authenticates it against the sending domain’s DNS records, and delivers it to the recipient’s mail server.
- The recipient’s mail server – the receiving MTA (Gmail, Outlook, Yahoo, a corporate Exchange server) accepts or rejects the message based on sender reputation, authentication results, and content filtering. The message either reaches the inbox, lands in spam, or bounces.
Most WordPress email configuration decisions map to one of these five stages. Mailer plugins operate at stage 3. Email services operate at stage 4. SPF, DKIM, and DMARC records tell stage 5 whether to trust stage 4. The default WordPress configuration skips stage 4 entirely and hopes stages 3 and 5 cooperate, which, on most modern hosting, they do not.
Why the default path fails
The default transport (wp_mail() → PHPMailer → PHP mail() → local MTA) depends on three things being true simultaneously: the server has a functioning MTA, the host permits outbound SMTP connections, and the receiving server trusts the sending IP. In 2026, this combination is rare.
Most cloud and managed hosting environments break at least one of these requirements. Hosts either block SMTP ports (preventing the MTA from connecting to the outside world), omit the MTA entirely (so PHP’s mail() returns false and messages vanish), or send from shared IPs with no domain authentication (so messages land in spam). nanoPost’s setup guide covers the provider-specific details.
The fix is structural: bypass the local MTA by routing mail through an external service that maintains dedicated sending infrastructure and pre-configured authentication. Routing through an external service is baseline configuration, not optional hardening, for a WordPress site that needs email to work.
What the operator controls
The email chain divides responsibility cleanly. Understanding which parts belong to the operator and which belong to the relay is the core diagnostic skill.
The operator controls:
- The
Fromaddress and display name (set in the mailer plugin or in code via thewp_mail_fromandwp_mail_from_namefilters) - The DNS records for the sending domain: SPF, DKIM, and DMARC TXT records
- The choice of external email service and the credentials connecting WordPress to it
- The mailer plugin configuration: transport method (SMTP vs API), port, encryption, authentication
- Which plugins and themes use
wp_mail()versus bypassing it (a common source of confusion, since plugins that call PHP’smail()directly skip the entire mailer plugin pipeline)
The operator does not control:
- The relay’s IP reputation – the email service manages its sending IPs and the reputation they carry. A service with poor IP hygiene (shared IPs used by spammers) degrades deliverability regardless of correct authentication.
- The receiving server’s spam filtering – Gmail’s, Outlook’s, and Yahoo’s inbound filtering algorithms are opaque and change without notice. Correct authentication improves the odds but does not guarantee inbox placement.
- Bounce handling and retry logic – the relay decides how to handle soft bounces (temporary failures) and hard bounces (permanent rejections). Retry windows vary by provider; some retry for as few as 12 hours (AWS SES), others for up to 72 hours.
- Rate limiting and throttling by receiving servers – large mailbox providers throttle inbound connections from any single sending IP. The relay manages this; the operator cannot.
When a WordPress email fails to arrive, the diagnostic starts with this division. If the mailer plugin’s logs show the message was accepted by the relay (an SMTP 250 OK response or an API 200 status; see how to read an SMTP session log for interpreting these), the problem is downstream: the relay’s reputation, the receiving server’s filtering, or the authentication records. If the plugin shows a connection failure, the problem is upstream: wrong credentials, blocked ports, or a misconfigured transport.
DNS authentication: SPF, DKIM, DMARC
Three DNS-based authentication standards tell receiving servers whether to trust mail claiming to come from a domain. All three are TXT records published in the domain’s DNS zone. Together they form a system; each standard covers a different aspect of trust, and all three are necessary for reliable delivery.
SPF (Sender Policy Framework) declares which IP addresses and mail servers are authorised to send email for the domain. When a receiving server gets a message claiming to come from example.com, it looks up example.com‘s SPF record and checks whether the sending server’s IP is listed. SPF authorises the relay but does not verify the message itself. Defined in RFC 7208.
DKIM (DomainKeys Identified Mail) adds a cryptographic signature to outgoing messages. The sending service signs each message with a private key; the corresponding public key is published as a DNS TXT record. The receiving server retrieves the public key and verifies that the message was not modified in transit and was sent by an authorised system. DKIM proves message integrity but does not declare policy. Defined in RFC 6376.
DMARC (Domain-based Message Authentication, Reporting, and Conformance) ties SPF and DKIM together with a policy. The DMARC record, published at _dmarc.example.com, tells receiving servers what to do when a message fails both SPF and DKIM alignment checks: none (monitor only), quarantine (send to spam), or reject (refuse delivery). DMARC also specifies a reporting address (rua tag) where mailbox providers send daily aggregate reports in XML format. DMARC without SPF and DKIM has nothing to enforce. Originally defined in RFC 7489, updated by RFC 9989 (DMARCbis, May 2026).
SPF authorises the relay’s IPs. DKIM proves the message integrity. DMARC sets the enforcement policy. The three standards are designed to work as a system, and deploying only one or two leaves gaps that receiving servers notice.
The DNS setup guide covers the record syntax, provider-specific configuration, and common mistakes (the SPF merge problem, DKIM key publication) in detail.
The pluggable function architecture
wp_mail() is a
pluggable function. WordPress defines it in wp-includes/pluggable.php, wrapped in a if ( ! function_exists( 'wp_mail' ) ) check. Any plugin loaded before pluggable functions are defined can replace wp_mail() entirely by defining its own version.
This is how mailer plugins work. WP Mail SMTP, FluentSMTP, and Post SMTP each hook into or replace the default wp_mail() implementation. The effect is global: every call to wp_mail() from any plugin or theme routes through the replacement.
The corollary matters: plugins that bypass wp_mail() and call PHP’s mail() directly, or that instantiate their own PHPMailer instance, skip the mailer plugin entirely. These plugins’ messages go through the default (broken) transport path even when a mailer plugin is active. This is the most common source of “some emails work, some don’t” debugging scenarios. The fix is either to modify the offending plugin or to replace it with one that uses wp_mail().
Where to go deeper
If email is not arriving at all, start with the setup guide; it walks through external service selection, mailer plugin configuration, and DNS authentication step by step. If it was working and stopped, troubleshoot WordPress email covers the common failure modes. If email arrives but something seems wrong, test the SMTP connection directly with swaks to isolate whether the problem is in WordPress or in the mail infrastructure.
For choosing components: the SMTP email services directory covers per-provider pricing, free tiers, and WordPress integration. The mailer plugin comparison covers WP Mail SMTP, FluentSMTP, Post SMTP, and the rest of the field. For configuring without a plugin: WordPress SMTP without a plugin.
For the protocol-level detail this reference deliberately omits (SMTP session mechanics, MIME encoding, TLS negotiation, the full authentication handshake), Kaspar Etter’s
Email explained from first principles is the most thorough single resource available, complete with
interactive tools for DNS lookups, SMTP session simulation, and encoding utilities.
