diff --git a/.changeset/mighty-wasps-perform.md b/.changeset/mighty-wasps-perform.md new file mode 100644 index 000000000000..96c13ed481c2 --- /dev/null +++ b/.changeset/mighty-wasps-perform.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-node': minor +--- + +feat: add opt-in HTTPS and HTTP/2 support diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index 575c6b5296df..fdb464d33d17 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -110,6 +110,20 @@ We instead read from the _right_, accounting for the number of trusted proxies. The maximum request body size to accept in bytes including while streaming. Defaults to 512kb. You can disable this option with a value of 0 and implement a custom check in [`handle`](hooks#server-hooks-handle) if you need something more advanced. +### `CERT_PATH`, `CERT_KEY_PATH`, and `HTTPS_PORT`: +By default, the server only listens for plain HTTP requests. To enable an additional HTTPS endpoint, you must provide the `CERT_PATH` and `CERT_KEY_PATH` environment variables pointing to a file containing a valid certificate and a file containing its associated private key respectively, both in the PEM format. +The files are read once on startup, so if the contents of the files change, the app will need to be restarted. + +The default port that will be used for the HTTPS endpoint is 3001. You can customize this via the `HTTPS_PORT` environment variable. + +> We generally recommend you use a reverse proxy in front of your application to enable HTTPS instead, and have your reverse proxy communicate with your SvelteKit application via plain HTTP; only use these options when you know what you're doing, or as a last resort. + +### `NO_HTTP2`: +By default, the HTTPS endpoint will support HTTP/2 as well as HTTP/1.1. If, for some reason, supporting HTTP/2 isn't desirable, you can disable it by setting the `NO_HTTP2` environment variable to a non-empty value (e.g. `1`). + +### `ONLY_HTTPS`: +If you only need an HTTPS endpoint and want to opt out of the default plain HTTP one, you can do so by setting the `ONLY_HTTPS` environment variable to a non-empty value (e.g. `1`). + ## Options The adapter can be configured with various options: diff --git a/packages/adapter-node/src/env.js b/packages/adapter-node/src/env.js index 841bddf2c237..ec791b3cdc29 100644 --- a/packages/adapter-node/src/env.js +++ b/packages/adapter-node/src/env.js @@ -9,7 +9,12 @@ const expected = new Set([ 'ADDRESS_HEADER', 'PROTOCOL_HEADER', 'HOST_HEADER', - 'BODY_SIZE_LIMIT' + 'BODY_SIZE_LIMIT', + 'CERT_PATH', + 'CERT_KEY_PATH', + 'HTTPS_PORT', + 'ONLY_HTTPS', + 'NO_HTTP2' ]); if (ENV_PREFIX) { diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index ea3feee05860..5122a59dc9f3 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -1,15 +1,42 @@ import { handler } from 'HANDLER'; import { env } from 'ENV'; import polka from 'polka'; +import http from 'node:http'; +import https from 'node:https'; +import http2 from 'node:http2'; +import fs from 'node:fs'; export const path = env('SOCKET_PATH', false); export const host = env('HOST', '0.0.0.0'); export const port = env('PORT', !path && '3000'); +export const certPath = env('CERT_PATH', false); +export const certKeyPath = env('CERT_KEY_PATH', false); +export const httpsPort = env('HTTPS_PORT', !path && '3001'); +export const onlyHttps = env('ONLY_HTTPS', false); +export const noHttp2 = env('NO_HTTP2', false); -const server = polka().use(handler); +const app = polka().use(handler); -server.listen({ path, host, port }, () => { - console.log(`Listening on ${path ? path : host + ':' + port}`); -}); +if (!onlyHttps) { + // TODO Remove the `@ts-expect-error`s below once https://github.com/lukeed/polka/issues/194 is fixed + // @ts-expect-error + http.createServer(app.handler).listen({ path, host, port }, () => { + console.log(`Listening on http://${path ? path : host + ':' + port}`); + }); +} -export { server }; +if (certPath && certKeyPath) { + const cert = fs.readFileSync(certPath); + const key = fs.readFileSync(certKeyPath); + const https_server = noHttp2 + ? // @ts-expect-error + https.createServer({ cert, key }, app.handler) + : // @ts-expect-error + http2.createSecureServer({ allowHTTP1: true, cert, key }, app.handler); + + https_server.listen({ path, host, port: httpsPort }, () => { + console.log(`Listening on https://${path ? path : host + ':' + httpsPort}`); + }); +} + +export { app as server };