Architecture of code-server's Proxy System for Domain and Path Routing

code-server implements a dual-mode proxy architecture that routes HTTP and WebSocket traffic to arbitrary local ports using either domain-based host matching or path-based URL segments, with shared authentication and CORS handling.

The proxy system in coder/code-server enables developers to expose services running on local ports through the same HTTP(S) endpoint as the IDE. This architecture supports two distinct routing strategies—domain-based and path-based—that share common infrastructure for security, request forwarding, and WebSocket upgrade handling.

Domain-Based vs. Path-Based Proxy Routing

code-server provides two complementary mechanisms to accommodate different deployment scenarios:

  • Domain-based proxy: Uses the HTTP Host header to extract the target port from a subdomain pattern (e.g., 8080-myapp.example.com). This requires DNS wildcard configuration but provides clean URLs.
  • Path-based proxy: Embeds the target port in the URL path (e.g., /proxy/8080/api/status). This works without DNS changes but requires the proxied application to handle path prefixes or use absolute proxy mode.

Both mechanisms rely on the central http-proxy instance defined in src/node/proxy.ts and enforce authentication via ensureProxyEnabled and authenticated checks.

Domain-Based Proxy Implementation

The domain proxy logic resides in src/node/routes/domainProxy.ts. It intercepts all incoming requests and attempts to match the Host header against user-defined patterns.

Port Extraction from Host Headers

The system converts CLI patterns (supplied via --proxy-domain) into regular expressions. For a pattern like {{port}}.example.com, the code generates a regex that captures the port number from a host such as 8080.example.com:

// From src/node/routes/domainProxy.ts
const proxyDomainToRegex = (pattern: string): RegExp => {
  return new RegExp(pattern.replace("{{port}}", "(\\d+)").replace("{{host}}", ".+"))
}

The maybeProxy function iterates through these regexes and returns the first captured port group.

Request Handling and WebSocket Support

Once a port is identified, the domain proxy:

  1. Validates proxy enablement via ensureProxyEnabled(req) (returns 403 if --disable-proxy is set)
  2. Enforces authentication via authenticated(req), redirecting unauthenticated users to the login page
  3. Forwards HTTP traffic using proxy.web(req, res, { target: "http://0.0.0.0:<port>" })
  4. Upgrades WebSocket connections via proxy.ws(req, socket, head, { target: ... })

CORS pre-flight requests are handled specially when --skip-auth-preflight is enabled, allowing unauthenticated OPTIONS requests to pass through.

Path-Based Proxy Implementation

The path proxy logic in src/node/routes/pathProxy.ts handles routes mounted at /proxy/:port and /absproxy/:port.

Standard Proxy Routes

For standard proxy requests (/proxy/3000/api/status), the system:

  1. Extracts the port from req.params.port
  2. Constructs the upstream URL as http://0.0.0.0:<port>
  3. Strips the /proxy/<port> prefix from the path before forwarding, so the upstream service receives /api/status instead of /proxy/3000/api/status

This requires the upstream application to be path-agnostic or configured with a base URL.

Absolute Proxy with Base Path Preservation

The /absproxy/:port route provides passthrough mode for applications that require fixed base paths. When using this mode:

  • passthroughPath: true prevents the proxy from stripping the base path
  • The upstream receives the full path including /absproxy/4000/...
  • An optional --abs-proxy-base-path CLI argument can prepend a custom prefix to the upstream URL

The proxy also rewrites absolute Location headers in redirects. For standard proxy requests, a redirect to /foo becomes /proxy/<port>/foo, while absproxy requests leave headers unchanged since the base path is already preserved.

Shared Proxy Infrastructure

Both routing mechanisms rely on common utilities in src/node/proxy.ts and src/node/http.ts.

Enablement and Authentication Checks

The ensureProxyEnabled function checks the --disable-proxy flag and returns a 403 response if the feature is disabled. Authentication is enforced via authenticated (for HTTP) and ensureAuthenticated (for WebSockets), with special handling for static login assets.

Target URL Construction and Redirect Rewriting

The central http-proxy instance handles the actual forwarding. Key features include:

  • Error handling: Intercepts ECONNREFUSED and returns a descriptive error page when the target port is not listening
  • Redirect rewriting: The redirect event handler in src/node/proxy.ts (lines 21-27) rewrites Location headers to maintain the proxy path prefix for non-absproxy requests
  • WebSocket upgrades: Separate proxy.ws calls handle the Upgrade header for both domain and path-based routes

Configuration Examples

Enable Domain-Based Proxying

code-server --proxy-domain "*.example.com" --proxy-domain "dev-{{port}}.example.com"

Requests to https://dev-8080.example.com/ route to port 8080.

Path-Based Proxy Request

curl -H "Cookie: $(code-server --print-auth-cookie)" \
     https://myhost.com/proxy/3000/api/status

Forwards to http://0.0.0.0:3000/api/status.

Absolute Proxy with Custom Base Path

code-server --abs-proxy-base-path "/myapp"
curl https://myhost.com/absproxy/4000/dashboard

The upstream receives /myapp/absproxy/4000/dashboard with passthroughPath: true.

WebSocket Proxying

const ws = new WebSocket("wss://dev-8080.example.com/vscode/websocket");

The domain proxy extracts port 8080 and upgrades the connection to ws://0.0.0.0:8080.

Summary

  • code-server implements dual proxy mechanisms: domain-based routing (via Host header patterns) and path-based routing (via /proxy/:port and /absproxy/:port URL segments).
  • Both systems share common infrastructure in src/node/proxy.ts for HTTP/WebSocket forwarding, redirect rewriting, and error handling.
  • Domain proxies use regex patterns built from --proxy-domain CLI arguments to extract target ports from subdomains.
  • Path proxies parse ports from URL parameters, with /absproxy preserving the full path for applications requiring fixed base URLs.
  • Authentication is enforced via authenticated and ensureAuthenticated, with ensureProxyEnabled gating the feature via --disable-proxy.

Frequently Asked Questions

How does code-server determine which port to proxy to when using domain-based routing?

code-server extracts the port from the HTTP Host header using regular expressions built from --proxy-domain patterns. In src/node/routes/domainProxy.ts, the proxyDomainToRegex function converts patterns like {{port}}.example.com into regexes that capture the port number (e.g., 8080 from 8080.example.com). The first matching pattern yields the target port.

What is the difference between /proxy/:port and /absproxy/:port routes?

The /proxy/:port route strips the proxy path prefix before forwarding, so an upstream service receives only the subpath (e.g., /api/status instead of /proxy/3000/api/status). The /absproxy/:port route uses passthroughPath: true to preserve the full URL path, allowing applications that require a fixed base path to function correctly. Additionally, /absproxy disables automatic redirect rewriting since the base path is already part of the request URL.

How does code-server handle WebSocket connections through the proxy?

Both domain and path-based proxies support WebSocket upgrades via the http-proxy library. In src/node/routes/domainProxy.ts, WebSocket requests are handled by proxy.ws() after host validation and authentication via ensureAuthenticated. Similarly, src/node/routes/pathProxy.ts exposes wsProxy handlers mounted on the WebSocket router. The proxy forwards the Upgrade header to the target port (e.g., ws://0.0.0.0:8080), enabling real-time communication with proxied services.

Can I disable the proxy functionality entirely, and what happens if I try to use it when disabled?

Yes, you can disable both proxy mechanisms by launching code-server with the --disable-proxy CLI flag. When disabled, the ensureProxyEnabled function (defined in src/node/http.ts and invoked in both proxy route handlers) returns a 403 Forbidden response to any request matching proxy routes. This prevents unauthorized access to internal services when the proxy feature is not explicitly enabled.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →