Architecture · Protected static delivery

Secure Static WordPress with CloudFront Signed Cookies

Use Cognito to prove identity, a signer to issue time-limited CloudFront access, and CloudFront to enforce protected static paths without restoring WordPress PHP sessions.

Visitor → CloudFront protected path
   ↓ no valid cookie
Gatey → Amazon Cognito
   ↓ accepted identity
Signer service → signed cookies
   ↓
CloudFront → private S3 object

Trust boundaries

Identity, static-file access and API authorization are different controls

A Cognito token proves an identity. A CloudFront signed cookie controls retrieval of selected static objects. Protected APIs still need their own JWT, IAM or service-side authorization. Keeping these artifacts separate avoids turning one credential into a universal permission.

Identity: browser → Cognito → tokens
Static access: accepted identity → signer → CloudFront cookie → protected path
API action: browser → authorized API → backend validation

WordPress remains the CMS and does not enforce visitor access at request time.

Delivery boundary Private S3 objects should remain behind CloudFront. Hiding links, JavaScript gating or a successful login does not by itself protect a static object.

What the secure static stack provisions

The protection layer spans storage, edge delivery, signing, identity and abuse controls. This table shows what each component owns and which security boundary it enforces.

Resource / capabilityPurposeSecurity boundaryDesign note
S3 originStores exported static WordPress filesObjects should not be public when CloudFront is the delivery boundaryStatic Publisher can publish files; the protection stack controls access path.
CloudFront distributionServes public and protected paths at the edgeProtected behaviors require signed cookiesThe protected path list is an architectural parameter, not a theme setting.
CloudFront key group / public keyLets CloudFront validate signed-cookie policy signaturesOnly CloudFront sees the public key; the private key stays in the signer sideKey rotation needs a runbook.
Signer serviceIssues signed cookies after identity checksPrivate key in KMS/SSM or equivalent protected storageCan be API Gateway + Lambda or same-domain edge logic depending on cookie scoping needs.
Cognito/Gatey integrationAuthenticates the visitor before cookies are issuedIdentity tokens remain separate from CloudFront cookiesThe login page can be part of the static site.
Route/DNS/certificate optionsMap the public site and optional API/signer domainTLS and hostnames define cookie behaviorDomain strategy matters as much as Lambda code.
WAF / rate controlsReduce abuse of public and signer routesEdge-level filtering before runtime executionEspecially useful when protected paths and signer endpoints are public-facing.

Signed cookies versus JWTs

These credentials prove different things. The comparison below keeps identity tokens, temporary AWS credentials, CloudFront access cookies and WordPress sessions from being treated as interchangeable authorization artifacts.

ArtifactIssued byValidated byBest used for
Cognito ID/access tokenAmazon Cognito after authenticationFrontend code, API Gateway authorizer, backend LambdaProving who the user is and what identity-related scopes or claims they have.
IAM credentialsCognito Identity Pool / STSAWS service authorizationCalling IAM-authorized APIs or services from a browser with temporary scoped credentials.
CloudFront signed cookieA trusted signer that owns the private keyCloudFront at the edgeAllowing or denying retrieval of static objects under selected path patterns.
WordPress login cookieWordPress/PHP runtimeWordPressAdmin or traditional dynamic WordPress sessions, not static edge authorization.

Path protection model

Path classes should be explicit before deployment. This table distinguishes public pages, protected static objects, account pages, APIs and higher-risk runtime endpoints so each surface receives the correct enforcement model.

Path classExampleEnforcementCommon mistake
Public content/, /about/, /blog/, assetsCloudFront cache and S3 origin access controlAccidentally placing private JSON, uploads or generated files under public prefixes.
Protected static content/members/*, /training/*, /client/*CloudFront signed cookies on matching behaviors/path patternsOnly hiding links while leaving static objects directly fetchable.
Sign-in and account pages/signin/, /profile/Gatey + Cognito browser flowTreating the login page itself as server-side WordPress state.
Protected APIs/api/* or configured API Gateway domainCognito authorizer, JWT scopes or IAMAssuming a CloudFront cookie proves authorization for API mutations.
AI or workflow endpoints/frontend/prompt, /forms/submitEndpoint-specific auth, WAF, reCAPTCHA and rate limitsReusing page-access logic for higher-risk runtime actions.

Failure modes that matter

The protection model is easiest to operate when common failure states are explicit. This table maps visible symptoms to likely causes and the boundary that should be inspected first.

Failure modeSymptomLikely causeFix direction
Protected route loops to sign-inUser logs in but keeps returning to sign-inCookie domain/path mismatch, signer not setting all required CloudFront cookies or browser rejecting attributesInspect Set-Cookie headers, host-only/domain settings and SameSite/Secure attributes.
Protected file is publicPrivate URL opens in incognito without loginS3 object public, CloudFront behavior not protected or direct origin URL exposedLock S3 public access, enforce CloudFront origin access and verify behavior path patterns.
403 after valid loginUser receives AccessDenied from CloudFrontExpired cookie, wrong key pair ID, invalid signature or policy path mismatchCheck cookie TTL, key group, signer private key and CloudFront policy resource pattern.
Works on one subdomain, fails on anotherCookies are not sent or wrong cookies are sentDomain cookie vs host-only cookie mismatch or collision across sitesMove toward same-domain issuance or isolate environments with distinct hosts and keys.
API succeeds without page access or vice versaUser can call API but not view static page, or view page but cannot call APISeparate auth layers configured differentlyTreat static access and API authorization as separate policies and document both.

Implementation path

Design protected paths before publishing them

The protected-content model should be explicit before the static artifact reaches production.

  1. Define public and protected path classes — Identify which URLs and assets remain public and which path patterns require authenticated signed-cookie access.
  2. Configure Cognito-backed identity — Use Gatey and the configured Cognito User Pool for browser login, MFA, profile or SSO requirements without making WordPress the frontend session authority.
  3. Deploy the signer and CloudFront policy — Keep signing key material on the trusted signer side, scope cookie policies to the required paths and use cookie TTLs appropriate to the protected content.
  4. Test access as a delivery problem — Verify anonymous denial, post-login access, expiry, direct S3 access, cookie-domain behavior and API authorization separately.

When signed-cookie protection fits

Good fit

Shared static content with authenticated access

  • A static WordPress site has member, client, documentation, training or portal paths that can be represented as shared static objects.
  • You want public pages and protected pages delivered by CloudFront rather than reintroducing PHP sessions.
  • Cognito already owns visitor identity or is the intended application identity boundary.

Choose another pattern

Use a different authorization model when

  • The page itself must be rendered differently for each user on the server.
  • Access must be revoked immediately per request and cannot rely on an appropriately short signed-cookie lifetime.
  • The site remains fully dynamic and ordinary WordPress role/session protection already meets the requirement.

Problem guides

Buyer problems this architecture supports

How do I add login to static WordPress without PHP sessions?

Use Add Login to Static WordPress Without Bringing PHP Sessions Back as the pain-first entry point. It explains the identity split; this architecture explains the separate CloudFront delivery control.

Should Cognito replace WordPress as the application identity layer?

See Use Amazon Cognito Instead of WordPress as the Application Identity Layer when login must extend beyond WordPress itself to APIs, static frontends or other application surfaces.

Can existing SAML or OIDC identity providers still be used?

See Connect WordPress to Existing SAML and OIDC Identity Providers Without Building Separate Login Flows for the federation use case. Cognito can remain the identity hub while the protected-static layer consumes the resulting authenticated state.

Does static login automatically authorize APIs?

No. Signed static access and protected API actions are separate boundaries. The backend must independently validate JWT, IAM or another supported authorization mechanism.

Start with the access problem

Add login without bringing the WordPress session layer back

Use the secure-static solution for the buyer problem, then use this architecture when selected files or paths also need CloudFront-enforced protection.