diff --git a/.changeset/thick-steaks-sneeze.md b/.changeset/thick-steaks-sneeze.md new file mode 100644 index 000000000000..18b9e48c2e02 --- /dev/null +++ b/.changeset/thick-steaks-sneeze.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Passthrough server-side fetch cookies for most same-origin scenarios diff --git a/documentation/docs/03-loading.md b/documentation/docs/03-loading.md index 3aa7e6035855..bcf516cafe18 100644 --- a/documentation/docs/03-loading.md +++ b/documentation/docs/03-loading.md @@ -98,6 +98,8 @@ So if the example above was `src/routes/blog/[slug].svelte` and the URL was `htt > When `fetch` runs on the server, the resulting response will be serialized and inlined into the rendered HTML. This allows the subsequent client-side `load` to access identical data immediately without an additional network request. +> Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it. + #### session `session` can be used to pass data from the server related to the current request, e.g. the current user. By default it is `undefined`. See [`getSession`](#hooks-getsession) to learn how to use it. diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 86065c8094fe..5b97eeb6bc6c 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -4,6 +4,8 @@ import { resolve } from './resolve.js'; const s = JSON.stringify; +const hasScheme = (/** @type {string} */ url) => /^[a-zA-Z]+:/.test(url); + /** * * @param {{ @@ -97,10 +99,32 @@ export async function load_node({ let response; - if (/^[a-zA-Z]+:/.test(url)) { - // external fetch - const request = new Request(url, /** @type {RequestInit} */ (opts)); - response = await options.hooks.serverFetch.call(null, request); + if (hasScheme(url)) { + // possibly external fetch + if (typeof request.host !== 'undefined') { + const { hostname: fetchHostname } = new URL(url); + const [serverHostname] = request.host.split(':'); + + // allow cookie passthrough for "same-origin" + // if SvelteKit is serving my.domain.com: + // - domain.com WILL NOT receive cookies + // - my.domain.com WILL receive cookies + // - api.domain.dom WILL NOT receive cookies + // - sub.my.domain.com WILL receive cookies + // ports do not affect the resolution + // leading dot prevents mydomain.com matching domain.com + if (`.${fetchHostname}`.endsWith(`.${serverHostname}`) && opts.credentials !== 'omit') { + uses_credentials = true; + + opts.headers = { + ...opts.headers, + cookie: request.headers.cookie + }; + } + } + + const externalRequest = new Request(url, /** @type {RequestInit} */ (opts)); + response = await options.hooks.serverFetch.call(null, externalRequest); } else { const [path, search] = url.split('?');