Methodology Deliverables Insights Notes Readiness Scope brief

Notes / 01

2026-09-03

WAF and Lambda Function URLs: put CloudFront in front

While building a public intake endpoint for a multi-tenant evidence vault, we wanted rate limiting and managed rule packs in front of a simple Lambda Function URL. The first OpenTofu apply failed for a boring reason that is easy to miss in the docs.

Thesis: AWS WAF cannot associate a regional web ACL directly with a Lambda Function URL. Put Amazon CloudFront in front, use a CloudFront-scoped web ACL in us-east-1, and publish a branded hostname so your Content-Security-Policy never needs a wildcard for Function URL hosts.

What we tried first

Function URLs are attractive for a small POST/OPTIONS API: no API Gateway stage to babysit, IAM auth or public invoke, quick deploy. For a marketing-site form that posts JSON to the cloud, that shape is fine — until you ask for WAF.

AssociateWebACL against the Function URL ARN fails. Regional WAF expects Application Load Balancers, API Gateway REST/HTTP APIs, AppSync, Cognito user pools, and a few other resource types. Function URLs are not on that list.

The working shape

Browser
  │  HTTPS → intake.example.com
  ▼
CloudFront (custom domain + ACM in us-east-1)
  │  origin: Function URL (HTTPS-only)
  │  WAF: scope = CLOUDFRONT (must live in us-east-1)
  ▼
Lambda Function URL

Three details matter in practice:

Why CSP cares

If the browser posts straight to a Function URL, teams often paper over the hostname with a CSP wildcard. That fingerprints the stack and widens the connect surface. A branded intake host is both cleaner for buyers reading your headers and stricter for what the page may call.

After cutover, verify with a simple header check on the marketing origin: connect-src should name the branded intake URL and any intentional analytics hosts — nothing broader.

Cost and ops notes

CloudFront plus WAF is not free. For a low-traffic form, treat it as a fixed control cost you turn on when the endpoint is public and worth defending — not as an afterthought once bots find the Function URL. Log WAF samples and CloudFront access logs to the same place you already watch for abuse.

Rate-based rules have AWS-side minimums; do not invent thresholds from memory — read the WAF documentation when you tune. Prefer starting with managed rule groups you understand, then add a conservative rate limit.

Checklist

Further reading: AWS documentation on Lambda Function URLs, AWS WAF scopes, and CloudFront custom origins. Related note: branded API hostnames and CSP.

Engineering commentary only — not audit, legal, or certification advice.