diff --git a/.changeset/orange-roses-destroy.md b/.changeset/orange-roses-destroy.md new file mode 100644 index 000000000000..456c6daffa33 --- /dev/null +++ b/.changeset/orange-roses-destroy.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +docs: Elaborate on credentialed `fetch` behaviour diff --git a/documentation/docs/20-core-concepts/20-load.md b/documentation/docs/20-core-concepts/20-load.md index 1ae8bc44bc55..5906f519a4fa 100644 --- a/documentation/docs/20-core-concepts/20-load.md +++ b/documentation/docs/20-core-concepts/20-load.md @@ -231,10 +231,11 @@ Given a `route.id` of `/a/[b]/[...c]` and a `url.pathname` of `/a/x/y/z`, the `p To get data from an external API or a `+server.js` handler, you can use the provided `fetch` function, which behaves identically to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) with a few additional features: -- it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request -- it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context) -- internal requests (e.g. for `+server.js` routes) go direct to the handler function when running on the server, without the overhead of an HTTP call -- during server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](hooks#server-hooks-handle). Then, during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request - if you got a warning in your browser console when using the browser `fetch` instead of the `load` `fetch`, this is why. +- It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request. +- It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context). +- Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call. +- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](hooks#server-hooks-handle). +- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request - if you received a warning in your browser console when using the browser `fetch` instead of the `load` `fetch`, this is why. ```js /// file: src/routes/items/[id]/+page.js @@ -247,9 +248,7 @@ export async function load({ fetch, params }) { } ``` -> Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it. - -## Cookies and headers +## Cookies A server `load` function can get and set [`cookies`](types#public-types-cookies). @@ -274,8 +273,20 @@ export async function load({ cookies }) { } ``` +Cookies will only be passed through the provided `fetch` function if the target host is the same as the SvelteKit application or a more specific subdomain of it. + +For example, 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 + +Other cookies will not be passed when `credentials: 'include'` is set, because SvelteKit does not know which domain which cookie belongs to (the browser does not pass this information along), so it's not safe to forward any of them. Use the [handleFetch hook](hooks#server-hooks-handlefetch) to work around it. + > When setting cookies, be aware of the `path` property. By default, the `path` of a cookie is the current pathname. If you for example set a cookie at page `admin/user`, the cookie will only be available within the `admin` pages by default. In most cases you likely want to set `path` to `'/'` to make the cookie available throughout your app. +## Headers + Both server and universal `load` functions have access to a `setHeaders` function that, when running on the server, can set headers for the response. (When running in the browser, `setHeaders` has no effect.) This is useful if you want the page to be cached, for example: ```js diff --git a/documentation/docs/20-core-concepts/50-state-management.md b/documentation/docs/20-core-concepts/50-state-management.md index facf1ceee1c8..78f2fc3e9afb 100644 --- a/documentation/docs/20-core-concepts/50-state-management.md +++ b/documentation/docs/20-core-concepts/50-state-management.md @@ -36,7 +36,7 @@ export const actions = { The `user` variable is shared by everyone who connects to this server. If Alice submitted an embarrassing secret, and Bob visited the page after her, Bob would know Alice's secret. In addition, when Alice returns to the site later in the day, the server may have restarted, losing her data. -Instead, you should _authenticate_ the user using [`cookies`](load#cookies-and-headers) and persist the data to a database. +Instead, you should _authenticate_ the user using [`cookies`](load#cookies) and persist the data to a database. ## No side-effects in load diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 9e9fe3e8926f..905f67d8c3b6 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -697,13 +697,13 @@ export interface LoadEvent< /** * `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features: * - * - it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request - * - it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context) - * - internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call - * - during server-side rendering, the response will be captured and inlined into the rendered HTML. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle) - * - during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request + * - It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request. + * - It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context). + * - Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call. + * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle) + * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing 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. + * You can learn more about making credentialed requests with cookies [here](https://kit.svelte.dev/docs/load#cookies) */ fetch: typeof fetch; /** @@ -949,11 +949,13 @@ export interface RequestEvent< /** * `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features: * - * - it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request - * - it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context) - * - internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call + * - It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request. + * - It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context). + * - Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call. + * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle) + * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing 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. + * You can learn more about making credentialed requests with cookies [here](https://kit.svelte.dev/docs/load#cookies) */ fetch: typeof fetch; /** diff --git a/packages/kit/src/runtime/server/fetch.js b/packages/kit/src/runtime/server/fetch.js index f07fbb19e3eb..8431e06fc5c8 100644 --- a/packages/kit/src/runtime/server/fetch.js +++ b/packages/kit/src/runtime/server/fetch.js @@ -51,7 +51,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade } if (url.origin !== event.url.origin) { - // allow cookie passthrough for "same-origin" + // Allow cookie passthrough for "credentials: same-origin" and "credentials: include" // if SvelteKit is serving my.domain.com: // - domain.com WILL NOT receive cookies // - my.domain.com WILL receive cookies @@ -59,6 +59,8 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade // - sub.my.domain.com WILL receive cookies // ports do not affect the resolution // leading dot prevents mydomain.com matching domain.com + // Do not forward other cookies for "credentials: include" because we don't know + // which cookie belongs to which domain (browser does not pass this info) if (`.${url.hostname}`.endsWith(`.${event.url.hostname}`) && credentials !== 'omit') { const cookie = get_cookie_header(url, request.headers.get('cookie')); if (cookie) request.headers.set('cookie', cookie);