I'm going to explain a solution to my somewhat related problem here, all according to my best understanding, which is limited.
My solution to set up a trusted Sveltekit dev server (running in a private subnet without DNS) was to configure a Nginx reverse proxy that acts as a trusted HTTPS middle man between the Vite server (running in plain HTTP mode) that is bundled in Sveltekit, and the clients (like my Android phone).
I found the most useful guidance from the following resources:
The main steps to the solution were:
- Become a local certificate authority and register the authority in your clients (like in the Chrome browser on the desktop, or in the Credential storage of an Android phone).
- Being a certificate authority, sign a x509 certificate for the IP-address (subjectAltName) of the dev server in the local network.
- Setup a Nginx HTTPS reverse proxy (proxy_pass etc.) to forward traffic to the Vite server (typically running in the port 3000). Assign the created certificate and the key for its use. Also add Websocket support as explained in the setup guide linked above.
- Declare
kit.vite.server.hmr.port = <port of the Nginx proxy>
in svelte.config.js. This is important so that the Sveltekit middleware (?) does not try to bypass the proxy.
Relevant snippets from my configuration:
openssl genrsa -out myCA.key 2048
openssl req -x509 -new -nodes -key myCA.key -sha256 -days 10000 -out myCA.pem
openssl genrsa -out 192.168.22.88.key 2048
openssl req -new -key 192.168.22.88.key -out 192.168.22.88.csr
>192.168.22.88.ext cat <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.22.88
IP.2 = 127.0.0.1
DNS.1 = localhost
DNS.2 = localhost.local
EOF
openssl x509 -req -in 192.168.22.88.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out 192.168.22.88.crt -days 10000 -sha256 -extfile 192.168.22.88.ext
openssl dhparam -out dhparam.pem 2048
server {
listen 2200 http2 ssl default_server;
listen [::]:2200 http2 ssl default_server;
ssl_certificate /etc/nginx/ssl/192.168.22.88.crt;
ssl_certificate_key /etc/nginx/ssl/192.168.22.88.key;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
index index.html;
server_name 192.168.22.88;
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 90;
proxy_redirect http://127.0.0.1:3000 https://192.168.22.88:2200;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
kit: {
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
adapter: adapter(),
vite: {
server: {
hmr: {
port: 2200,
}
}
}
}
pnpm run dev