Skip to content

Netlify preset: defineCachedEventHandler doesn't emit Netlify-Vary: query, causing query-variant cache collapse #4165

@peralta

Description

@peralta

Environment

nitro v2, using netlify-edge preset.
The issue should also happen with netlify functions.

Reproduction

I spotted the bug using @nuxt/icon but the issue is broader.

@nuxt/icon registers /api/_nuxt_icon/[collection].json using:

defineCachedEventHandler(handler, {
  getKey(event) {                                                                                                                                                                    
    const icons = String(getQuery(event).icons || "");
    return `${collection}_${icons.split(",")[0]}_${icons.length}_${hash(icons)}`;                                                                                                    
  },                                                                                                                                                                                 
  swr: true,                                                                                                                                                                         
  maxAge: 60 * 60 * 24 * 7,                                                                                                                                                          
});             

The handler returns different JSON depending on the ?icons= query param. Nitro emits:

  cache-control: s-maxage=604800, stale-while-revalidate                                                                                                                               

Netlify CDN caches this. The next request to the same path with a different ?icons= value gets the cached response from the first request — wrong icons.

Describe the bug

When using the Netlify preset, defineCachedEventHandler with a query-based getKey emits cache-control: s-maxage=..., stale-while-revalidate — which Netlify CDN honours by caching the response. But because no Netlify-Vary: query header is present, the CDN collapses all query string variants into a single cache entry, serving the first-cached response for every subsequent variant.

Nitro's server-side cache works correctly (it keys by the query-derived value). The CDN layer silently breaks it.

I managed to work around the issue by adding the netlify-vary header manually in Nuxt's routeRules:

  // nuxt.config.ts                                                                                                                                                                    
  routeRules: {   
    '/api/_nuxt_icon/*': {
      headers: { 'Netlify-Vary': 'query' },
    },
  }   

Additional context

No response

Logs

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions