Allow to pass custom headers on load responses
See original GitHub issueIs your feature request related to a problem? Please describe. This can be related to my previous issue, which is really just some details missing in the docs https://github.com/sveltejs/kit/issues/793
Despite svelte-kit having a way to define maxage to control caching for HTML pages, we really don’t have a way of setting any other headers. The more common use cases are there: redirect and basic caching. But I feel like we need more control. On Next.js you can use the context object and use setHeader. Here on svelte-kit you can’t even use the context because that’s for layout only, so I need to resort to “hacky” solutions to get what I need.
My basic use case is to use s-maxage instead of maxage. I don’t want to cache HTML pages on the client because JS and CSS hashes change between deploys, and if a user with an HTML page from a previous deploy refreshes the page (without clearing cache), the user gets an unstyled and unscripted page. Browsers make a request even if the assets are cached, but the 404 produced by the CDN (in this case, Vercel), results in those unstyled and unscripted pages. The only way around this is to probably use service workers and cache assets with it, but I want to avoid that.
I feel like a headers prop is just something that sooner or later will become handy, the same way it is on endpoints.
Describe the solution you’d like
Just allow us to specify the headers on a load response, just like on endpoints:
export const load = async ({ fetch }) => {
const res = await fetch("/apistuff.json");
if (res.ok) {
const data = await res.json();
return {
props: {
data
},
headers: {
"cache-control": "public, s-maxage=3600"
}
};
}
return {
error: new Error("error during fetch")
};
};
Describe alternatives you’ve considered I’ve read about hooks and managed to work around this by doing:
import type { Handle } from "@sveltejs/kit";
/**
* Replace all max-age cache controls with s-maxage
*/
export const handle: Handle = async (request, render) => {
const response = await render(request);
if (!response) return response;
const cacheControlHeader = response?.headers?.["cache-control"];
if (cacheControlHeader) {
response.headers["cache-control"] = cacheControlHeader.replace("max-age", "s-maxage");
}
return {
...response
};
};
Not that pretty honestly, but works for me. This replaces all maxage usages with s-maxage which is something I’m 100% ok with, but just for this project. But this just fixes the issue for this project and this scenario, if someone wants to pass another random header, no dice.
I also tried using the context object to pass in headers during the load function and then apply them on handle, but you can’t modify the context from page load functions. So that was a no-go from the start.
How important is this feature to you?
Very. Working with these recent CDN providers like Vercel, Netlify, and even Cloudflare is amazing, and being able to use s-maxage to avoid running cloud functions over and over again is just what I need, especially as I do a ton of work with local non-profits that really want to keep costs to a minimum.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:12 (9 by maintainers)

Top Related StackOverflow Question
To clarify,
statusandmaxageare not ignored in the client.statusprovides the status code for the subsequent error page, andmaxagecauses the result of runningloadto be cached in memory for the appropriate length of timeWe talked about this in today’s maintainer’s meeting. Folks thought it made sense to support it on individual pages. Won’t add it to layouts yet since you can use handle, other options are configured only at leaf level, and there’s not a clear use case for adding it to layouts at the moment.