How to close a WebSocket connection gracefully
close() method, close code semantics, reason string, initiating close from client, server-initiated close, CLOSING state handling, cleanup checklist
Closing a WebSocket the Right Way
Calling ws.close() without arguments closes with code 1000. Always pass a code and reason in production for debuggability:
// Normal closure — user logged out ws.close(1000, 'User logged out'); // Going away — page unload ws.close(1001, 'Page navigating away'); // Application error ws.close(4000, 'Auth token expired');
Codes 4000–4999 are reserved for application-defined use. Use them to communicate application state to the client in the close reason.
Cleanup Checklist
Forgetting cleanup causes memory leaks and phantom reconnects. Every WebSocket setup should have a paired teardown:
function cleanup(ws) { clearInterval(heartbeatTimer); clearTimeout(pongTimer); clearTimeout(retryTimeout); ws.onopen = null; ws.onmessage = null; ws.onerror = null; ws.onclose = null; if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) { ws.close(1000, 'Cleanup'); } }
In React, call cleanup in the useEffect return function. In Vue, call it in onUnmounted. In plain JS, call it on beforeunload.
