Skip to content
prod 352bb92
Browse

5 · DNS email records

Objective — make transactional email deliverable by authenticating the sending domain: verify the provider, add SPF/DKIM/DMARC (plus MX), interpret CAA safely, and confirm with dig — so password resets, receipts, and notifications don’t land in spam or get rejected.

Steps at a glance:

  1. Verify the email provider and record credentials — Confirm which service sends mail (Postmark, SES, Mailgun, SendGrid, your host’s SMTP) and that the domain is verified there.
  2. Add the three records and understand what each does — publish SPF, DKIM, and DMARC records, so outbound email can pass authentication instead of landing in spam.
  3. Add MX records (if the domain receives mail) — If the domain also receives mail, add the provider’s MX records at the documented priorities. If it only sends (transactional-only), MX may point elsewhere or be omitted — match your provider’s guidance.
  4. Interpret CAA safely — empty CAA is fine; Cloudflare-managed permissive multi-issuer CAA is also fine. Flag restrictive single-issuer records before final certificate strategy.
  5. Verify with dig — Read the resolver output directly and record the Phase 12 live-mail proof owner.
flowchart LR
App[Laravel mailer] --> ESP[Email provider]
ESP --> DNS[SPF + DKIM + DMARC DNS]
DNS --> Inbox[Recipient inbox]
DNS --> Reject[Reject / spam folder]

1. Verify the email provider and record credentials

Section titled “1. Verify the email provider and record credentials”

Confirm which service sends mail (Postmark, SES, Mailgun, SendGrid, your host’s SMTP) and that the domain is verified there.

Record the SMTP/API credentials into the production .env (MAIL_*) from 3 · Production .env — double-quoted, never committed.

2. Add the three records and understand what each does

Section titled “2. Add the three records and understand what each does”
RecordTypeAnswersWithout it
SPFTXTWhich servers may send for this domainMail flagged as spoofed
DKIMTXTCryptographic signature proving integrityTampering undetectable; spam scoring
DMARCTXTPolicy when SPF/DKIM fail + where to reportNo alignment policy; inconsistent delivery
  1. Publish the SPF record — one TXT at the root, listing every sender. Merge into a single record — multiple SPF records is itself a failure.

    DNS records
    example.com. TXT "v=spf1 include:_spf.provider.com ~all"
    • ✅ A single SPF TXT record exists at the root, listing every sender.
  2. Publish the DKIM record — your provider gives a selector and key; publish it at the selector host they specify.

    DNS records
    selector._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=<public-key>"
    • ✅ The DKIM key is published at the provider’s selector host.
  3. Publish the DMARC record — start at p=none (monitor only), collect reports, then tighten to quarantine/reject.

    DNS records
    _dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; fo=1"
    • ✅ A DMARC TXT record at _dmarc.<domain> starts at p=none.

3. Add MX records (if the domain receives mail)

Section titled “3. Add MX records (if the domain receives mail)”

If the domain also receives mail, add the provider’s MX records at the documented priorities. If it only sends (transactional-only), MX may point elsewhere or be omitted — match your provider’s guidance.

  1. Publish the MX records at the provider’s priorities.

    DNS records
    example.com. MX 10 mx1.provider.com.
    example.com. MX 20 mx2.provider.com.
    • ✅ MX records are present if the domain receives mail (or correctly omitted if send-only).

CAA records restrict which certificate authorities may issue for the domain. The risk is not “CAA exists”; the risk is a restrictive record that excludes the issuer your final SSL path uses.

Read the resolver output directly. Do not treat a real test email as Phase 4 work — that is [RUN-LIVE] proof and belongs in production after the app can actually send mail.

  1. Confirm all records resolve.

    Terminal window
    dig +short TXT example.com | grep spf1 # SPF present, single record
    dig +short TXT selector._domainkey.example.com # DKIM key resolves
    dig +short TXT _dmarc.example.com | grep DMARC1 # DMARC policy present
    dig +short MX example.com # MX (if receiving mail)
    # Expected: one SPF line, the DKIM key, a DMARC1 policy, and MX records (if receiving mail)
    • ✅ SPF (single), DKIM, DMARC, and MX (if receiving) all resolve.
  2. Record the live-mail proof owner. The real send/receive test happens in Phase 12 Step 3 · Harden, verify & sync after production SMTP settings and the live app exist.

    • ✅ Phase 12 owns the live mail-header proof; Phase 4 owns DNS record resolution only.
SymptomCauseFix
spf=softfail/failSender not in SPF includeAdd the provider’s include; keep one SPF record
dkim=noneSelector record missing/typo’dRe-publish exactly as the provider specifies
dmarc ignoredRecord at wrong hostMust be _dmarc.<domain>, not the root
Multiple SPF recordsTwo TXT v=spf1 entriesMerge into one
Restrictive CAA before cert strategyOnly one CA is allowed and it is not your active/final issuerAdd the intended issuer now, or add a Zaj-BACKLOG.md Phase 7 renewal-monitoring row with owner, trigger, and acceptance

Do not mark this step done until every box below is checked.

  • 👤 Provider verified — email provider verified; MAIL_* recorded in production .env.
  • 🔀 Records resolve — SPF (single record), DKIM selector, and DMARC (p=none to start) all resolve via dig.
  • 👤 MX correct — MX records correct if the domain receives mail.
  • 🤖 CAA interpreted — CAA is empty/permissive, or restrictive CAA has a named Zaj-BACKLOG.md cert-strategy follow-up before Phase 7 hardening.
  • 🤖 RUN-LIVE owner recorded — real mail-header proof is assigned to Phase 12 after production can send mail.