kit
kit copied to clipboard
Adapters should add cache-control headers to static assets
Describe the problem
As noted in https://github.com/sveltejs/kit/issues/909#issuecomment-1004128975, most adapters don't configure cache-control headers for the three categories of static assets — hashed (Vite output), static
directory contents, and prerendered pages.
Describe the proposed solution
For hashed assets there's no real reason they shouldn't have cache-control: public, immutable, max-age=31536000
.
For the contents of the static
directory, it really ought to be configurable. This could be done on a per-adapter basis (e.g. there's a PR for adapter-cloudflare-workers
here), but since it's a universal requirement I think it belongs in the main config. Straw man:
// svelte.config.js
export default {
kit: {
static: {
// cache everything in `static` for 1 hour
cache: 3600
// cache large images for one hour, small images for ten minutes, don't cache anything else
cache: ({ filename, size }) => {
if (filename.startsWith('images') {
return size > 1_000_000 ? 3600 : 600;
}
return 0;
}
}
}
};
Prerendered pages should adhere to whatever the cache-control
header was at the time of prerendering. For some platforms it may be difficult or impossible to set cache headers for static assets, but here we can at least cache stuff in the browser with a <meta http-equiv>
tag.
Alternatives considered
No response
Importance
would make my life easier
Additional Information
No response
This would be great.
@Rich-Harris Shouldn't this return a cache-control string, instead of a max-age integer? That way we can control everything. private, no-store, etc.
Static assets are public by definition; there's no value in being able to mark them non-public. Perhaps it makes sense to expose revalidate
, but what's a situation in which you would need no-store
(rather than just setting max-age=0
)?
Flexibility isn't always a good thing — cache-control headers can be a footgun. I'd argue it's a good thing, for example, not to expose the ability to inadvertently mark prerendered pages or static
contents as immutable
. For the sake of both avoiding the possibility of those bugs, and exposing a more intuitive API (nothing is simpler than a single max-age
value), it's very possible that the more restrictive API is preferable. Are there real world use cases that would be prevented by that?
There is an edge case here if you set export const ssr = false;
where a user can get a broken page view when returning to a tab that has been unloaded from memory.
Repro steps:
- User loads the application on his phone
- A new version is deployed by the developer so the JS file names change
- Some time passes, the tab gets unloaded from memory
- When the user comes back to the tab, it tries to load in again, but the HTML is not refetched, so the JS references are outdated leading to 404 and white screen of death.
I've observed this issue with sleeping tabs on both Firefox/Win11 and Chrome/Android. Browser behavior is a bit fickle especially with tab hibernation that afaik doesn't have a "standard" per se, but it could hopefully be solved by setting meta tags. https://cristian.sulea.net/blog/disable-browser-caching-with-meta-html-tags/
Since I've set ssr = false
for my whole application, I could add these meta tags in app.html
so that they work across the whole application, but having it on a per page level would be ideal.
Hi, any updates on this issue ? This is a real problem for production applications, no caching for static assets is just detrimental for performance.
I'm also interested. Browsing the internet, I found this was a thing: https://github.com/sveltejs/kit/blob/59702554b3c917f8a0d219f326f80b4d78fa29de/packages/adapter-node/src/handler.js#L22. I would love to have this instead of relying on a proxy to set it.
I came here due to Lighthouse reporting this problem for fonts. I would love to see it implemented.
I came here due to Lighthouse reporting this problem for fonts. I would love to see it implemented.
Same here, not having this feature impacts the lighthouse performance score negatively. It would be too lovely to see this feature implemented.
As a workaround for now, it's recommended to have your assets processed by Vite so they get cached. https://vitejs.dev/guide/assets.html#importing-asset-as-url
For example:
src/routes/your-page/+page.svelte
<script>
// The image and font are both cached.
import Image1 from './your-image.jpg';
</script>
<img src={Image1} />
<style>
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
/* src/routes/your-page/font.woff2 */
src: url('./font.woff2') format('woff2');
}
</style>
A much required thing to solve the Caching issue on production for static assets. What's the update on this bug, btw?
If the assets in the /static
directory cannot be cached, why not discourage its usage?
Users are most-likely using this directory because favicon.png
is included in the default template.
A root +layout.svelte
file with an favicon import would be a workaround.
<!-- src/routes/+layout.svelte -->
<script>
import favicon from '$lib/static/favicon.png';
</script>
<svelte:head>
<link rel="icon" href={favicon} />
</svelte:head>
<slot />
It would also handle tackle these issues:
- https://github.com/sveltejs/kit/issues/11158 -
<svelte:head>
inapp.html
- https://github.com/sveltejs/kit/issues/11498 - browser's aggressive favicon caching
I use realfavicongenerator.net which gives a bunch of lanes that should be added to the head
of index.html
. Currently this is not working as icons are outside of Vite serving allow list.
. What is the correct way to set a favicon then?
I use realfavicongenerator.net which gives a bunch of lanes that should be added to the
head
ofindex.html
. Currently this is not working as icons areoutside of Vite serving allow list.
. What is the correct way to set a favicon then?
Please post your question on the svelte discord or create a new discussion on github as this is off topic from the issue.