← Back to list

Host-Only vs Domain Cookies in CloudFront Authentication

What we learned while building the WordPress Static Site Guardian

László Tóth in AWS in Plain English · 2025-09-18 01:34 · 0 claps · 3.1 min read
#aws #cloudfront #signed-cookies #lambda-edge #kiro
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 📰 · Journalism & News

Protecting static WordPress sites with CloudFront signed cookies — host-only cookies keep each domain secure.

Protecting static WordPress sites with CloudFront signed cookies — host-only cookies keep each domain secure.

Host-Only vs Domain Cookies in CloudFront Authentication

What we learned while building the WordPress Static Site Guardian

When we first built the WordPress Static Site Guardian, our stack looked straightforward:

  • API Gateway + Lambda on api.<domain> to issue CloudFront signed cookies
  • Static site + protected resources served from CloudFront on <domain>

At first glance, this worked — until we ran into cookie domain scoping issues.

The Problem: Domain-Wide Cookies Collide

When the /issue-cookie Lambda sits on api.<domain>, the response often needs to include:

Set-Cookie: CloudFront-Key-Pair-Id=...; Domain=<domain>; ...

Why? Because a host-only cookie (no Domain, scoped only to the issuing host) won’t be sent with requests to <domain> if it was issued on api.<domain>. But once you add Domain=<domain>, the cookie automatically applies to all subdomains.

That’s fine if you only ever have one site. But if you deploy the same template to both the main domain and a subdomain — each with different CloudFront key pairs and policies — the browser can attach multiple cookie triples to a single request. Suddenly, authentication breaks in subtle, hard-to-debug ways.

The Demo That Broke: wpsuite.io vs kirodev.wpsuite.io

To illustrate, here’s what actually happened during testing:

  • Main site: wpsuite.io – Cognito User Pool in us-east-1
  • Demo site: kirodev.wpsuite.io – separate User Pool in us-west-1

Each site had its own CloudFront distribution, key pair, and policy. Each also had its own signing API:

  • api.wpsuite.io for wpsuite.io → issued cookies with Domain=wpsuite.io
  • api.kirodev.wpsuite.io for kirodev.wpsuite.io → issued cookies with Domain=kirodev.wpsuite.io

Here’s where domain scoping hit us:

  • The browser correctly sent the kirodev-scoped cookie triple to kirodev.wpsuite.io.
  • Additionally, because Domain=wpsuite.io matches all subdomains, the wpsuite-scoped cookie triple also rode along to kirodev.wpsuite.io.
  • When we visited kirodev.wpsuite.io/blog or /profile, the CloudFront viewer script assumed we were authenticated — it detected CloudFront cookies.
  • But the CloudFront behind kirodev.wpsuite.io rejected the request with 403, because the wrong policy and key-pair ID (from the wpsuite.io cookies) were present alongside the expected ones.

Result: the viewer script didn’t redirect us to /sign-in, and protected resources failed with 403 due to conflicting cookie triples sharing the same cookie names.

Why Host-Only Cookies Matter

  • Isolation — Each subdomain can enforce its own policy without interference from broader-scoped cookies.
  • Security — Host-only cookies reduce cross-site leakage across sibling subdomains.
  • Correctness — CloudFront receives exactly one authoritative cookie triple per request.

Our Pivot: Lambda@Edge for Same-Domain Issuance

The fix was architectural. Instead of issuing cookies from api.<domain>, we moved to Lambda@Edge behind a CloudFront behavior (/issue-cookie*) directly on the same <domain> that serves the protected content.

This change means:

  • The Set-Cookie header can omit the Domain attribute.
  • Cookies become host-only, scoped to exactly the domain that serves the content.
  • Subdomains issuing their own cookies no longer collide.
  • The viewer-request Lambda@Edge now validates the Cognito JWT token directly instead of relying on IAM-protected endpoints, reducing moving parts and keeping authentication fully browser-to-Cognito. ✅

Bonus: fewer moving parts — no API Gateway endpoint to maintain — and cookie issuance happens at the edge on the same domain as the content.

Lessons Learned (and How Kiro Helped)

  • Domain scoping is not a footnote — it can make or break your CloudFront auth model.
  • Testing across multiple subdomains is essential; browsers happily send every matching cookie.
  • Thanks to Kiro, we quickly regenerated the CloudFormation spec, pivoted the design, and re-tested end-to-end.
  • The new Lambda@Edge approach is leaner, safer, and globally performant.

Takeaway for AWS Developers

If you’re protecting your CloudFront resources with signed cookies:

  • Prefer host-only cookies (no Domain attribute).
  • Issue cookies from the same domain that serves the protected resources (e.g., via Lambda@Edge on <domain>/issue-cookie*).
  • Always test with multiple subdomains — the browser’s cookie merging behavior can surprise you.

The latest version of WordPress Static Site Guardian bakes this in, so you can deploy with confidence.

Links:

A message from our Founder

Hey, Sunil here. I wanted to take a moment to thank you for reading until the end and for being a part of this community.

Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don’t receive any funding, we do this to support the community. ❤️

If you want to show some love, please take a moment to follow me on LinkedIn, TikTok, **Instagram. You can also subscribe to our [weekly newsletter](https://newsletter.plainenglish.io/)**.

And before you go, don’t forget to clap and follow the writer️!


메타데이터
post_id
1d5f2dfdb7a0
slug
host-only-vs-domain-cookies-in-cloudfront-authentication-1d5f2dfdb7a0
url
https://aws.plainenglish.io/host-only-vs-domain-cookies-in-cloudfront-authentication-1d5f2dfdb7a0
canonical_url
https://aws.plainenglish.io/host-only-vs-domain-cookies-in-cloudfront-authentication-1d5f2dfdb7a0
author_url
https://medium.com/@tothl74
status
ok
fetched_at
2026-06-16 19:09:56