{"record":{"id":"b16199669e8a5b42","repo":"nats-io/nats-server","slug":"not-same-origin","errorCode":null,"errorMessage":"not same origin","messagePattern":"not same origin","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/websocket.go","lineNumber":1073,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\toh, op, err := wsGetHostAndPort(u.Scheme == \"https\", u.Host)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// If checking same origin, compare with the http's request's Host.\n\tif checkSame {\n\t\trh, rp, err := wsGetHostAndPort(r.TLS != nil, r.Host)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trs := \"http\"\n\t\tif r.TLS != nil {\n\t\t\trs = \"https\"\n\t\t}\n\t\tif oh != rh || op != rp || !strings.EqualFold(u.Scheme, rs) {\n\t\t\treturn errors.New(\"not same origin\")\n\t\t}\n\t\t// I guess it is possible to have cases where one wants to check\n\t\t// same origin, but also that the origin is in the allowed list.\n\t\t// So continue with the next check.\n\t}\n\tif !listEmpty {\n\t\tw.mu.RLock()\n\t\torigins := w.allowedOrigins[oh]\n\t\tw.mu.RUnlock()\n\t\tvar allowed bool\n\t\tfor _, ao := range origins {\n\t\t\tif u.Scheme == ao.scheme && op == ao.port {\n\t\t\t\tallowed = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !allowed {\n\t\t\treturn errors.New(\"not in the allowed list\")","sourceCodeStart":1055,"sourceCodeEnd":1091,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/websocket.go#L1055-L1091","documentation":"When websocket allowed_origins contains a same-origin style entry, the server compares the request's Origin with the expected Host (host, port, and scheme, case-insensitive for scheme). If any differ, the upgrade fails with \"not same origin\". This is the strict same-origin branch of the CORS check before falling through to the allowed-list check.","triggerScenarios":"Client Origin header host or port differs from the Host header of the request (proxy rewriting Host), or Origin scheme (http/https) mismatches whether the connection is TLS — e.g. an https browser page connecting over plain ws through a TLS-terminating setup that mangles the comparison.","commonSituations":"Reverse proxy forwards requests without preserving the Host header; server behind TLS termination compares https origin against http; dev server on a different port than the API host.","solutions":["Configure the proxy to pass the original Host header (e.g. nginx: proxy_set_header Host $host)","Align the Origin scheme with the actual TLS state of the connection","Instead of relying on same-origin matching, list the real frontend origin explicitly in allowed_origins","Ensure clients connect to the same host:port that serves the web page, or use the allowed list"],"exampleFix":"// before (nginx)\nproxy_set_header Host $backend_host;\n// after\nproxy_set_header Host $host;","handlingStrategy":"validation","validationCode":"// client-side pre-check before connecting\nconst origin = window.location.origin;           // e.g. https://app.example.com\nconst target = new URL('wss://nats.example.com:443');\nconst sameOrigin = origin === `${target.protocol === 'wss:' ? 'https:' : 'http:'}//${target.host}`;\nif (!sameOrigin) console.warn('origin differs; ensure allowed_origins covers', origin);","typeGuard":null,"tryCatchPattern":"// server-side: surface and inspect the rejection\nif err := wsCheckOrigin(r); err != nil {\n    log.Printf(\"origin check failed: origin=%q host=%q err=%v\", r.Header.Get(\"Origin\"), r.Host, err)\n    http.Error(w, \"origin rejected\", http.StatusForbidden)\n    return\n}","preventionTips":["Configure proxies to preserve the Host header","Keep TLS termination consistent with the Origin scheme (wss->https)","Prefer explicit allowed_origins over implicit same-origin assumptions","Log Origin and Host headers when debugging upgrade failures"],"tags":["websocket","cors","same-origin","proxy"],"backgroundTag":"origin-not-allowed","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}