{"record":{"id":"b2a0fdd23f2bdc3a","repo":"nats-io/nats-server","slug":"websocket-v","errorCode":null,"errorMessage":"websocket: %v","messagePattern":"websocket: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/websocket.go","lineNumber":1175,"sourceCode":"\t\t}\n\t}\n\t// Token/Username not possible if there are users/nkeys\n\tif len(o.Users) > 0 || len(o.Nkeys) > 0 {\n\t\tif wo.Username != _EMPTY_ {\n\t\t\treturn fmt.Errorf(\"websocket authentication username not compatible with presence of users/nkeys\")\n\t\t}\n\t\tif wo.Token != _EMPTY_ {\n\t\t\treturn fmt.Errorf(\"websocket authentication token not compatible with presence of users/nkeys\")\n\t\t}\n\t}\n\t// Using JWT requires Trusted Keys\n\tif wo.JWTCookie != _EMPTY_ {\n\t\tif len(o.TrustedOperators) == 0 && len(o.TrustedKeys) == 0 {\n\t\t\treturn fmt.Errorf(\"trusted operators or trusted keys configuration is required for JWT authentication via cookie %q\", wo.JWTCookie)\n\t\t}\n\t}\n\tif err := validatePinnedCerts(wo.TLSPinnedCerts); err != nil {\n\t\treturn fmt.Errorf(\"websocket: %v\", err)\n\t}\n\n\t// Check for invalid headers here.\n\tfor key := range wo.Headers {\n\t\tk := strings.ToLower(key)\n\t\tswitch k {\n\t\tcase \"host\",\n\t\t\t\"content-length\",\n\t\t\t\"connection\",\n\t\t\t\"upgrade\",\n\t\t\t\"nats-no-masking\":\n\t\t\treturn fmt.Errorf(\"websocket: invalid header %q not allowed\", key)\n\t\t}\n\n\t\tif strings.HasPrefix(k, \"sec-websocket-\") {\n\t\t\treturn fmt.Errorf(\"websocket: invalid header %q, \\\"Sec-WebSocket-\\\" prefix not allowed\", key)\n\t\t}\n\t}","sourceCodeStart":1157,"sourceCodeEnd":1193,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/websocket.go#L1157-L1193","documentation":"Generic wrapper the websocket validator uses for TLS pinned-certificate failures: validatePinnedCerts rejected an entry in websocket.tls_pinned_certs and the raw validation error is wrapped with a \"websocket: \" prefix. The server fails startup.","triggerScenarios":"Configuring websocket { tls_pinned_certs: [...] } containing an invalid SPKI SHA-256 base64 hash, a cert format the validator doesn't recognize, or mixed/malformed entries.","commonSituations":"Hand-copying a pin with wrong base64 padding, using the full certificate hash instead of the SPKI hash, or typo'd prefix (must be like \"sha256/...\").","solutions":["Regenerate each pin as base64(SHA256(DER of SubjectPublicKeyInfo)) and use the standard 'sha256/<base64>' form","Validate the pin value independently (e.g. openssl x509 | derive SPKI digest) before adding it","Remove or fix the offending entry named in the wrapped error"],"exampleFix":"// before\nwebsocket { tls_pinned_certs: [\"abcdef\"] }\n// after\nwebsocket { tls_pinned_certs: [\"sha256/UC1o3S8GQyMWZaUOB4LE6HiuZNqSgIIfW1rNKghabH4=\"] }","handlingStrategy":"validation","validationCode":"for _, pin := range opts.Websocket.TLSPinnedCerts {\n  if !strings.HasPrefix(pin, \"sha256/\") {\n    return fmt.Errorf(\"bad pin %q: must be sha256/<base64>\", pin)\n  }\n  if _, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(pin, \"sha256/\")); err != nil || len(mustDecode(pin)) != 32 {\n    return fmt.Errorf(\"bad pin %q\", pin)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate pins with a script (openssl SPKI digest + base64), never by hand","Verify pins against the live cert chain before committing config"],"tags":["websocket","tls","pinned-certs"],"backgroundTag":"invalid-tls-pinning","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}