Skip to content

vercel provider hard-codes minimumCacheTTL: 300 with no module option to override #2271

Description

@JonathanXDR

The bug

The vercel provider writes a fixed minimumCacheTTL: 60 * 5 (300 seconds, 5 minutes) into the generated Vercel build output. Unlike the sibling fields domains, sizes, and formats, which read from moduleOptions or providerOptions.options, minimumCacheTTL is a literal. There is no image.providers.vercel.options.minimumCacheTTL or any module-level option to change it.

After nuxi build with the vercel preset, .vercel/output/config.json contains:

images.minimumCacheTTL = 300

The only user-side escape hatch is reaching past the module and overriding nitro.vercel.config.images.minimumCacheTTL directly, which bypasses the provider's defu merge for the other image fields and is a regression risk for any future change to the provider.

This matters because the Vercel image optimizer cache TTL is the floor for remote-image cache invalidation. A 5 minute floor is far too low for remote sources that change rarely (GitHub repo covers, music artwork, and similar), so every cache miss hits upstream and counts against the image transformation quota. Vercel recommends tuning this per project, and local images on the same platform default to 31 days.

To reproduce

https://stackblitz.com/github/JonathanXDR/repro-nuxt-image-vercel-minimum-cache-ttl

Expected behavior

@nuxt/image exposes a typed option such as image.providers.vercel.options.minimumCacheTTL (or a shared image.minimumCacheTTL) that flows through the same defu merge as domains, sizes, and formats, so consumers can raise the floor without reaching into nitro.vercel.config.

Additional context

Root cause is in the installed package, node_modules/@nuxt/image/dist/module.js (v2.0.0):

// module.js:113
vercel(providerOptions, moduleOptions, nuxt) {
  nuxt.options.nitro = defu(nuxt.options.nitro, {
    vercel: {
      config: {
        images: {
          domains: moduleOptions.domains,
          minimumCacheTTL: 60 * 5,                 // module.js:119, hard-coded
          sizes: Array.from(new Set(Object.values(moduleOptions.screens || {}))),
          formats: providerOptions.options?.formats ?? ["image/webp", "image/avif"]
        }
      }
    }
  });
}

The adjacent awsAmplify provider in the same file has the identical hard-coding at module.js:137 (minimumCacheTTL: 60 * 5), so a generalised fix would cover both providers.

User-side workaround that currently sticks but skips the module merge path:

// nuxt.config.ts
nitro: {
  vercel: {
    config: {
      images: {
        minimumCacheTTL: 60 * 60 * 24 * 28, // 28 days
      },
    },
  },
}
Operating System Darwin
Node Version v24.18.0
Nuxt Version 4.4.6
CLI Version 3.36.0
Nitro Version 2.13.4
Package Manager npm@11.17.0
Builder vite
User Config compatibilityDate, modules, nitro, image
Runtime Modules @nuxt/image@2.0.0
Build Modules -

Verified native-free with a clean npm install plus npm run build (the proof is a generated config value, no image serving or native sharp needed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions