nats-io/nats-server · error
not same origin
Error message
not same origin
What it means
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.
Source
Thrown at server/websocket.go:1073
if err != nil {
return err
}
oh, op, err := wsGetHostAndPort(u.Scheme == "https", u.Host)
if err != nil {
return err
}
// If checking same origin, compare with the http's request's Host.
if checkSame {
rh, rp, err := wsGetHostAndPort(r.TLS != nil, r.Host)
if err != nil {
return err
}
rs := "http"
if r.TLS != nil {
rs = "https"
}
if oh != rh || op != rp || !strings.EqualFold(u.Scheme, rs) {
return errors.New("not same origin")
}
// I guess it is possible to have cases where one wants to check
// same origin, but also that the origin is in the allowed list.
// So continue with the next check.
}
if !listEmpty {
w.mu.RLock()
origins := w.allowedOrigins[oh]
w.mu.RUnlock()
var allowed bool
for _, ao := range origins {
if u.Scheme == ao.scheme && op == ao.port {
allowed = true
break
}
}
if !allowed {
return errors.New("not in the allowed list")View on GitHub (pinned to 3a66a489d2)
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
Example fix
// before (nginx) proxy_set_header Host $backend_host; // after proxy_set_header Host $host;
Defensive patterns
Strategy: validation
Validate before calling
// client-side pre-check before connecting
const origin = window.location.origin; // e.g. https://app.example.com
const target = new URL('wss://nats.example.com:443');
const sameOrigin = origin === `${target.protocol === 'wss:' ? 'https:' : 'http:'}//${target.host}`;
if (!sameOrigin) console.warn('origin differs; ensure allowed_origins covers', origin); Try / catch
// server-side: surface and inspect the rejection
if err := wsCheckOrigin(r); err != nil {
log.Printf("origin check failed: origin=%q host=%q err=%v", r.Header.Get("Origin"), r.Host, err)
http.Error(w, "origin rejected", http.StatusForbidden)
return
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- not in the allowed list
- proxy is configured but remote URL %s requires TLS and no TL
- MQTT clients over websocket must connect to the Websocket po
- ${message} (dynamic websocket protocol error, sent to client
- websocket requires TLS configuration
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/b16199669e8a5b42.
Report an issue: GitHub.