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
Hostheader 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:
- Validates proxy enablement via
ensureProxyEnabled(req)(returns 403 if--disable-proxyis set) - Enforces authentication via
authenticated(req), redirecting unauthenticated users to the login page - Forwards HTTP traffic using
proxy.web(req, res, { target: "http://0.0.0.0:<port>" }) - 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:
- Extracts the port from
req.params.port - Constructs the upstream URL as
http://0.0.0.0:<port> - Strips the
/proxy/<port>prefix from the path before forwarding, so the upstream service receives/api/statusinstead 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: trueprevents the proxy from stripping the base path- The upstream receives the full path including
/absproxy/4000/... - An optional
--abs-proxy-base-pathCLI 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
ECONNREFUSEDand returns a descriptive error page when the target port is not listening - Redirect rewriting: The
redirectevent handler insrc/node/proxy.ts(lines 21-27) rewritesLocationheaders to maintain the proxy path prefix for non-absproxy requests - WebSocket upgrades: Separate
proxy.wscalls handle theUpgradeheader 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
Hostheader patterns) and path-based routing (via/proxy/:portand/absproxy/:portURL segments). - Both systems share common infrastructure in
src/node/proxy.tsfor HTTP/WebSocket forwarding, redirect rewriting, and error handling. - Domain proxies use regex patterns built from
--proxy-domainCLI arguments to extract target ports from subdomains. - Path proxies parse ports from URL parameters, with
/absproxypreserving the full path for applications requiring fixed base URLs. - Authentication is enforced via
authenticatedandensureAuthenticated, withensureProxyEnabledgating 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →