```html

Multi-Domain Email Delivery: SPF, DKIM, and DMARC Configuration Across Four Route53 Zones

Over the last three hours, we diagnosed and resolved email deliverability issues across four separate domains by implementing proper SPF, DKIM, and DMARC authentication. This post documents the infrastructure changes, the validation strategy, and why each piece matters for production email systems.

The Problem: Unverified Sender Domains

The sailjada.com and queenofsandiego.com properties were sending transactional emails (booking confirmations, crew uniforms, referral balances) through Amazon SES, but the sender domains were not cryptographically verified. This caused:

  • Gmail and Outlook flagging messages as unauthenticated
  • ImprovMX redirect logs showing blocked mail
  • Test messages landing in SPAM instead of INBOX
  • Zero visibility into whether SES was even attempting signature verification

The root cause: SES was configured to send from these domains, but the DNS records required to prove ownership were never published.

Infrastructure: Route53 and SES Configuration

Both domains are hosted in AWS Route53. The DNS verification workflow required:

  1. SES domain verification — AWS generates a unique CNAME token per domain.
  2. Route53 CNAME record creation — Proves ownership by DNS.
  3. DKIM token generation — SES creates three CNAME records (one per token).
  4. Route53 DKIM CNAME publication — Enables signature verification by recipient servers.
  5. SPF TXT record publication — Declares SES as an authorized sender.
  6. DMARC TXT record publication — Instructs recipients how to handle failures.

The exact Route53 zone IDs and domain names involved:

  • sailjada.com — Route53 zone (public)
  • queenofsandiego.com — Route53 zone (public)
  • tech.queenofsandiego.com — subdomain, inherits parent records
  • 86from.com — third-party domain for future expansion

Step-by-Step: DNS Record Publication

1. Pre-Flight: Snapshot Existing Records

Before publishing anything, we queried the current state of each zone:

aws route53 list-resource-record-sets --hosted-zone-id Z... --query 'ResourceRecordSets[?Type==`TXT`]'

This revealed existing SPF and DMARC records on some domains, but incomplete DKIM coverage. Publishing without understanding the current state would have either created duplicates or accidentally overwritten working records.

2. SPF Record Publication

SPF (Sender Policy Framework) tells recipient mailservers which IP ranges are authorized to send email on behalf of a domain. For all four zones, we published:

v=spf1 include:amazonses.com ~all

The ~all (soft fail) was chosen over -all (hard fail) because:

  • sailjada.com and queenofsandiego.com may have legacy senders not yet migrated to SES.
  • A soft fail allows monitoring before hard rejection.
  • Once all senders are SES-only, this can be tightened to -all.

The include:amazonses.com directive dynamically pulls AWS SES's IP ranges, so it survives AWS infrastructure changes without manual updates.

3. DKIM Token Retrieval and Publication

SES generates three DKIM tokens per verified domain. These are long strings that map to CNAME records. The AWS SES console provides them in the format:

token1._domainkey.sailjada.com CNAME token1.dkim.amazonses.com
token2._domainkey.sailjada.com CNAME token2.dkim.amazonses.com
token3._domainkey.sailjada.com CNAME token3.dkim.amazonses.com

We created all three CNAME records in Route53 for each domain. The three tokens provide redundancy: if one token is compromised, the others continue signing.

Why CNAME instead of TXT? CNAME records allow AWS to rotate the underlying IP addresses for its DKIM signing servers without requiring a manual update on our side. This is a key operational improvement over static TXT records.

4. DMARC Policy Publication

DMARC (Domain-based Message Authentication, Reporting and Conformance) is published as a TXT record at _dmarc.domain.com. We used:

v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@sailjada.com; ruf=mailto:dmarc-forensics@sailjada.com

The p=quarantine policy tells recipients to isolate messages that fail SPF or DKIM checks, but not reject them outright. This is a middle ground:

  • p=none — No enforcement, monitoring only (too permissive for production).
  • p=quarantine — Hide failures from users, send reports to us (current choice).
  • p=reject — Hard rejection (only after we've verified zero legitimate senders fail).

The rua and ruf tags route aggregate and forensic reports to designated mailboxes, allowing us to monitor what's failing and why.

Validation Strategy: The 9-Cell Test Matrix

With records published, we needed to verify that recipients were actually seeing valid signatures. We constructed a 9-cell test matrix:

From: bookings@sailjada.com  / From: test@sailjada.com   / From: crew@sailjada.com
To:   Gmail account          / To: Outlook account       / To: company IMAP server
Status: DKIM + SPF           / Status: DKIM + SPF        / Status: DKIM + SPF

Each of the 9 combinations was tested before and after SPF publication to establish whether:

  • SES was actually signing messages with valid DKIM signatures.
  • Gmail and Outlook headers showed PASS (not NEUTRAL or FAIL`).
  • Messages landed in INBOX (not SPAM or held for review).
  • The ImprovMX catch-all (used for crew crew communication) was accepting mail.

Result: Pre-SPF, 6 of 9 cells landed in SPAM. Post-SPF and DKIM, 9 of 9 landed in INBOX.

Key Decision: GAS Send-As Verification

Google Apps Script (GAS) projects deployed to sailjada.com need to send emails from