Skip to content

Commit 82adaee

Browse files
authored
Allow to opt-out from preflight cache (vercel#32767)
Fixes vercel#32727 With this PR we introduce a new header that can be used to respond from Middleware `x-middleware-cache`. When the value of this header is set to `no-cache`, the client will **not** store the effects read from a preflight response to be used in an upcoming check. Instead of using `Cache-Control` we are using a custom header to not mess with browser specific caching. Accepting a specific value to opt out (`no-cache`) opens the future opportunity of having other caching strategies. This feature solves the issue of having a preflight request whose parameters can change from the client affecting the effects. For example, having a cookie that would make a middleware rewrite to one pathname or another and allowing to change that cookie from the client. In that case we'd always need to revalidate the effects on navigation synchronously. Of course when using this feature it is possible that we add some latency on navigation so the preferred mechanism will be caching by default since it covers the most common use cases.
1 parent 4d30771 commit 82adaee

File tree

4 files changed

+35
-3
lines changed

4 files changed

+35
-3
lines changed

packages/next/shared/lib/router/router.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ interface NextHistoryState {
6363
}
6464

6565
interface PreflightData {
66+
cache?: string | null
6667
redirect?: string | null
6768
refresh?: boolean
6869
rewrite?: string | null
@@ -1950,14 +1951,15 @@ export default class Router implements BaseRouter {
19501951
}
19511952

19521953
return {
1954+
cache: res.headers.get('x-middleware-cache'),
19531955
redirect: res.headers.get('Location'),
19541956
refresh: res.headers.has('x-middleware-refresh'),
19551957
rewrite: res.headers.get('x-middleware-rewrite'),
19561958
ssr: !!res.headers.get('x-middleware-ssr'),
19571959
}
19581960
})
19591961
.then((data) => {
1960-
if (shouldCache) {
1962+
if (shouldCache && data.cache !== 'no-cache') {
19611963
this.sde[cacheKey] = data
19621964
}
19631965

test/integration/middleware/core/pages/rewrites/_middleware.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,14 @@ export async function middleware(request) {
3030
}
3131

3232
if (url.pathname === '/rewrites/rewrite-me-without-hard-navigation') {
33-
url.pathname = '/rewrites/about'
3433
url.searchParams.set('middleware', 'foo')
35-
return NextResponse.rewrite(url)
34+
url.pathname =
35+
request.cookies['about-bypass'] === '1'
36+
? '/rewrites/about-bypass'
37+
: '/rewrites/about'
38+
39+
const response = NextResponse.rewrite(url)
40+
response.headers.set('x-middleware-cache', 'no-cache')
41+
return response
3642
}
3743
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export default function AboutBypass({ message }) {
2+
return (
3+
<div>
4+
<h1 className="title">About Bypassed Page</h1>
5+
<p className={message}>{message}</p>
6+
</div>
7+
)
8+
}
9+
10+
export const getServerSideProps = ({ query }) => ({
11+
props: { message: query.message || '' },
12+
})

test/integration/middleware/core/test/index.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,18 @@ function rewriteTests(locale = '') {
213213
const element = await browser.elementByCss('.middleware')
214214
expect(await element.text()).toEqual('foo')
215215
})
216+
217+
it('should allow to opt-out preflight caching', async () => {
218+
const browser = await webdriver(context.appPort, '/rewrites/')
219+
await browser.addCookie({ name: 'about-bypass', value: '1' })
220+
await browser.eval('window.__SAME_PAGE = true')
221+
await browser.elementByCss('#link-with-rewritten-url').click()
222+
await browser.waitForElementByCss('.refreshed')
223+
await browser.deleteCookies()
224+
expect(await browser.eval('window.__SAME_PAGE')).toBe(true)
225+
const element = await browser.elementByCss('.title')
226+
expect(await element.text()).toEqual('About Bypassed Page')
227+
})
216228
}
217229

218230
function redirectTests(locale = '') {

0 commit comments

Comments
 (0)