Product Decisions This Supports
- Feature work unblocked: strict, nonce-based CSP can now coexist with full-page caching. Previously this was a hard either/or — CSP nonce per response meant either no caching or broken inline scripts — so this removes a real architectural fork in the road for high-traffic public pages.
- Roadmap: de-risks the "security hardening / compliance" track (pen-test remediation, SOC 2 / ISO evidence, CSP rollout deadlines) without regressing the performance track. Both can now be scheduled in the same quarter instead of sequenced.
- Build vs buy: the alternative is bespoke nonce plumbing inside our own cache middleware — high-maintenance, easy to get subtly wrong, and invisible until a CSP violation hits production. This is a config-level integration and a version pin, not a rewrite; buy.
- Use cases it unlocks: marketing/landing pages, docs, blog, and public content that carry inline JSON-LD, analytics bootstraps, or Vite-injected module preloads, served from cache behind a CDN.
- Upgrade planning: the release declares an explicit compatibility contract with
laravel-security-headers 2.1+. That's useful as a concrete dependency constraint to pin in the hardening epic rather than discovering the interaction in staging.
When to Consider This Package
Adopt when all of these hold:
- You serve cacheable, largely anonymous
GET HTML and care about hit ratio/TTFB.
- You already ship, or have committed to, a nonce-based CSP built by a middleware that runs after the cache layer (e.g.
laravel-security-headers 2.1+).
- You're on a Laravel version where
Vite::cspNonce() is the nonce source. Apps without a CSP nonce are explicitly unaffected — zero behavior change, so a low-risk trial is cheap.
Look elsewhere or scope carefully when:
- CSP isn't on the roadmap — you get no benefit and carry an unused code path.
- Responses are personalized — per-user cache keys explode, and a per-user nonce defeats caching anyway.
- Security policy demands per-response nonces. Worth saying plainly: this works by freezing the nonce for the lifetime of a cached entry, so the nonce becomes observable and predictable to anyone who sees one cached response. That's a reasonable tradeoff for public, effectively-static pages; it's a weaker posture for authenticated surfaces with user-generated content. Get security sign-off and scope the middleware to public routes first.
- Deploy/invalidation hygiene is weak — markup and nonce are now coupled, so stale cache after a deploy means mismatched header and markup. Confirm cache busting on release.
- Edge workers already rewrite nonces, or a CDN handles CSP — you'd have two mechanisms fighting.
How to Pitch It (Stakeholders)
Executives: A low-risk upgrade that dissolves a tradeoff we'd otherwise have to make — keep our cache hit rate and meet the CSP/compliance deadline. No new infrastructure, no new vendor, no migration. Apps without a CSP nonce see no change at all, so it's a contained rollout.
Engineering: On a cache HIT, the cached page now retains the CSP nonce it was rendered with (from Vite::cspNonce()) and restores it, so a downstream CSP header middleware — laravel-security-headers 2.1+ — emits a header that matches the cached markup instead of blocking its own inline scripts. Requirements: laravel-security-headers 2.1+ (or an equivalent header builder), correct middleware ordering (header builder after the cache layer), and cache invalidation on deploy since markup and nonce are now coupled. Behavior change is additive and backward compatible.
Risk posture: Contained. The default path is a no-op for non-CSP apps; the meaningful risk is the nonce reuse inherent to caching, which we mitigate by scoping to anonymous, publicly cacheable routes and documenting the tradeoff for security review before extending to authenticated pages.