From f789349d26b7f6a33d2e28f8e0154969b89c8f97 Mon Sep 17 00:00:00 2001 From: FHachez Date: Fri, 18 Jul 2025 23:06:52 +0200 Subject: [PATCH 1/3] docs: improve docs around cache control order and redirects --- .../routes/docs/(qwikcity)/caching/index.mdx | 20 +++++++++++++++---- .../(qwikcity)/guides/redirects/index.mdx | 17 ++++++++++++++++ .../docs/(qwikcity)/middleware/index.mdx | 2 ++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx index 54a218efdca..003ceb5c643 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx @@ -47,10 +47,6 @@ export default component$(() => { With the above setup, you will not only have better performance (pages are always served instantly from cache), but you can also have significantly decreased hosting costs, as our server or edge functions only need to run at most once every 5 seconds per page. -## `cacheControl` - -Any method that takes a [request event](https://qwik.dev/docs/advanced/request-handling/#request-event) can call `request.cacheControl` to set the cache control headers for the response: - ```tsx title="src/routes/layout.tsx" import type { RequestHandler } from "@builder.io/qwik-city"; @@ -83,6 +79,22 @@ export const onGet: RequestHandler = async ({ cacheControl }) => { You can see the full [API reference](https://qwik.dev/api/qwik-city-middleware-request-handler/) of options you can pass to `request.cacheControl`. +## `cacheControl` Order + +Any method that takes a [request event](https://qwik.dev/docs/advanced/request-handling/#request-event) can call `request.cacheControl` to set the cache control headers for the response. + +Qwik City executes request handlers in a specific order. This is important because if multiple handlers set the cache control policy, **the last one to execute wins**. + +The execution order is as follows: + +1. **Middleware (`src/middleware/index.ts`)** +2. **Layouts (from the root layout downwards)** +3. **Page endpoints (`src/routes/..../index.tsx`)** + +This means that the cache control set in a page's `onGet` handler will override any cache control set in a layout or middleware. For example, you can set a default cache policy in a root layout, and then override it in a specific page. + +When using `redirect()`, there is a special case for caching. See [redirects and caching](/docs/(qwikcity)/guides/redirects/#caching) for more information. + ## When not to cache Caching is generally beneficial, but not right for every page all the time. If your site has URLs that will show different content to different people — for example, pages exclusive to logged-in users or pages that show content based on a user's location — you should avoid using cache-control headers to cache these pages. Instead, render the content of these pages on the server side on a per-visitor basis. diff --git a/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx index 0af6b2c122d..ef3169cdc3b 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx @@ -53,6 +53,23 @@ If you do not provide a status code, Qwik City will default to a `302` Found sta Read more about redirect status codes [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages). +## Caching + +When you issue a redirect using the `redirect()` method from the `RequestEvent`, Qwik City applies a default cache control policy if one hasn't been explicitly set. This includes `Cache-Control` headers set in a higher-level layout or middleware. For more details on the order of execution, see the [caching documentation](/docs/caching/). + +If the redirect's status code is greater than `301` (e.g., `302`, `307`), and you haven't called `cacheControl()` for that request, the `Cache-Control` header will automatically be set to `'no-store'`. This prevents the redirect from being cached by the browser or intermediate caches. + +```typescript +export const onGet: RequestHandler = ({ redirect, cacheControl }) => { + // This will result in a `Cache-Control: no-store` header except if cacheControl was called in a middleware or layout above. + throw redirect(302, '/new-location'); + + // To override the default, set it explicitly + cacheControl('day'); + throw redirect(302, '/new-location'); +}; +``` + ## Managing multiple redirects In some cases, you may need to manage multiple redirects based on different conditions. For example, you might want to redirect users from old URLs to new URLs after a site restructure. Additionally, you may want editors in a CMS to manage these redirects as well. Here's one of the ways you can handle multiple redirects in Qwik: diff --git a/packages/docs/src/routes/docs/(qwikcity)/middleware/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/middleware/index.mdx index 2149cecdd59..f2e25c33190 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/middleware/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/middleware/index.mdx @@ -460,6 +460,8 @@ export const onGet: RequestHandler = async ({ status, getWritableStream }) => { Redirect to a new URL. Notice the importance of throwing to prevent other middleware functions from running. The `redirect()` method will automatically set the `Location` header to the redirect URL. +> **Note**: When using `redirect()`, Qwik City applies a default `Cache-Control` header of `no-store` for redirects with a status code greater than 301. You can learn more about this behavior in the [redirects guide](/docs/guides/redirects/#caching). + ```tsx import { type RequestHandler } from '@builder.io/qwik-city'; From 4415d80f5fbcf8dba086a995d051c05634dae7bb Mon Sep 17 00:00:00 2001 From: FHachez Date: Fri, 18 Jul 2025 23:23:20 +0200 Subject: [PATCH 2/3] docs: improve the caching doc order --- .../docs/src/routes/docs/(qwikcity)/caching/index.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx index 003ceb5c643..6d938d1d199 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx @@ -47,6 +47,10 @@ export default component$(() => { With the above setup, you will not only have better performance (pages are always served instantly from cache), but you can also have significantly decreased hosting costs, as our server or edge functions only need to run at most once every 5 seconds per page. +## `cacheControl` + +Any method that takes a [request event](https://qwik.dev/docs/advanced/request-handling/#request-event) can call `request.cacheControl` to set the cache control headers for the response: + ```tsx title="src/routes/layout.tsx" import type { RequestHandler } from "@builder.io/qwik-city"; @@ -79,9 +83,7 @@ export const onGet: RequestHandler = async ({ cacheControl }) => { You can see the full [API reference](https://qwik.dev/api/qwik-city-middleware-request-handler/) of options you can pass to `request.cacheControl`. -## `cacheControl` Order - -Any method that takes a [request event](https://qwik.dev/docs/advanced/request-handling/#request-event) can call `request.cacheControl` to set the cache control headers for the response. +### `cacheControl` Order Qwik City executes request handlers in a specific order. This is important because if multiple handlers set the cache control policy, **the last one to execute wins**. From 6617caa9c2daa25396e1e082ca6e9ce9fb9fd454 Mon Sep 17 00:00:00 2001 From: FHachez Date: Sun, 20 Jul 2025 13:15:12 +0200 Subject: [PATCH 3/3] docs: split the redirect caching example in two and add FHachez in the list of contributors of the doc. --- .../docs/src/routes/docs/(qwikcity)/caching/index.mdx | 1 + .../routes/docs/(qwikcity)/guides/redirects/index.mdx | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx index 6d938d1d199..68f0e4dc05a 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/caching/index.mdx @@ -11,6 +11,7 @@ contributors: - chsanch - hamatoyogi - Jemsco + - FHachez updated_at: '2023-10-03T18:53:23Z' created_at: '2023-05-24T03:52:24Z' --- diff --git a/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx b/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx index ef3169cdc3b..886d97decd5 100644 --- a/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx +++ b/packages/docs/src/routes/docs/(qwikcity)/guides/redirects/index.mdx @@ -7,6 +7,7 @@ contributors: - mhevery - mrhoodz - hamatoyogi + - FHachez updated_at: '2023-06-25T19:43:33Z' created_at: '2023-03-20T23:45:13Z' --- @@ -60,10 +61,16 @@ When you issue a redirect using the `redirect()` method from the `RequestEvent`, If the redirect's status code is greater than `301` (e.g., `302`, `307`), and you haven't called `cacheControl()` for that request, the `Cache-Control` header will automatically be set to `'no-store'`. This prevents the redirect from being cached by the browser or intermediate caches. ```typescript -export const onGet: RequestHandler = ({ redirect, cacheControl }) => { - // This will result in a `Cache-Control: no-store` header except if cacheControl was called in a middleware or layout above. +export const onGet: RequestHandler = ({ redirect }) => { + // This will result in a `Cache-Control: no-store` header throw redirect(302, '/new-location'); +}; +``` +To override this default behavior and explicitly set a cache control policy for a redirect, you can call `cacheControl()` before throwing the redirect. + +```typescript +export const onGet: RequestHandler = ({ redirect, cacheControl }) => { // To override the default, set it explicitly cacheControl('day'); throw redirect(302, '/new-location');