Resolving 1006 WebSocket Connection Issues in Coder
Last updated: June 25, 2026
Applies to Coder v2 and use of:
VS Code Web 1.97+ and newer
Symptom
When using web-based applications in Coder, such as VS Code Web, the application
fails to load correctly. Extensions (e.g., GitHub Copilot) do not function,
and the browser's developer console shows a
WebSocket close with status code 1006 error.
This indicates an abnormal closure of the connection.
You may also observe that the WebSocket connection attempts are being made
to an incorrect port (e.g., port 80 for a secure
wss:// connection, or an internal-only port
like 3000, 8000 or 8080).
Cause
This issue is almost always caused by a misconfiguration of the reverse proxy, ingress controller, or load balancer that sits in front of your Coder installation.
The typical scenario is:
- The proxy/load balancer terminates TLS (handling HTTPS traffic).
- It then forwards the request to Coder over plain HTTP.
-
Coder, seeing a plain HTTP request, incorrectly constructs WebSocket
URLs using
ws://or the wrong port, based on the headers it receives from the proxy (likeX-Forwarded-Proto: httpandX-Forwarded-Port: 80).
This problem is more prominent with recent versions of VS Code Web (v1.97 and newer), which are stricter about how they handle forwarded headers when establishing WebSocket connections. In some cases, this can also be triggered by HTTP/2 connection coalescing issues within the proxy.
Solution
The recommended approach is to correctly configure your proxy/ingress to forward proper headers to Coder so it can construct the correct WebSocket URLs. Alternatively, you can enable TLS directly on Coder.
Solution 1: Configure Proxy Headers (Recommended)
This is the most common deployment pattern where TLS is terminated at the
proxy/ingress layer. Ensure Coder receives the correct
X-Forwarded-Proto and related headers.
Step 1: Set Coder's Access URL
Ensure Coder is configured with the correct public-facing URL. Set at minimum these environment variables:
# Required: The external URL users access Coder at
CODER_ACCESS_URL=https://coder.example.com
# Optional: Required for port forwarding via subdomains
CODER_WILDCARD_ACCESS_URL=*.coder.example.com
For Helm deployments, configure these in values.yaml and recommend
adding the CODER_PROXY_* or your X-Forwarded-For may be lost:
coder:
env:
- name: CODER_ACCESS_URL
value: "https://coder.example.com"
- name: CODER_WILDCARD_ACCESS_URL
value: "*.coder.example.com"
- CODER_PROXY_TRUSTED_HEADERS:
value: "X-Forwarded-For,X-Forwarded-Proto"
- CODER_PROXY_TRUSTED_ORIGINS:
value: "10.0.0.0/8" # LB pod CIDR
Step 2: Configure Your Proxy/Ingress
Ensure your proxy forwards the correct headers and properly handles WebSocket upgrades.
NGINX (Standalone)
server {
server_name coder.example.com *.coder.example.com;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
# Forward correct headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
NGINX Ingress Controller (Kubernetes)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: coder
annotations:
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
spec:
ingressClassName: nginx
tls:
- hosts:
- coder.example.com
- "*.coder.example.com"
secretName: coder-tls
rules:
- host: coder.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: coder
port:
number: 80
- host: "*.coder.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: coder
port:
number: 80
Kubernetes Gateway API (HTTPRoute)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: coder
spec:
parentRefs:
- name: my-gateway
hostnames:
- coder.example.com
- "*.coder.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set:
- name: X-Forwarded-Proto
value: https
backendRefs:
- name: coder
port: 80
Caddy
coder.example.com, *.coder.example.com {
reverse_proxy localhost:3000
}
Solution 2: Enable TLS Directly on Coder
If you prefer Coder to handle TLS termination directly, you can configure it with TLS certificates.
Environment Variables
CODER_TLS_ENABLE=true
CODER_TLS_CERT_FILE=/path/to/certificate.crt
CODER_TLS_KEY_FILE=/path/to/private.key
CODER_ACCESS_URL=https://coder.example.com
CODER_WILDCARD_ACCESS_URL=*.coder.example.com
Helm Chart Configuration
Create a Kubernetes TLS secret and reference it in values.yaml:
# Create the TLS secret
kubectl create secret tls coder-tls \
--cert=/path/to/certificate.crt \
--key=/path/to/private.key \
-n coder
coder:
env:
- name: CODER_ACCESS_URL
value: "https://coder.example.com"
- name: CODER_WILDCARD_ACCESS_URL
value: "*.coder.example.com"
# Mount TLS certificates
tls:
secretNames:
- coder-tls
tls.secretNames, the Helm
chart automatically sets CODER_TLS_ENABLE,
CODER_TLS_CERT_FILE, and CODER_TLS_KEY_FILE. Do
not set these manually.
With this configuration:
- Coder listens on port 8080 (HTTP) and 8443 (HTTPS)
- Your load balancer/ingress should use TLS passthrough or forward traffic to the HTTPS port
Using Helm Ingress with TLS Termination at Coder
If you want Coder to handle TLS and also use the built-in ingress support, configure the ingress to point to the HTTPS backend:
coder:
env:
- name: CODER_ACCESS_URL
value: "https://coder.example.com"
tls:
secretNames:
- coder-tls
# When using TLS on Coder, typically you would NOT use
# the built-in ingress as it creates HTTP backends.
# Instead, use a LoadBalancer service or configure
# ingress with TLS passthrough externally.
ingress:
enable: false
service:
type: LoadBalancer
Troubleshooting
Verify Headers Are Being Forwarded
Check the Coder server logs or use browser developer tools to verify the
WebSocket URL being constructed. If you see URLs like ws://
instead of wss://, or incorrect ports, the headers are not being
forwarded correctly.
Check for HTTP/2 Issues
Some proxies have issues with WebSocket upgrades over HTTP/2. Try forcing HTTP/1.1 between your proxy and Coder:
-
NGINX:
proxy_http_version 1.1; - Envoy: Configure the cluster to use HTTP/1.1
Cloudflare Considerations
If using Cloudflare:
- Ensure WebSockets are enabled in your Cloudflare dashboard
- Verify SSL/TLS mode is set to "Full" or "Full (strict)"
-
Check that Cloudflare isn't modifying
X-Forwarded-*headers unexpectedly
Common Misconfigurations
CODER_HTTP_ADDRESS, CODER_TLS_ENABLE,
CODER_TLS_CERT_FILE, or CODER_TLS_KEY_FILE when
using the Helm chart's tls.secretNames option, as these are
automatically configured and will cause conflicts.