Last night I was having an issue with websockets and TLS in an Express.js node.js app. My websocket was working just fine when developing locally over plain HTTP but when I deployed the app to Heroku I received an error as that app runs over HTTPS but the websocket was still plain HTTP (using ws:// instead of wss://). Hmmm…. I started digging into websockets over TLS and how that would work without any luck. So I asked around but then it dawned on me and I answered my own question… Sometimes finding the answer is all about the question you ask 🙂
So the main realisation is that TLS connections are terminated by the Heroku router and forwarded to a web dyno hence there was no need to listen for TLS based websocket connections in my app. Also remembering how websocket connections are created is important. A websocket connection is a normal HTTP connection which is then upgraded to a websocket connection. So the real solution was to understand how a web dyno in node.js using Express could share it’s port with websockets using a HTTP server and that the same HTTP server would be used for both HTTP transport and websocket connections.
The solution was as follows:
const express = require('express') const http = require('http') const WebSocket = require('ws') const port = process.env.PORT || 8080 const app = express() const httpServer = http.createServer(app) const wss = new WebSocket.Server({ 'server': httpServer }) httpServer.listen(port)
So in essence:
- Create the Express.js app (but do not set it up to listen in a port)
- Create HTTP server in node.js passing in the Express.js app
- Create websocket server agin using the HTTP server as the server
- Make the HTTP server listen on the port provided through environment variable