Overview
NATS Server provides native WebSocket connectivity, enabling web browsers and other WebSocket clients to communicate using the NATS protocol. This makes it possible to build real-time web applications with direct NATS messaging from the browser.
WebSocket support is built directly into NATS Server with no additional proxy or gateway required.
Why WebSocket?
WebSocket connectivity unlocks NATS for web applications:
Browser Support : Native NATS messaging directly from JavaScript
Real-Time Web Apps : Build interactive dashboards, chat, notifications
No HTTP Polling : Persistent bidirectional connection
Single Port : WebSocket and HTTP monitoring can share a port
TLS Support : Secure WebSocket (WSS) with TLS certificates
Per-Message Compression : Optional compression for bandwidth reduction
WebSocket Protocol
NATS implements the RFC 6455 WebSocket Protocol with extensions:
Binary and Text Frames : Support for both message types
Compression : Optional per-message deflate compression (RFC 7692)
Masking : Configurable frame masking for clients
Control Frames : PING/PONG for keepalive, CLOSE for clean shutdown
Frame Types
Implemented WebSocket opcodes (websocket.go:41-49):
wsTextMessage = 0x 1 // UTF-8 text data
wsBinaryMessage = 0x 2 // Binary data
wsCloseMessage = 0x 8 // Connection close
wsPingMessage = 0x 9 // Keepalive ping
wsPongMessage = 0x A // Keepalive pong
WebSocket Extensions
Per-Message Compression (websocket.go:89-93):
Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover
Compression is negotiated during the WebSocket handshake and applied per-frame.
Configuration
Basic WebSocket Setup
Configure WebSocket by specifying a port:
websocket {
port : 8080
no_tls : true
# Optionally bind to specific host
# host: "0.0.0.0"
# Enable compression (optional)
compression : true
}
Clients connect to: ws://localhost:8080
WebSocket with TLS
Secure WebSocket (WSS) requires TLS configuration:
websocket {
port : 443
tls {
cert_file : "/path/to/server-cert.pem"
key_file : "/path/to/server-key.pem"
# Optional CA for client cert verification
ca_file : "/path/to/ca.pem"
verify : true
}
# Compression recommended for WAN clients
compression : true
}
Clients connect to: wss://yourserver.com
Always use WSS (TLS) in production. Unencrypted WebSocket connections expose credentials and message content.
Shared Port with HTTP Monitoring
WebSocket can share a port with HTTP monitoring:
http_port : 8222
websocket {
port : 8222
no_tls : true
# Same port serves both /varz and WebSocket
}
The server automatically detects WebSocket upgrade requests on the HTTP port.
Advanced Configuration
websocket {
port : 8080
# Listen address
host : "0.0.0.0"
# Compression settings
compression : true
# TLS configuration
tls {
cert_file : "/path/to/cert.pem"
key_file : "/path/to/key.pem"
timeout : 2.0 # TLS handshake timeout
}
# Authentication
# Inherits from server auth configuration
# Can be overridden per WebSocket connection
# CORS and origin restrictions
same_origin : false
allowed_origins : [
"https://app.example.com" ,
"https://dashboard.example.com"
]
# Handshake timeout
handshake_timeout : 2s
# No masking (server to client)
# Server responses can skip masking for performance
no_masking : true
}
Listen address for WebSocket connections
Enable per-message deflate compression
Require connections from same origin as server
List of allowed origins for CORS
Maximum time to complete WebSocket handshake
Disable masking for server-to-client frames (performance optimization)
Browser Clients
JavaScript/TypeScript
Connect from the browser using the NATS WebSocket client:
import { connect } from '@nats-io/nats.ws' ;
// Connect to NATS via WebSocket
const nc = await connect ({
servers: [ 'ws://localhost:8080' ],
// Or secure: ['wss://nats.example.com']
});
// Subscribe to messages
const sub = nc . subscribe ( 'updates' );
( async () => {
for await ( const msg of sub ) {
console . log ( 'Received:' , new TextDecoder (). decode ( msg . data ));
}
})();
// Publish messages
nc . publish ( 'events' , new TextEncoder (). encode ( 'Hello from browser!' ));
// Request-reply
const response = await nc . request (
'api.user.info' ,
new TextEncoder (). encode ( 'user123' ),
{ timeout: 1000 }
);
React Application
import { useEffect , useState } from 'react' ;
import { connect , NatsConnection } from '@nats-io/nats.ws' ;
function useNATS ( url : string ) {
const [ nc , setNc ] = useState < NatsConnection | null >( null );
useEffect (() => {
let connection : NatsConnection ;
( async () => {
connection = await connect ({ servers: [ url ] });
setNc ( connection );
})();
return () => {
connection ?. close ();
};
}, [ url ]);
return nc ;
}
function Dashboard () {
const nc = useNATS ( 'ws://localhost:8080' );
const [ messages , setMessages ] = useState < string []>([]);
useEffect (() => {
if ( ! nc ) return ;
const sub = nc . subscribe ( 'dashboard.>' );
( async () => {
for await ( const msg of sub ) {
const text = new TextDecoder (). decode ( msg . data );
setMessages ( prev => [ ... prev , text ]);
}
})();
return () => sub . unsubscribe ();
}, [ nc ]);
return (
< div >
{ messages . map (( msg , i ) => < div key = { i } > { msg } </ div > ) }
</ div >
);
}
Web Client Support
Authentication
WebSocket clients support all NATS authentication methods:
// Token authentication
await connect ({
servers: [ 'ws://localhost:8080' ],
token: 'your-secret-token'
});
// Username/password
await connect ({
servers: [ 'ws://localhost:8080' ],
user: 'webapp' ,
pass: 'secret'
});
// JWT/NKey authentication
await connect ({
servers: [ 'ws://localhost:8080' ],
authenticator: jwtAuthenticator ( jwt , seed )
});
// From cookie (set by server)
await connect ({
servers: [ 'ws://localhost:8080' ]
// Server can set auth via cookie
});
Cookie-Based Authentication
NATS supports passing authentication via cookies (websocket.go:119-123):
// Server sets cookies in HTTP response:
// - jwt: JWT token
// - username/password: Basic auth
// - token: Token auth
// Client connects without explicit credentials
const nc = await connect ({
servers: [ 'ws://localhost:8080' ]
});
// Credentials automatically extracted from cookies
Compression
Enable compression for bandwidth-constrained clients:
const nc = await connect ({
servers: [ 'ws://localhost:8080' ],
// Compression negotiated automatically if server supports it
});
Compression is applied when message size exceeds threshold (websocket.go:62).
MQTT over WebSocket
MQTT clients can connect via WebSocket using the /mqtt path:
import Paho from 'paho-mqtt' ;
const client = new Paho . Client (
'ws://localhost:8080/mqtt' , // Note: /mqtt path
'client-' + Math . random ()
);
client . connect ({
onSuccess : () => {
console . log ( 'MQTT connected via WebSocket' );
client . subscribe ( 'sensors/#' );
}
});
See MQTT for more details on MQTT support (mqtt.go:191).
Implementation Details
WebSocket Handshake
The server performs standard WebSocket upgrade (websocket.go:102-103):
Client sends HTTP Upgrade request with Sec-WebSocket-Key
Server validates and computes accept hash using GUID
Server responds with 101 Switching Protocols
Connection upgraded to WebSocket protocol
Frame Processing
WebSocket frames are processed efficiently (websocket.go:125-193):
Masking : Client-to-server frames must be masked per RFC 6455
Fragmentation : Large messages can be fragmented across frames
Control Frames : PING/PONG handled automatically
Browser Optimization : Frame size limited to 4KB for better browser performance (websocket.go:61)
Connection Detection
The server detects WebSocket clients via the ws field (websocket.go:197-199):
func ( c * client ) isWebsocket () bool {
return c . ws != nil
}
Disable Masking Use no_masking: true to skip server-to-client masking for better performance (websocket.go:85-86).
Compression Threshold Compression only applied for messages larger than 64 bytes (websocket.go:62).
Frame Size Server uses 4KB frames for optimal browser performance (websocket.go:61).
Connection Pooling Reuse WebSocket connections for multiple subscriptions to reduce overhead.
Security
TLS Best Practices
Always Use WSS in Production
Unencrypted WebSocket exposes all traffic including credentials.
Verify Certificates
Set verify: true in TLS configuration to validate client certificates.
Restrict Origins
Configure allowed_origins to prevent unauthorized domains from connecting.
Use Strong Authentication
Prefer JWT/NKey authentication over username/password for web clients.
Origin Restrictions
websocket {
port : 443
# Only allow specific domains
allowed_origins : [
"https://app.example.com" ,
"https://dashboard.example.com"
]
# Or require same origin
same_origin : true
}
X-Forwarded-For
The server respects X-Forwarded-For headers for client IP tracking (websocket.go:87):
X-Forwarded-For: 203.0.113.195, 198.51.100.178
Useful when behind a reverse proxy or load balancer.
Monitoring
Monitor WebSocket connections via /connz:
curl http://localhost:8222/connz | jq '.connections[] | select(.kind == "Websocket")'
Check WebSocket configuration in /varz:
curl http://localhost:8222/varz | jq '.websocket'
Use Cases
Real-Time Dashboard
import { connect } from '@nats-io/nats.ws' ;
const nc = await connect ({ servers: [ 'wss://nats.example.com' ] });
// Subscribe to metrics
const sub = nc . subscribe ( 'metrics.*' );
for await ( const msg of sub ) {
const metric = JSON . parse ( new TextDecoder (). decode ( msg . data ));
updateChart ( metric );
}
Chat Application
const nc = await connect ({ servers: [ 'ws://localhost:8080' ] });
// Join room
const room = 'chat.room.general' ;
// Receive messages
const sub = nc . subscribe ( room );
for await ( const msg of sub ) {
displayMessage ( msg );
}
// Send messages
function sendMessage ( text : string ) {
nc . publish ( room , new TextEncoder (). encode ( JSON . stringify ({
user: currentUser ,
text: text ,
timestamp: Date . now ()
})));
}
Live Notifications
const nc = await connect ({ servers: [ 'wss://nats.example.com' ] });
// Subscribe to user-specific notifications
const userSub = nc . subscribe ( `notifications. ${ userId } ` );
for await ( const msg of userSub ) {
const notification = JSON . parse ( new TextDecoder (). decode ( msg . data ));
showNotification ( notification );
}
Troubleshooting
Check that WebSocket port is configured and server is listening:
Ensure client origin is in allowed_origins or disable origin checking in development.
Verify certificate paths and ensure certificates are valid. Check server logs for TLS errors.
Both client and server must support and negotiate permessage-deflate extension.
Next Steps
Client SDKs Explore NATS WebSocket client libraries
Security Configure authentication and TLS
MQTT MQTT clients can also use WebSocket
Monitoring Monitor WebSocket connections