<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Steven Browning</title>
    <description>The latest articles on DEV Community by Steven Browning (@steven_browning_70ac8fbfa).</description>
    <link>https://dev.to/steven_browning_70ac8fbfa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4132219%2F63fad681-fa21-4c99-9ba6-52d6f70b9fd0.jpg</url>
      <title>DEV Community: Steven Browning</title>
      <link>https://dev.to/steven_browning_70ac8fbfa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/steven_browning_70ac8fbfa"/>
    <language>en</language>
    <item>
      <title>Your DMARC record might contain something that no longer exists</title>
      <dc:creator>Steven Browning</dc:creator>
      <pubDate>Sat, 19 Sep 2026 22:07:42 +0000</pubDate>
      <link>https://dev.to/steven_browning_70ac8fbfa/your-dmarc-record-might-contain-something-that-no-longer-exists-nii</link>
      <guid>https://dev.to/steven_browning_70ac8fbfa/your-dmarc-record-might-contain-something-that-no-longer-exists-nii</guid>
      <description>&lt;p&gt;Most of us set up SPF, DKIM and DMARC once, watch the reports for a week or two, and then leave them alone. That's usually the right call. They aren't systems that need constant attention.&lt;/p&gt;

&lt;p&gt;This year is worth an exception, though — and not quite for the reason you'd expect. The spec changed in May, and one of those changes means that &lt;em&gt;tidying up&lt;/em&gt; your DMARC record can quietly make it stricter than you intended.&lt;/p&gt;

&lt;p&gt;Here's what moved, and what I'd look at.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changed
&lt;/h2&gt;

&lt;p&gt;On 19 May 2026, DMARC moved onto the IETF Standards Track as &lt;a href="https://www.rfc-editor.org/info/rfc9989/" rel="noopener noreferrer"&gt;RFC 9989&lt;/a&gt;, replacing RFC 7489 and RFC 9091. Reporting moved out into two companion documents: RFC 9990 for aggregate reports, RFC 9991 for per-message failure reports.&lt;/p&gt;

&lt;p&gt;For the ten years before that, DMARC carried the status &lt;em&gt;Informational&lt;/em&gt; — a description of something the industry had already built, rather than a standard anyone was obliged to follow. A lot of records out there were written against that older spec and haven't been touched since. Some of them contain a tag that no longer exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;pct&lt;/code&gt; tag is gone, and deleting it isn't free
&lt;/h2&gt;

&lt;p&gt;If you rolled DMARC out gradually, you probably used &lt;code&gt;pct&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was a reassuring idea: apply the policy to a quarter of failing messages, watch what breaks, then raise the number. In practice receivers interpreted it inconsistently enough that RFC 9989 removes it altogether — there's an appendix titled "Removal of the 'pct' Tag."&lt;/p&gt;

&lt;p&gt;This is the part I'd slow down for. If you delete &lt;code&gt;pct=25&lt;/code&gt; from that record, you don't land back where you started. Receivers that don't recognise the tag already treat the policy as applying to &lt;strong&gt;everything&lt;/strong&gt;, and removing it just makes that explicit. You've gone from quarantining a quarter of failing mail to quarantining all of it.&lt;/p&gt;

&lt;p&gt;That may be exactly what you want. It's nicer to be the person who decided it than the person who finds out from the helpdesk.&lt;/p&gt;

&lt;p&gt;The replacement is a &lt;code&gt;t&lt;/code&gt; tag, for testing. &lt;code&gt;t=y&lt;/code&gt; asks receivers to handle mail one step below your published policy — &lt;code&gt;p=reject&lt;/code&gt; behaves as quarantine, &lt;code&gt;p=quarantine&lt;/code&gt; behaves as none — while you carry on collecting reports. It's a cleaner mechanism than sampling, though it does mean the gradual percentage ramp isn't available any more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stricter isn't automatically the goal
&lt;/h2&gt;

&lt;p&gt;There's an assumption running through a lot of DMARC advice that &lt;code&gt;p=reject&lt;/code&gt; is the finish line and everything else is a waypoint. RFC 9989 is more careful than that. Section 7.4 puts it directly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;domains that host users who might post messages to mailing lists SHOULD NOT publish Domain Owner Assessment Policies of &lt;code&gt;p=reject&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reasoning is practical rather than theoretical. Mailing lists and forwarders relay and rewrite messages in ways that break the original authentication, so rejecting on failure can bounce your own users' perfectly legitimate mail — and list software often responds to those bounces by unsubscribing them.&lt;/p&gt;

&lt;p&gt;If your domain is purely transactional, &lt;code&gt;p=reject&lt;/code&gt; is a sound destination. If real people send mail from it, quarantine may be as far as you want to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three smaller changes worth knowing about
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Organizational domain discovery no longer uses the Public Suffix List.&lt;/strong&gt; RFC 9989 replaces it with a series of DNS queries the spec calls the &lt;strong&gt;DNS Tree Walk&lt;/strong&gt;. Worth knowing by name — it's what you'll be searching for the day something behaves oddly on a multi-level domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;np&lt;/code&gt; covers non-existent subdomains.&lt;/strong&gt; Carried over from RFC 9091, it lets you publish a separate policy for subdomains that don't exist at all, which is handy, because those are a favourite for spoofing. Without it they inherit &lt;code&gt;sp&lt;/code&gt;, or failing that &lt;code&gt;p&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;sp&lt;/code&gt; only applies where you'd expect it to.&lt;/strong&gt; It's ignored on records published below the organizational domain or public suffix level. If you've ever set it on a subdomain and wondered why nothing happened, that's the reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  SPF: a short record can still be doing a lot of work
&lt;/h2&gt;

&lt;p&gt;An SPF record can look completely manageable and still sit close to a hard limit, because every &lt;code&gt;include:&lt;/code&gt; pulls in whatever &lt;em&gt;that&lt;/em&gt; provider has published — and their includes count too.&lt;/p&gt;

&lt;p&gt;RFC 7208 puts the ceiling at ten DNS-querying terms per evaluation, and going over it produces a &lt;code&gt;permerror&lt;/code&gt;. That's a hard failure, not a soft one, and it isn't something you'll notice unless you're reading your reports. Three or four mainstream providers is enough to get uncomfortably close.&lt;/p&gt;

&lt;p&gt;There's also a recommended limit of two "void" lookups — queries that come back empty, or for a name that doesn't exist. Which is a good reason to clear out includes for services you stopped using two migrations ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check how your SPF record ends
&lt;/h2&gt;

&lt;p&gt;This one takes ten seconds, and it's the first thing I'd look at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=spf1 include:_spf.google.com all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That record passes every sender on the internet.&lt;/p&gt;

&lt;p&gt;A mechanism with no qualifier defaults to &lt;code&gt;+&lt;/code&gt;, so a bare &lt;code&gt;all&lt;/code&gt; means &lt;code&gt;+all&lt;/code&gt; — pass. It's one missing character, the record looks perfectly tidy, and nothing anywhere warns you. &lt;code&gt;~all&lt;/code&gt; is softfail and &lt;code&gt;-all&lt;/code&gt; is fail; one of those is almost certainly what was intended.&lt;/p&gt;

&lt;p&gt;While you're in there: if you added a second &lt;code&gt;v=spf1&lt;/code&gt; record when you onboarded a new provider, the two don't combine. More than one SPF record at the same name is a &lt;code&gt;permerror&lt;/code&gt;, and the fix is to merge the mechanisms into a single record.&lt;/p&gt;

&lt;h2&gt;
  
  
  DKIM: "not found" often means "not found by this checker"
&lt;/h2&gt;

&lt;p&gt;If a checking tool tells you DKIM is missing, it's worth a second look before you go and re-key anything.&lt;/p&gt;

&lt;p&gt;DKIM keys live at names called selectors, and there's no standard lookup that lists them. So a checker has to guess: it tries a handful of common selectors and reports what comes back. If yours isn't on its list, you get an empty result and a record that is perfectly healthy.&lt;/p&gt;

&lt;p&gt;A real signed message settles it in seconds. Find the &lt;code&gt;DKIM-Signature&lt;/code&gt; header on a message you sent externally — &lt;code&gt;s=&lt;/code&gt; is the selector, &lt;code&gt;d=&lt;/code&gt; is the signing domain. That domain often isn't the one in the From field, particularly when a marketing platform signs on your behalf, and that's expected rather than a problem.&lt;/p&gt;

&lt;p&gt;Two things worth filing away. Finding a key doesn't prove your messages are actually being signed correctly — it only proves the key is published. And a record with an empty &lt;code&gt;p=&lt;/code&gt; value means the key has been &lt;em&gt;revoked&lt;/em&gt;, not that it's missing. The record is there; the key just isn't usable any more.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you only do three things
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Look at a real message header&lt;/strong&gt; from your own domain, on something that went to an external recipient. It'll hand you your DKIM selector and signing domain straight away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the end of your SPF record&lt;/strong&gt;, and count the includes while you're there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read a week of DMARC aggregate reports before you tighten anything.&lt;/strong&gt; They're the only thing that tells you who is genuinely sending as you, and there's nearly always one legitimate sender nobody remembered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I built a free checker for this, mostly because I got tired of tools reporting "DKIM: missing" when they simply didn't know my selector: &lt;a href="https://stashgrid.site/emailcheck/" rel="noopener noreferrer"&gt;stashgrid.site/emailcheck&lt;/a&gt;. It reads the public SPF, DKIM, DMARC, MTA-STS, TLS-RPT and DNSSEC records for a domain and explains each result in plain English. It runs in the browser, there's no sign-up, and nothing is stored. Treat it as a starting point rather than an audit — the fuller picture comes from your reports and your own message headers.&lt;/p&gt;

&lt;p&gt;One last thing, worth saying plainly: these records stop people sending mail &lt;em&gt;as your domain&lt;/em&gt;. They don't filter what arrives in your inbox, and they can't stop someone registering a domain that merely looks like yours and sending from that instead. Different problem, different tools.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://www.rfc-editor.org/rfc/rfc9989.html" rel="noopener noreferrer"&gt;RFC 9989&lt;/a&gt; (DMARC), &lt;a href="https://www.rfc-editor.org/rfc/rfc7208.html" rel="noopener noreferrer"&gt;RFC 7208&lt;/a&gt; (SPF), &lt;a href="https://www.rfc-editor.org/info/rfc6376/" rel="noopener noreferrer"&gt;RFC 6376&lt;/a&gt; (DKIM).&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>dns</category>
      <category>email</category>
    </item>
  </channel>
</rss>
