From 8e1e1aed8c62679cf60ebe697369eb4124e823ba Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 15 Mar 2022 07:44:01 -0700 Subject: [PATCH 01/10] [breaking] rename xForwardedForIndex to xForwardedForNumProxies --- packages/adapter-node/README.md | 14 ++++---------- packages/adapter-node/index.d.ts | 2 +- packages/adapter-node/index.js | 8 ++++++-- packages/adapter-node/src/handler.d.ts | 2 +- packages/adapter-node/src/handler.js | 9 +++++++-- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index e301cbe7fb58..ed431df797a8 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -94,23 +94,17 @@ MY_ORIGINURL=https://my.site \ node build ``` -### xForwardedForIndex +### xForwardedForNumProxies -If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. For example, if there are three proxies between your server and the client, proxy 3 will forward the addresses of the client and the first two proxies: +If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. `xForwardedForNumProxies` should specify how many trusted proxies sit between your server and the client. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the client and the first two proxies: ``` , , ``` -To get the client address we could use `xForwardedFor: 0` or `xForwardedFor: -3`, which counts back from the number of addresses. +To get the client address we could use `xForwardedFor: 3`. -**X-Forwarded-For is [trivial to spoof](https://adam-p.ca/blog/2022/03/x-forwarded-for/), howevever**: - -``` -, , , -``` - -For that reason you should always use a negative number (depending on the number of proxies) if you need to trust `event.clientAddress`. In the above example, `0` would yield the spoofed address while `-3` would continue to work. +If there may be a variable number of proxies, you would have to read the `X-Forwarded-For` header yourself and read the IP address from the left, but be very careful that you do not use the result for any applications with possible security implications. ## Custom server diff --git a/packages/adapter-node/index.d.ts b/packages/adapter-node/index.d.ts index 4422fade6753..fbc0aa744f39 100644 --- a/packages/adapter-node/index.d.ts +++ b/packages/adapter-node/index.d.ts @@ -20,7 +20,7 @@ interface AdapterOptions { host?: string; }; }; - xForwardedForIndex?: number; + xForwardedForNumProxies?: number; } declare function plugin(options?: AdapterOptions): Adapter; diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 3ed23d87576f..4fbb53e57ac8 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -24,12 +24,16 @@ export default function ({ host: host_header_env = 'HOST_HEADER' } = {} } = {}, - xForwardedForIndex = -1 + xForwardedForNumProxies = 1 } = {}) { return { name: '@sveltejs/adapter-node', async adapt(builder) { + if (xForwardedForNumProxies < 1) { + throw new Error('xForwardedForNumProxies cannot be less than 1'); + } + builder.rimraf(out); builder.log.minor('Copying assets'); @@ -56,7 +60,7 @@ export default function ({ PROTOCOL_HEADER: JSON.stringify(protocol_header_env), HOST_HEADER: JSON.stringify(host_header_env), ADDRESS_HEADER: JSON.stringify(address_header_env), - X_FORWARDED_FOR_INDEX: JSON.stringify(xForwardedForIndex) + X_FORWARDED_FOR_PROXIES: JSON.stringify(xForwardedForNumProxies) } }); diff --git a/packages/adapter-node/src/handler.d.ts b/packages/adapter-node/src/handler.d.ts index 5c89cb6732f1..af4fd5b9a594 100644 --- a/packages/adapter-node/src/handler.d.ts +++ b/packages/adapter-node/src/handler.d.ts @@ -5,7 +5,7 @@ declare global { const ADDRESS_HEADER: string; const HOST_HEADER: string; const PROTOCOL_HEADER: string; - const X_FORWARDED_FOR_INDEX: number; + const X_FORWARDED_FOR_PROXIES: number; } export const handler: Handle; diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index b96b98a2cf78..07e7e4d5d8c5 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -7,7 +7,7 @@ import { getRequest, setResponse } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest } from 'MANIFEST'; -/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER, X_FORWARDED_FOR_INDEX */ +/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER, X_FORWARDED_FOR_PROXIES */ const server = new Server(manifest); const origin = ORIGIN; @@ -62,7 +62,12 @@ const ssr = async (req, res) => { if (address_header === 'x-forwarded-for') { const addresses = value.split(','); - return addresses[(addresses.length + X_FORWARDED_FOR_INDEX) % addresses.length].trim(); + if (X_FORWARDED_FOR_PROXIES > addresses.length) { + throw new Error( + `Received xForwardedForNumProxies of ${X_FORWARDED_FOR_PROXIES}, but only found ${addresses.length} addresses` + ); + } + return addresses[addresses.length - X_FORWARDED_FOR_PROXIES].trim(); } return value; From fd06024a25e9923417da6bb324a9aba2573df26e Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 15 Mar 2022 07:48:50 -0700 Subject: [PATCH 02/10] add back link to blog post --- packages/adapter-node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index ed431df797a8..e146832076ae 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -104,7 +104,7 @@ If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a co To get the client address we could use `xForwardedFor: 3`. -If there may be a variable number of proxies, you would have to read the `X-Forwarded-For` header yourself and read the IP address from the left, but be very careful that you do not use the result for any applications with possible security implications. +If there may be a variable number of proxies, you would have to read the `X-Forwarded-For` header yourself and read the IP address from the left, but be very careful that you do not use the result for any applications with possible security implications since `X-Forwarded-For` is [trivial to spoof](https://adam-p.ca/blog/2022/03/x-forwarded-for/). ## Custom server From 2b12a1648f01e4458327bcea425c636c810fd5f4 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 15 Mar 2022 11:16:05 -0700 Subject: [PATCH 03/10] changeset --- .changeset/sweet-parents-sell.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/sweet-parents-sell.md diff --git a/.changeset/sweet-parents-sell.md b/.changeset/sweet-parents-sell.md new file mode 100644 index 000000000000..717f361de745 --- /dev/null +++ b/.changeset/sweet-parents-sell.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': patch +--- + +[breaking] rename `xForwardedForIndex` to `xForwardedForNumProxies` From 6b584f3ace9a38a40a25dc95adbe582828884cfe Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:59:49 -0700 Subject: [PATCH 04/10] update based on code review --- packages/adapter-node/README.md | 8 ++++---- packages/adapter-node/index.d.ts | 1 - packages/adapter-node/index.js | 10 ++-------- packages/adapter-node/src/handler.js | 23 +++++++++++++++++++---- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index e146832076ae..238e2ffe1ced 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -94,17 +94,17 @@ MY_ORIGINURL=https://my.site \ node build ``` -### xForwardedForNumProxies +### `XFF_DEPTH` -If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. `xForwardedForNumProxies` should specify how many trusted proxies sit between your server and the client. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the client and the first two proxies: +If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. The `XFF_DEPTH` environment variable should specify how many trusted proxies sit between your server and the client. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the client and the first two proxies: ``` , , ``` -To get the client address we could use `xForwardedFor: 3`. +To get the client address we could use `XFF_DEPTH=3`. -If there may be a variable number of proxies, you would have to read the `X-Forwarded-For` header yourself and read the IP address from the left, but be very careful that you do not use the result for any applications with possible security implications since `X-Forwarded-For` is [trivial to spoof](https://adam-p.ca/blog/2022/03/x-forwarded-for/). +This is [the most secure way](https://adam-p.ca/blog/2022/03/x-forwarded-for/) to read from the `X-Forwarded-For` header. It assumes a constant number of proxies between the client and server. If you are not using this value for a security-sensitive application, you may find it more reliable in some instances to avoid this assumption and read the first entry from the `X-Forwarded-For` header yourself (e.g. some users may be connecting through a corporate proxy while others are not breaking the assumption of a constant number of proxies). ## Custom server diff --git a/packages/adapter-node/index.d.ts b/packages/adapter-node/index.d.ts index fbc0aa744f39..6082ef1d2773 100644 --- a/packages/adapter-node/index.d.ts +++ b/packages/adapter-node/index.d.ts @@ -20,7 +20,6 @@ interface AdapterOptions { host?: string; }; }; - xForwardedForNumProxies?: number; } declare function plugin(options?: AdapterOptions): Adapter; diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 4fbb53e57ac8..55ead0ef98bb 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -23,17 +23,12 @@ export default function ({ protocol: protocol_header_env = 'PROTOCOL_HEADER', host: host_header_env = 'HOST_HEADER' } = {} - } = {}, - xForwardedForNumProxies = 1 + } = {} } = {}) { return { name: '@sveltejs/adapter-node', async adapt(builder) { - if (xForwardedForNumProxies < 1) { - throw new Error('xForwardedForNumProxies cannot be less than 1'); - } - builder.rimraf(out); builder.log.minor('Copying assets'); @@ -59,8 +54,7 @@ export default function ({ ORIGIN: origin_env ? `process.env[${JSON.stringify(origin_env)}]` : 'undefined', PROTOCOL_HEADER: JSON.stringify(protocol_header_env), HOST_HEADER: JSON.stringify(host_header_env), - ADDRESS_HEADER: JSON.stringify(address_header_env), - X_FORWARDED_FOR_PROXIES: JSON.stringify(xForwardedForNumProxies) + ADDRESS_HEADER: JSON.stringify(address_header_env) } }); diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 07e7e4d5d8c5..15d8953e2178 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -7,17 +7,32 @@ import { getRequest, setResponse } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest } from 'MANIFEST'; -/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER, X_FORWARDED_FOR_PROXIES */ +/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER */ const server = new Server(manifest); const origin = ORIGIN; +const xff_depth = get_xff_depth(); const address_header = ADDRESS_HEADER && (process.env[ADDRESS_HEADER] || '').toLowerCase(); const protocol_header = PROTOCOL_HEADER && process.env[PROTOCOL_HEADER]; const host_header = (HOST_HEADER && process.env[HOST_HEADER]) || 'host'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); +function get_xff_depth() { + const value = process.env['XFF_DEPTH']; + let xff_depth; + try { + xff_depth = value ? parseInt(value) : 1; + } catch (err) { + throw new Error('Expected XFF_DEPTH to be an integer. Received ${value}'); + } + if (xff_depth < 1) { + throw new Error('XFF_DEPTH cannot be less than 1'); + } + return xff_depth; +} + /** * @param {string} path * @param {number} max_age @@ -62,12 +77,12 @@ const ssr = async (req, res) => { if (address_header === 'x-forwarded-for') { const addresses = value.split(','); - if (X_FORWARDED_FOR_PROXIES > addresses.length) { + if (xff_depth > addresses.length) { throw new Error( - `Received xForwardedForNumProxies of ${X_FORWARDED_FOR_PROXIES}, but only found ${addresses.length} addresses` + `XFF_DEPTH specified as ${xff_depth}, but only found ${addresses.length} addresses` ); } - return addresses[addresses.length - X_FORWARDED_FOR_PROXIES].trim(); + return addresses[addresses.length - xff_depth].trim(); } return value; From d79b35c38e0943442572cd3fe7ed1de21a62311f Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 16 Mar 2022 10:00:48 -0700 Subject: [PATCH 05/10] update changeset --- .changeset/sweet-parents-sell.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/sweet-parents-sell.md b/.changeset/sweet-parents-sell.md index 717f361de745..bd017b5a9f98 100644 --- a/.changeset/sweet-parents-sell.md +++ b/.changeset/sweet-parents-sell.md @@ -2,4 +2,4 @@ '@sveltejs/adapter-node': patch --- -[breaking] rename `xForwardedForIndex` to `xForwardedForNumProxies` +[breaking] rename `xForwardedForIndex` to `XFF_DEPTH` and make it an environment variable From 09b1f4532dd87010107a18267918c0e1daa911f2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Mar 2022 14:27:37 -0400 Subject: [PATCH 06/10] XFF_DEPTH_ENV (#4357) * use XFF_DEPTH_ENV * get rid of get_xff_depth * typo --- packages/adapter-node/index.d.ts | 1 + packages/adapter-node/index.js | 2 ++ packages/adapter-node/src/handler.d.ts | 2 +- packages/adapter-node/src/handler.js | 25 ++++++++----------------- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/adapter-node/index.d.ts b/packages/adapter-node/index.d.ts index 6082ef1d2773..b7785dfb63e7 100644 --- a/packages/adapter-node/index.d.ts +++ b/packages/adapter-node/index.d.ts @@ -14,6 +14,7 @@ interface AdapterOptions { host?: string; port?: string; origin?: string; + xffDepth?: string; headers?: { address?: string; protocol?: string; diff --git a/packages/adapter-node/index.js b/packages/adapter-node/index.js index 55ead0ef98bb..b84f6b4e8b76 100644 --- a/packages/adapter-node/index.js +++ b/packages/adapter-node/index.js @@ -18,6 +18,7 @@ export default function ({ host: host_env = 'HOST', port: port_env = 'PORT', origin: origin_env = 'ORIGIN', + xffDepth: xff_depth_env = 'XFF_DEPTH', headers: { address: address_header_env = 'ADDRESS_HEADER', protocol: protocol_header_env = 'PROTOCOL_HEADER', @@ -52,6 +53,7 @@ export default function ({ HOST_ENV: JSON.stringify(host_env), PORT_ENV: JSON.stringify(port_env), ORIGIN: origin_env ? `process.env[${JSON.stringify(origin_env)}]` : 'undefined', + XFF_DEPTH_ENV: xff_depth_env, PROTOCOL_HEADER: JSON.stringify(protocol_header_env), HOST_HEADER: JSON.stringify(host_header_env), ADDRESS_HEADER: JSON.stringify(address_header_env) diff --git a/packages/adapter-node/src/handler.d.ts b/packages/adapter-node/src/handler.d.ts index af4fd5b9a594..2249bf2757c6 100644 --- a/packages/adapter-node/src/handler.d.ts +++ b/packages/adapter-node/src/handler.d.ts @@ -5,7 +5,7 @@ declare global { const ADDRESS_HEADER: string; const HOST_HEADER: string; const PROTOCOL_HEADER: string; - const X_FORWARDED_FOR_PROXIES: number; + const XFF_DEPTH_ENV: string; } export const handler: Handle; diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js index 15d8953e2178..208d75d4dce4 100644 --- a/packages/adapter-node/src/handler.js +++ b/packages/adapter-node/src/handler.js @@ -7,32 +7,18 @@ import { getRequest, setResponse } from '@sveltejs/kit/node'; import { Server } from 'SERVER'; import { manifest } from 'MANIFEST'; -/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER */ +/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER, XFF_DEPTH_ENV */ const server = new Server(manifest); const origin = ORIGIN; +const xff_depth = XFF_DEPTH_ENV ? parseInt(process.env[XFF_DEPTH_ENV]) : 1; -const xff_depth = get_xff_depth(); const address_header = ADDRESS_HEADER && (process.env[ADDRESS_HEADER] || '').toLowerCase(); const protocol_header = PROTOCOL_HEADER && process.env[PROTOCOL_HEADER]; const host_header = (HOST_HEADER && process.env[HOST_HEADER]) || 'host'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -function get_xff_depth() { - const value = process.env['XFF_DEPTH']; - let xff_depth; - try { - xff_depth = value ? parseInt(value) : 1; - } catch (err) { - throw new Error('Expected XFF_DEPTH to be an integer. Received ${value}'); - } - if (xff_depth < 1) { - throw new Error('XFF_DEPTH cannot be less than 1'); - } - return xff_depth; -} - /** * @param {string} path * @param {number} max_age @@ -77,9 +63,14 @@ const ssr = async (req, res) => { if (address_header === 'x-forwarded-for') { const addresses = value.split(','); + + if (xff_depth < 1) { + throw new Error(`${XFF_DEPTH_ENV} must be a positive integer`); + } + if (xff_depth > addresses.length) { throw new Error( - `XFF_DEPTH specified as ${xff_depth}, but only found ${addresses.length} addresses` + `${XFF_DEPTH_ENV} is ${xff_depth}, but only found ${addresses.length} addresses` ); } return addresses[addresses.length - xff_depth].trim(); From 5f595dfb4514720b41939e2fcedbb72d9272499a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Mar 2022 14:28:54 -0400 Subject: [PATCH 07/10] Update packages/adapter-node/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Maurício Kishi --- packages/adapter-node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index 238e2ffe1ced..21e680b91679 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -96,7 +96,7 @@ node build ### `XFF_DEPTH` -If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. The `XFF_DEPTH` environment variable should specify how many trusted proxies sit between your server and the client. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the client and the first two proxies: +If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. The `XFF_DEPTH` environment variable should specify how many trusted proxies sit in front of your server. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the original connection and the first two proxies: ``` , , From 7af009691b57102dd309af20ec78ed9225e7ef88 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Mar 2022 14:53:40 -0400 Subject: [PATCH 08/10] Update packages/adapter-node/README.md --- packages/adapter-node/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index 21e680b91679..2da44960591f 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -104,7 +104,7 @@ If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a co To get the client address we could use `XFF_DEPTH=3`. -This is [the most secure way](https://adam-p.ca/blog/2022/03/x-forwarded-for/) to read from the `X-Forwarded-For` header. It assumes a constant number of proxies between the client and server. If you are not using this value for a security-sensitive application, you may find it more reliable in some instances to avoid this assumption and read the first entry from the `X-Forwarded-For` header yourself (e.g. some users may be connecting through a corporate proxy while others are not breaking the assumption of a constant number of proxies). +> If you need to read the left-most address instead (and don't care about spoofing) — for example, to offer a geolocation service, where it's more important for the IP address to be _real_ than _trusted_, you can do so by inspecting the `x-forwarded-for` header within your app. ## Custom server From 4892a180f2d43e1d91da554e9780adb079ee6465 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Mar 2022 14:54:17 -0400 Subject: [PATCH 09/10] Update README.md --- packages/adapter-node/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index 2da44960591f..0a40d51013d3 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -102,7 +102,13 @@ If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a co , , ``` -To get the client address we could use `XFF_DEPTH=3`. +Some guides will tell you to read the left-most address, but this leaves you [vulnerable to spoofing](https://adam-p.ca/blog/2022/03/x-forwarded-for/): + +``` +, , , +``` + +Instead, we read from the _right_, accounting for the number of trusted proxies. In this case, we would use `XFF_DEPTH=3`. > If you need to read the left-most address instead (and don't care about spoofing) — for example, to offer a geolocation service, where it's more important for the IP address to be _real_ than _trusted_, you can do so by inspecting the `x-forwarded-for` header within your app. From fb3e63e109d992484639ab8d79a97b0643e657ad Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Mar 2022 15:01:43 -0400 Subject: [PATCH 10/10] overhaul readme --- packages/adapter-node/README.md | 111 +++++++++++++++++++------------- 1 file changed, 66 insertions(+), 45 deletions(-) diff --git a/packages/adapter-node/README.md b/packages/adapter-node/README.md index 0a40d51013d3..81d6d01ff6f8 100644 --- a/packages/adapter-node/README.md +++ b/packages/adapter-node/README.md @@ -12,37 +12,14 @@ import adapter from '@sveltejs/adapter-node'; export default { kit: { - adapter: adapter({ - // default options are shown - out: 'build', - precompress: false, - env: { - path: 'SOCKET_PATH', - host: 'HOST', - port: 'PORT', - origin: 'ORIGIN', - headers: { - protocol: 'PROTOCOL_HEADER', - host: 'HOST_HEADER' - } - }, - xForwardedForIndex: -1 - }) + adapter: adapter() } }; ``` -## Options - -### out - -The directory to build the server to. It defaults to `build` — i.e. `node build` would start the server locally after it has been created. - -### precompress +## Environment variables -Enables precompressing using gzip and brotli for assets and prerendered pages. It defaults to `false`. - -### env +### `PORT` and `HOST` By default, the server will accept connections on `0.0.0.0` using port 3000. These can be customised with the `PORT` and `HOST` environment variables: @@ -50,6 +27,8 @@ By default, the server will accept connections on `0.0.0.0` using port 3000. The HOST=127.0.0.1 PORT=4000 node build ``` +### `ORIGIN`, `PROTOCOL_HEADER` and `HOST_HEADER` + HTTP doesn't give SvelteKit a reliable way to know the URL that is currently being requested. The simplest way to tell SvelteKit where the app is being served is to set the `ORIGIN` environment variable: ``` @@ -64,6 +43,8 @@ PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build > [`x-forwarded-proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) and [`x-forwarded-host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) are de facto standard headers that forward the original protocol and host if you're using a reverse proxy (think load balancers and CDNs). You should only set these variables if you trust the reverse proxy. +### `ADDRESS_HEADER` and `XFF_DEPTH` + The [RequestEvent](https://kit.svelte.dev/docs/types#additional-types-requestevent) object passed to hooks and endpoints includes an `event.clientAddress` property representing the client's IP address. By default this is the connecting `remoteAddress`. If your server is behind one or more proxies (such as a load balancer), this value will contain the innermost proxy's IP address rather than the client's, so we need to specify an `ADDRESS_HEADER` to read the address from: ``` @@ -72,13 +53,71 @@ ADDRESS_HEADER=True-Client-IP node build > Headers can easily be spoofed. As with `PROTOCOL_HEADER` and `HOST_HEADER`, you should [know what you're doing](https://adam-p.ca/blog/2022/03/x-forwarded-for/) before setting these. -All of these environment variables can be changed, if necessary, using the `env` option: +If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. The `XFF_DEPTH` environment variable should specify how many trusted proxies sit in front of your server. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the original connection and the first two proxies: + +``` +, , +``` + +Some guides will tell you to read the left-most address, but this leaves you [vulnerable to spoofing](https://adam-p.ca/blog/2022/03/x-forwarded-for/): + +``` +, , , +``` + +Instead, we read from the _right_, accounting for the number of trusted proxies. In this case, we would use `XFF_DEPTH=3`. + +> If you need to read the left-most address instead (and don't care about spoofing) — for example, to offer a geolocation service, where it's more important for the IP address to be _real_ than _trusted_, you can do so by inspecting the `x-forwarded-for` header within your app. + +## Options + +The adapter can be configured with various options: + +```js +// svelte.config.js +import adapter from '@sveltejs/adapter-node'; + +export default { + kit: { + adapter: adapter({ + // default options are shown + out: 'build', + precompress: false, + env: { + path: 'SOCKET_PATH', + host: 'HOST', + port: 'PORT', + origin: 'ORIGIN', + xffDepth: 'XFF_DEPTH', + headers: { + address: 'ADDRESS_HEADER', + protocol: 'PROTOCOL_HEADER', + host: 'HOST_HEADER' + } + } + }) + } +}; +``` + +### out + +The directory to build the server to. It defaults to `build` — i.e. `node build` would start the server locally after it has been created. + +### precompress + +Enables precompressing using gzip and brotli for assets and prerendered pages. It defaults to `false`. + +### env + +If you need to change the name of the environment variables used to configure the deployment (for example, you need to run multiple deployments from a single environment), you can tell the app to expect custom environment variables using the `env` option: ```js env: { host: 'MY_HOST_VARIABLE', port: 'MY_PORT_VARIABLE', origin: 'MY_ORIGINURL', + xffDepth: 'MY_XFF_DEPTH', headers: { address: 'MY_ADDRESS_HEADER', protocol: 'MY_PROTOCOL_HEADER', @@ -94,24 +133,6 @@ MY_ORIGINURL=https://my.site \ node build ``` -### `XFF_DEPTH` - -If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. The `XFF_DEPTH` environment variable should specify how many trusted proxies sit in front of your server. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the original connection and the first two proxies: - -``` -, , -``` - -Some guides will tell you to read the left-most address, but this leaves you [vulnerable to spoofing](https://adam-p.ca/blog/2022/03/x-forwarded-for/): - -``` -, , , -``` - -Instead, we read from the _right_, accounting for the number of trusted proxies. In this case, we would use `XFF_DEPTH=3`. - -> If you need to read the left-most address instead (and don't care about spoofing) — for example, to offer a geolocation service, where it's more important for the IP address to be _real_ than _trusted_, you can do so by inspecting the `x-forwarded-for` header within your app. - ## Custom server The adapter creates two files in your build directory — `index.js` and `handler.js`. Running `index.js` — e.g. `node build`, if you use the default build directory — will start a server on the configured port.