How to Host Multiple Node.js Apps on the Same Server Port

Using Server Name Indication (SNI) for virtual hosting

by Joe Honton

In this episode Devin discovers how SNI allows Node.js to host multiple apps on the same server. Previously on Tangled Web Services, Ivana learned how to configure Node.js to use port 443.

It's been a busy year at Tangled Web Services. New clients, new websites, new API services. Success never felt so good.

But success was beginning to catch up with the guy handling all the DevOps issues. And that would be Devin.

The problem, as Devin was finding out, is that every new website and every new service had to be hosted somewhere. Back in the good ol' days, this would all be handled by Apache or Nginx. But lately it seemed like TWS was grinding out nothing but Node.js servers. Each one served over HTTPS. Each one vying to listen on port 443.

Something had to give.

The problem, in a nutshell, is the TLS handshake. It's that mysterious process by which the client and the server exchange a secret key that will be used to encrypt and decrypt messages.

The handshake goes something like this:

CLIENT: "Hello, let's chat using v1.1. I can handle ECDHE-ECDSA-AES256-SHA384 and a few others, how about you?"

SERVER: "Hello, ECDHE-ECDSA-AES256-SHA384 is fine with me. Here's my public key certificate."

CLIENT: "Got your cert, and it checks out as legit." The client generates a secret key rndb12wx98yz34 and encrypts it using the server's public key certificate. "Let's use this secret key in future messages: xY54za...pk32AA. Changing cipher spec. Finished."

SERVER: The server uses its private key to decrypt xY54za...pk32AA into the secret key rndb12wx98yz34. "Changing cipher spec. Finished."

CLIENT: "ab12ef89de45kj39mj89po77rt12jn00..."

SERVER: "32xy95jy41fd56mn90lm32uy53un99np..."

This exchange works when the server has one and only one certificate. But of course virtual servers are host to more than one domain, and each domain must have its own certificate. Which one should the server send in its initial "hello"?

Wait. Why can't the server just look at the HTTP request? After all, every request header using HTTP/1.1 has to have a host header for that very purpose. It works just fine for plain old HTTP, why not for HTTPS?

Well, because the TLS key exchange protocol occurs over the TCP layer, before HTTP comes into the picture. The entire HTTP message, both headers and body, are encrypted using the secret key exchanged in the handshake. Catch 22. Can't pick the right cert without knowing the host domain. Can't reveal the host domain without the right cert.

The solution is simple, and it's implemented in TLS v1.2. Simply append the host domain name to the client's opening "hello". So now the client begins the chat like this:

CLIENT: "Hello, let's chat using v1.2. I can handle ECDHE-ECDSA-AES256-SHA384 and a few others, how about you? Oh, by the way, I'll be sending messages to rock-my-world.com."

The server looks in its table of virtual hosts, finds the public key certificate for rock-my-world.com, and responds with that cert.

We've implemented Server Name Indication (SNI)!


Devin was glad to clear up his fuzzy understanding about HTTPS. He got right to work digging into the Node.js docs to see how he could implement it for himself. Yup, SNI is supported. "All I have to do is ..."

"Wait, wait," he shivered, "This looks like no fun, tls.createServer(), tls.connect(), server.addContext()?"

Google . . . Google.

"Aah," Devin exclaimed with a ray of hope, "Read Write Serve."

"SNI — yup,

"HTTP/2 — cool.

"Hmm," Devin mused, "Nailed it!"


Full disclosure: Joe Honton is the founder of Read Write Tools and the author of Read Write Serve.


No minifig characters were harmed in the production of this Tangled Web Services episode.

Follow the adventures of Antoní, Bjørne, Clarissa, Devin, Ernesto, Ivana, Ken and the gang as Tangled Web Services boldly goes where tech has gone before.

How to Host Multiple Node.js Apps on the Same Server Port — Using Server Name Indication (SNI) for virtual hosting

🔗 🔎