A self-signed HTTPS reverse proxy for Svelte/Vite dev servers that routes /api/* requests to a configurable local or staging backend while preserving cookies, headers, and SPA fallback.
/api/* requests to local or remote APIx-app-environment: local header to staging requestsnpm install --save-dev svelte-api-proxy concurrently
See QUICK_START.md for a complete installation and configuration guide.
Browser (https://dev-example.com:8443)
↓
HTTPS Proxy Server (port 8443)
↓
├── /api/* requests → Remote/Local API (e.g., https://api.example.com)
└── All other requests → Vite Dev Server (http://localhost:5173)
https://dev-example.com:8443/api/ → Forward to configured API/, /@vite/*, or contains . → Forward to Vite/)Host header to avoid routing conflictsx-app-environment: local for non-local APIsThe proxy automatically upgrades WebSocket connections:
/api/* WebSockets → Forwarded to APIAll configuration is done via the proxyConfig section in your package.json.
{
proxyConfig: {
appPort: number; // Port where Vite dev server runs
proxyPort: number; // Port where HTTPS proxy runs
devDomain: string; // Domain alias (must match SSL cert name)
apiLocal: boolean; // true = local API, false = remote API
apiBaseUrl: string; // Base URL of the API to proxy to
certsPath: string; // Path to SSL certificates directory
showLogs: boolean; // Show/hide proxy request logs
}
}
{
"proxyConfig": {
"appPort": 5173,
"proxyPort": 8443,
"devDomain": "dev-example.com",
"apiLocal": false,
"apiBaseUrl": "https://api.example.com",
"certsPath": "./certs",
"showLogs": true
}
}
| Option | Required | Default | Description |
|---|---|---|---|
appPort |
Yes | - | Port where your Vite dev server runs (e.g., 5173) |
proxyPort |
Yes | - | Port where the HTTPS proxy listens (e.g., 8443) |
devDomain |
Yes | - | Domain alias for local dev. Must match SSL certificate common name |
apiLocal |
No | false |
When true, disables SSL verification for local self-signed API certs |
apiBaseUrl |
Yes | - | Full base URL of the API (e.g., https://api.example.com) |
certsPath |
Yes | - | Relative or absolute path to directory containing SSL certificates |
showLogs |
No | true |
When true, logs all proxy requests and responses to console |
The proxy requires two files in your certsPath directory:
{devDomain}-key.pem - Private key{devDomain}.pem - CertificateExample: If devDomain is dev-example.com:
dev-example.com-key.pemdev-example.com.pemInstall mkcert:
# macOS
brew install mkcert
# Linux
sudo apt install mkcert
# Windows
choco install mkcert
Generate certificates:
cd certs
mkcert -install
mkcert dev-example.com
This creates:
dev-example.com.pem (certificate)dev-example.com-key.pem (private key)Important: Add ./certs to your .gitignore to avoid committing certificates.
The main proxy server class.
import { DevProxy } from "svelte-api-proxy";
const proxy = new DevProxy(config);
Parameters:
config (object) - Configuration object with required fields:appPort (number)proxyPort (number)devDomain (string)apiBaseUrl (string)certsPath (string)apiLocal (boolean, optional, default: false)showLogs (boolean, optional, default: true)Throws:
start()Starts the HTTPS proxy server.
proxy.start();
Output:
[PROXY] Starting HTTPS dev proxy...
[PROXY] API target → REMOTE (https://api.example.com)
[PROXY] HTTPS server → https://dev-example.com:8443
[PROXY] App → http://localhost:5173
[PROXY] API → https://api.example.com
stop()Stops the proxy server.
proxy.stop();
The proxy automatically strips the Host header from forwarded requests to prevent routing conflicts. When you access https://dev-example.com:8443, the browser sends:
Host: dev-example.com:8443
If forwarded as-is to api.example.com, the remote server would reject it. The proxy strips this header and lets http-proxy's changeOrigin: true set the correct Host header.
When apiLocal is false (staging/production API), the proxy adds:
x-app-environment: local
This allows your API to differentiate between requests from local development vs deployed apps.
When showLogs is true, the proxy logs:
Request:
[PROXY] GET /api/users → https://api.example.com/api/users
Response:
[PROXY] Response: 200 GET /api/users
Set showLogs: false to disable logging.
For advanced use cases, you can use the proxy programmatically:
import { DevProxy } from "svelte-api-proxy";
const proxy = new DevProxy({
appPort: 5173,
proxyPort: 8443,
devDomain: "dev-example.com",
apiLocal: false,
apiBaseUrl: "https://api.example.com",
certsPath: "./certs",
showLogs: true,
});
proxy.start();
// Later...
proxy.stop();
Cause: The proxyConfig section is missing from your package.json.
Solution: Add the proxyConfig section with all required fields.
Cause: SSL certificates are missing or incorrectly named.
Solution:
certsPath points to the correct directory{devDomain}-key.pem and {devDomain}.pemCause: The remote API is not responding.
Solution:
apiBaseUrl is correctcurl https://api.example.com/api/healthCause: Browser doesn't trust self-signed certificate.
Solution: Run mkcert -install to install the local CA in your browser's trust store.
Cause: API is rejecting requests due to CORS policy.
Solution: Since the proxy forwards requests from localhost, your API must allow requests from your dev domain origin. Configure CORS on your API to allow https://dev-example.com:8443.
Cause: WebSocket upgrade not being handled correctly.
Solution:
appPortYou can run multiple Svelte apps with different domain aliases:
App 1 (app1.dev-example.com):
{
"proxyConfig": {
"appPort": 5173,
"proxyPort": 8443,
"devDomain": "app1.dev-example.com"
}
}
App 2 (app2.dev-example.com):
{
"proxyConfig": {
"appPort": 5174,
"proxyPort": 8444,
"devDomain": "app2.dev-example.com"
}
}
Use a common parent domain for cookie sharing:
# /etc/hosts
127.0.0.1 dev-example.com
127.0.0.1 app1.dev-example.com
127.0.0.1 app2.dev-example.com
127.0.0.1 api.dev-example.com
Set cookies with domain=.dev-example.com to share across all subdomains.
MIT
Issues and PRs welcome at https://github.com/eduardocgarza/svelte-api-proxy