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

  1. Configure the proxy to pass the original Host header (e.g. nginx: proxy_set_header Host $host)
  2. Align the Origin scheme with the actual TLS state of the connection
  3. Instead of relying on same-origin matching, list the real frontend origin explicitly in allowed_origins
  4. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/b16199669e8a5b42. Report an issue: GitHub.