Without programmatic SPF validation, you have no guarantee that your sending domain's SPF record is correctly published — or published at all. RFC7208 alignment is a prerequisite for DMARC enforcement: if SPF fails, email from your domain reaches the spam folder or is rejected outright. Manual DNS management introduces silent misconfiguration: a deployment that adds a new sending IP without updating the SPF record will quietly fail authentication for weeks before anyone notices the deliverability drop.
Critical because an absent or misconfigured SPF record causes immediate authentication failure, breaking DMARC alignment and triggering spam classification or outright rejection at major mailbox providers.
Add startup or health-check validation using Node's built-in dns module — no third-party dependency required. Check that the SPF record is present and ends with ~all or -all, not the dangerously permissive +all.
import { promises as dns } from 'node:dns'
export async function validateSpfRecord(sendingDomain: string): Promise<void> {
const records = await dns.resolveTxt(sendingDomain)
const spfRecord = records.flat().find(r => r.startsWith('v=spf1'))
if (!spfRecord) throw new Error(`No SPF record found for ${sendingDomain}`)
if (!spfRecord.includes('~all') && !spfRecord.includes('-all')) {
throw new Error(`SPF record for ${sendingDomain} uses +all — too permissive`)
}
}
For a SendGrid-only sender the record is v=spf1 include:sendgrid.net ~all; for SES in us-east-1 use v=spf1 include:amazonses.com ~all. Version-control both the record value and this validation call so SPF gaps surface in CI before reaching production.
ID: deliverability-engineering.dns-auth.spf-validation
Severity: critical
What to look for: Enumerate all sending domains and for each, search for code that generates SPF record strings (v=spf1 ... ~all or -all) or calls DNS lookup functions to validate existing SPF records. Check for strings containing v=spf1, functions named validateSpf, generateSpf, checkSpf, or calls to DNS txt-record lookups. Also look for SPF record values in infrastructure-as-code files (Terraform, CloudFormation, CDK, Pulumi) or DNS configuration scripts.
Pass criteria: At least 1 of the following: (a) code generates a valid SPF record string for the sending domain, (b) code validates that the SPF record is correctly configured by querying DNS, (c) the SPF record is defined in infrastructure-as-code and tracked in version control.
Fail criteria: No code generates, validates, or version-controls SPF records. SPF is managed manually with no programmatic verification.
Skip (N/A) when: The project does not send email.
Cross-reference: Check deliverability-engineering.dns-auth.dmarc-policy — DMARC requires SPF alignment to pass, so SPF must be valid before DMARC can enforce.
Detail on fail: Describe what is missing. Example: "No SPF record generation or validation found — SPF is entirely manually managed with no programmatic checks" or "SPF record hardcoded in env vars but no validation code confirms it is published correctly to DNS"
Remediation: Add SPF validation on startup or as a health check using Node's built-in dns module:
import { promises as dns } from 'node:dns'
export async function validateSpfRecord(sendingDomain: string): Promise<void> {
const records = await dns.resolveTxt(sendingDomain)
const spfRecord = records.flat().find(r => r.startsWith('v=spf1'))
if (!spfRecord) {
throw new Error(`No SPF record found for ${sendingDomain}`)
}
// Verify SPF ends with ~all or -all (not +all)
if (!spfRecord.includes('~all') && !spfRecord.includes('-all')) {
throw new Error(`SPF record for ${sendingDomain} uses +all — too permissive`)
}
console.log(`SPF valid for ${sendingDomain}: ${spfRecord}`)
}
A minimal SPF record for a SendGrid-only sender:
v=spf1 include:sendgrid.net ~all
For SES in us-east-1:
v=spf1 include:amazonses.com ~all