Add optional HTTPS support for backend and Vite dev server#400
Add optional HTTPS support for backend and Vite dev server#400stephenfeather wants to merge 1 commit intositeboon:mainfrom
Conversation
📝 WalkthroughWalkthroughThis pull request adds SSL/HTTPS support infrastructure to the application. Environment variables control SSL activation for both the backend Express server and Vite development server. Certificate and key file paths are configurable. The system gracefully falls back to HTTP if certificates are unavailable or if SSL is disabled. Changes
Sequence DiagramsequenceDiagram
participant App as Application Start
participant Env as Environment Config
participant FS as File System
participant Server as HTTP/HTTPS Server
participant Vite as Vite Dev Server
participant Client as Client Browser
App->>Env: Read SSL_ENABLED, VITE_HTTPS flags
Env-->>App: Return SSL configuration values
alt SSL Enabled
App->>FS: Read certificate files
FS-->>App: Return cert & key data
App->>Server: Create HTTPS server
else SSL Disabled
App->>Server: Create HTTP server
end
Server-->>App: Server created with protocol
alt VITE_HTTPS Enabled
App->>Vite: Configure HTTPS proxy<br/>(api, ws, shell targets)
else VITE_HTTPS Disabled
App->>Vite: Configure HTTP proxy<br/>(api, ws, shell targets)
end
Vite-->>App: Dev server configured
App->>Client: Server ready on configured protocol
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
203-204:⚠️ Potential issue | 🟡 MinorMinor: Development URL still hardcodes
http://.Line 204 says
http://localhost:3001but with HTTPS enabled the server will be onhttps://. Consider noting this or making the URL protocol-agnostic (e.g., "Development:https://localhost:3001(orhttp://if SSL is not enabled)").🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` around lines 203 - 204, The README currently hardcodes the development URL as "http://localhost:3001"; update the text around the Development URL (the bullet under "Open your browser") to be protocol-agnostic or mention HTTPS explicitly (for example "https://localhost:3001 (or http:// if SSL is not enabled)" or use "//localhost:3001") so it accurately reflects environments with SSL enabled; change the line that currently reads `http://localhost:3001` to the chosen protocol-agnostic/HTTPS-aware wording.
🧹 Nitpick comments (1)
vite.config.js (1)
6-7:isTruthyEnvis duplicated acrossvite.config.jsandserver/index.js.Both files define an identical
isTruthyEnvhelper. Consider extracting it to a shared utility module (e.g.,server/utils.jsor a commonlib/file) to keep them in sync.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vite.config.js` around lines 6 - 7, Duplicate helper isTruthyEnv should be extracted to a shared module and both callers should import it; create a new utility file (e.g., utils.js or lib/env.js) that exports isTruthyEnv, move the existing function implementation there, then replace the local definitions in vite.config.js and server/index.js with imports of the shared isTruthyEnv; ensure the exported function name and behavior remain identical and update any references to call the imported symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@vite.config.js`:
- Line 13: The current httpsEnabled constant (const httpsEnabled =
isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED)) conflates Vite's
own server HTTPS and the backend proxy protocol; change logic so Vite's
server.https is driven only by VITE_HTTPS (and cert paths) while the proxy
target protocol is computed from SSL_ENABLED. Update the code references to stop
using httpsEnabled for both purposes: use a new viteHttps flag derived from
isTruthyEnv(env.VITE_HTTPS) for server.https configuration (and cert options),
and use a separate backendHttps flag derived from isTruthyEnv(env.SSL_ENABLED)
when building proxy target URLs (choose 'https://' or 'http://').
- Around line 42-50: The proxy entries for '/ws' and '/shell' already use object
form but need secure: false added to allow self-signed certs, and the '/api'
entry must be converted from string form to an object with target set to
`${apiProtocol}://localhost:${env.PORT || 3001}` and secure: false; update the
proxy config in vite.config.js (the '/api', '/ws', '/shell' entries) to include
secure: false so HTTPS/WSS proxies accept self-signed certificates.
---
Outside diff comments:
In `@README.md`:
- Around line 203-204: The README currently hardcodes the development URL as
"http://localhost:3001"; update the text around the Development URL (the bullet
under "Open your browser") to be protocol-agnostic or mention HTTPS explicitly
(for example "https://localhost:3001 (or http:// if SSL is not enabled)" or use
"//localhost:3001") so it accurately reflects environments with SSL enabled;
change the line that currently reads `http://localhost:3001` to the chosen
protocol-agnostic/HTTPS-aware wording.
---
Nitpick comments:
In `@vite.config.js`:
- Around line 6-7: Duplicate helper isTruthyEnv should be extracted to a shared
module and both callers should import it; create a new utility file (e.g.,
utils.js or lib/env.js) that exports isTruthyEnv, move the existing function
implementation there, then replace the local definitions in vite.config.js and
server/index.js with imports of the shared isTruthyEnv; ensure the exported
function name and behavior remain identical and update any references to call
the imported symbol.
|
|
||
|
|
||
|
|
||
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED) |
There was a problem hiding this comment.
Bug: Conflating Vite HTTPS and backend HTTPS will cause proxy failures.
httpsEnabled uses isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED), which means setting VITE_HTTPS=true alone (without SSL_ENABLED) will enable Vite HTTPS and derive proxy targets as https://, even though the backend is running plain HTTP. Conversely, SSL_ENABLED=true alone will enable Vite HTTPS even if the user only intended backend HTTPS.
These are independent concerns and should be decoupled:
- Vite's own
server.httpsshould be driven byVITE_HTTPS(+ its cert paths). - The proxy target protocol should be driven by
SSL_ENABLED(the backend's SSL state).
Proposed fix: decouple Vite HTTPS from backend HTTPS
- const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED)
- const certPath = env.VITE_SSL_CERT_PATH || env.SSL_CERT_PATH
- const keyPath = env.VITE_SSL_KEY_PATH || env.SSL_KEY_PATH
+ // Vite dev server HTTPS (independent of backend)
+ const viteHttpsEnabled = isTruthyEnv(env.VITE_HTTPS)
+ const viteCertPath = env.VITE_SSL_CERT_PATH || env.SSL_CERT_PATH
+ const viteKeyPath = env.VITE_SSL_KEY_PATH || env.SSL_KEY_PATH
+
+ // Backend SSL (controls proxy target protocol)
+ const backendSslEnabled = isTruthyEnv(env.SSL_ENABLED)
let httpsConfig
- if (httpsEnabled) {
- if (certPath && keyPath) {
+ if (viteHttpsEnabled) {
+ if (viteCertPath && viteKeyPath) {
try {
httpsConfig = {
- cert: fs.readFileSync(path.resolve(certPath)),
- key: fs.readFileSync(path.resolve(keyPath))
+ cert: fs.readFileSync(path.resolve(viteCertPath)),
+ key: fs.readFileSync(path.resolve(viteKeyPath))
}
} catch (error) {
console.warn('[vite] HTTPS enabled but SSL cert files could not be read. Falling back to HTTP.', error?.message || error)
}
} else {
- console.warn('[vite] HTTPS enabled but VITE_SSL_CERT_PATH/VITE_SSL_KEY_PATH (or SSL_CERT_PATH/SSL_KEY_PATH) are missing. Falling back to HTTP.')
+ console.warn('[vite] HTTPS enabled but VITE_SSL_CERT_PATH (or SSL_CERT_PATH) are missing. Falling back to HTTP.')
}
}
- const apiProtocol = httpsConfig ? 'https' : 'http'
- const wsProtocol = httpsConfig ? 'wss' : 'ws'
+ const apiProtocol = backendSslEnabled ? 'https' : 'http'
+ const wsProtocol = backendSslEnabled ? 'wss' : 'ws'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vite.config.js` at line 13, The current httpsEnabled constant (const
httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED))
conflates Vite's own server HTTPS and the backend proxy protocol; change logic
so Vite's server.https is driven only by VITE_HTTPS (and cert paths) while the
proxy target protocol is computed from SSL_ENABLED. Update the code references
to stop using httpsEnabled for both purposes: use a new viteHttps flag derived
from isTruthyEnv(env.VITE_HTTPS) for server.https configuration (and cert
options), and use a separate backendHttps flag derived from
isTruthyEnv(env.SSL_ENABLED) when building proxy target URLs (choose 'https://'
or 'http://').
| '/api': `${apiProtocol}://localhost:${env.PORT || 3001}`, | ||
| '/ws': { | ||
| target: `ws://localhost:${env.PORT || 3001}`, | ||
| target: `${wsProtocol}://localhost:${env.PORT || 3001}`, | ||
| ws: true | ||
| }, | ||
| '/shell': { | ||
| target: `ws://localhost:${env.PORT || 3001}`, | ||
| target: `${wsProtocol}://localhost:${env.PORT || 3001}`, | ||
| ws: true | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "vite.config.js" -o -name "vite.config.ts" | head -5Repository: siteboon/claudecodeui
Length of output: 81
🏁 Script executed:
cat -n vite.config.js | sed -n '35,55p'Repository: siteboon/claudecodeui
Length of output: 728
🌐 Web query:
Vite proxy self-signed certificate secure: false configuration
💡 Result:
To proxy from Vite to an HTTPS backend with a self‑signed cert, set secure: false on that proxy rule. This tells the underlying proxy (Vite uses node-http-proxy-style options) not to validate the target TLS certificate (i.e., it effectively disables rejectUnauthorized). Use this for local/dev only. [1], [2]
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
server: {
proxy: {
"/api": {
target: "https://localhost:5001", // your HTTPS backend
changeOrigin: true,
secure: false, // allow self-signed certs
// rewrite: (p) => p.replace(/^\/api/, ""), // optional
},
},
},
});Notes:
server.proxyis dev-server only; it’s not part of the production build. [3]- Prefer fixing trust properly (install a cert or trust a local CA) instead of leaving verification disabled. [4]
Sources: node-http-proxy HTTPS/cert validation behavior [1]; example Vite proxy config using secure: false [2]; Vite server proxy is dev-only context [3]; why disabling TLS verification is risky [4].
🏁 Script executed:
cat -n vite.config.js | sed -n '1,45p'Repository: siteboon/claudecodeui
Length of output: 1978
🏁 Script executed:
find . -name "Contributing.md" -o -name "README.md" | head -5Repository: siteboon/claudecodeui
Length of output: 114
🏁 Script executed:
cat -n README.md | head -100Repository: siteboon/claudecodeui
Length of output: 4137
🏁 Script executed:
grep -i "self-signed\|certificate\|https\|ssl" README.mdRepository: siteboon/claudecodeui
Length of output: 2504
Add secure: false to proxy targets to allow self-signed certificates.
When HTTPS is enabled (as documented in the setup), the proxies target https://localhost:3001 and wss://localhost:3001. Without secure: false, Node's TLS will reject the self-signed certificate with UNABLE_TO_VERIFY_LEAF_SIGNATURE, causing all proxied requests to fail. The /api proxy must be expanded to object form to support the secure property.
Proposed fix
'/api': {
+ target: `${apiProtocol}://localhost:${env.PORT || 3001}`,
+ secure: false
},
'/ws': {
target: `${wsProtocol}://localhost:${env.PORT || 3001}`,
- ws: true
+ ws: true,
+ secure: false
},
'/shell': {
target: `${wsProtocol}://localhost:${env.PORT || 3001}`,
- ws: true
+ ws: true,
+ secure: false
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vite.config.js` around lines 42 - 50, The proxy entries for '/ws' and
'/shell' already use object form but need secure: false added to allow
self-signed certs, and the '/api' entry must be converted from string form to an
object with target set to `${apiProtocol}://localhost:${env.PORT || 3001}` and
secure: false; update the proxy config in vite.config.js (the '/api', '/ws',
'/shell' entries) to include secure: false so HTTPS/WSS proxies accept
self-signed certificates.
|
@stephenfeather Thanks for the PR. Can you address the conflicts and also comments from CodeRabbit and Copilot? |
There was a problem hiding this comment.
Pull request overview
This PR adds optional HTTPS support for both the backend Express server and the Vite development server. It complements PR #362 by adding related HTTPS improvements including protocol-aware proxy configuration in Vite and consistent environment variable handling.
Changes:
- Added HTTPS server creation for the backend via
SSL_ENABLED,SSL_CERT_PATH, andSSL_KEY_PATHenvironment variables with graceful fallback to HTTP - Added HTTPS support for Vite dev server via
VITE_HTTPS,VITE_SSL_CERT_PATH, andVITE_SSL_KEY_PATHwith protocol-aware proxy targets that automatically switch betweenhttp/wsandhttps/wss - Documented HTTPS configuration in README.md with self-signed certificate generation example and in .env.example with clear variable descriptions
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| vite.config.js | Adds HTTPS configuration for Vite dev server, reads SSL certificates from environment-specified paths, and configures protocol-aware proxy targets for backend API and WebSocket endpoints |
| server/index.js | Implements createWebServer() function to conditionally create HTTPS or HTTP server based on SSL configuration, updates protocol references in redirects and logging to use detected protocol |
| README.md | Adds documentation for enabling HTTPS in local development including OpenSSL command for generating self-signed certificates and environment variable configuration examples |
| .env.example | Documents new SSL/HTTPS environment variables for both backend and Vite with clear comments explaining their purpose |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
|
|
||
|
|
||
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED) |
There was a problem hiding this comment.
The fallback for certPath uses SSL_CERT_PATH, which is configured for the backend server. While this allows certificate reuse, it creates tight coupling between Vite and backend SSL configuration. Additionally, the fallback order means that setting SSL_CERT_PATH will override VITE_SSL_CERT_PATH even when the user specifically wants different certificates for different servers. Consider documenting this fallback behavior clearly in comments, or removing it to keep configurations independent.
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED) | |
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED) | |
| // Prefer Vite-specific TLS env vars; fall back to backend SSL_* only if VITE_SSL_* are not set. |
| key: fs.readFileSync(path.resolve(keyPath)) | ||
| } | ||
| } catch (error) { | ||
| console.warn('[vite] HTTPS enabled but SSL cert files could not be read. Falling back to HTTP.', error?.message || error) |
There was a problem hiding this comment.
The error object is being logged but the full error might not have a message property in all cases. Using error?.message || error will log the error object itself if there's no message, but it would be more robust to use error?.message || String(error) or add .toString() to ensure a string representation is always logged.
| console.warn('[vite] HTTPS enabled but SSL cert files could not be read. Falling back to HTTP.', error?.message || error) | |
| console.warn('[vite] HTTPS enabled but SSL cert files could not be read. Falling back to HTTP.', error?.message || String(error)) |
| cert: fs.readFileSync(path.resolve(certPath)), | ||
| key: fs.readFileSync(path.resolve(keyPath)) |
There was a problem hiding this comment.
The certificate and key file paths from environment variables are resolved without validation or sanitization. Consider adding path validation to ensure the paths don't traverse outside expected directories or reference sensitive system files. For example, check that the resolved paths are within the project directory or a designated certificates directory.
| const key = fs.readFileSync(path.resolve(keyPath)); | ||
| const cert = fs.readFileSync(path.resolve(certPath)); |
There was a problem hiding this comment.
The certificate and key file paths from environment variables are resolved without validation or sanitization. Consider adding path validation to ensure the paths don't traverse outside expected directories or reference sensitive system files. For example, check that the resolved paths are within the project directory or a designated certificates directory.
| const isTruthyEnv = (value) => | ||
| typeof value === 'string' && ['1', 'true', 'yes', 'on'].includes(value.toLowerCase()) |
There was a problem hiding this comment.
The isTruthyEnv function is duplicated - it's defined here as an arrow function and also in server/index.js as a regular function (line 211-213). Consider extracting this to a shared utility module to avoid code duplication and ensure consistency. This would make maintenance easier and prevent potential divergence in behavior.
| Optional: **Enable HTTPS with a self-signed certificate** (for local SSL testing) | ||
| ```bash | ||
| mkdir -p certs | ||
| openssl req -x509 -newkey rsa:2048 -nodes \ | ||
| -keyout certs/localhost.key \ | ||
| -out certs/localhost.crt \ | ||
| -days 365 \ | ||
| -subj "/CN=localhost" |
There was a problem hiding this comment.
The documentation shows how to generate a self-signed certificate but doesn't warn users that browsers will show security warnings for self-signed certificates. Consider adding a note that users will need to accept the security warning in their browser or add the certificate to their trusted certificates for local development.
| const apiProtocol = httpsConfig ? 'https' : 'http' | ||
| const wsProtocol = httpsConfig ? 'wss' : 'ws' |
There was a problem hiding this comment.
The proxy target protocol is determined by whether Vite successfully loaded HTTPS certificates (httpsConfig), but the backend's actual protocol is determined independently by SSL_ENABLED and its certificate loading. While both try to use the same certificate paths as fallback, a mismatch can occur if one server successfully loads the certificates while the other fails (e.g., due to permission differences, timing, or different file system states). This would cause proxy failures. Consider: (1) checking isTruthyEnv(env.SSL_ENABLED) directly and assuming the backend would successfully use HTTPS if enabled, (2) providing a separate BACKEND_PROTOCOL environment variable, or (3) adding validation logic to ensure both servers end up with matching protocols and warning the user if they don't.
| const apiProtocol = httpsConfig ? 'https' : 'http' | |
| const wsProtocol = httpsConfig ? 'wss' : 'ws' | |
| // Determine backend protocol based on backend's SSL configuration, not Vite's cert loading. | |
| const backendHttpsEnabled = isTruthyEnv(env.SSL_ENABLED) | |
| const apiProtocol = backendHttpsEnabled ? 'https' : 'http' | |
| const wsProtocol = backendHttpsEnabled ? 'wss' : 'ws' |
|
|
||
|
|
||
|
|
||
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED) |
There was a problem hiding this comment.
The condition checks SSL_ENABLED as a fallback for enabling HTTPS in Vite, but SSL_ENABLED is intended for the backend server, not the Vite dev server. This creates ambiguity about which server is being configured. Consider removing || isTruthyEnv(env.SSL_ENABLED) from this check to keep the configuration explicit and clear. If HTTPS should be enabled for Vite, users should set VITE_HTTPS=true explicitly.
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) || isTruthyEnv(env.SSL_ENABLED) | |
| const httpsEnabled = isTruthyEnv(env.VITE_HTTPS) |
Summary
Relationship to #362
This is not intended to compete with #362. It is a complementary follow-up that can be folded into #362 if preferred.
PR #362 adds backend SSL fallback behavior. This PR adds related improvements for consistency:
http/ws->https/wsswhen enabled)If maintainers prefer, I can close this PR and provide these commits/changes to be cherry-picked directly into #362.
Summary by CodeRabbit
Release Notes
New Features
Documentation