Enabling HTTPS on express.js
Enabling HTTPS on express.js
I'm trying to get HTTPS working on express.js for node, and I can't figure it out.
This is my app.js
code.
app.js
var express = require('express');
var fs = require('fs');
var privateKey = fs.readFileSync('sslcert/server.key');
var certificate = fs.readFileSync('sslcert/server.crt');
var credentials = {key: privateKey, cert: certificate};
var app = express.createServer(credentials);
app.get('/', function(req,res) {
res.send('hello');
});
app.listen(8000);
When I run it, it seems to only respond to HTTP requests.
I wrote simple vanilla node.js
based HTTPS app:
node.js
var fs = require("fs"),
http = require("https");
var privateKey = fs.readFileSync('sslcert/server.key').toString();
var certificate = fs.readFileSync('sslcert/server.crt').toString();
var credentials = {key: privateKey, cert: certificate};
var server = http.createServer(credentials,function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Worldn');
});
server.listen(8000);
And when I run this app, it does respond to HTTPS requests. Note that I don't think the toString() on the fs result matters, as I've used combinations of both and still no es bueno.
EDIT TO ADD:
For production systems, you're probably better off using Nginx or HAProxy to proxy requests to your nodejs app. You can setup nginx to handle the ssl requests and just speak http to your node app.js.
EDIT TO ADD (4/6/2015)
For systems on using AWS, you are better off using EC2 Elastic Load Balancers to handle SSL Termination, and allow regular HTTP traffic to your EC2 web servers. For further security, setup your security group such that only the ELB is allowed to send HTTP traffic to the EC2 instances, which will prevent external unencrypted HTTP traffic from hitting your machines.
Regarding the last comment on AWS: is it that a server doesn't need to be created with the https module? My certificates are uploaded into AWS via Jenkins and handled with ARN; I have no file paths to use (in https options)
– sqldoug
Jan 29 '16 at 1:00
@sqldoug I'm not sure I understand the question. AWS ELBs can be configured to accept HTTPS connections and act as the SSL termination point. That is, they speak to your app servers via regular HTTP. There typically isn't a reason to have nodejs deal with SSL, because it's just extra processing overhead which can be handled up the stack at either the ELB level or at the HTTP Proxy level.
– Alan
May 5 '16 at 17:15
Thanks Alan; yes I've since realized that Node doesn't need to deal with SSL when AWS ELBs can be so configured.
– sqldoug
May 6 '16 at 0:14
5 Answers
5
In express.js (since version 3) you should use that syntax:
var fs = require('fs');
var http = require('http');
var https = require('https');
var privateKey = fs.readFileSync('sslcert/server.key', 'utf8');
var certificate = fs.readFileSync('sslcert/server.crt', 'utf8');
var credentials = {key: privateKey, cert: certificate};
var express = require('express');
var app = express();
// your express configuration here
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);
httpServer.listen(8080);
httpsServer.listen(8443);
In that way you provide express middleware to the native http/https server
If you want your app running on ports below 1024, you will need to use sudo
command (not recommended) or use a reverse proxy (e.g. nginx, haproxy).
sudo
Okay, let me give that a go. Am I referencing old docs?
– Alan
Jul 31 '12 at 16:49
All is written here: github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x Paragraph Application function
– codename-
Jul 31 '12 at 16:50
Note that although 443 is the default port for HTTPS, during development you probably want to use something like 8443 because most systems don't allow non-root listeners on low-numbered ports.
– ebohlman
Aug 1 '12 at 6:48
express 4 it doesn't work, it works for
localhost:80
but not https://localhost:443
– Muhammad Umer
Feb 22 '15 at 18:56
localhost:80
https://localhost:443
if you're going to use nginx for reverse proxy, that can handle the ssl certs for you instead of node
– Gianfranco P.
Nov 3 '15 at 1:41
I ran into a similar issue with getting SSL to work on a port other than port 443. In my case I had a bundle certificate as well as a certificate and a key. The bundle certificate is a file that holds multiple certificates, node requires that you break those certificates into separate elements of an array.
var express = require('express');
var https = require('https');
var fs = require('fs');
var options = {
ca: [fs.readFileSync(PATH_TO_BUNDLE_CERT_1), fs.readFileSync(PATH_TO_BUNDLE_CERT_2)],
cert: fs.readFileSync(PATH_TO_CERT),
key: fs.readFileSync(PATH_TO_KEY)
};
app = express()
app.get('/', function(req,res) {
res.send('hello');
});
var server = https.createServer(options, app);
server.listen(8001, function(){
console.log("server running at https://IP_ADDRESS:8001/")
});
In app.js you need to specify https and create the server accordingly. Also, make sure that the port you're trying to use is actually allowing inbound traffic.
i have a key and a bundled cert, i am not sure what cert: fs.readFileSync(PATH_TO_CERT), would be and how to "break" the bundled cert, there are like 20+ keys in cert if u ask me :)
– Muhammad Umar
Sep 21 '17 at 10:24
This is how its working for me. The redirection used will redirect all the normal http as well.
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const http = require('http');
const app = express();
var request = require('request');
//For https
const https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('certificates/private.key'),
cert: fs.readFileSync('certificates/certificate.crt'),
ca: fs.readFileSync('certificates/ca_bundle.crt')
};
// API file for interacting with MongoDB
const api = require('./server/routes/api');
// Parsers
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Angular DIST output folder
app.use(express.static(path.join(__dirname, 'dist')));
// API location
app.use('/api', api);
// Send all other requests to the Angular app
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
app.use(function(req,resp,next){
if (req.headers['x-forwarded-proto'] == 'http') {
return resp.redirect(301, 'https://' + req.headers.host + '/');
} else {
return next();
}
});
http.createServer(app).listen(80)
https.createServer(options, app).listen(443);
SSL configuration
In Sailsjs there are two ways to configure all the stuff, first is to configure in config folder with each one has their separate files (like database connection regarding settings lies within connections.js ). And second is configure on environment base file structure, each environment files presents in config/env
folder and each file contains settings for particular env.
config/env
Sails first looks in config/env folder and then look forward to config/ *.js
Now lets setup ssl in config/local.js
.
config/local.js
var local = {
port: process.env.PORT || 1337,
environment: process.env.NODE_ENV || 'development'
};
if (process.env.NODE_ENV == 'production') {
local.ssl = {
secureProtocol: 'SSLv23_method',
secureOptions: require('constants').SSL_OP_NO_SSLv3,
ca: require('fs').readFileSync(__dirname + '/path/to/ca.crt','ascii'),
key: require('fs').readFileSync(__dirname + '/path/to/jsbot.key','ascii'),
cert: require('fs').readFileSync(__dirname + '/path/to/jsbot.crt','ascii')
};
local.port = 443; // This port should be different than your default port
}
module.exports = local;
Alternative you can add this in config/env/production.js too. (This snippet also show how to handle multiple CARoot certi)
Or in production.js
module.exports = {
port: 443,
ssl: {
secureProtocol: 'SSLv23_method',
secureOptions: require('constants').SSL_OP_NO_SSLv3,
ca: [
require('fs').readFileSync(__dirname + '/path/to/AddTrustExternalCARoot.crt', 'ascii'),
require('fs').readFileSync(__dirname + '/path/to/COMODORSAAddTrustCA.crt', 'ascii'),
require('fs').readFileSync(__dirname + '/path/to/COMODORSADomainValidationSecureServerCA.crt', 'ascii')
],
key: require('fs').readFileSync(__dirname + '/path/to/jsbot.key', 'ascii'),
cert: require('fs').readFileSync(__dirname + '/path/to/jsbot.crt', 'ascii')
}
};
http/https & ws/wss redirection
Here ws is Web Socket and wss represent Secure Web Socket, as we set up ssl then now http and ws both requests become secure and transform to https and wss respectively.
There are many source from our app will receive request like any blog post, social media post but our server runs only on https so when any request come from http it gives “This site can’t be reached” error in client browser. And we loss our website traffic. So we must redirect http request to https, same rules allow for websocket otherwise socket will fails.
So we need to run same server on port 80 (http), and divert all request to port 443(https). Sails first compile config/bootstrap.js file before lifting server. Here we can start our express server on port 80.
In config/bootstrap.js (Create http server and redirect all request to https)
module.exports.bootstrap = function(cb) {
var express = require("express"),
app = express();
app.get('*', function(req, res) {
if (req.isSocket)
return res.redirect('wss://' + req.headers.host + req.url)
return res.redirect('https://' + req.headers.host + req.url)
}).listen(80);
cb();
};
Now you can visit http://www.yourdomain.com, it will redirect to https://www.yourdomain.com
Automated HTTPS, Free SSL with greenlock-express
You can use Greenlock:
express-app.js
:
express-app.js
var express = require('express');
var app = express();
app.use('/', function (req, res) {
res.send({ msg: "Hello, Encrypted World!" })
});
// DO NOT DO THIS app.listen
// Instead do this:
module.exports = app;
server.js
:
server.js
require('greenlock-express').create({
// Let's Encrypt v2 is ACME draft 11
version: 'draft-11'
, server: 'https://acme-v02.api.letsencrypt.org/directory'
// You MUST change this to a valid email address
// You MUST change these to valid domains
, email: 'john.doe@example.com'
, approveDomains: [ 'example.com', 'www.example.com' ]
, agreeTos: true
, configDir: "~/acme/etc"
, app: require('./express-appexpress')
// Get notified of important updates and help me make greenlock better
, communityMember: true
// Contribute telemetry data to the project
, telemetry: true
}).listen(80, 443);
Screencast
Watch the QuickStart demonstration: https://youtu.be/e8vaR4CEZ5s
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Answered succinctly here: stackoverflow.com/a/23894573/1882064
– arcseldon
Oct 9 '14 at 15:32