kit icon indicating copy to clipboard operation
kit copied to clipboard

Adapters should add cache-control headers to static assets

Open Rich-Harris opened this issue 3 years ago • 11 comments

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

Rich-Harris avatar Jan 03 '22 16:01 Rich-Harris

This would be great.

bjon avatar Jan 03 '22 17:01 bjon

@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.

bjon avatar Jan 04 '22 13:01 bjon

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?

Rich-Harris avatar Jan 04 '22 15:01 Rich-Harris

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:

  1. User loads the application on his phone
  2. A new version is deployed by the developer so the JS file names change
  3. Some time passes, the tab gets unloaded from memory
  4. 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.

khromov avatar Jan 15 '23 20:01 khromov

Hi, any updates on this issue ? This is a real problem for production applications, no caching for static assets is just detrimental for performance.

itssumitrai avatar Mar 23 '23 17:03 itssumitrai

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.

djdembeck avatar Aug 15 '23 19:08 djdembeck

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.

fayez-nazzal avatar Aug 15 '23 20:08 fayez-nazzal

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>

eltigerchino avatar Aug 16 '23 11:08 eltigerchino

A much required thing to solve the Caching issue on production for static assets. What's the update on this bug, btw?

Shekhar-Zealous avatar Sep 25 '23 12:09 Shekhar-Zealous

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> in app.html
  • https://github.com/sveltejs/kit/issues/11498 - browser's aggressive favicon caching

hyunbinseo avatar Feb 20 '24 14:02 hyunbinseo

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?

mjdNjhNJ avatar Mar 29 '24 18:03 mjdNjhNJ

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?

Please post your question on the svelte discord or create a new discussion on github as this is off topic from the issue.

eltigerchino avatar Mar 30 '24 02:03 eltigerchino