Architecture
How this site is hosted and secured A static site with one small serverless function, deployed by Terraform from GitHub Actions. Everything a visitor touches goes through the CDN; nothing else is reachable from the internet.
Architecture diagram Visitors resolve the domain in Route 53 and connect over TLS to CloudFront. CloudFront adds security headers and serves static files from a private S3 bucket through Origin Access Control. Requests to /api/contact go to a throttled HTTP API, which invokes a Lambda function that sends mail through Amazon SES. GitHub Actions authenticates to AWS with OIDC to run Terraform, whose state is stored in a separate S3 bucket, and to publish the site files. AWS · us-east-1 · single account Visitor browser Route 53 alias · CAA · DKIM · SPF CloudFront ACM cert · TLS 1.2+ · HTTP/3 security headers policy edge function: redirects S3 site bucket private · OAC only encrypted · versioned HTTP API POST /api/contact stage throttling Lambda validate · honeypot SES DKIM-signed Inbox never public 1 DNS 2 HTTPS /* OAC /api/* Delivery GitHub Actions PR: plan · main: apply PII scan gate IAM OIDC roles short-lived creds repo + branch scoped Terraform state own S3 bucket versioned · locked OIDC plan/apply sync files · invalidate cache Solid arrows: request path. Dashed: deployment. Nothing outside the CDN is publicly reachable; the S3 website endpoint is not used. Controls by layer DNS
Route 53 hosted zone Alias records to the CDN (no IPs to drift) CAA records allow only Amazon to issue certificates SPF, DKIM and DMARC for the mail domain Edge / TLS
CloudFront + ACM certificate TLS 1.2+ (TLSv1.2_2021 policy), HTTP → HTTPS redirect HTTP/2 and HTTP/3 Edge function: www → apex redirect, clean URLs Headers
Response headers policy HSTS (2 years, includeSubDomains, preload) Strict CSP: self-only scripts, no inline script, frame-ancestors none nosniff, Referrer-Policy, Permissions-Policy, COOP/CORP Origin
Private S3 bucket Block Public Access on, ACLs disabled (bucket owner enforced) Origin Access Control: bucket policy allows only this distribution SSE encryption, versioning for instant rollback, TLS-only bucket policy Contact API
HTTP API → Lambda → SES Same-origin route through the CDN (no CORS, no extra CSP origin) Stage throttling returns 429 on bursts; honeypot drops bots Secret origin header so the API only answers the CDN Lambda role can send only from the site domain identity; recipient never leaves the backend IaC
Terraform Remote state in a dedicated S3 bucket: versioned, encrypted, public access blocked, TLS-only Native S3 state locking Account-specific values kept out of git CI/CD
GitHub Actions OIDC federation – no long-lived AWS keys anywhere PRs assume a read-only plan role; main uses an apply role behind a protected environment Build fails if the PII scanner finds an email, phone or address Want to verify? curl -sI https://jasonratcliffe.dev shows every header above. The infrastructure is written in Terraform – read about it on the blog .