Skip to content

Consider the URL that comes into handleFetch #11128

@aradalvand

Description

@aradalvand

Describe the bug

I have two apps served behind a reverse proxy; requests to /api/* get forwarded to my API server, and all the others are forwarded to my SvelteKit app.

In my SvelteKit app's load functions, I use fetch('/api/some-api-endpoint') to send requests to the API (which is, again, separate from the SvelteKit app). I also have a handleFetch hook that looks at these requests, and and changes their URL to fetch('http://localhost:5000/some-api-endpoint') (where localhost:5000 is the server app), in order to take a shortcut (very similar to the example in the docs).

The problem is I have to manually forward the Cookie header and so on, because the modified URL is no longer considered the same origin:

export async function handleFetch({ request, event, fetch }) {
    const thisOrigin = new URL(event.request.url).origin;
    if (request.url.startsWith(thisOrigin + '/api')) {
        request = new Request(request.url.replace(thisOrigin, PRIVATE_API_ORIGIN), request);
        // Manually forward headers...
    }
    return fetch(request);
}

SvelteKit will also automatically send an Origin header along with the request, which is not actually needed in this case, because (thankfully) SvelteKit only "simulates" CORS based on the "original" URL, and not the URL coming out of handleFetch, and rightly so, because handleFetch only runs on the server:

throw new Error(
`CORS error: ${
acao ? 'Incorrect' : 'No'
} 'Access-Control-Allow-Origin' header is present on the requested resource`

What I'm suggesting is that all the logic here:

if (!request.headers.has('origin')) {
request.headers.set('origin', event.url.origin);
}
if (info !== original_request) {
mode = (info instanceof Request ? info.mode : init?.mode) ?? 'cors';
credentials =
(info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
}
// Remove Origin, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin#description
if (
(request.method === 'GET' || request.method === 'HEAD') &&
((mode === 'no-cors' && url.origin !== event.url.origin) ||
url.origin === event.url.origin)
) {
request.headers.delete('origin');
}
if (url.origin !== event.url.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
// - 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
// 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);
}
return fetch(request);
}

Should ALSO be based on the "original" URL (and not the URL modified by handleFetch), in order to simulate browser behavior more accurately. The current behavior is inconsistent. Tell me if I'm wrong.

Reproduction

N/A

Logs

No response

System Info

System:
    OS: Linux 5.15 Ubuntu 22.04.2 LTS 22.04.2 LTS (Jammy Jellyfish)
    CPU: (4) x64 Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
    Memory: 8.07 GB / 11.63 GB
    Container: Yes
    Shell: 3.6.1 - /home/linuxbrew/.linuxbrew/bin/fish
  Binaries:
    Node: 20.6.0 - /home/linuxbrew/.linuxbrew/bin/node
    npm: 9.8.1 - /home/linuxbrew/.linuxbrew/bin/npm
    pnpm: 8.7.1 - /home/linuxbrew/.linuxbrew/bin/pnpm
    bun: 1.0.11 - ~/.bun/bin/bun
  npmPackages:
    @sveltejs/adapter-node: ^1.3.1 => 1.3.1
    @sveltejs/kit: ^1.27.6 => 1.27.6
    svelte: ^4.2.5 => 4.2.5
    vite: ^4.5.0 => 4.5.0

Severity

serious, but I can work around it

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions