426

Given an SSL key and certificate, how does one create an HTTPS service?

8
  • 2
    I used restify.js instead of express.js, but the idea is the same. Here's how I set up a node.js server that accepts both HTTP and HTTPS qugstart.com/blog/node-js/… Commented Feb 28, 2012 at 22:55
  • 2
    without express and with the newest version of node - look here: stackoverflow.com/a/21809393/388026 Commented Apr 9, 2014 at 7:04
  • 1
    What happened to this question? The answers imply that it was originally about express.js. Commented Oct 4, 2015 at 4:42
  • it's trivial to create a valid, self-signed SSL cert and launch an HTTPS server, just a few steps Commented Dec 9, 2015 at 21:26
  • 4
    Its a bit late but if someone need a complete nodejs https tutorial, can find here: programmerblog.net/nodejs-https-server Commented Jun 10, 2018 at 19:07

9 Answers 9

546

The Express API doc spells this out pretty clearly.

Additionally this answer gives the steps to create a self-signed certificate.

I have added some comments and a snippet from the Node.js HTTPS documentation:

var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');

// This line is from the Node.js HTTPS documentation.
var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.cert')
};

// Create a service (the app object is just a callback).
var app = express();

// Create an HTTP service.
http.createServer(app).listen(80);
// Create an HTTPS service identical to the HTTP service.
https.createServer(options, app).listen(443);
Sign up to request clarification or add additional context in comments.

13 Comments

Nice, was just about to post this myself. Thank you. Additionally, I found this article helpful for generating a self-signed certificate.
Make sure you put options first in https.createServer, to avoid cryptic errors.
I am setting up an almost identical https server port 8888 and not sure how to change the routes. when I run curl curl --insecure localhost:8888 curl: (35) Unknown SSL protocol error in connection to localhost:8888 what is the error coming from and how to get around it. When I type localhost:8888 in the browser, it hangs and https:/localhost:8888 gives SSL error
@Costa you can redirect users from http to https with express-force-ssl or hand written middleware - it pretty straightforward
@NathanMcKaskle You can disable the password: Check this guide but if you are using macOS make sure that the generated key length is at least 2048: openssl genrsa -out key.pem 2048
|
175

For Node 0.3.4 and above all the way up to the current LTS (v21 at the time of this edit), https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener has all the example code you need:

const https = require('node:https');
const fs = require('node:fs');

const options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'),
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(8000); 

Note that if want to use Let's Encrypt's certificates using the certbot tool, the private key is called privkey.pem and the certificate is called fullchain.pem:

const certDir = `/etc/letsencrypt/live`;
const domain = `YourDomainName`;
const options = {
  key: fs.readFileSync(`${certDir}/${domain}/privkey.pem`),
  cert: fs.readFileSync(`${certDir}/${domain}/fullchain.pem`)
};

8 Comments

setSecure is deprecated. Check this out instead stackoverflow.com/questions/5136353/node-js-https-secure-error
See the official express answer below by @Jacob Marble.
This sample doesn't work anymore as the HTTPS implementation was re-done in Node.JS 0.4. See the corresponding docs at nodejs.org. stackoverflow.com/questions/5136353/…
This answer is very old and does not work anymore. Please see the answer by pkyeck below, or go to: nodejs.org/api/https.html
Also the link is broken
|
94

Found this question while googling "node https" but the example in the accepted answer is very old - taken from the docs of the current (v21) version of node, it should look like this:

const https = require('node:https');
const fs = require('node:fs');

const options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'),
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(8000);

6 Comments

Worked like a charm. This information came in very handy as I run a node.js tool (PDFJS) on top of a PHP app that was recently forced to run over https. The iframe was very unhappy to load my node.js app on an alternate, non-https port.
This looks good, but how can I generate the files you are requiring there (*.pem)? I tried following this page, but when opening localhost:8000 in the browser, no data is received (just loading...).
@IonicăBizău, for generating keys, install openssl, then in cmd prompt, type openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3001
@IonicăBizău you need to directly go to https://localhost:8080. HTTP is NOT HTTPS.
Is it possible to create https-server with folder? So you can put file in it and access that file like localhost:81/main.js
|
49

The above answers are good but with Express and node this will work fine.

Since express create the app for you, I'll skip that here.

var express = require('express')
  , fs = require('fs')
  , routes = require('./routes');

var privateKey = fs.readFileSync('cert/key.pem').toString();
var certificate = fs.readFileSync('cert/certificate.pem').toString();  

// To enable HTTPS
var app = module.exports = express.createServer({key: privateKey, cert: certificate});

3 Comments

This seems to be deprecated since "applications no longer inherit from http.Server"
Why you setting module.exports? There's no need for that
@matejkramny, probably because it makes it easy to test.
23

The minimal setup for an HTTPS server in Node.js would be something like this :

const https = require('node:https');
const fs = require('node:fs');
    
const httpsOptions = {
    key : fs.readFileSync('path/to/server-key.pem'),
    cert : fs.readFileSync('path/to/server-crt.pem')
};

const app = function (req, res) {
    res.writeHead(200);
    res.end("hello world\n");
}
    
https.createServer(httpsOptions, app).listen(4433);

const options = {
  key : fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert : fs.readFileSync('test/fixtures/keys/agent2-cert.pem'),
};

If you also want to support http requests, you need to make just this small modification :

const http = require('node:http');
const https = require('node:https');
const fs = require('node:fs');

const httpsOptions = {
    key : fs.readFileSync('path/to/server-key.pem'),
    cert : fs.readFileSync('path/to/server-crt.pem')
};

const app = function (req, res) {
    res.writeHead(200);
    res.end("hello world\n");
}
    
http.createServer(app).listen(8888);
https.createServer(httpsOptions, app).listen(4433);

2 Comments

What are you talking about? What do you mean createServer doesn't exist anymore? It's still documented in the Node.js v16.5.0 documentation in both nodejs.org/api/http.html & nodejs.org/api/https.html and not flagged as deprecated. Have you actually tried running this code? And, if so, which errors did you get?
actually I figured out my mistake. There was one line that needed changed var https = require('https').Server(app); to var https = require('https'); Now, everything works... Thanks.
18

Update

Use Let's Encrypt via Greenlock.js

Original Post

I noticed that none of these answers show that adding a Intermediate Root CA to the chain, here are some zero-config examples to play with to see that:

Snippet:

var options = {
  // this is the private key only
  key: fs.readFileSync(path.join('certs', 'my-server.key.pem'))

// this must be the fullchain (cert + intermediates)
, cert: fs.readFileSync(path.join('certs', 'my-server.crt.pem'))

// this stuff is generally only for peer certificates
//, ca: [ fs.readFileSync(path.join('certs', 'my-root-ca.crt.pem'))]
//, requestCert: false
};

var server = https.createServer(options);
var app = require('./my-express-or-connect-app').create(server);
server.on('request', app);
server.listen(443, function () {
  console.log("Listening on " + server.address().address + ":" + server.address().port);
});

var insecureServer = http.createServer();
server.listen(80, function () {
  console.log("Listening on " + server.address().address + ":" + server.address().port);
});

This is one of those things that's often easier if you don't try to do it directly through connect or express, but let the native https module handle it and then use that to serve you connect / express app.

Also, if you use server.on('request', app) instead of passing the app when creating the server, it gives you the opportunity to pass the server instance to some initializer function that creates the connect / express app (if you want to do websockets over ssl on the same server, for example).

4 Comments

This is a nice explanation but, the provided link in the update section, is broken (gives 500 error)
All these answers are OLD and OUTDATED. I wish StackOverflow would cleanup up questions and answers that are VERY VERY OLD.
@ThN What isn't working for you? What's a newer, better solution?
@coolaj86 After long time of trial and turbulence (lol), I have figured out my issue. It works now. I had a line var https = require("https").server(app); And when I went to create server with https.createServer(...) I got the error message createServer not found. After changing the line to var https = require("https");, everything fell into place. Thank you...
10

To enable your app to listen for both http and https on ports 80 and 443 respectively, do the following

Create an express app:

var express = require('express');
var app = express();

The app returned by express() is a JavaScript function. It can be be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app using the same code base.

You can do so as follows:

var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
var app = express();

var options = {
  key: fs.readFileSync('/path/to/key.pem'),
  cert: fs.readFileSync('/path/to/cert.pem')
};

http.createServer(app).listen(80);
https.createServer(options, app).listen(443);

For complete detail see the doc

Comments

0

You can use also archive this with the Fastify framework:

const { readFileSync } = require('fs')
const Fastify = require('fastify')

const fastify = Fastify({
  https: {
    key: readFileSync('./test/asset/server.key'),
    cert: readFileSync('./test/asset/server.cert')
  },
  logger: { level: 'debug' }
})

fastify.listen(8080)

(and run openssl req -nodes -new -x509 -keyout server.key -out server.cert to create the files if you need to write tests)

Comments

0

If you need it only locally for local development, I've created utility exactly for this task - https://github.com/pie6k/easy-https

import { createHttpsDevServer } from 'easy-https';

async function start() {
  const server = await createHttpsDevServer(
    async (req, res) => {
      res.statusCode = 200;
      res.write('ok');
      res.end();
    },
    {
      domain: 'my-app.dev',
      port: 3000,
      subdomains: ['test'], // will add support for test.my-app.dev
      openBrowser: true,
    },
  );
}

start();

It:

  • Will automatically add proper domain entries to /etc/hosts
  • Will ask you for admin password only if needed on first run / domain change
  • Will prepare https certificates for given domains
  • Will trust those certificates on your local machine
  • Will open the browser on start pointing to your local server https url

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.