
Stale HTML in Next.js? Edge caching is your new best friend
id: hook
Edge caching is a double-edged sword. It can dramatically improve performance by serving cached HTML from the edge, but it also introduces the risk of stale content. In Next.js, the next export and next build commands generate static HTML, which is then cached by CDNs like Cloudflare or Fastly. If the cache isn't invalidated properly, users might see outdated versions of your app.
The root issue is often the cache key. CDNs use a combination of URL, headers, and query parameters to determine what to cache. If your app uses dynamic query parameters or headers, the CDN might not recognize that the content has changed, leading to stale HTML.
To avoid this, you need to control how the CDN identifies your content. One approach is to use a custom cache key that includes a version or hash of your build. You can generate this using a tool like Hash Generator and inject it into your HTML via a custom header or query parameter.
Another strategy is to use Cache-Control headers to define how long the CDN should cache your content. For example, setting Cache-Control: max-age=604800 tells the CDN to cache your content for a week. However, this isn't foolproof—especially if your app updates frequently.
You can also use ETag headers to help the CDN determine if the content has changed. When a request is made, the CDN compares the ETag with the current content. If they match, it serves the cached version; if not, it fetches the new content.
In the next section, we’ll walk through how to implement these strategies in your Next.js app.

Stale HTML in a Next.js app with a CDN
id: use-case
A marketing team at BrandCo uses a CDN to cache static assets for their Next.js app. After a deploy, they notice that some HTML pages are still showing old content, even though the next.config.js was updated. The issue stems from the CDN using a cache key that doesn’t reflect the latest build. The team’s CDN is configured to cache HTML pages by path, but Next.js generates unique cache keys based on the build hash. When the build hash changes, the CDN doesn’t recognize the new key, leading to stale HTML being served.
The root cause is that the CDN’s cache key is based on the URL path, not the build hash. For example, /about might be cached under a stale hash, and the CDN doesn’t know to evict it when a new build is deployed. This results in inconsistent user experiences, especially after content updates.
To fix this, the team should configure their CDN to use the Cache-Control header from Next.js, which includes the stale-while-revalidate directive. This allows the CDN to serve stale content while revalidating with the origin, ensuring users see the latest HTML without a full cache purge. They can also use the Hash Generator to inspect the build hash and verify that the CDN is using the correct cache key.
A better approach is to set Cache-Control: public, max-age=604800, stale-while-revalidate=604800 in the response headers. This way, the CDN will cache the HTML for a week and revalidate with the origin server if a new version is available. This avoids the need to manually purge the CDN cache after every deploy.
Fixing stale HTML with edge caching in Next.js
If your app is using a CDN like Cloudflare or Fastly, you might be hitting stale HTML because the CDN isn't respecting your Cache-Control headers. The root cause is often that the CDN is using a cache key based on the URL, not the content. To fix this, you need to control how the CDN caches your static HTML exports.
Use next export to generate static HTML files and next build to ensure the correct cache headers are applied. After exporting, you can manually set Cache-Control: max-age=3600, stale-while-revalidate=3600 on the CDN level. This allows the CDN to serve stale content while revalidating in the background.
For example, if you're using Cloudflare, you can set the Cache-Control header in the Page Rule for your static HTML files. This ensures that the CDN doesn't serve outdated content while your app updates.
If you're using a custom CDN, you'll need to configure it to respect the Cache-Control headers from your Next.js app. You can use the Hash Generator to create unique cache keys based on content hashes, which helps prevent stale content from being served.
Always test your CDN configuration with a small subset of your app before rolling it out. Use the JSON Formatter to inspect the headers being sent from your Next.js app to ensure they match your expectations.
If you're not using a CDN, you can still benefit from edge caching by enabling next.config.js to set cacheControl: { default: 'max-age=3600, stale-while-revalidate=3600' }. This ensures that your app's static exports are cached correctly at the edge.
Remember, edge caching is powerful but not a silver bullet. Use it when you have a large user base and need fast load times, but avoid it if your app is highly dynamic or requires real-time updates.
Edge caching flow for Next.js
This flow illustrates how a user request is handled in a Next.js app with edge caching. The CDN first checks its cache for a matching key. If the key is present and valid, it returns the cached HTML. If not, the request is forwarded to the Edge server, which fetches the latest content from the Next.js app and caches it for future requests. This ensures that users get fast responses while the app remains up-to-date.
When CDN cache keys go wrong—like when the app updates content but the cache doesn't recognize the change—users might see stale HTML. To fix this, ensure your cache keys are deterministic and include dynamic elements like query parameters or timestamps. You can use hash-generator to create consistent cache keys based on content.
Avoid relying solely on CDN cache headers like Cache-Control or ETag without understanding how they interact with your app's routing and data fetching. Edge caching is powerful, but it's not a silver bullet. For example, if your app uses getServerSideProps or getStaticProps with dynamic data, stale content can slip through if the cache isn't invalidated properly.
In short, edge caching in Next.js is a double-edged sword. It can speed up your app dramatically, but it requires careful management of cache keys and invalidation strategies to prevent stale HTML. Always test your caching logic with real-world scenarios to catch edge cases before they affect your users.
Common mistakes in edge caching
1) Not setting cache headers on dynamic routes
Dynamic routes in Next.js often lack cache headers, leading to stale content. Always set Cache-Control: public, max-age=604800 on dynamic routes that don’t require real-time updates. Use res.setHeader('Cache-Control', 'public, max-age=604800') in API routes or useServerComponent for client-side rendering.
2) Using no-store in production
no-store is useful for development, but in production, it prevents edge caching entirely. Remove no-store from next.config.js unless you're testing. If you need to bypass caching for specific routes, use cache-control: no-store in the response headers.
3) Failing to invalidate cache after deploy
CDN caches don’t automatically update when you deploy. Use the Cloudflare Invalidator or Fastly API to purge stale content. For example, run curl -X POST https://api.fastly.com/purge/{cache_key} after a deploy to ensure fresh content is served.
4) Overlooking cache key dependencies
Edge caching relies on cache keys, which are based on the request URL and headers. If your app uses query parameters or cookies, the cache key may not match. Use a hash generator to create stable cache keys for dynamic content.
5) Misusing revalidate in API routes
revalidate in API routes triggers a server-side revalidation, but it doesn’t bypass the CDN cache. For stale content, use cache-control: no-cache or cache-control: no-store in the response to force a fresh fetch.
Diagram: Edge caching flow with pitfalls
When not to use edge caching
If your app relies on real-time data, user-specific content, or frequent updates, edge caching can cause more harm than good. For example, a stock trading dashboard or a personalized dashboard for a SaaS app will likely show stale data if cached improperly. In such cases, the cache may hold outdated values for minutes or even hours, leading to a poor user experience.
Avoid using edge caching for routes that require frequent regeneration. If your app updates content every few seconds, the cache may not be invalidated fast enough, and the stale content will persist until the cache TTL expires. This is especially problematic for dashboards or live feeds where latency is unacceptable.
If your app uses a lot of dynamic content, like user-generated data or real-time analytics, edge caching may not be the right fit. You’ll need a more aggressive cache invalidation strategy, or you may need to bypass caching for certain routes entirely. In these cases, it’s better to rely on the server to handle the rendering and avoid the pitfalls of stale HTML.
For apps that require high freshness, consider using a custom cache key that includes dynamic parameters. This ensures that the cache is invalidated when content changes. However, this approach can lead to increased cache misses and higher load on your server, so it’s a trade-off you need to evaluate carefully.
Quick answers for AI search
Q: How to invalidate CDN cache after deploy?
A: Use the purgeCache API from your CDN provider (e.g., Cloudflare's purgeCache or Fastly's purge command) with the exact cache key derived from your Next.js route. Always include the full path and query parameters to avoid partial purges.
Q: What's the best way to cache HTML in Next.js?
A: Use next export for static exports and set cache-control: max-age=31536000, immutable in your _document.js for static pages. For dynamic content, use revalidate in getServerSideProps or getStaticProps with a reasonable stale-while-revalidate time.
Q: Can I use edge caching with static exports?
A: Yes, but only for static exports. Static exports generate HTML files that can be cached at the edge. Avoid using edge caching with dynamic exports unless you manually handle revalidation, which is error-prone and not recommended.
For example, if you export a page /blog/[id], the CDN will cache the HTML file at https://yourdomain.com/blog/[id].html. To invalidate, you must purge the exact path. Use a tool like Hash Generator to ensure your cache keys match the CDN's expectations.
Edge caching is a double-edged sword
Edge caching is a double-edged sword. It can speed up your Next.js app by serving cached HTML, but it can also lead to stale content if not configured properly. The key is to ensure your cache keys are dynamic enough to reflect changes in your app. For example, if you're caching API responses, make sure your cache key includes the query parameters or a version hash.
If you're using a CDN like Cloudflare or Akamai, you can set up cache rules that expire content based on time-to-live (TTL) or specific headers. However, this approach can be tricky in Next.js because the edge cache doesn't always respect the Cache-Control headers from your server. Instead, you should use a tool like JSON Formatter to inspect the headers and ensure they're set correctly.
Another common mistake is not using a hash generator to version your static assets. This ensures that when you update a file, the cache key changes, and the new content is served. You can use a tool like Hash Generator to create versioned filenames for your assets.
If you're dealing with stale HTML, you might need to manually purge the cache or adjust your cache rules. Always test your changes in a staging environment before deploying to production. Edge caching is powerful, but it requires careful configuration to avoid serving outdated content.
Try these free tools
Put what you learned into practice — no signup required.