<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>wp_mail | John Nessime</title>
	<atom:link href="https://john-nessime.com/blog/tag/wp_mail/feed/" rel="self" type="application/rss+xml" />
	<link>https://john-nessime.com/blog/tag/wp_mail/</link>
	<description>Cloud, DevOps, Data &#38; AI — Built, Tested, Explained</description>
	<lastBuildDate>Thu, 06 Aug 2026 12:10:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.3</generator>

<image>
	<url>https://john-nessime.com/blog/wp-content/uploads/2026/07/cropped-jn-32x32.png</url>
	<title>wp_mail | John Nessime</title>
	<link>https://john-nessime.com/blog/tag/wp_mail/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Why Your WordPress Emails Vanish: SMTP, SPF, DKIM and DMARC</title>
		<link>https://john-nessime.com/blog/technical-guides/wordpress-email-deliverability-spf-dkim-dmarc/</link>
					<comments>https://john-nessime.com/blog/technical-guides/wordpress-email-deliverability-spf-dkim-dmarc/#respond</comments>
		
		<dc:creator><![CDATA[John Nessime]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 13:00:00 +0000</pubDate>
				<category><![CDATA[Technical Guides]]></category>
		<category><![CDATA[Web Security]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[DKIM]]></category>
		<category><![CDATA[DMARC]]></category>
		<category><![CDATA[DNS]]></category>
		<category><![CDATA[DNS Records]]></category>
		<category><![CDATA[Email Authentication]]></category>
		<category><![CDATA[Email Deliverability]]></category>
		<category><![CDATA[SMTP]]></category>
		<category><![CDATA[SPF]]></category>
		<category><![CDATA[Transactional Email]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[WordPress Hosting]]></category>
		<category><![CDATA[wp_mail]]></category>
		<guid isPermaLink="false">https://john-nessime.com/blog/?p=180</guid>

					<description><![CDATA[<p>WordPress tells you the mail was sent. The customer never got it. This is a working guide to the four places WordPress email deliverability actually breaks: PHP mail() transport, SPF lookup limits, DKIM alignment, and DMARC policy. Includes real diagnostic commands, a troubleshooting order, and what changed now that DMARC has moved onto the IETF standards track.</p>
<p>The post <a href="https://john-nessime.com/blog/technical-guides/wordpress-email-deliverability-spf-dkim-dmarc/">Why Your WordPress Emails Vanish: SMTP, SPF, DKIM and DMARC</a> appeared first on <a href="https://john-nessime.com/blog">John Nessime</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">The ticket says &#8220;customer never received the password reset.&#8221; You check the site. The form submits fine. You trigger a reset yourself and it lands in your inbox in four seconds. Nothing in the error log. Plugin says the mail was sent. Everything is green.</p>



<p class="wp-block-paragraph">So you close the ticket. Then it happens again next week, to a different customer, on a different mailbox provider.</p>



<p class="wp-block-paragraph">This is the shape of almost every WordPress email deliverability problem I&#8217;ve had to unpick. The site is not broken and the mail is genuinely leaving. It is being evaluated somewhere downstream and quietly filed into spam by a receiver that has decided it cannot prove who sent it. Nothing in WordPress will ever tell you this happened.</p>



<p class="wp-block-paragraph">This post covers the four places that failure actually lives: how the message leaves your server, whether SPF survives evaluation, whether DKIM signs and <em>aligns</em>, and what DMARC does with the result. It is organised by failure family rather than by protocol tour, because the protocols are individually well documented and the failures are not.</p>



<h2 class="wp-block-heading">The failure mode that costs you money: silent success</h2>



<p class="wp-block-paragraph">Start with the thing that makes this hard to debug. <code>wp_mail()</code> returns a boolean. That boolean tells you whether PHPMailer handed the message off without throwing. It tells you nothing about whether the message was delivered, and nothing at all about whether it reached an inbox rather than a spam folder.</p>



<p class="wp-block-paragraph">So &#8220;WordPress says it sent&#8221; and &#8220;the customer got it&#8221; are separated by four or five systems you have no visibility into. That gap is where the money leaks: WooCommerce order confirmations, membership welcome mails, password resets, form notifications. All of them fail the same silent way.</p>



<p class="wp-block-paragraph">So stop guessing about the boolean. WordPress fires an action when the handoff itself fails, and almost nobody hooks it:</p>



<pre class="wp-block-code"><code>// Log the real reason PHPMailer refused, instead of a bare false.
add_action( 'wp_mail_failed', function ( $error ) {
    error_log( 'wp_mail failed: ' . $error-&gt;get_error_message() );
} );</code></pre>



<p class="wp-block-paragraph">Then force a send from the command line so you are testing the mail path and not a contact form plugin:</p>



<pre class="wp-block-code"><code>wp eval 'var_dump( wp_mail( "you@example.com", "deliverability test", "body" ) );'</code></pre>



<p class="wp-block-paragraph">If that prints <code>bool(true)</code> and the mail still never arrives, you have ruled out WordPress entirely. Everything from here is transport and DNS.</p>



<h2 class="wp-block-heading">Failure family one: how the message leaves the box</h2>



<p class="wp-block-paragraph">By default <code>wp_mail()</code> falls through to PHP&#8217;s <code>mail()</code>, which shells out to the local mail transfer agent. On a shared host or a default VPS build, that means your marketing site is now an email server with none of the reputation, warm-up, or bounce handling that implies.</p>



<p class="wp-block-paragraph">Receivers are not being unreasonable about this. A message arriving from a web server IP, with no authentication, from a domain the IP has no relationship with, looks exactly like a compromised WordPress install pumping out spam. Because most of the time, it is.</p>



<h3 class="wp-block-heading">The three transport options, honestly</h3>



<p class="wp-block-paragraph"><strong>Local <code>mail()</code>.</strong> Genuine case for it: zero dependencies, no credentials to leak, and it works fine when the box already runs a properly configured MTA with correct reverse DNS and a warmed IP. Where it fails: almost everywhere else. Most hosts either block outbound port 25 or run an MTA nobody has configured for authentication. Skip it unless you own the mail infrastructure and know its reputation.</p>



<p class="wp-block-paragraph"><strong>Authenticated SMTP submission.</strong> WordPress connects to a real mail provider over port 587 with STARTTLS, or 465 with implicit TLS, and authenticates. This is the portable option, it works with any provider, and it is what I reach for first on a site I did not build. The cost is that SMTP is a chatty, connection-oriented protocol running inside a page request, so a slow or throttled provider makes checkout feel slow.</p>



<p class="wp-block-paragraph"><strong>Provider HTTP API.</strong> The plugin posts the message to an API endpoint over 443 instead of speaking SMTP. Faster, immune to hosts that block SMTP ports outbound, and usually gives you richer delivery events back. The cost is real lock-in: your sending config is now provider-specific and moving means reconfiguring, not just changing a hostname.</p>



<p class="wp-block-paragraph">On the plugin side, WP Mail SMTP and Post SMTP both cover the common providers and both have usable free tiers. For providers, transactional-focused services like Postmark, Amazon SES, Mailgun, Brevo and SendGrid all do this job well; the differences that matter are bounce webhook quality, log retention, and how quickly a human answers when your account gets throttled. If your host bundles a transactional relay, check whether it authenticates as <em>your</em> domain or the host&#8217;s shared domain before you rely on it. Providers like InterServer, Cloudways and Kinsta differ meaningfully here.</p>



<h3 class="wp-block-heading">Prove the transport before you touch DNS</h3>



<p class="wp-block-paragraph">Before blaming records, confirm the server can actually reach the relay. This opens a TLS session against the submission port and shows you the SMTP banner and advertised capabilities:</p>



<pre class="wp-block-code"><code>openssl s_client -starttls smtp -crlf -connect smtp.example.net:587</code></pre>



<p class="wp-block-paragraph"><code>-starttls smtp</code> issues the STARTTLS upgrade for you, and <code>-crlf</code> sends proper line endings so the session does not hang. If this times out, your host is filtering outbound SMTP and you want the HTTP API transport instead of a longer argument with support.</p>



<p class="wp-block-paragraph">To send a full test message end to end, <code>swaks</code> is the tool worth having on every box:</p>



<pre class="wp-block-code"><code>swaks --to you@example.com --from wp@example.com 
      --server smtp.example.net:587 --auth --tls</code></pre>



<p class="wp-block-paragraph">It prints the entire conversation, so an authentication failure or a policy rejection shows up as an actual SMTP response code rather than a shrug from a plugin.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Failure family two: SPF passes until it silently doesn&#8217;t</h2>



<p class="wp-block-paragraph">SPF is a DNS TXT record listing who may send using your domain in the envelope sender. It looks like this:</p>



<pre class="wp-block-code"><code>v=spf1 include:_spf.google.com include:sendgrid.net ~all</code></pre>



<p class="wp-block-paragraph">Two things about that record cause more WordPress email deliverability incidents than anything else in SPF.</p>



<h3 class="wp-block-heading">The ten lookup budget</h3>



<p class="wp-block-paragraph">RFC 7208 caps SPF evaluation at ten mechanisms that require DNS lookups. <code>include</code>, <code>a</code>, <code>mx</code>, <code>ptr</code>, <code>exists</code> and <code>redirect</code> all count, and crucially so does everything nested inside someone else&#8217;s <code>include</code>. <code>ip4</code> and <code>ip6</code> cost nothing.</p>



<p class="wp-block-paragraph">Cross the limit and the receiver returns <code>permerror</code>. That is not a soft fail. The receiver could not evaluate your record at all, treats SPF as failed, and every message from the domain is affected at once.</p>



<p class="wp-block-paragraph">Here is why it bites so hard on WordPress sites specifically. Nobody adds ten includes in one sitting. Someone adds Google Workspace. Six months later the marketing team adds a newsletter platform. Then a CRM. Then a help desk. Then you add a transactional provider for the WooCommerce mail, and the record tips over. Nothing in DNS warns you, no plugin surfaces it, and the only visible signal is in DMARC aggregate reports you probably aren&#8217;t reading yet.</p>



<p class="wp-block-paragraph">Check the record and count what it actually resolves to:</p>



<pre class="wp-block-code"><code>dig +short TXT example.com</code></pre>



<p class="wp-block-paragraph">Counting nested includes by hand is tedious and error-prone, so use a checker that expands them. When you are over budget, the durable fixes are removing senders you no longer use and moving distinct sending systems onto their own subdomains, each with their own SPF record. Flattening includes into raw IPs works, but you have just taken ownership of someone else&#8217;s IP ranges and they will change without telling you.</p>



<h3 class="wp-block-heading">One record, and mind the envelope</h3>



<p class="wp-block-paragraph">Publish exactly one SPF record per domain. Two records is not additive, it is invalid, and receivers may fail the check outright.</p>



<p class="wp-block-paragraph">Also remember SPF checks the envelope sender, not the address your visitor sees. On WordPress those can easily differ, especially when a plugin sets a friendly From address while the MTA uses the system user as the envelope. If you need to control it explicitly:</p>



<pre class="wp-block-code"><code>add_action( 'phpmailer_init', function ( $phpmailer ) {
    $phpmailer-&gt;Sender = 'bounces@example.com';
} );</code></pre>



<p class="wp-block-paragraph">Setting <code>Sender</code> sets the envelope from, which is what SPF evaluates and where bounces are returned. It is not the same field as <code>From</code>, and conflating the two is a classic source of &#8220;but SPF passes when I test it&#8221; confusion.</p>



<h2 class="wp-block-heading">Failure family three: DKIM signs, but alignment is what counts</h2>



<p class="wp-block-paragraph">This is the invisible one, and if you take one thing from this post, take this.</p>



<p class="wp-block-paragraph">DKIM adds a cryptographic signature to the message. The receiver fetches your public key from DNS at the selector your provider gave you and verifies the signature held. Check yours like this:</p>



<pre class="wp-block-code"><code>dig +short TXT selector1._domainkey.example.com</code></pre>



<p class="wp-block-paragraph">Straightforward enough. Here is the part that catches people: <strong>SPF and DKIM can both pass and DMARC can still fail.</strong></p>



<p class="wp-block-paragraph">DMARC does not care that <em>a</em> domain authenticated. It cares that the domain the recipient <em>sees</em> in the From header matches the domain that authenticated. That match is called alignment, and it is the whole point of DMARC. SPF aligns when the envelope sender domain matches the From domain. DKIM aligns when the signing domain in the <code>d=</code> tag matches the From domain. You need at least one of them aligned. Not both, but at least one.</p>



<p class="wp-block-paragraph">Now picture the default setup on a WordPress site with a transactional provider. The From header says <code>orders@example.com</code>. The envelope sender is a bounce address on the provider&#8217;s own domain, so SPF authenticates the provider and does not align with you. And if you skipped the DNS records the provider asked you to add, the message is signed with the provider&#8217;s shared key, so <code>d=</code> is the provider&#8217;s domain and DKIM does not align either.</p>



<p class="wp-block-paragraph">Both checks pass. Alignment fails. DMARC fails. Every deliverability tester you paste the record into says your DNS is fine.</p>



<p class="wp-block-paragraph">The fix is nearly always the same: complete your provider&#8217;s domain authentication step so mail is signed as your own domain rather than their shared one. That usually means publishing a CNAME or TXT record at a selector under <code>_domainkey</code>, and often a CNAME for a custom return path so SPF aligns too.</p>



<h3 class="wp-block-heading">Read the headers, don&#8217;t trust the dashboard</h3>



<p class="wp-block-paragraph">Send yourself a real message from the site and open the raw source. The <code>Authentication-Results</code> header the receiver added is the ground truth. You are looking for the SPF result, the DKIM result, the <code>d=</code> domain on the DKIM signature, and the DMARC verdict. If <code>d=</code> is not your domain and the SPF domain is not your domain, you have found your problem regardless of what any dashboard claims.</p>



<p class="wp-block-paragraph">One practical note on the key itself: a 2048-bit DKIM public key does not fit in a single 255 character DNS string and has to be published as multiple concatenated strings. Most DNS providers handle this transparently. Some do not, and the symptom is a key that looks present but fails verification.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Failure family four: DMARC policy, and what just changed</h2>



<p class="wp-block-paragraph">DMARC ties the previous two together. It tells receivers what to do when neither SPF nor DKIM aligns, and it asks them to send you reports about it.</p>



<pre class="wp-block-code"><code>_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"</code></pre>



<p class="wp-block-paragraph">Start at <code>p=none</code>. It changes nothing about how mail is handled and turns on the aggregate reports, which are the only view you will ever get of who is sending as your domain. Read them for a few weeks, find the legitimate senders you forgot about, fix their alignment, then move to <code>p=quarantine</code> and eventually <code>p=reject</code>.</p>



<p class="wp-block-paragraph">Do not skip straight to <code>p=reject</code> on a domain you have not observed. You will reject your own invoicing system.</p>



<p class="wp-block-paragraph">The specification itself was updated. RFC 7489, the original DMARC document from 2015, has been obsoleted by RFC 9989 together with RFC 9990 for aggregate reporting and RFC 9991 for failure reporting, and DMARC has moved onto the IETF standards track. Records still begin with <code>v=DMARC1</code> and nothing you have published stops working, so this is not an emergency. Two changes are worth knowing:</p>



<ul class="wp-block-list">
<li>The <code>pct</code> tag is gone, along with <code>rf</code> and <code>ri</code>. Percentage-based staged rollout was implemented inconsistently by receivers and has been replaced by a simple <code>t</code> testing flag. If your record still carries a partial <code>pct</code> value from a rollout that never finished, that is worth cleaning up.</li>



<li>Organisational domain discovery no longer depends on the Public Suffix List; receivers now walk the DNS tree. Single-domain owners will not notice. If you run a lot of subdomains, publishing explicit records rather than relying on inheritance removes the ambiguity.</li>
</ul>



<p class="wp-block-paragraph">There are also new optional tags. The one worth considering is <code>np</code>, which sets a policy for non-existent subdomains so attackers cannot spoof from a subdomain you never created, without changing your main policy.</p>



<p class="wp-block-paragraph">Aggregate reports arrive as compressed XML and are unpleasant to read by hand. Feeding them to a processor is worth it; dmarcian, Postmark&#8217;s free DMARC digest and MXToolbox all do this, and Cloudflare can host the DNS side.</p>



<h3 class="wp-block-heading">Are the bulk sender rules your problem?</h3>



<p class="wp-block-paragraph">Probably not, and it is worth saying so plainly because a lot of writing on this topic implies otherwise. Google&#8217;s stricter bulk sender requirements apply to domains sending roughly 5,000 or more messages a day to personal Gmail accounts. A typical WordPress business site sends a few dozen.</p>



<p class="wp-block-paragraph">What changed for everyone is the direction of travel. Authentication that used to be a nice-to-have is now the baseline for being evaluated on your merits rather than treated as suspicious by default. If you do cross into bulk territory, the additional obligations are one-click unsubscribe on marketing mail via the <code>List-Unsubscribe</code> and <code>List-Unsubscribe-Post</code> headers, and keeping spam complaints low. Google&#8217;s published guidance is to stay under 0.1% and never reach 0.3%.</p>



<h2 class="wp-block-heading">Domain strategy: keep resets away from newsletters</h2>



<p class="wp-block-paragraph">One structural decision saves more grief than any single record. Separate your transactional mail from your marketing mail at the domain level.</p>



<p class="wp-block-paragraph">Send password resets and order confirmations from something like <code>mail.example.com</code>, and send campaigns from <code>news.example.com</code>. Each subdomain gets its own SPF record, which keeps you comfortably inside the lookup budget. More importantly, reputation is largely tracked per sending domain. When a campaign goes badly and complaints spike, the damage stays contained. Your password resets keep landing.</p>



<p class="wp-block-paragraph">The trade-off is real: more DNS records to maintain, more DMARC reports to read, and a From address that is slightly less clean. On a site where a lost order confirmation costs actual revenue, that is a trade worth making.</p>



<h2 class="wp-block-heading">Troubleshooting: a working order</h2>



<ol class="wp-block-list">
<li><strong>Does the message leave?</strong> Hook <code>wp_mail_failed</code> and send via WP-CLI. A logged error here means credentials, ports or a plugin, not DNS.</li>



<li><strong>Can the box reach the relay?</strong> <code>openssl s_client -starttls smtp</code> against the submission port. A timeout means outbound filtering.</li>



<li><strong>What does the receiver say?</strong> Send to a mailbox you control, open the raw source, read <code>Authentication-Results</code>. This single header settles most arguments.</li>



<li><strong>Is the DKIM <code>d=</code> your domain?</strong> If not, you have not completed provider domain authentication. Fix that before anything else.</li>



<li><strong>Does SPF actually evaluate?</strong> Expand nested includes and count. A <code>permerror</code> looks like a pass in most naive testers.</li>



<li><strong>What do the aggregate reports show?</strong> If mail is failing for a subset of recipients only, the reports will name the sending source you forgot about.</li>
</ol>



<p class="wp-block-paragraph">If Gmail is deferring rather than rejecting, the enhanced status code in the bounce tells you which check failed. Codes in the <code>4.7.x</code> range point at authentication or alignment specifically rather than content, which narrows the search considerably.</p>



<h2 class="wp-block-heading">Common mistakes</h2>



<ul class="wp-block-list">
<li>Publishing two SPF records and assuming they merge. They do not.</li>



<li>Treating a green tick from an online record checker as proof of delivery. Those tools validate syntax, not alignment on your actual mail.</li>



<li>Jumping to <code>p=reject</code> before reading a single aggregate report.</li>



<li>Setting a From address on a domain you do not control, such as a customer&#8217;s own address on a contact form. Use your domain in From and put theirs in Reply-To.</li>



<li>Adding a new SaaS <code>include</code> without re-counting the lookup budget.</li>



<li>Leaving a stale <code>include</code> for a service you stopped using two years ago, which is both a wasted lookup and a live authorisation.</li>



<li>Configuring an SMTP plugin and never testing what happens when the provider is down. Mail queued nowhere is mail lost.</li>
</ul>



<h2 class="wp-block-heading">Best practices</h2>



<ul class="wp-block-list">
<li>Route WordPress mail through a real provider, and prefer the HTTP API transport when the host filters SMTP ports.</li>



<li>Complete provider domain authentication properly so DKIM signs as your domain. This is the single highest-value action on the list.</li>



<li>Publish DMARC at <code>p=none</code> with <code>rua</code> on day one, even before you intend to enforce. The reports are the visibility you are missing.</li>



<li>Keep SPF under the ten lookup budget with headroom for the next service someone adds.</li>



<li>Split transactional and marketing sending onto separate subdomains.</li>



<li>Enable email logging in your SMTP plugin so you have a record of what was attempted and when.</li>



<li>Monitor DKIM key expiry and provider selector changes the same way you monitor TLS certificates. Both fail silently and both fail completely.</li>
</ul>



<h2 class="wp-block-heading">Frequently asked questions</h2>



<h3 class="wp-block-heading">Do I need an SMTP plugin if my host already sends mail?</h3>



<p class="wp-block-paragraph">Check what the host&#8217;s relay authenticates as. If it signs with the host&#8217;s shared domain rather than yours, DKIM will not align with your From address and DMARC will fail even though mail is technically going out. If it authenticates as your domain, you are fine without a plugin.</p>



<h3 class="wp-block-heading">Is SPF enough on its own?</h3>



<p class="wp-block-paragraph">No. SPF breaks on forwarding, because the forwarding server is not in your record, and it authenticates the envelope sender rather than the visible From address. DKIM survives forwarding and gives you a second, independent path to alignment. Publish both.</p>



<h3 class="wp-block-heading">Why do my emails reach Gmail but not Outlook, or the reverse?</h3>



<p class="wp-block-paragraph">Receivers weight signals differently and each maintains its own reputation view of your sending IP and domain. Divergent behaviour usually means your authentication is borderline rather than solid, so one receiver&#8217;s threshold catches it and another&#8217;s does not. Fix alignment rather than chasing individual receivers.</p>



<h3 class="wp-block-heading">How long until DNS changes take effect?</h3>



<p class="wp-block-paragraph">As long as the TTL on the record, plus whatever the resolver in front of you decides to cache. Lower the TTL before you plan changes, not after. Verify with <code>dig</code> against an authoritative nameserver rather than trusting a browser-based checker.</p>



<h3 class="wp-block-heading">Does DMARC stop people spoofing my domain?</h3>



<p class="wp-block-paragraph">At <code>p=reject</code> it stops exact-domain spoofing at receivers that honour the policy, which is most large ones. It does nothing about lookalike domains, display-name spoofing, or a compromised mailbox sending genuinely authenticated mail. It is one control, not a complete answer to phishing.</p>



<h3 class="wp-block-heading">Should I update my DMARC record for the new RFCs?</h3>



<p class="wp-block-paragraph">Nothing breaks if you do not. At your next DNS edit, remove <code>pct</code>, <code>rf</code> and <code>ri</code> if present, and consider adding <code>np</code> if you want to close the non-existent subdomain gap. Leave the new tags unset otherwise, because the defaults are sensible.</p>



<h3 class="wp-block-heading">Why did deliverability get worse after I moved hosts?</h3>



<p class="wp-block-paragraph">Usually because the old host had a relay configured that the new one does not, so PHP <code>mail()</code> is now sending direct from an unwarmed IP with no authentication. It is also common for a migration to move the site but not the DNS records the old provider had added. Re-verify SPF, DKIM selectors and the return path after any move.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Wrapping up</h2>



<p class="wp-block-paragraph">If you remember one thing about WordPress email deliverability, remember that authentication and alignment are different questions. SPF passing and DKIM verifying does not mean DMARC passes. The receiver is asking whether the domain your recipient can see is the domain that proved itself, and on a default WordPress plus transactional provider setup, the honest answer is often no.</p>



<p class="wp-block-paragraph">Get the transport off PHP <code>mail()</code>, complete your provider&#8217;s domain authentication so <code>d=</code> is your own domain, keep SPF inside its lookup budget, and publish DMARC at <code>p=none</code> so you can finally see what is happening. Then read the reports for a month before you enforce anything.</p>



<p class="wp-block-paragraph">The alternative is closing the same ticket every week and never learning why.</p>



<h2 class="wp-block-heading">Need someone to fix this properly?</h2>



<p class="wp-block-paragraph">Mail problems are unusually well suited to outside help, because the failure is almost always in a place the site owner cannot see. This is work I take on regularly:</p>



<ul class="wp-block-list">
<li>Auditing an existing setup end to end and telling you exactly which of the four failure families you are in</li>



<li>Rebuilding SPF records that have drifted past the ten lookup limit, without breaking a live sender</li>



<li>Completing DKIM and return-path domain authentication with your provider so alignment actually holds</li>



<li>Taking a domain from <code>p=none</code> to enforcement safely, using the aggregate reports rather than guesswork</li>



<li>Splitting transactional and marketing sending onto separate subdomains with their own records and reputation</li>



<li>Migrating WordPress mail off PHP <code>mail()</code> onto a provider, with logging and failure alerting wired up</li>
</ul>



<p class="wp-block-paragraph">If you want a second opinion, send me a raw message source with the full headers, or your current SPF and DMARC records, and I will tell you what I see.</p>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://www.upwork.com/freelancers/~01f15a912ad84a6620" target="_blank" rel="noreferrer noopener">Work with me on Upwork</a></div>
</div>
<p>The post <a href="https://john-nessime.com/blog/technical-guides/wordpress-email-deliverability-spf-dkim-dmarc/">Why Your WordPress Emails Vanish: SMTP, SPF, DKIM and DMARC</a> appeared first on <a href="https://john-nessime.com/blog">John Nessime</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://john-nessime.com/blog/technical-guides/wordpress-email-deliverability-spf-dkim-dmarc/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
