Netlify CDN ignored query strings causing personalized content to show wrong versions and the cache-key tweak that fixed it

When you’re working with static site generators, performance optimization, and CDNs like Netlify, it’s easy to take for granted that what you see is what your users get. However, dynamic experiences sometimes depend on seemingly small factors—like query strings in URLs—that can profoundly impact what content users are served. Recently, a caching oversight involving Netlify’s CDN surfaced, where query strings were being ignored entirely, resulting in incorrect personalization and content mismatches for users. This is the story of that bug, how it was uncovered, and the cache-key tweak that restored sanity.

TL;DR

Personalized pages use query strings to determine how content should be rendered. Netlify’s CDN default behavior ignores these query strings when caching, meaning users received cached content that might not match their personalization data. Adding a custom cache key that includes query strings fixed this issue. This article explains what went wrong, how it was diagnosed, and how to prevent such issues in your own projects.

The Problem: Cache Misbehavior Due to Ignored Query Strings

One of the key benefits of using a CDN like Netlify’s is speed. By serving cached versions of your site’s pages from edge nodes around the world, the time it takes for users to see your content can be dramatically reduced. However, this efficiency can come with unexpected caveats.

Imagine you’re building a site that shows personalized recommendations for logged-in users. You’re using query parameters like ?user=premium or ?campaign=spring_sale to change what’s rendered on the page. Seems simple enough—until you find users are seeing completely irrelevant offers.

What went wrong? The short answer: Netlify’s CDN ignores query strings when creating cache keys by default. That means a request for /sale?campaign=spring and /sale?campaign=fall will both be served the same cached page, even though they should look different.

Real-World Impact

This subtle behavior manifested in surprising ways:

  • Campaign tracking links showed the wrong product recommendations.
  • Users thought their accounts weren’t being recognized because the personalized greeting didn’t load correctly.
  • Marketing A/B tests returned skewed results because content intended for group A was shown to group B.

The subtlety made the issue even harder to detect—it wasn’t obvious until someone noted that browsing in incognito mode produced different results than expected. After ruling out server errors and JavaScript bugs, attention turned to the CDN.

Understanding How Netlify Caches Page Content

Netlify uses a global Content Delivery Network (CDN) to serve content from cache at HTTP request level. Here’s the catch: by default, the CDN only considers the path component of a URL when it generates the cache key. This means that:

/page?session=a and /page?session=b → both resolve to /page

This behavior is helpful in most static contexts because it simplifies caching and reduces overhead. However, it becomes problematic when query parameters define how the content is rendered or what data is shown.

Signs of the Problem

  • Your console shows correct parameters passed to the browser, but the page seems “stuck.”
  • Personalized content appears correct only on the first load and wrong on refresh.
  • Content mismatches disappear when dev tools are used to disable cache.

In developer terms, the CDN was acting like a very optimistic assistant who, hearing “Don’t worry, it’s always the same page,” stopped listening for the rest of the sentence.

The Fix: Customizing the CDN Cache Key

The good news is Netlify provides a mechanism to customize how the caching works using the _headers file or Netlify Edge Functions. You can instruct Netlify to vary its caching behavior based on query strings using specific directives.

In our case, a small change made a big difference. We added “cache-control” and “vary” headers like so:


/*
  Cache-Control: public, max-age=0, must-revalidate
  Vary: query-string

Alternatively, for projects that use the newer, more flexible Netlify Edge Functions, you can manage caching at the function level:


export default async (request, context) => {
  const url = new URL(request.url);
  const cacheKey = `${url.pathname}?${url.search}`;

  context.cacheKey = cacheKey;

  // Your custom response here

  return new Response(yourContent, {
    headers: {
      "cache-control": "public, max-age=60",
    },
  });
};

This explicit handling ensures Netlify caches pages based on the full URL, including query strings. Once deployed, the incorrect cache hits disappeared almost overnight.

Bonus Tip: Testing CDN Behavior

If you’re unsure how your cache is behaving, you can inspect response headers from Netlify. Look for:

  • x-nf-cache-status: Shows whether the response came from cache (HIT) or not (MISS).
  • cache-control: Tells the browser and CDN how long to serve from cache.
  • vary: Indicates which parts of the request should influence the cached version.

These headers can reveal a lot about why you’re seeing unexpected behavior.

What Developers Should Keep in Mind

This incident served as a great reminder of how developer assumptions can conflict with platform defaults. Personalization is no longer a back-end luxury; in the modern JAMstack era, personalized experiences are often rendered statically but depend heavily on query parameters, cookies, or local storage.

Here are a few suggestions for avoiding similar traps:

  • Audit your caching strategy: Review all endpoints, especially those that depend on query strings.
  • Think like a CDN: If you were a robot serving cached pages, what signals would you need to distinguish one version from another?
  • Use staging environments to test edge cases, varying queries and inspecting headers closely.
  • Ensure headers propagate properly: CDNs depend on cache headers, so test them thoroughly.

Benefits of the Fix

After deploying the cache-key fix, we observed the following improvements:

  • 90% drop in misrendered content-related bug reports
  • Increased conversion from campaign-specific links due to correct personalization
  • More reliable metrics in A/B testing efforts

Most importantly, user trust was restored. People saw the content tailored to their needs, not someone else’s digital leftovers.

Conclusion: Small Tweaks, Big Results

CDNs are powerful tools that significantly improve web performance, but they require careful configuration when personalization or query parameters are involved. Netlify’s default caching behavior—friendly but naive—can betray you when things get dynamic.

Fortunately, a small change to the caching logic made personalization accurate again without sacrificing performance. By adjusting the cache key to include query strings, we aligned the CDN’s behavior with the application’s expectations.

If you’re working with Netlify or any CDN, take time to understand your cache keys, headers, and when to bypass the default assumptions. Your users—and your metrics—will thank you.