joewalnes/websocketd · error

same origin policy violated

Error message

same origin policy violated

What it means

checkOrigin enforces cross-origin WebSocket security. When the request's Origin header parses to a host/port different from the server's own host/port (and no --origin list is consulted first to save it), the upgrade is rejected with this error and gorilla/websocket answers the handshake with HTTP 403. It exists to prevent other websites' JavaScript from driving your websocketd scripts with the user's credentials.

Source

Thrown at libwebsocketd/http.go:379

	}

	log.Associate("origin", originParsed.String())

	if config.SameOrigin || config.AllowOrigins != nil {
		originServer, originPort, err := tellHostPort(originParsed.Host, originParsed.Scheme == "https")
		if err != nil {
			log.Access("session", "Origin hostname parsing error: %s", err)
			return err
		}
		if config.SameOrigin {
			localServer, localPort, err := tellHostPort(req.Host, req.TLS != nil)
			if err != nil {
				log.Access("session", "Request hostname parsing error: %s", err)
				return err
			}
			if originServer != localServer || originPort != localPort {
				log.Access("session", "Same origin policy mismatch")
				return fmt.Errorf("same origin policy violated")
			}
		}
		if config.AllowOrigins != nil {
			if !matchOrigin(originServer, originPort, originParsed.Scheme, config.AllowOrigins) {
				log.Access("session", "Origin is not listed in allowed list")
				return fmt.Errorf("origin list matches were not found")
			}
		}
	}
	return nil
}

// matchOrigin checks if the given origin server/port/scheme matches any entry
// in the allowed origins list. Extracted for testability.
//
// Port semantics (issue #473): an entry with an explicit port matches that
// port only. A portless entry matches only the scheme's default port (80 for
// http, 443 for https — both, if the entry carries no scheme). Appending

View on GitHub (pinned to 7a8683dc7f)

Solutions

  1. Add the frontend's exact origin to --origin, e.g. --origin=http://app.example.com:8080 (repeatable or comma-separated per matchOrigin semantics).
  2. Serve the client page from the same host and port as websocketd so the same-origin check passes automatically.
  3. If behind a proxy, ensure the Host header forwarded to websocketd matches the origin your page uses, or disable the proxy's host rewriting.
  4. For non-browser clients (curl, wscat) the Origin header is irrelevant or absent — this only blocks browser handshakes that send a mismatching Origin.

Example fix

// before
websocketd --port=8080 ./chat.sh
// after
websocketd --port=8080 --origin=http://localhost:3000 ./chat.sh
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL('ws://localhost:8080/echo');
if (url.host !== location.host) throw new Error(`origin ${url.host} != server ${location.host}; pass --origin=${location.origin}`);

Try / catch

try {
  const ws = new WebSocket('ws://localhost:8080/echo');
  ws.onerror = () => console.error('handshake rejected (likely 403 origin mismatch)');
} catch (e) { console.error('ws setup failed', e); }

Prevention

When it happens

Trigger: A browser page served from http://app.example.com:8080 opens a WebSocket to ws://otherhost:9999/ while the server runs without --origin; or the page uses localhost vs 127.0.0.1 mismatch; or a reverse proxy rewrites the Host so the server compares it against a different origin than the browser sent.

Common situations: Frontend on a different port than the dev server; serving the HTML from a CDN/static host while websocketd runs elsewhere; forgetting the --origin flag when splitting frontend and backend; tests (TestCheckOrigin) exercising mismatched host/port pairs.

Related errors


AI-assisted analysis of joewalnes/websocketd@7a8683dc7f (2026-09-03). Data as JSON: /api/errors/182795634ba8d884. Report an issue: GitHub.