kit
kit copied to clipboard
Why is `__vdpl` cookie used for Skew Protection in Vercel instead of `x-deployment-id` header?
Describe the problem
We're trying to understand why our app multiple times a day report the Failed to fetch dynamically imported module issue to Sentry.
It seems that this is caused by the user opening a new version of the app in a new tab, which is documented on SvelteKit docs:
Cookie-based skew protection comes with one caveat: if a user has multiple versions of your app open in multiple tabs, requests from older versions will be routed to the newer one, meaning they will fall back to SvelteKit's built-in skew protection.
I may be understanding this wrong, but wouldn't the x-deployment-id header make it so different application versions in different tabs would not be affected by the new cookie that is set? Instead of setting the cookie across all app versions, wouldn't each application be able to use the separate deployment ID's in the header from the server its connected to?
Describe the proposed solution
Using x-deployment-id header instead of __vdpl cookie.
Alternatives considered
SvelteKits internal Skew Protection will reload the page which "solves" the error, but why not let the application keep working with Vercel's Skew protection if it's enabled?
Importance
would make my life easier
Additional Information
No response
cc: @Rich-Harris @dummdidumm
Presumably because it was easier to set the cookie once and have SK's and the browser's automatic cookie handling take over. I don't know what setting this header on every request looks like. We'd need new hooks in SK to make that happen it feels like.
Presumably because it was easier to set the cookie once and have SK's and the browser's automatic cookie handling take over. I don't know what setting this header on every request looks like. We'd need new hooks in SK to make that happen it feels like.
Can this be done in the adapter? I don't know how the adapter and routes fully work, but would it be possible to add a route that has src: '/.*' and setting 'x-deployment-id': process.env.VERCEL_DEPLOYMENT_ID in the header?
Presumably because it was easier to set the cookie once and have SK's and the browser's automatic cookie handling take over. I don't know what setting this header on every request looks like. We'd need new hooks in SK to make that happen it feels like.
Can this be done in the adapter? I don't know how the adapter and routes fully work, but would it be possible to add a route that has
src: '/.*'and setting'x-deployment-id': process.env.VERCEL_DEPLOYMENT_IDin the header?
Sounds like what the generated vercel static config is suppose to do. ~I’m not sure if~ this only applies to static asset requests ~or also requests that hit serverless functions~ https://github.com/sveltejs/kit/blob/761b8afd33f45e6b9cf0be1fb3933213d4542b49/packages/adapter-vercel/index.js#L499
But we'd also need to inject that header into all requests made from the browser as well, right? That's more the part I'm concerned about. I don't think we have a mechanism to do that, and it's requests from the browser that are the primary concern in preventing version skew.
But we'd also need to inject that header into all requests made from the browser as well, right? That's more the part I'm concerned about. I don't think we have a mechanism to do that, and it's requests from the browser that are the primary concern in preventing version skew.
If that's the case, could a Service Worker achieve this behaviour? Could the service-worker inject the x-deployment-id header via the fetch event in the ServiceWorkerGlobalScope ?
Would it be possible to avoid injecting the header from the browser by instead adding the x-deployment-id in a middleware on the server? It doesn't matter where the header was set as long as it is present on the request before being handled, right? @Conduitry @eltigerchino
No, we can't inject the header later. We need to know what version the browser has. That's the whole point of skew protection.
But we'd also need to inject that header into all requests made from the browser as well, right? That's more the part I'm concerned about. I don't think we have a mechanism to do that, and it's requests from the browser that are the primary concern in preventing version skew.
If that's the case, could a Service Worker achieve this behaviour? Could the service-worker inject the
x-deployment-idheader via thefetchevent in theServiceWorkerGlobalScope?
I think it's worth experimenting with this. But then we'd also need a way for the adapter to add the service worker registration script.
No, we can't inject the header later. We need to know what version the browser has. That's the whole point of skew protection.
True... i kinda forgot how servers work
But we'd also need to inject that header into all requests made from the browser as well, right? That's more the part I'm concerned about. I don't think we have a mechanism to do that, and it's requests from the browser that are the primary concern in preventing version skew.
If that's the case, could a Service Worker achieve this behaviour? Could the service-worker inject the
x-deployment-idheader via thefetchevent in theServiceWorkerGlobalScope?I think it's worth experimenting with this. But then we'd also need a way for the adapter to add the service worker registration script.
I tried to experiment with no luck, but i have a almost no experience with Service Workers.
There should be ways to make the script appear in the final application if the adapter is present in the svelte.config.js file, right? I have no strong opinions on how, but it sounds like a solvable task.
We have been encountering this issue for quite a long time as well. In some cases the app breaks entirely and no reload is triggered. Is there no solution to this still?
@MathiasWP someone has made a reproduction of skew protection being broken due to the cookie changing on opening a new tab with a new deployment, leaving the old tab with the new cookie too, causing it to use the wrong skew protection id https://github.com/sveltejs/kit/issues/9089#issuecomment-2414230989
I want to ping this issue, because it can cause some really bad user experiences if they're unlucky. We've seen that if a user opens our app at the wrong moment after a deployment then they're just greeted with a loading screen that never loads because of the Failed to fetch dynamically imported module error being thrown, causing the app to stop running.
Is there an easy way to workaround this?
Same issue #14437 Any solution yet?
After trying to implement a solution using a service worker, I can understand why the cookie based solution is the preferred solution. There's a lot of overhead involved with a service worker tracking the different deployment IDs for each client (new tab or window).
A rough implementation (which is doable in user-land today if you follow the ideas from the two patches in the provided repo) involves:
- Dispatching a message to the service worker when the page has completed loading.
- Updating the cached map of the current browsing context with the Vercel deployment ID.
- Intercepting subsequent fetches with the service worker and adding the correct deployment ID in the request header.
https://github.com/teemingc/vercel-skew-protection/blob/main/patches/%40sveltejs__adapter-vercel.patch
This takes up additional browser storage, requires a service worker, uses up more energy and memory to run the code, and doesn't currently work in Safari (I've no idea why). In comparison, the current solution is a small cookie that gets sent with every request but doesn't work with multiple tabs. We also haven't answered the question of how this would interact with existing user service workers.
It feels like the service worker solution is probably not the best solution. Unless there's some other way we can ensure all network requests include the deployment ID in its header, I'm in favour of keeping the existing cookie based solution but finding a better way to handle these failed fetches.