How TLS Certificates Are Generated and Managed in code-server: Self-Signed vs Custom
code-server supports both zero-configuration self-signed TLS certificates and production custom certificates, automatically generating self-signed credentials via the pem library when invoked with --cert alone, while requiring explicit --cert and --cert-key paths for custom deployments.
The coder/code-server project enables browser-based VS Code access, requiring robust TLS certificate generation and management to secure remote development sessions. The codebase implements a dual-mode architecture that distinguishes between automatic self-signed provisioning for local development and manual certificate loading for production environments. Understanding these implementation details ensures proper HTTPS configuration across different deployment scenarios.
TLS Certificate Modes in code-server
Self-Signed Certificate Generation (Zero-Config Mode)
When you launch code-server with the --cert flag without specifying a file path, the application enters automatic TLS provisioning mode. In this configuration, defined in src/node/cli.ts (lines 56-66), the system checks for existing certificate files and generates new ones only if absent.
Custom Certificate Deployment (Production Mode)
For production environments requiring certificates signed by a trusted Certificate Authority (CA), code-server accepts explicit file paths via --cert /path/to/cert.crt and --cert-key /path/to/key.pem. This mode, validated in src/node/cli.ts (lines 95-101), bypasses all generation logic and loads your provided cryptographic materials directly into the HTTPS server. If --cert receives a path but --cert-key is omitted, the CLI throws an explicit error: --cert-key is missing.
Certificate Generation Implementation Details
The core generation logic resides in src/node/util.ts within the generateCertificate() function (lines 89-106). This utility leverages the pem npm package to create X.509 certificates with proper Subject Alternative Names (SAN) and server authentication extensions.
The function first attempts to access existing files at paths.data (typically ~/.local/share/code-server on Linux/macOS) using fs.access(). If the files <hostname>.crt and <hostname>.key do not exist, it invokes pem.createCertificate() with a custom OpenSSL configuration that includes:
basicConstraints = CA:trueextendedKeyUsage = serverAuthsubjectAltNamematching the--cert-hostparameter (defaulting tolocalhost)
Once generated, the certificate and private key are persisted to the data directory, ensuring subsequent server restarts reuse the same credentials and avoid repetitive browser trust warnings.
HTTPS Server Initialization and Certificate Loading
Server creation occurs in src/node/app.ts (lines 73-81), where the application conditionally uses httpolyglot to handle both HTTP and HTTPS traffic on the same port. When args.cert is present, the server options include:
cert: Buffer fromfs.readFileof the certificate pathkey: Buffer fromfs.readFileof the key path
For self-signed mode, these paths point to the files generated in paths.data; for custom mode, they reference the user-supplied locations from the CLI arguments.
Enforcing TLS Connections
To ensure encrypted traffic, src/node/routes/index.ts (lines 88-95) implements middleware that checks the connection encryption state. When args.cert is enabled and (req.connection as tls.TLSSocket).encrypted is falsy, the server responds with a 301 redirect to the equivalent https:// URL, preventing accidental unencrypted access.
Configuration Examples
Self-signed certificate with default localhost
code-server --cert
This generates localhost.crt and localhost.key in the data directory.
Self-signed certificate for custom hostname
code-server --cert --cert-host my.dev.local
The SAN field in the generated certificate will contain my.dev.local.
Custom CA-signed certificate
code-server \
--cert /etc/ssl/certs/mydomain.crt \
--cert-key /etc/ssl/private/mydomain.key \
--bind-addr 0.0.0.0:8080
Reference implementation for certificate generation
import { promises as fs } from "fs"
import path from "path"
import { paths } from "./util"
async function generateSelfSigned(hostname: string) {
const certPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.crt");
const keyPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.key`);
try {
await Promise.all([fs.access(certPath), fs.access(keyPath)]);
} catch {
const pem = require("pem");
const { certificate, serviceKey } = await new Promise((res, rej) =>
pem.createCertificate(
{
selfSigned: true,
commonName: hostname,
config: `
[req]
req_extensions = v3_req
[ v3_req ]
basicConstraints = CA:true
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${hostname}
`,
},
(err: any, result: any) => (err ? rej(err) : res(result))
)
);
await fs.mkdir(paths.data, { recursive: true });
await Promise.all([
fs.writeFile(certPath, certificate),
fs.writeFile(keyPath, serviceKey),
]);
}
return { cert: certPath, certKey: keyPath };
}
Summary
- Self-signed mode: Triggered by
--certwithout a value; generates certificates viagenerateCertificate()insrc/node/util.tsand stores them inpaths.data. - Custom mode: Requires both
--cert <path>and--cert-key <path>; loads existing files without generation. - Persistence: Generated certificates are reused across restarts to avoid trust prompt churn.
- Server: Uses
httpolyglotinsrc/node/app.tsto serve HTTP/HTTPS on the same port. - Security: Automatic HTTP-to-HTTPS redirects are enforced in
src/node/routes/index.tswhen TLS is active.
Frequently Asked Questions
Where does code-server store self-signed TLS certificates?
Self-signed certificates are stored in the platform-specific data directory (accessed via paths.data in src/node/util.ts), typically located at ~/.local/share/code-server on Linux and macOS systems. The files are named <hostname>.crt and <hostname>.key, with dots in hostnames converted to underscores.
Can I use a self-signed certificate for a domain other than localhost?
Yes. Specify your desired hostname using the --cert-host flag (e.g., code-server --cert --cert-host myserver.local). The generateCertificate() function incorporates this value into the certificate's Subject Alternative Name (SAN) field, allowing the certificate to validate against that specific domain.
Why does code-server require both --cert and --cert-key for custom certificates?
The CLI validation logic in src/node/cli.ts enforces this requirement to prevent misconfigurations. When providing a custom certificate path, the corresponding private key is mandatory for TLS handshakes. If --cert receives a file path but --cert-key is omitted, the application throws an explicit error: --cert-key is missing.
How does code-server handle HTTP traffic when HTTPS is enabled?
When TLS is active, the middleware in src/node/routes/index.ts inspects the req.connection.encrypted property on every request. If the connection is not encrypted, the server responds with a 301 redirect to the HTTPS equivalent URL, ensuring all traffic uses the encrypted channel provided by httpolyglot.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →