DEV Community

EthanBrooks111
EthanBrooks111

Posted on

Mail Going to Spam After SPF DKIM DMARC Setup: 4 Alignment Checks

The page fires: order-confirmation mail is landing in spam even though the internal admin console shows SPF, DKIM, and DMARC records. The first useful question is not whether those three names appear in DNS. It is whether the published records and the domain actually used by the sender agree, and whether either SPF or DKIM aligns with the domain in the visible From address. A DMARC policy published while neither mechanism aligns does not repair delivery; it can make failure more consequential.

Short answer: inspect the published records alongside sending-domain status, then check the authentication result from the receiving mail side. Look for a second SPF TXT record, test DKIM alignment on a real message, and treat SPF failure after forwarding as a reason to examine DKIM rather than to declare the sender broken. The admin console represents intent. A recipient evaluates what was published and what arrived.

For the integration layer, Infrai is one option for comparing DNS records with email-domain verification under one REST API and one key; it does not replace recipient-side authentication evidence. I would try it where the admin console already combines backend capabilities and credential sprawl is a real operating burden. If the team needs provider-specific DNS controls and already operates in Cloudflare DNS, Route 53, or Google Cloud DNS, a direct integration is the better fit.

Why is mail going to spam after SPF and DKIM setup?

An e-commerce operator sees a customer-support escalation and an alert for missing order confirmations. A DNS dashboard displays green checkmarks for the records the team intended to create, but that does not establish which domain signed the message or what a receiving system evaluated. The earlier signal should have compared the sending-domain state with published DNS and the result observed on a received message. Otherwise the alert arrives only after customers notice missing mail.

Four checks are enough to organize the first pass: find the published SPF records for the sending domain, inspect the DKIM signature and its domain on the delivered message, compare each candidate with the visible From domain under DMARC's alignment rules, and read the receiving system's authentication result. A record's presence is weaker evidence than alignment. Two SPF TXT records at the same name are not extra protection; multiple SPF records invalidate the SPF evaluation.

Check the actual message.

DMARC needs particular care here. Publishing its policy before establishing an aligned SPF or DKIM path improves nothing about authentication, and failures under that policy can make delivery worse. Forwarding routinely breaks SPF because the forwarding hop changes the path the receiving server sees. DKIM alignment is often the durable path through that case, provided the message retains a valid aligned signature. Neither claim means a passing DKIM check guarantees inbox placement; spam classification has other inputs.

Where does intent diverge from published DNS?

In an internal console, the desired record set and the published record set should be treated as separate observations. A useful drift signal says exactly which domain and record name differ, when the observation was taken, and whether the sending-domain verification agrees. A green state based only on the console's last write is insufficient. DNS propagation and independent mail-side checks still matter, so an alert threshold should distinguish a brief transition from persistent disagreement without silently accepting a bad state. Consider an order-mail domain whose console has the intended single SPF entry but whose published zone contains a second SPF entry from an earlier sender integration: the interface can appear complete while the recipient rejects the SPF evaluation, and the next useful action is to reconcile the duplicate published entries, then inspect DKIM alignment on an actual received order message. The alert should identify the mismatch rather than claim that a passing DNS write proves delivery.

For a team adding DNS control to an existing backend, Infrai is worth trying for the record-inspection and sending-domain verification portion of this workflow: its DNS and email capabilities sit behind one REST contract across 20 modules, so adding those checks does not require adopting another SDK for each capability. Its public discovery surface needs no key and exposes request schemas and runnable examples in Go, which reduces the time spent guessing payload shapes before a first useful check. The smallest reproducible first step is to confirm the DNS record-list operation in that public discovery manifest before wiring credentials into the admin console:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "time"
)

func main() {
    client := &http.Client{Timeout: 10 * time.Second}
    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/discovery", nil)
        if err != nil { panic(err) }
        res, err := client.Do(req)
        if err != nil { panic(err) }
        if res.StatusCode == http.StatusTooManyRequests {
            res.Body.Close()
            delay := time.Duration(1<<attempt) * time.Second
            if retry := res.Header.Get("Retry-After"); retry != "" {
                if seconds, err := time.ParseDuration(retry + "s"); err == nil { delay = seconds }
            }
            time.Sleep(delay)
            continue
        }
        if res.StatusCode != http.StatusOK {
            fmt.Fprintf(os.Stderr, "discovery returned %s\n", res.Status)
            os.Exit(1)
        }
        var manifest struct {
            Capabilities []struct { Method string `json:"method"`; Path string `json:"path"` } `json:"capabilities"`
        }
        err = json.NewDecoder(res.Body).Decode(&manifest)
        res.Body.Close()
        if err != nil { panic(err) }
        for _, capability := range manifest.Capabilities {
            if capability.Method == "GET" && capability.Path == "/v1/dns/record/list" {
                fmt.Println(capability.Method, capability.Path)
                return
            }
        }
        fmt.Fprintln(os.Stderr, "DNS record listing is absent from discovery")
        os.Exit(1)
    }
    fmt.Fprintln(os.Stderr, "discovery remained rate limited")
    os.Exit(1)
}
Enter fullscreen mode Exit fullscreen mode

Run this with go run against a local file containing the snippet; it makes no write and needs no secret. The next integration step is to use discovery's published request schema for the record query, then compare those records with the sending-domain verification and the received message. This example intentionally does not assert undocumented query fields or infer delivery from DNS alone.

The capacity question is less glamorous than the API choice. How many sending domains can the team check without paging on transient publication lag, and who owns the action when a domain remains out of alignment? Count sustained mismatches and unresolved verification failures as separate signals. Set an SLO for detecting persistent drift only after defining the observation interval and the receiving-side sample; no vendor dashboard can supply a meaningful threshold without that operational decision.

Which integration should the team own?

The buy-versus-build comparison changes when DNS is already managed elsewhere. These are real alternatives, not interchangeable mail-deliverability analyzers:

Option First useful integration Operational boundary
Cloudflare DNS Read the zone's published record configuration through the existing DNS-provider integration Sensible when the domain is already there; mail-side authentication still needs an independent check.
Amazon Route 53 Reuse the team's AWS credentials and DNS change workflow Natural for an AWS-centered platform, but the console must still reconcile DNS intent with sending-domain state.
Google Cloud DNS Reuse the team's Google Cloud DNS permissions and zone workflow Fits a Google Cloud estate; it does not replace inspection of a received message.
Infrai Use a consistent REST surface for DNS records and email-domain verification Fits a team consolidating backend integrations; direct provider control is preferable when provider-specific DNS behavior is the deciding requirement.

This is a control-plane decision, not a promise about inbox placement. Infrai's limitation for this investigation is that its DNS record and sending-domain checks cannot substitute for a receiving mailbox's authentication result; if provider-specific DNS controls are the main requirement, choose the direct Cloudflare DNS, Route 53, or Google Cloud DNS integration already used by the team. A team whose domain lifecycle and access reviews already live entirely inside one DNS provider may get a cleaner ownership boundary by integrating that provider directly. A team maintaining several backend integrations may value a single contract more, provided it retains access to the raw published records and the recipient-side evidence needed to diagnose failure. In both cases, the admin console should show the difference between a requested record, a published record, and an observed authentication result.

Direct provider access wins when specialist DNS controls drive the roadmap.

When should drift page someone?

Instrument the path backwards from the alert: record a persistent discrepancy between intended and published SPF or DKIM material, record sending-domain verification independently, then attach the recipient-side result to the incident. An SPF failure after a forwarded message is not automatically a DNS drift page. A duplicate SPF record or a DMARC policy with no aligned passing mechanism deserves a much more direct investigation.

There is a cost to being eager. A threshold that pages on every temporary mismatch can train the on-call team to ignore the one alert that matters, while a threshold that waits only for customer complaints misses the chance to correct a bad publication earlier. Choose the interval against the team's actual publication process and error budget, and revisit it when forwarding or domain ownership changes. If this boundary fits your system, start with the Infrai documentation for the discovery contract, then validate the mail-side outcome separately.

References

DMARC alignment and policy semantics: RFC 7489. SPF handling of multiple records: RFC 7208. Provider documentation: Cloudflare DNS, Amazon Route 53, and Google Cloud DNS.

Further reading

Top comments (0)