CDN troubleshooting
Diagnose caching, redirects, compression, HTTPS, and routing issues on Easel CDN.
Use this guide when a request is not being cached, stale content remains visible, compression is missing, HTTPS is failing, or the response does not follow the expected route.
Start by inspecting the response headers:
Useful headers include:
Age may also appear on some responses. Treat X-Easel-Cache as the
authoritative cache-result header. Absence of Age is not an error.
Run the same request more than once when testing cache behavior:
A cacheable response commonly returns MISS on the first request and HIT on a
later request.
Identify the request path
The fastest way to diagnose a CDN issue is to determine how Easel handled the request.
| Observation | Likely path |
|---|---|
| Firewall or challenge response | Security rules completed the request |
| Redirect response | A routing or application redirect matched |
X-Easel-Cache: HIT | A fresh cached response was served |
X-Easel-Cache: STALE | A stale cached response was served while refreshing in the background |
X-Easel-Cache: MISS | Easel retrieved or generated a new response |
X-Easel-Cache: BYPASS | The request or response was not cacheable |
| No function log | The request may have been cached, static, redirected, or blocked |
| Function log present | Application compute handled the request |
Use X-Easel-Id to correlate the client response with Easel logs and
traces.
The response is not cached
X-Easel-Cache is BYPASS
BYPASS means Easel did not attempt to store or reuse the response.
Check for these common causes:
- The request method is not
GETorHEAD. - The request contains
Authorization. - The request contains
Range. - The response contains
Set-Cookie. - The response is marked
private. - The response contains
no-storeorno-cache. - The response does not include a positive shared-cache lifetime.
- The response status is not cacheable.
Inspect the relevant headers:
A basic cacheable policy looks like:
If the response includes:
Easel bypasses shared caching.
Separate cookie-setting behavior from cacheable content when possible.
X-Easel-Cache is always MISS
A repeated MISS means the request is eligible for cache processing, but Easel
is not finding a reusable matching entry.
Check that repeated requests use the same:
- hostname
- path
- query string
- deployment
- request method
- supported
Varyvalues
These URLs may create different cache entries:
Also check whether:
- the TTL is too short
- the response changes its
Varyheader - a deployment occurs between requests
- the route is being purged repeatedly
- the framework marks the route as dynamic
- the response exceeds a platform cache limit
The second request still returns MISS
Wait long enough for the first response to complete, then repeat the exact request.
Use:
Confirm that the first response actually includes a positive CDN lifetime.
For example:
A browser extension, authentication layer, cookie, or upstream proxy may also alter otherwise identical requests.
The response has Cache-Control, but is not cached
Easel uses cache headers in this order:
Vercel-CDN-Cache-ControlCDN-Cache-ControlCache-Control
A higher-priority header may override the policy you are inspecting.
For example:
The response is not cached because the Vercel-compatible header takes precedence.
Inspect all three headers together.
Cached content is stale
Content remains old after a purge
Check that:
- the purge targeted the correct project
- the correct environment was selected
- the tag or URL exactly matches the cached entry
- the browser is not serving its own cached copy
- the underlying application data was updated before the purge
- another cache layer still contains old data
- the route regenerated successfully after invalidation
A CDN purge cannot remove a response already stored in the visitor’s browser.
Compare:
The browser may reuse its copy for one hour even after Easel’s shared cache is purged.
For content that must reflect CDN invalidation quickly, use:
A page regenerates with old data
The CDN response may have been invalidated while the application continued using stale Runtime Cache or framework data-cache entries.
Use a shared cache tag across:
- the rendered HTTP response
- Runtime Cache values
- framework data-cache entries
- related routes
Alternatively, invalidate each layer explicitly.
Update the source of truth before initiating invalidation:
Purging before the update completes can cause the route to regenerate using old data.
A purge affects less content than expected
A URL purge targets one cache key. It does not automatically invalidate related URLs.
Purging:
does not necessarily purge:
Use a shared tag such as:
when one data change affects multiple representations.
Browser caching differs from CDN caching
The browser and Easel can use different cache lifetimes.
This means:
- the browser may reuse its copy for 60 seconds
- Easel may reuse its copy for one hour
When debugging, test with curl to avoid relying on the browser’s local cache.
You can also disable the browser cache temporarily in browser developer tools.
Do not use a cache-busting query parameter as the first diagnostic step. A changed query string may create a different CDN cache key and hide the original problem.
A static file returns 404
Check that:
- the file exists in the framework’s public or output directory
- the filename uses the correct letter casing
- the build includes the file
- the route does not conflict with a rewrite or function
- the configured root directory is correct
- the expected deployment is receiving the request
Production filesystems are case-sensitive.
For example:
may refer to different files.
Inspect the build output and preview deployment before promoting the change to production.
An old static asset is still being served
Confirm that the page references the current asset URL.
Content-hashed files should receive a new filename when their contents change:
If HTML still references the old asset, the problem may be stale HTML rather than stale asset storage.
Check:
- the HTML cache policy
- deployment promotion status
- service-worker caches
- browser caches
- hardcoded asset URLs
- upstream proxies
A service worker can continue serving old files independently of Easel’s CDN cache.
A redirect is unexpected
Inspect the response without following redirects:
Look for:
Possible redirect sources include:
- HTTP-to-HTTPS enforcement
- project routing rules
- framework configuration
- middleware
- application code
- canonical-domain redirects
- an upstream proxy
Follow the complete redirect chain with:
Review each Location header to identify where the chain begins.
The site is stuck in a redirect loop
Redirect loops commonly happen when two layers independently enforce the same rule.
Check for combinations such as:
- Easel and an upstream proxy both forcing HTTPS
- apex and
wwwredirects pointing at each other - middleware redirecting back to its own matcher
- application code disagreeing with forwarded protocol headers
- locale redirects repeatedly adding or removing a path segment
Use:
Inspect each hop.
When a proxy sits in front of Easel, ensure that it connects to Easel over HTTPS and preserves the original protocol correctly.
A firewall blocks a valid request
Use the returned request ID and any firewall diagnostic headers to inspect the matching security event.
Possible causes include:
- a custom WAF rule
- managed-rule detection
- an IP or network restriction
- rate limiting
- Attack Mode
- bot or abuse detection
- an upstream proxy causing many visitors to share one apparent IP
Reproduce the request with the same:
- URL
- method
- headers
- body
- authentication state
Do not disable broad security protections permanently to resolve one false positive. Narrow the matching rule or add a scoped exception.
See Security.
A function was not invoked
The request may have completed before reaching application compute.
Common causes include:
- a fresh CDN cache hit
- a static deployment asset
- a redirect
- a firewall block
- a challenge response
- middleware returning a response
- the request reaching a different route than expected
Check:
Then inspect project request logs rather than only function logs.
A missing function invocation does not necessarily indicate that Easel dropped the request.
A function runs when a cache hit was expected
Check that:
- the response includes a positive CDN TTL
- the route does not set cookies
- the request does not include authentication
- query strings are identical
Varyvalues are stable- no purge occurs between requests
- the framework has not opted the route into dynamic rendering
- middleware does not alter the cache key or response
- the response status is cacheable
For Next.js, request-time APIs such as cookies or headers can make a route dynamic depending on the framework version and route configuration.
See the relevant framework guide.
Compression is missing
Inspect the request and response:
Look for:
Compression may not be applied when:
- the client does not advertise Brotli or gzip
- the response type is not compressible
- the response is below the minimum size
- the response is already encoded
- compression would not reduce the response size
- the request is a range request
- the response does not include a recognized
Content-Type - streaming behavior prevents buffering or transformation
Compare the compressed and uncompressed response sizes:
See Compression.
HTTPS is not active
Check the custom-domain status in Easel.
Common causes include:
- missing DNS records
- conflicting DNS records
- DNS changes that have not propagated
- a restrictive CAA record
- a proxied DNS record
- DNSSEC errors
- the domain pointing to another project
- certificate issuance rate limits
Verify DNS independently:
Use the exact DNS values displayed in Easel.
Do not repeatedly remove and re-add the domain while DNS is still propagating.
The browser shows a certificate warning
Confirm that:
- the exact hostname is configured
- certificate provisioning is complete
- DNS points to the correct Easel project
- an upstream proxy is not serving another certificate
- the device clock is correct
- local security software is not intercepting TLS
Inspect the certificate:
A certificate for example.com does not automatically cover every subdomain.
See HTTPS and TLS.
HTTP does not redirect to HTTPS
Check:
The response should redirect to the HTTPS URL.
If it does not, confirm that:
- the hostname is active
- the request reaches Easel
- an upstream proxy is not intercepting HTTP
- no conflicting routing rule handles the request first
A custom response header is missing
The header may have been:
- overwritten by a higher-priority routing rule
- removed by framework behavior
- replaced by Easel because it uses a reserved name
- treated as hop-by-hop
- removed by an upstream proxy
- rejected because of a size limit
- added only to a route that did not handle the request
Avoid custom headers in the reserved X-Easel-* namespace.
Use:
instead of:
Inspect the complete redirect chain because headers on an intermediate response do not necessarily appear on the final destination.
A request metadata header is missing
Some headers are available only on deployed requests and do not appear during local development.
Also check whether:
- the route reached application compute
- the framework exposes the original request headers
- an upstream proxy changed the request
- the header is supported for that route type
- the documented header name is correct
Do not depend on undocumented internal headers.
See Request and response headers.
Client IP or country appears incorrect
IP geolocation is approximate.
Unexpected values may also occur when:
- a VPN is in use
- a corporate gateway sends the request
- a mobile carrier proxies traffic
- another CDN or reverse proxy sits in front of Easel
- the application reads an untrusted forwarded header
- the IP database has not yet reflected a recent allocation
Use the documented trusted Easel header rather than arbitrary client-supplied
X-Forwarded-For values.
Do not use IP geolocation as the only signal for high-impact access or compliance decisions.
Preview and production behave differently
Check for differences in:
- environment variables
- custom domains
- root directories
- build commands
- framework versions
- routing rules
- firewall policies
- function placement relative to databases
- cache state
- preview access protection
- database branches or external services
Preview and production deployments have separate URLs and deployment contexts. A warm production cache does not imply that the preview cache is also warm.
Reproduce the issue using the preview deployment’s exact hostname.
A change works locally but not after deployment
Production builds can expose differences involving:
- case-sensitive paths
- missing environment variables
- build-time versus runtime variables
- unsupported filesystem assumptions
- native dependencies
- framework adapter output
- route generation
- static versus dynamic rendering
- Node.js versions
- external network access
Inspect the build logs first, then use the request ID to inspect runtime logs.
Do not assume that a local development server has the same caching, routing, or security behavior as a deployed preview.
An external rewrite fails
Check that:
- the destination URL uses the correct protocol
- the origin hostname resolves publicly
- the origin accepts requests from Easel
- the origin certificate is valid
- required authentication headers are present
- the rewrite preserves the required path and query string
- the origin is not redirecting back to the Easel URL
- an upstream firewall is not rejecting Easel traffic
Test the origin directly where appropriate:
Then test through Easel:
Compare the status, Location, cache, and request headers.
A response is unexpectedly personalized
A shared cached response should not contain visitor-specific data.
Immediately check for:
- missing
privateorno-store - authentication performed without cache bypass
- personalization based on cookies not represented in the cache key
- unsupported or incorrect
Vary - middleware adding user-specific content before caching
- application state leaking between requests
User-specific responses should generally return:
Do not rely on short TTLs to protect private content.
Purge the affected content and correct the cache policy before restoring the route.
A response is unexpectedly slow
Determine whether the request is:
- a cache hit
- a cache miss
- a bypass
- a static asset
- a dynamic function
- an external-origin request
Start with:
A slow cache hit points toward network conditions, response size, or connection setup.
A slow miss may involve:
- function startup
- application execution
- database latency
- external API latency
- distance between the CDN edge and US East compute
- response streaming
- cache regeneration
Use Easel traces to separate platform time from application and downstream-service time.
Collecting information for support
Include the following when reporting a CDN issue:
- project name or ID
- deployment ID
- affected hostname
- complete URL
- approximate request time and timezone
- request method
- response status
X-Easel-IdX-Easel-Cache- relevant cache-control headers
- whether the issue affects preview, production, or both
- reproduction steps
- whether an upstream proxy is present
Do not send:
- session cookies
- authorization tokens
- private API keys
- password-reset links
- unredacted personal data
A reproducible request is especially useful:
Include safe request headers only when they are required to reproduce the behavior.