Web App Caching and Performance Configuration
This guide explains how to configure caching and performance settings for a self-hosted web app.
It is written for customers, implementation engineers, and CDN/infrastructure teams configuring production environments.
Use this guide to configure:
- Zapp caching settings
- Environment variables
- CDN / CloudFront cache behavior
- Redis-related runtime settings
- Feed timeout behavior
For full self-hosting infrastructure requirements, see Web Hosting Guidelines.
Configuration Areas
Caching and performance are configured in three places.
| Area | What it controls |
|---|---|
| Zapp | Feed rendering behavior, Feed Server Abort Timeout, component-level loading behavior |
| Environment variables | Runtime caching behavior, Redis behavior, user-group cache behavior |
| CDN / CloudFront | Cache keys, TTLs, route bypass rules, forwarded headers/cookies/query strings |
Use this rule:
Zapp controls how the app renders. Environment variables control runtime behavior. CDN controls which responses can be reused.
Recommended Starting Configuration
For most apps, start with this setup.
Zapp
Enable Feed Server Abort Timeout: true
Timeout: 1500 ms
Environment Variables
CONFIGS_TTL=300
CONFIGS_SWR=3600
CLIENT_LOADED_FEEDS_ABORT_TIMEOUT_MS=10000
Optional, depending on deployment:
REDIS_TLS=true
REDIS_USE_CLUSTER=true
NO_INDEX=true
Use CACHING_GROUP_COOKIE only when the server-rendered page changes for a small, fixed set of user groups, such as anonymous users, logged-in users, premium users, or non-premium users.
CACHING_GROUP_COOKIE=<access-token-claim-name>
CDN / CloudFront
For cacheable page routes:
Cache query strings: all
Cache cookies: none by default
Cache headers:
- CloudFront-Viewer-Country only if SSR differs by country
- CloudFront-Viewer-Time-Zone only if SSR differs by time zone
For user-specific, auth, payment, and player routes:
Disable caching
Forward required cookies
Forward Set-Cookie from origin to browser
Respect origin Cache-Control
Feature Configuration
1. Feed Server Abort Timeout
Feed Server Abort Timeout prevents slow server-side feed requests from blocking page rendering.
If a feed exceeds the configured timeout:
- The feed request is aborted on the server
- The page continues loading
- The feed is loaded from the client after the page loads
This is useful when one slow feed can delay the full SSR response.
When to Use It
Use Feed Server Abort Timeout when:
- A page contains multiple feeds
- Some feeds are slow or unreliable
- First page load is too slow
- SSR is important, but the page should not wait indefinitely for every feed
- A slow feed can be loaded after the page appears
Zapp Configuration
In the Caching plugin:
Enable Feed Server Abort Timeout: true
Timeout: 1500 ms
Recommended values:
| App behavior | Timeout |
|---|---|
| Default starting point | 1500 ms |
| Mostly stable VOD/catalog app | 1500 ms |
| News, sports, or frequently updated app | 1000–1500 ms |
| First paint is more important than SSR completeness | 1000 ms |
| No known performance issue | 2000–3000 ms |
Avoid:
100 ms, because it may abort too many feeds- Values below
500 ms, unless specifically advised 5000 ms, because it usually gives little performance benefit
2. Client-Loaded Feed Timeout
CLIENT_LOADED_FEEDS_ABORT_TIMEOUT_MS controls how long client-loaded feed requests can run before being aborted in the browser.
This applies to feeds that are loaded from the client, including feeds that load from the client after server-side loading was aborted.
When to Use It
Use this setting when:
- Client-loaded feeds sometimes hang
- A slow feed hurts the browser experience
- You want to show fallback or empty states instead of waiting too long
- The app uses client-side feed loading for dynamic or frequently updated content
Environment Variable
Recommended starting value:
CLIENT_LOADED_FEEDS_ABORT_TIMEOUT_MS=10000
This allows client-loaded feeds to run for up to 10 seconds.
Use a shorter value, such as 5000, when:
- Feeds are often slow
- The page has many feeds
- The UX should fail fast
Use a longer value only when:
- Feed providers are slow but usually return valid data
- The feed is important enough to wait for
- Users are expected to have slower network conditions
3. Configuration Cache TTL and SWR
Configuration data is cached to reduce origin work and improve response time.
Two environment variables control this behavior:
CONFIGS_TTL=300
CONFIGS_SWR=3600
CONFIGS_TTL
CONFIGS_TTL controls how long configuration data is considered fresh.
Example:
CONFIGS_TTL=300
This means config data can be treated as fresh for 300 seconds.
Use a shorter TTL when:
- Configuration changes must appear quickly
- The customer frequently updates layout, navigation, or content configuration
- You are testing in staging
Use a longer TTL when:
- Configuration changes are infrequent
- Traffic is high
- Reducing origin/cache refresh pressure is more important than immediate freshness
CONFIGS_SWR
CONFIGS_SWR controls how long stale configuration data can be reused while fresh data is revalidated.
Example:
CONFIGS_SWR=3600
This allows stale config to be served while the app refreshes config in the background.
Use a longer SWR window when:
- Availability is more important than immediate freshness
- The app should keep serving pages if config refresh is slow
- The configuration source may occasionally be unavailable
Use a shorter SWR window when:
- Stale configuration is unacceptable
- The customer frequently changes app structure or navigation
Recommended starting point:
CONFIGS_TTL=300
CONFIGS_SWR=3600
4. CDN Page Caching
The CDN is the main caching layer for HTML documents and static assets.
Most performance gains should come from CDN cache hits.
Default Rule
For cacheable page routes, use the smallest cache key that still returns correct content.
Include:
URL path
Query string
Country header, only if content differs by country
Time zone header, only if content differs by time zone
caching-group-cookie, only if SSR output differs by entitlement group
Do not include:
Full Cookie header
Session cookies
JWT cookies
User ID
Favorites state
Continue Watching state
Analytics cookies
Third-party cookies
CloudFront Default Page Cache Policy
Use this for most cacheable page routes.
Query strings in cache key: all
Cookies in cache key: none
Headers in cache key:
- none by default
- CloudFront-Viewer-Country only if SSR differs by country
- CloudFront-Viewer-Time-Zone only if SSR differs by time zone
Compression: enabled
Origin request policy:
Forward query strings: all
Forward cookies: none
Forward headers:
- CloudFront-Viewer-Country only if the app needs it
- CloudFront-Viewer-Time-Zone only if the app needs it
When to Use Short CDN TTL
Use a short CDN TTL for pages that change frequently but can tolerate brief staleness.
Examples:
- News pages
- Sports schedules
- Recently updated rails
- Live-event listing pages
CloudFront cache policy:
Minimum TTL: 0
Default TTL: short
Maximum TTL: short
Query strings in cache key: all
Cookies in cache key: none by default
Headers in cache key:
- CloudFront-Viewer-Country only if needed
- CloudFront-Viewer-Time-Zone only if needed
For highly dynamic content, prefer:
Cache page shell at CDN
Load fast-changing feeds from the client
Keep real-time APIs non-cacheable or short-lived
5. User-Group Cache Variation
Use user-group cache variation only when the SSR document changes by entitlement or login group.
Examples:
- Premium users see different SSR content than non-premium users
- Anonymous users see different SSR page structure than logged-in users
- Server-rendered metadata differs by subscription tier
- Premium shelves are rendered during SSR only for premium users
Do not use this for per-user personalization.
Environment Variable
Configure:
CACHING_GROUP_COOKIE=<access-token-claim-name>
Example:
CACHING_GROUP_COOKIE=subscription_tier
The access-token claim should produce low-cardinality values.
Good values:
anonymous
registered
premium
non-premium
vip
kids
Bad values:
user_id
email
session_id
jwt_id
device_id
If CACHING_GROUP_COOKIE is not configured:
- The app does not produce
caching-group-cookie - The CDN must not depend on it
CloudFront Configuration
Use this only for cacheable page routes where SSR differs by group.
Cache policy:
Query strings in cache key: all
Cookies in cache key:
- caching-group-cookie
Headers in cache key:
- CloudFront-Viewer-Country only if needed
- CloudFront-Viewer-Time-Zone only if needed
Origin request policy:
Forward query strings: all
Forward cookies:
- caching-group-cookie
Forward headers:
- CloudFront-Viewer-Country only if used
- CloudFront-Viewer-Time-Zone only if used
Never include:
Full Cookie header
Session cookie
JWT cookie
User ID
Rule of thumb:
Use
caching-group-cookiefor groups, not users.
6. Country and Time-Zone Variation
Use country or time-zone variation only when the SSR document changes based on country or time zone.
Country Variation
Use this when:
- Catalog differs by country
- Availability differs by country
- Regional rails differ by country
- Geo-blocking affects SSR output
CloudFront header:
CloudFront-Viewer-Country
Vercel header:
x-vercel-ip-country
CloudFront cache policy:
Headers in cache key:
- CloudFront-Viewer-Country
Origin request policy:
Forward headers:
- CloudFront-Viewer-Country
Do not vary by country if the page shell is identical and regional data loads from the client.
Time-Zone Variation
Use this when:
- Broadcast schedules differ by time zone
- Live sports schedules differ by time zone
- Time-windowed availability affects SSR output
- “Available today” content differs by local time
CloudFront header:
CloudFront-Viewer-Time-Zone
Vercel header:
x-vercel-ip-timezone
CloudFront cache policy:
Headers in cache key:
- CloudFront-Viewer-Time-Zone
Origin request policy:
Forward headers:
- CloudFront-Viewer-Time-Zone
Do not vary by time zone if schedule data loads from the client.
7. Non-Cacheable Routes
Some routes must never be cached by CDN because they are user-specific, state-changing, or security-sensitive.
CDN / CloudFront Configuration
For these routes:
Disable caching
Forward required cookies
Forward required query strings
Forward Set-Cookie from origin to browser
Respect origin Cache-Control
Allow required HTTP methods
At minimum, support:
GET
POST
OPTIONS
Routes to Bypass
Authentication and session:
/api/is-logged-in
/api/logout
/api/oauth
/login
/login/token
/logout
/oauth
/users/sign_in
/users/login
/users/password/edit
Payment and billing:
/payment
/billing
/api/payment-auth
/api/stripe-create-payment-session
/api/stripe-billing-portal
/successful-payment
/paypal-purchase
User-specific data:
/api/favorites
/api/favorite-action
/api/continue-watching
/api/client-feed
/api/preference-editor
/api/maybe-redirect
Player:
/player
Parental controls:
/lock
/parent-lock
8. Personalized Features
Features such as Continue Watching, Favorites, profile state, and personal recommendations should not define the page cache key.
Recommended setup:
Keep page/document routes shared and cacheable
Render personal widgets after page load
Fetch personal data from non-cacheable APIs
Do not vary CDN cache by user ID, session, JWT, favorites, or watch history
Use CACHING_GROUP_COOKIE only for low-cardinality entitlement groups, not personalization.
9. Redis Configuration
Redis is used for:
- Caching Zapp configuration JSONs
- Caching content feeds
- Storing user JWTs
Optional Redis variables:
REDIS_TLS=true
REDIS_USE_CLUSTER=true
Use REDIS_TLS=true when the Redis provider requires TLS.
Use REDIS_USE_CLUSTER=true when Redis Cluster is used.
Redis should be located in the same region as the origin server when possible.
Clearing Redis may log out users because JWTs are stored there.
10. Staging Configuration
For staging environments, configure:
NO_INDEX=true
Use this to prevent SEO bots from indexing staging deployments.
Staging should mirror production as closely as possible so caching and performance behavior can be tested before production release.
CloudFront Behavior Summary
Configure CloudFront with separate behaviors for different route types.
| Priority | Behavior | Example path pattern | Cache setup |
|---|---|---|---|
| 1 | Auth/session routes | /login*, /logout*, /oauth*, /users/* | Disabled caching |
| 2 | Payment routes | /payment*, /billing*, payment APIs | Disabled caching |
| 3 | User-specific APIs | /api/favorites*, /api/continue-watching* | Disabled caching |
| 4 | Player routes | /player* | Disabled caching |
| 5 | Static assets | /_next/static/*, /assets/*, *.js, *.css | Long-lived caching |
| 6 | Page/document routes | Default behavior | Custom page cache policy |
More specific behaviors should have higher priority than the default behavior.
Static Assets CloudFront Policy
Use this for JavaScript, CSS, images, fonts, and other static assets.
Minimum TTL: 0
Default TTL: 86400
Maximum TTL: 31536000
Query strings in cache key: all
Cookies in cache key: none
Headers in cache key: none, except normalized Accept-Encoding when compression is enabled
Compression: enabled
Do not forward cookies for static assets.
Testing Checklist
Before production release, verify:
Zapp
- Feed Server Abort Timeout is enabled when needed
- Timeout value matches the app behavior
- Slow or non-critical feeds are loaded from the client when appropriate
- Personalized widgets are not unnecessarily part of SSR output
Environment Variables
-
CONFIGS_TTLis configured -
CONFIGS_SWRis configured -
CLIENT_LOADED_FEEDS_ABORT_TIMEOUT_MSis configured -
CACHING_GROUP_COOKIEis configured only when SSR differs by user group -
CACHING_GROUP_COOKIEdoes not use user ID, email, session ID, or device ID -
REDIS_TLS=trueis set if Redis requires TLS -
REDIS_USE_CLUSTER=trueis set if Redis Cluster is used -
NO_INDEX=trueis set for staging
CDN / CloudFront
- Static assets have a dedicated cache behavior
- Cacheable page routes use a custom page cache policy
- Auth routes bypass cache
- Payment routes bypass cache
- User-specific API routes bypass cache
- Player routes bypass cache
- Query strings are included for page/document routes
- Full
Cookieheader is not included in the cache key -
caching-group-cookieis included only when SSR differs by user group - Session and JWT cookies are not included in the cache key
- Country header is included only when SSR differs by country
- Time-zone header is included only when SSR differs by time zone
-
Set-Cookiefrom origin reaches the browser on auth/session routes - Origin
Cache-Controlheaders are respected
Related Documentation
For full self-hosting CDN cache-key guidance, cookie-based variation, non-cacheable route handling, Redis configuration, server requirements, and infrastructure requirements, see Web Hosting Guidelines.