Serving Static Files over HTTP/HTTPS with NodeJS on Windows

TL;DR Explanation – Step-by-step instructions on setting up a basic HTTP/HTTPS server that can serve static files with NodeJS on Windows.

Overview and Prerequisites

Earlier this week I found myself needing to setup a HTTPS server so that I could serve a basic JavaScript based front-end that I had written for one of my personal projects. In the past I would have probably run up IIS but there’s just so much faffing about setting it up for HTTPS and it’s not something I could really be bothered doing for something so trivial. It was just going to be for development purposes so I figured it’s a good chance to do a bit more with NodeJS and use that instead. It was fairly trivial to do but I did have to go to a few different sources for the different parts of what I wanted so I figured I’d save others the time and put it all in one place. Below are the links to the various sources that helped me come up with this step of instructions and you can find some of my simple scripts at the GitHub link below.

GitHub Link for Sample Scripts

https://github.com/chris-tomich/mymemorysucks/tree/master/NodeJSHTTPSServerWindows

References that helped get these steps together

http://stackoverflow.com/questions/5998694/how-to-create-an-https-server-in-node-js

http://stackoverflow.com/questions/6084360/using-node-js-as-a-simple-web-server

http://www.jayway.com/2014/09/03/creating-self-signed-certificates-with-makecert-exe-for-development/

http://blog.samkendall.net/2010/03/22/converting-an-exported-pfx-certificate-to-pem-format-on-windows/

Required Tools

https://nodejs.org/

https://www.openssl.org/ (Windows binaries)

The Setup Process

The high level steps you need to follow to get it all running are roughly –

  1. Create root certificates
  2. Create server certificates
  3. Configure JavaScript source that NodeJS executes to start the server listening for requests

Installation of Tools

So if you haven’t already done so, go and get NodeJS and OpenSSL and add them to your $PATH to make it easier for you to access. We need OpenSSL because we use it to help us convert some of the certificates into the required PKCS12 and X.509 formats that NodeJS requires.

Step 1 – Create root certificates

So I’ve got a snippet of the script I used below (thanks to Elizabeth Andrews’ post) but as I mentioned you can find this in the makeRootCertificate.cmd file on my GitHub here. All you need to do is replace the “MyMemorySucksRootCA” token with the name you want to give your root certificate. For a more in-depth look at what it all does, I recommend reading Elizabeth’s post.

Just to explain, if you’re not familiar with how certificates work (and I’m definitely no authority on it), but this root certificate is NOT the certificate we will be using in the NodeJS server. This certificate will be the root certificate that we place in our trusted certificate authorities on our machine. Any certificates that then derive from this certificate will be trusted by our browser when we browse to the page. As a result, DON’T make the CN of this certificate your domain name – that certificate comes later.

makecert.exe ^
-n "CN=MyMemorySucksRootCA" ^
-r ^
-pe ^
-a sha512 ^
-len 4096 ^
-cy authority ^
-sv MyMemorySucksRootCA.pvk ^
MyMemorySucksRootCA.cer

pvk2pfx.exe ^
-pvk MyMemorySucksRootCA.pvk ^
-spc MyMemorySucksRootCA.cer ^
-pfx MyMemorySucksRootCA.pfx ^
-po thisismypassphrase

Once you have made the certificate with the above script, you can import this into your “Trusted Root Certification Authorities/Certificates” folder through the Certificates MMC Snap-In. I won’t bother re-posting an explanation as Elizabeth has already done a good step-by-step tutorial for how to do that if you don’t already know what to do.

Another thing to note is DON’T FORGET YOUR PASSPHRASE. I guess it’s not a big deal if you’re doing this for a development environment, but you’ll have the fun time of going through this process all over again if you forget it.

Step 2 – Create server certificates

The script for this differs slightly to the one used for making the root certificate. For a start we pass in the root certificate created previously as an input and we specify the domain that we are wanting to use at this point.

makecert.exe ^
-n "CN=dev.mymemorysucks.local" ^
-iv MyMemorySucksRootCA.pvk ^
-ic MyMemorySucksRootCA.cer ^
-pe ^
-a sha512 ^
-len 4096 ^
-b 01/01/2014 ^
-e 01/01/2016 ^
-sky exchange ^
-eku 1.3.6.1.5.5.7.3.1 ^
-sv dev.mymemorysucks.local.pvk ^
dev.mymemorysucks.local.cer

pvk2pfx.exe ^
-pvk dev.mymemorysucks.local.pvk ^
-spc dev.mymemorysucks.local.cer ^
-pfx dev.mymemorysucks.local.pfx ^
-po thisismypassphrase

openssl.exe pkcs12 -in .\dev.mymemorysucks.local.pfx -out .\dev.mymemorysucks.local.pem
openssl.exe x509 -in .\dev.mymemorysucks.local.pem -out .\dev.mymemorysucks.local.cert

The script I’ve provided above also differ from the ones Elizabeth provides in her post as they use OpenSSL to convert the PFX certificates created by makecert.exe into a PKCS12 formatted PEM file. It then uses this file to extract an X.509 certificate which I’ve given the extension “cert” as the “cer” certificate created by makecert.exe is a binary format which NodeJS doesn’t appear to understand. If you attempt to use that one you’ll probably get an error similar to the one below.

crypto.js:135
 if (options.cert) c.context.setCert(options.cert);
 ^
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
 at Object.exports.createCredentials (crypto.js:135:31)
 at Server (tls.js:1128:28)
 at new Server (https.js:35:14)
 at Object.exports.createServer (https.js:54:10)
 at Object.<anonymous> (C:\Temp\server.js:17:7)
 at Module._compile (module.js:456:26)
 at Object.Module._extensions..js (module.js:474:10)
 at Module.load (module.js:356:32)
 at Function.Module._load (module.js:312:12)
 at Function.Module.runMain (module.js:497:10)

You won’t need to add this certificate into your certificate store as you are already trusting the root certificate from which this certificate is derived.

Again… DON’T FORGET YOUR PASSPHRASE.

Step 3 – Configuring Your Workspace and NodeJS

The method I describe here makes use of two external packages that you can get through NPM. From the root directory of your NodeJS project run the following two commands to pull down the packages for Express and Serve-Static.

npm install express serve-static
</pre>
Once you've installed both these packages you just need to customise the server.js file I've used below to load the certificates that were created previously with the passphrase you used during the creation process.
<pre>

var express = require("express");
var https = require("https");
var http = require("http");
var fs = require("fs");
var serveStatic = require("serve-static");

var options = {
key: fs.readFileSync(__dirname + "\\cert\\dev.mymemorysucks.local.pem"),
cert: fs.readFileSync(__dirname + "\\cert\\dev.mymemorysucks.local.cert"),
passphrase: "thisismypassphrase"
};

var app = express();

app.use(serveStatic(__dirname));
http.createServer(app).listen(4000);
https.createServer(options, app).listen(4001);

If you don't specify the "passphrase" in the configuration you're likely to get the following error. It's a little cryptic and in fact I only experienced with what is currently the latest version of NodeJS for me (v0.12.5). When I was using an older version (v0.10.x), I actually didn't have a problem from leaving out the "passphrase" option as it would ask me for the pass phrase.

_tls_common.js:108
c.context.setKey(options.key);
^
Error: error:0907B068:PEM routines:PEM_READ_BIO_PRIVATEKEY:bad password read
at Error (native)
at Object.createSecureContext (_tls_common.js:108:19)
at Server (_tls_wrap.js:598:25)
at new Server (https.js:36:14)
at Object.exports.createServer (https.js:56:10)
at Object.<anonymous> (C:\Temp\server.js:17:7)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)

That's it! You should now have a web server once you run the following code from the command line. You should be able to go to http://localhost:4000/index.html and https://localhost:4001/index.html (assuming your firewall is unblocked AND you have a file called "index.html").

node server.js
Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s