diff --git a/packages/next/src/build/templates/app-page.ts b/packages/next/src/build/templates/app-page.ts index b95e44f1e703..260706d15c15 100644 --- a/packages/next/src/build/templates/app-page.ts +++ b/packages/next/src/build/templates/app-page.ts @@ -1389,12 +1389,7 @@ export async function handler( // In dev, we should not cache pages for any reason. if (routeModule.isDev) { - res.setHeader( - 'Cache-Control', - nextConfig.experimental.devCacheControlNoCache - ? 'no-cache, must-revalidate' - : 'no-store, must-revalidate' - ) + res.setHeader('Cache-Control', 'no-cache, must-revalidate') } if (!cacheEntry) { diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index cd49c0169995..6a314f107339 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -1803,12 +1803,7 @@ export default abstract class Server< // In dev, we should not cache pages for any reason. if (this.dev) { - res.setHeader( - 'Cache-Control', - this.nextConfig.experimental.devCacheControlNoCache - ? 'no-cache, must-revalidate' - : 'no-store, must-revalidate' - ) + res.setHeader('Cache-Control', 'no-cache, must-revalidate') cacheControl = undefined } diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index 57d06b2bddb1..191cee5c0e73 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -404,7 +404,6 @@ export const experimentalSchema = { hideLogsAfterAbort: z.boolean().optional(), runtimeServerDeploymentId: z.boolean().optional(), immutableAssetToken: z.string().optional(), - devCacheControlNoCache: z.boolean().optional(), deferredEntries: z.array(z.string()).optional(), onBeforeDeferredEntries: z.function().returns(z.promise(z.void())).optional(), reportSystemEnvInlining: z.enum(['warn', 'error']).optional(), diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 72d44c8407c3..d143119f2140 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -1039,18 +1039,6 @@ export interface ExperimentalConfig { */ immutableAssetToken?: string - /** - * Use 'no-cache' instead of 'no-store' in the Cache-Control header for development. - * This allows conditional requests to the server, which can help with development - * workflows that benefit from caching validation. - * - * When enabled, the Cache-Control header changes from 'no-store, must-revalidate' - * to 'no-cache, must-revalidate'. - * - * @default false - */ - devCacheControlNoCache?: boolean - /** * An array of paths in app or pages directories that should wait to be processed * until all other entries have been processed. This is useful for deferring @@ -1812,7 +1800,6 @@ export const defaultConfig = Object.freeze({ turbopackFileSystemCacheForBuild: false, turbopackInferModuleSideEffects: true, turbopackPluginRuntimeStrategy: 'childProcesses', - devCacheControlNoCache: false, }, htmlLimitedBots: undefined, bundlePagesRouterDependencies: false, @@ -1911,7 +1898,6 @@ export interface NextConfigRuntime { | 'testProxy' | 'runtimeServerDeploymentId' | 'maxPostponedStateSize' - | 'devCacheControlNoCache' | 'cachedNavigations' | 'partialFallbacks' | 'exposeTestingApiInProductionBuild' @@ -1982,7 +1968,6 @@ export function getNextConfigRuntime( testProxy: ex.testProxy, runtimeServerDeploymentId: ex.runtimeServerDeploymentId, maxPostponedStateSize: ex.maxPostponedStateSize, - devCacheControlNoCache: ex.devCacheControlNoCache, cachedNavigations: ex.cachedNavigations, partialFallbacks: ex.partialFallbacks, exposeTestingApiInProductionBuild: ex.exposeTestingApiInProductionBuild, diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 80f1fbe75756..f866b8f46323 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -507,12 +507,7 @@ export async function initialize(opts: { matchedOutput.type === 'nextStaticFolder' ) { if (opts.dev && !isNextFont(parsedUrl.pathname)) { - res.setHeader( - 'Cache-Control', - config.experimental.devCacheControlNoCache - ? 'no-cache, must-revalidate' - : 'no-store, must-revalidate' - ) + res.setHeader('Cache-Control', 'no-cache, must-revalidate') } else { res.setHeader( 'Cache-Control', diff --git a/packages/next/src/server/route-modules/pages/pages-handler.ts b/packages/next/src/server/route-modules/pages/pages-handler.ts index 37d66d8cf1ef..792d78eb02ad 100644 --- a/packages/next/src/server/route-modules/pages/pages-handler.ts +++ b/packages/next/src/server/route-modules/pages/pages-handler.ts @@ -706,12 +706,7 @@ export const getHandler = ({ // In dev, we should not cache pages for any reason. if (routeModule.isDev) { - res.setHeader( - 'Cache-Control', - nextConfig.experimental.devCacheControlNoCache - ? 'no-cache, must-revalidate' - : 'no-store, must-revalidate' - ) + res.setHeader('Cache-Control', 'no-cache, must-revalidate') } // Draft mode should never be cached diff --git a/test/development/dev-cache-control-no-cache-disabled/app/app-route/page.js b/test/development/dev-cache-control-no-cache-disabled/app/app-route/page.js deleted file mode 100644 index cabb5263b521..000000000000 --- a/test/development/dev-cache-control-no-cache-disabled/app/app-route/page.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function AppRoute() { - return
App Route
-} diff --git a/test/development/dev-cache-control-no-cache-disabled/app/layout.js b/test/development/dev-cache-control-no-cache-disabled/app/layout.js deleted file mode 100644 index 4ee00a218505..000000000000 --- a/test/development/dev-cache-control-no-cache-disabled/app/layout.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function RootLayout({ children }) { - return ( - - {children} - - ) -} diff --git a/test/development/dev-cache-control-no-cache-disabled/dev-cache-control-no-cache-disabled.test.ts b/test/development/dev-cache-control-no-cache-disabled/dev-cache-control-no-cache-disabled.test.ts deleted file mode 100644 index 5f1cf1f12fa7..000000000000 --- a/test/development/dev-cache-control-no-cache-disabled/dev-cache-control-no-cache-disabled.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { nextTestSetup } from 'e2e-utils' - -describe('experimental.devCacheControlNoCache disabled (default)', () => { - const { next } = nextTestSetup({ - files: __dirname, - }) - - it('should use no-store for pages router by default', async () => { - const res = await next.fetch('/pages-route') - expect(res.headers.get('Cache-Control')).toBe('no-store, must-revalidate') - }) - - it('should use no-store for app router by default', async () => { - const res = await next.fetch('/app-route') - expect(res.headers.get('Cache-Control')).toBe('no-store, must-revalidate') - }) -}) diff --git a/test/development/dev-cache-control-no-cache-disabled/next.config.js b/test/development/dev-cache-control-no-cache-disabled/next.config.js deleted file mode 100644 index 5a877d2dbfab..000000000000 --- a/test/development/dev-cache-control-no-cache-disabled/next.config.js +++ /dev/null @@ -1,2 +0,0 @@ -/** @type {import('next').NextConfig} */ -module.exports = {} diff --git a/test/development/dev-cache-control-no-cache-disabled/pages/pages-route.js b/test/development/dev-cache-control-no-cache-disabled/pages/pages-route.js deleted file mode 100644 index 80120c33615c..000000000000 --- a/test/development/dev-cache-control-no-cache-disabled/pages/pages-route.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function PagesRoute() { - return
Pages Route
-} diff --git a/test/development/dev-cache-control-no-cache/dev-cache-control-no-cache.test.ts b/test/development/dev-cache-control-no-cache/dev-cache-control-no-cache.test.ts index edbe93714b89..2a38178d4344 100644 --- a/test/development/dev-cache-control-no-cache/dev-cache-control-no-cache.test.ts +++ b/test/development/dev-cache-control-no-cache/dev-cache-control-no-cache.test.ts @@ -1,19 +1,17 @@ import { nextTestSetup } from 'e2e-utils' -describe('experimental.devCacheControlNoCache', () => { - describe('when enabled', () => { - const { next } = nextTestSetup({ - files: __dirname, - }) +describe('dev Cache-Control header', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) - it('should use no-cache instead of no-store for pages router', async () => { - const res = await next.fetch('/pages-route') - expect(res.headers.get('Cache-Control')).toBe('no-cache, must-revalidate') - }) + it('should use no-cache for pages router', async () => { + const res = await next.fetch('/pages-route') + expect(res.headers.get('Cache-Control')).toBe('no-cache, must-revalidate') + }) - it('should use no-cache instead of no-store for app router', async () => { - const res = await next.fetch('/app-route') - expect(res.headers.get('Cache-Control')).toBe('no-cache, must-revalidate') - }) + it('should use no-cache for app router', async () => { + const res = await next.fetch('/app-route') + expect(res.headers.get('Cache-Control')).toBe('no-cache, must-revalidate') }) }) diff --git a/test/development/dev-cache-control-no-cache/next.config.js b/test/development/dev-cache-control-no-cache/next.config.js index 0f939a1f9b51..5a877d2dbfab 100644 --- a/test/development/dev-cache-control-no-cache/next.config.js +++ b/test/development/dev-cache-control-no-cache/next.config.js @@ -1,6 +1,2 @@ /** @type {import('next').NextConfig} */ -module.exports = { - experimental: { - devCacheControlNoCache: true, - }, -} +module.exports = {} diff --git a/test/development/pages-dir/client-navigation/rendering.test.ts b/test/development/pages-dir/client-navigation/rendering.test.ts index 17c5a5255323..e4bb383f4f95 100644 --- a/test/development/pages-dir/client-navigation/rendering.test.ts +++ b/test/development/pages-dir/client-navigation/rendering.test.ts @@ -396,7 +396,7 @@ describe('Client Navigation rendering', () => { responses.forEach((res) => { try { expect(res.headers.get('Cache-Control')).toBe( - 'no-store, must-revalidate' + 'no-cache, must-revalidate' ) } catch (err) { err.message = res.url + ' ' + err.message diff --git a/test/e2e/app-dir/custom-cache-control/custom-cache-control.test.ts b/test/e2e/app-dir/custom-cache-control/custom-cache-control.test.ts index ffe2610ccd03..121ecc0207f0 100644 --- a/test/e2e/app-dir/custom-cache-control/custom-cache-control.test.ts +++ b/test/e2e/app-dir/custom-cache-control/custom-cache-control.test.ts @@ -15,14 +15,14 @@ describe('custom-cache-control', () => { it('should have custom cache-control for app-ssg prerendered', async () => { const res = await next.fetch('/app-ssg/first') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=30' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=30' ) }) it('should have custom cache-control for app-ssg lazy', async () => { const res = await next.fetch('/app-ssg/lazy') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=31' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=31' ) }) ;(process.env.__NEXT_CACHE_COMPONENTS ? it.skip : it)( @@ -32,7 +32,7 @@ describe('custom-cache-control', () => { // eslint-disable-next-line jest/no-standalone-expect expect(res.headers.get('cache-control')).toBe( isNextDev - ? 'no-store, must-revalidate' + ? 'no-cache, must-revalidate' : 's-maxage=120, stale-while-revalidate=31535880' ) } @@ -41,28 +41,28 @@ describe('custom-cache-control', () => { it('should have custom cache-control for app-ssr', async () => { const res = await next.fetch('/app-ssr') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=32' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=32' ) }) it('should have custom cache-control for auto static page', async () => { const res = await next.fetch('/pages-auto-static') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=33' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=33' ) }) it('should have custom cache-control for pages-ssg prerendered', async () => { const res = await next.fetch('/pages-ssg/first') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=34' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=34' ) }) it('should have custom cache-control for pages-ssg lazy', async () => { const res = await next.fetch('/pages-ssg/lazy') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=35' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=35' ) }) @@ -70,7 +70,7 @@ describe('custom-cache-control', () => { const res = await next.fetch('/pages-ssg/another') expect(res.headers.get('cache-control')).toBe( isNextDev - ? 'no-store, must-revalidate' + ? 'no-cache, must-revalidate' : 's-maxage=120, stale-while-revalidate=31535880' ) }) @@ -78,7 +78,7 @@ describe('custom-cache-control', () => { it('should have default cache-control for pages-ssr', async () => { const res = await next.fetch('/pages-ssr') expect(res.headers.get('cache-control')).toBe( - isNextDev ? 'no-store, must-revalidate' : 's-maxage=36' + isNextDev ? 'no-cache, must-revalidate' : 's-maxage=36' ) }) }) diff --git a/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts b/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts index ec10619bef1c..34e67c8b17a3 100644 --- a/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts +++ b/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts @@ -10,7 +10,9 @@ describe('fallback-prefetch', () => { const browser = await next.browser('/', { beforePageLoad: (page) => { page.on('response', (response) => { - if (!response.ok()) { + // 304 Not Modified is a valid revalidation response (from no-cache), + // not a network error + if (!response.ok() && response.status() !== 304) { hasNetworkError = true } }) diff --git a/test/e2e/app-dir/metadata/metadata.test.ts b/test/e2e/app-dir/metadata/metadata.test.ts index 97d9014a3122..17f0a6186913 100644 --- a/test/e2e/app-dir/metadata/metadata.test.ts +++ b/test/e2e/app-dir/metadata/metadata.test.ts @@ -691,7 +691,7 @@ describe('app dir - metadata', () => { expect(res.headers.get('content-type')).toBe('image/x-icon') expect(res.headers.get('cache-control')).toBe( isNextDev - ? 'no-store, must-revalidate' + ? 'no-cache, must-revalidate' : 'public, max-age=0, must-revalidate' ) }) @@ -706,14 +706,14 @@ describe('app dir - metadata', () => { expect(resAppleIcon.headers.get('content-type')).toBe('image/png') expect(resAppleIcon.headers.get('cache-control')).toBe( isNextDev - ? 'no-store, must-revalidate' + ? 'no-cache, must-revalidate' : 'public, max-age=0, must-revalidate' ) expect(resIcon.status).toBe(200) expect(resIcon.headers.get('content-type')).toBe('image/png') expect(resIcon.headers.get('cache-control')).toBe( isNextDev - ? 'no-store, must-revalidate' + ? 'no-cache, must-revalidate' : 'public, max-age=0, must-revalidate' ) }) diff --git a/test/e2e/app-dir/ppr-full/ppr-full.test.ts b/test/e2e/app-dir/ppr-full/ppr-full.test.ts index 7e76d4abab43..0d5d305d78ba 100644 --- a/test/e2e/app-dir/ppr-full/ppr-full.test.ts +++ b/test/e2e/app-dir/ppr-full/ppr-full.test.ts @@ -192,7 +192,7 @@ describe.skip('ppr-full', () => { if (isNextDeploy) { expect(cacheControl).toEqual('public, max-age=0, must-revalidate') } else if (isNextDev) { - expect(cacheControl).toEqual('no-store, must-revalidate') + expect(cacheControl).toEqual('no-cache, must-revalidate') } else if (dynamic === false || dynamic === 'force-static') { expect(cacheControl).toEqual( revalidate === undefined diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index c045f54cf4d3..1a280d6e3372 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -91,7 +91,7 @@ describe('Prerender', () => { } function isCachingHeader(cacheControl) { - return !cacheControl || !/no-store/.test(cacheControl) + return !cacheControl || !/no-store|no-cache/.test(cacheControl) } const allowHeader = [ diff --git a/test/rspack-dev-tests-manifest.json b/test/rspack-dev-tests-manifest.json index 1910ec5dc7f1..48081cf71a88 100644 --- a/test/rspack-dev-tests-manifest.json +++ b/test/rspack-dev-tests-manifest.json @@ -1993,20 +1993,10 @@ "flakey": [], "runtimeError": false }, - "test/development/dev-cache-control-no-cache-disabled/dev-cache-control-no-cache-disabled.test.ts": { - "passed": [ - "experimental.devCacheControlNoCache disabled (default) should use no-store for app router by default", - "experimental.devCacheControlNoCache disabled (default) should use no-store for pages router by default" - ], - "failed": [], - "pending": [], - "flakey": [], - "runtimeError": false - }, "test/development/dev-cache-control-no-cache/dev-cache-control-no-cache.test.ts": { "passed": [ - "experimental.devCacheControlNoCache when enabled should use no-cache instead of no-store for app router", - "experimental.devCacheControlNoCache when enabled should use no-cache instead of no-store for pages router" + "dev Cache-Control header should use no-cache for app router", + "dev Cache-Control header should use no-cache for pages router" ], "failed": [], "pending": [],