{"record":{"id":"e31ff36ff16ce26d","repo":"coder/code-server","slug":"unauthorized-e31ff3","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"src/node/routes/domainProxy.ts","lineNumber":94,"sourceCode":"\n    // Assume anything that explicitly accepts text/html is a user browsing a\n    // page (as opposed to an xhr request). Don't use `req.accepts()` since\n    // *every* request that I've seen (in Firefox and Chromium at least)\n    // includes `*/*` making it always truthy. Even for css/javascript.\n    if (req.headers.accept && req.headers.accept.includes(\"text/html\")) {\n      // Let the login through.\n      if (/\\/login\\/?/.test(req.path)) {\n        return next()\n      }\n      // Redirect all other pages to the login.\n      const to = self(req)\n      return redirect(req, res, \"login\", {\n        to: to !== \"/\" ? to : undefined,\n      })\n    }\n\n    // Everything else gets an unauthorized message.\n    throw new HttpError(\"Unauthorized\", HttpCode.Unauthorized)\n  }\n\n  proxy.web(req, res, {\n    ignorePath: true,\n    target: `http://0.0.0.0:${port}${req.originalUrl}`,\n  })\n})\n\nexport const wsRouter = WsRouter()\n\nwsRouter.ws(/.*/, async (req, _, next) => {\n  const port = maybeProxy(req)\n  if (!port) {\n    return next()\n  }\n\n  ensureProxyEnabled(req)\n  ensureOrigin(req)","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/coder/code-server/blob/51f90a376b42e217b38937410fe2855e0c1db87e/src/node/routes/domainProxy.ts#L76-L112","documentation":"The domain proxy route (domainProxy.ts:94) checks authentication for non-login requests. If the user is not authenticated and the path is not /login (which is allowed through), it throws HttpError 401 instead of proxying. This prevents unauthenticated users from reaching proxied ports via the domain-based proxy.","triggerScenarios":"A request to a subdomain proxy URL (e.g. port-3000.codomain) without a valid session cookie and not targeting /login.","commonSituations":"Sessions expired while a proxied app tab was open; embedding a proxied app in another page with no cookie; race at startup where the proxied app loads before login completes.","solutions":["Authenticate at code-server's /login first to obtain a valid session cookie","Ensure the proxied app shares/sends the code-server session cookie","For automated clients, perform the login flow then reuse the cookie for proxy requests"],"exampleFix":"// before\nfetch('https://3000--user.host/proxied-path')  // 401\n\n// after\nawait login('https://user.host/login', password)\nfetch('https://3000--user.host/proxied-path')  // session cookie sent","handlingStrategy":"try-catch","validationCode":"// For API clients: ensure a valid session before proxy calls\nasync function ensureLoggedIn(): Promise<void> {\n  const r = await fetch(\"/api/status\")\n  if (r.status === 401) throw new Error(\"Not authenticated for domain proxy\")\n}","typeGuard":"import { HttpError, HttpCode } from \"../../common/http\"\nfunction isProxyUnauthorized(e: unknown): boolean {\n  return e instanceof HttpError && e.status === HttpCode.Unauthorized\n}","tryCatchPattern":"try {\n  // proxied request\n} catch (e) {\n  if (isProxyUnauthorized(e)) {\n    window.location.href = `/login?to=${encodeURIComponent(window.location.href)}`\n  } else throw e\n}","preventionTips":["Authenticate once and reuse the session cookie for all proxied subdomains","Handle 401 in proxied-app clients by redirecting to code-server /login","Avoid putting proxied apps in iframes without sharing the session cookie"],"tags":["http","proxy","auth","middleware","unauthorized","domain-proxy"],"backgroundTag":null,"analyzedSha":"51f90a376b42e217b38937410fe2855e0c1db87e","analyzedAt":"2026-08-12T11:27:34.273Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}