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

> Explore code-servers dual-mode proxy architecture for domain and path routing. Learn how it handles HTTP and WebSocket traffic with shared authentication and CORS support.

- Repository: [Coder/code-server](https://github.com/coder/code-server)
- Tags: architecture
- Published: 2026-03-01

---

**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](https://github.com/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`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/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`:

```typescript
// 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`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/src/node/proxy.ts) and [`src/node/http.ts`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/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

```bash
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

```bash
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

```bash
code-server --abs-proxy-base-path "/myapp"

```

```bash
curl https://myhost.com/absproxy/4000/dashboard

```

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

### WebSocket Proxying

```javascript
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`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/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`](https://github.com/coder/code-server/blob/main/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.