diff --git a/packages/next/next-server/server/image-optimizer.ts b/packages/next/next-server/server/image-optimizer.ts index 101d6cc6a0d89..b5407c63d711d 100644 --- a/packages/next/next-server/server/image-optimizer.ts +++ b/packages/next/next-server/server/image-optimizer.ts @@ -163,7 +163,12 @@ export async function imageOptimizer( const contentType = getContentType(extension) const fsPath = join(hashDir, file) if (now < expireAt) { - res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate') + res.setHeader( + 'Cache-Control', + isStatic + ? 'public, max-age=315360000, immutable' + : 'public, max-age=0, must-revalidate' + ) if (sendEtagResponse(req, res, etag)) { return { finished: true } } diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index da29828598762..2a5d8ecdaab14 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -512,11 +512,21 @@ function runTests({ w, isDev, domains }) { q: 100, } const opts = { headers: { accept: 'image/webp' } } - const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) - expect(res.status).toBe(200) - expect(res.headers.get('cache-control')).toBe( + + const res1 = await fetchViaHTTP(appPort, '/_next/image', query, opts) + expect(res1.status).toBe(200) + expect(res1.headers.get('cache-control')).toBe( + 'public, max-age=315360000, immutable' + ) + await expectWidth(res1, w) + + // Ensure subsequent request also has immutable header + const res2 = await fetchViaHTTP(appPort, '/_next/image', query, opts) + expect(res2.status).toBe(200) + expect(res2.headers.get('cache-control')).toBe( 'public, max-age=315360000, immutable' ) + await expectWidth(res2, w) } })