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:
- Scope. CloudFront distributions use CLOUDFRONT-scoped web ACLs. Those ACLs are created and managed in us-east-1 even if your Lambda runs elsewhere.
- Origin. Point CloudFront at the Function URL as a custom HTTPS origin. Prefer restricting who can invoke the Function URL so casual callers cannot bypass the distribution.
- Hostname. Attach ACM (also in us-east-1 for CloudFront) and an alias such as intake.example.com. Your marketing CSP connect-src then lists that host — not a wildcard for Function URL domains.
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
- Do not AssociateWebACL on a Function URL ARN — plan for CloudFront first.
- Create the web ACL with CloudFront scope in us-east-1.
- Issue ACM in us-east-1 for the branded hostname.
- Pin marketing CSP connect-src to that hostname.
- Confirm the Function URL is not a useful bypass path for anonymous callers.
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.