create a trusted self-signed SSL cert for localhost (for use with Express/Node)

191,537

Solution 1

The answers above were partial. I've spent so much time getting this working, it's insane. Note to my future self, here is what you need to do:

I'm working on Windows 10, with Chrome 65. Firefox is behaving nicely - just confirm localhost as a security exception and it will work. Chrome doesn't:

Step 1. in your backend, create a folder called security. we will work inside it.

Step 2. create a request config file named req.cnf with the following content (credit goes to: @Anshul)

req.cnf :

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = Country initials like US, RO, GE
ST = State
L = Location
O = Organization Name
OU = Organizational Unit 
CN = www.localhost.com
[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.localhost.com
DNS.2 = localhost.com
DNS.3 = localhost

An explanation of this fields is here.

Step 3. navigate to the security folder in the terminal and type the following command :

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout cert.key -out cert.pem -config req.cnf -sha256

Step 4. then outside of security folder, in your express app do something like this: (credit goes to @Diego Mello)

backend 
 /security
 /server.js

server.js:

const express = require('express')
const app = express()
const https = require('https')
const fs = require('fs')
const port = 3000

app.get('/', (req, res) => {
    res.send("IT'S WORKING!")
})

const httpsOptions = {
    key: fs.readFileSync('./security/cert.key'),
    cert: fs.readFileSync('./security/cert.pem')
}
const server = https.createServer(httpsOptions, app)
    .listen(port, () => {
        console.log('server running at ' + port)
    })

Step 5. start the server, node server.js, and go to https://localhost:3000.

At this point we have the server setup. But the browser should show a warning message.

We need to register our self-signed certificate, as a CA trusted Certificate Authority, in the chrome/windows certificates store. (chrome also saves this in windows,)

Step 6. open Dev Tools in chrome, go to Security panel, then click on View Certificate. enter image description here

Step 7. go to Details panel, click Copy File, then when the Certificate Export Wizard appears, click Next as below:

go to details - copy file - next on export wizard

Step 8. leave DER encoding, click next, choose Browse, put it on a easy to access folder like Desktop, and name the certificate localhost.cer, then click Save and then Finish.. You should be able to see your certificate on Desktop.

Step 9. Open chrome://settings/ by inserting it in the url box. Down below, click on Advanced / Advanced Options, then scroll down to find Manage Certificates.

choose manage certificates

Step 10. Go to Trusted Root Certification Authorities panel, and click import.

Go to Trusted Root Certification Authorities panel, and click import

We will import the localhost.cer certificate we just finished exporting in step 8.

Step 11. click browse, find the localhost.cer, leave the default values click next a bunch of times - until this warning appears, click yes.

confirm security exception

Step 12. close everything, and restart chrome. Then, when going to https://localhost:3000 you should see: gotta love the green

Solution 2

Shortest way. Tested on MacOS, but may work similarly on other OS.

Generate pem

> openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365

> openssl rsa -in keytmp.pem -out key.pem

Your express server

const express = require('express')
const app = express()
const https = require('https')
const fs = require('fs')
const port = 3000

app.get('/', (req, res) => {
  res.send('WORKING!')
})

const httpsOptions = {
  key: fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem')
}
const server = https.createServer(httpsOptions, app).listen(port, () => {
  console.log('server running at ' + port)
})
  • Open https://localhost:3000 in Google Chrome and you'll see that it's not secure. Yet!
  • In Developer Tools > Security > View Certificate: Drag image to your desktop and double click it.
  • Click 'Add'
  • Find it in Keychain Access and double click it
  • Expand 'Trust' and change 'When using this certificate' to 'Always trust'.
  • You may be prompted to authenticate.
  • Restart your server.
  • Refresh your browser.
  • Enjoy! :)

Solution 3

You can try openSSL to generate certificates. Take a look at this.

You are going to need a .key and .crt file to add HTTPS to node JS express server. Once you generate this, use this code to add HTTPS to server.

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

var options = {
    key: fs.readFileSync('/etc/apache2/ssl/server.key'),
    cert: fs.readFileSync('/etc/apache2/ssl/server.crt'),
    requestCert: false,
    rejectUnauthorized: false
};


var app = express();

var server = https.createServer(options, app).listen(3000, function(){
    console.log("server started at port 3000");
});

This is working fine in my local machine as well as the server where I have deployed this. The one I have in server was bought from goDaddy but localhost had a self signed certificate.

However, every browser threw an error saying connection is not trusted, do you want to continue. After I click continue, it worked fine.

If anyone has ever bypassed this error with self signed certificate, please enlighten.

Solution 4

Mkcert from @FiloSottile makes this process infinitely simpler:

  1. Install mkcert, there are instructions for macOS/Windows/Linux
  2. mkcert -install to create a local CA
  3. mkcert localhost 127.0.0.1 ::1 to create a trusted cert for localhost in the current directory
  4. You're using node (which doesn't use the system root store), so you need to specify the CA explicitly in an environment variable, e.g: export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"
  5. Finally run your express server using the setup described in various other answers (e.g. below)
  6. boom. localhost's swimming in green.

Basic node setup:

const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();    
const server = https.createServer({
    key: fs.readFileSync('/XXX/localhost+2-key.pem'), // where's me key?
    cert: fs.readFileSync('/XXX/localhost+2.pem'), // where's me cert?
    requestCert: false,
    rejectUnauthorized: false,
}, app).listen(10443); // get creative

Solution 5

How to generate an SSL certificate for localhost: link

openssl genrsa -des3 -out server.key 1024

you need to enter a password here which you need to retype in the following steps

openssl req -new -key server.key -out server.csr

when asked "Common Name" type in: localhost

openssl x509 -req -days 1024 -in server.csr -signkey server.key -out server.crt
Share:
191,537

Related videos on Youtube

JasonS
Author by

JasonS

I wrote (and am writing) a SaaS to help extract data from javascript websites: http://PhantomJsCloud.com, for screen-scraping too I guess!

Updated on March 16, 2022

Comments

  • JasonS
    JasonS about 2 years

    Trying to follow various instructions on creating a self-signed cert for use with localhost, Most of the instructions seem to be for IIS, but I'm trying to use Nodejs/Express. None of them work properly because while the cert gets installed, it is not trusted. here's what I've tried that fails:

    Can someone offer a workflow that can do this? I can get a cert installed, but I can't get the cert to be trusted in either chrome (v32) or IE (v10).

    EDIT: it was suggested in comments that the problem is no trusted cert-root. I installed the cert via IE but it's still not being trusted.

    • Admin
      Admin over 10 years
      None of the self signed certificates can be made trusted for web browsers. They are not signed by trusted signing authorities.
    • JasonS
      JasonS over 10 years
      that's not true: you can install a root certificate to get your self-signed cert trusted. however I can't seem to do this properly. I read that you can install the cert chain in IE (not in chrome), so I tried that but it's still not being recognized. I don't know if it's because localhost is special or if the self-signed cert is just not correct.
    • JasonS
      JasonS almost 8 years
      I never got a self-signed cert working with browsers like Chrome. Here is my workaround: I created a DNS entry for local.MYDOMAIN.com pointing to 127.0.0.1 (localhost) and then just use my production cert. This has the added benefit of making sure there are no problems with your production cert chain, etc.
  • JasonS
    JasonS over 10 years
    Your certificate is still not trusted, so you have the same problem I'm describing. I need it to be trusted to test/debug a webservice properly.
  • Admin
    Admin over 10 years
    So you want this certificate to be trusted only in your local machine and not in the network?
  • JasonS
    JasonS over 10 years
    yeah, my dev environment is running on a single machine, and of course the "localhost" domain is only available locally :)
  • JasonS
    JasonS about 10 years
    Hi Troy, thanks for sharing this. Someone else will have to comment on if this works or not. My workaround: I ended up adding dev.phantomjscloud.com to my hosts file, and then using my production cert. That is only useful if you want your production keys available on your dev box though, so I think your solution could still be valid, if someone else can please verify
  • TroyWorks
    TroyWorks about 10 years
    It works for me and my team, in a combination of ways, secure local to local server to secure local to production server.
  • Binvention
    Binvention over 8 years
    does it still give the warning if you add the certificate to the browsers certificate list?
  • steampowered
    steampowered almost 8 years
    the link at the top of the answer recomends 1024 bit 3DES encryption, which is way outdated. Better to use openssl genrsa -out key.pem 2048 for a better key.
  • 2-bits
    2-bits over 7 years
    He mentions IE, which means he is using Windows.
  • Diego Mello
    Diego Mello over 7 years
    Your certificate is still not trusted.
  • e_m0ney
    e_m0ney about 7 years
    As far as getting a certificate goes - this is probably better than using some untrusted self signed cert (and equally as free) letsencrypt.org
  • Jason Goemaat
    Jason Goemaat about 7 years
    For Windows, the git bash console works great using openssl commands from here. Just have to install the root certificate and you can create multiple site-specific certificates signed by it if you want.
  • Laurent Debricon
    Laurent Debricon over 6 years
    Then, in Chrome, go to chrome://flags and activate "Allow invalid certificates for resources loaded from localhost."
  • Jose A
    Jose A over 6 years
    Beautiful! This worked! I'd like to addition: Install OpenSSL from here: indy.fulgan.com/SSL/?C=M;O=A. Get the .cnf file from here: and then, configure it from here: gist.githubusercontent.com/pandurang90/dbe6a67339747ef5bacf/‌​raw/… and configure openSSL from here: stackoverflow.com/questions/7360602/…
  • Jose A
    Jose A over 6 years
    I'd like to add that for Chrome 58+, you're going to receive an error "Subject Alternative Name missing".stackoverflow.com/a/42917227/1057052. Check the answers below for more help: stackoverflow.com/a/43666288/1057052, stackoverflow.com/a/44398368/1057052
  • AIon
    AIon over 6 years
    Drag image to your desktop and double click it -> i can't drag anything to my desktop, is not draggable.. What image are you talking about ore exactly?
  • Jos
    Jos over 6 years
    This should not be the accepted answer (yet). Note that some things require a green bar. E.g. if your website is also a progressive web ap (pwa), TLS is mandatory. For chrome 58+ to trust the certificate properly, you also need to become/create a CA (Certificate Authority). I found this resource quite usefull: ram.k0a1a.net/self-signed_https_cert_after_chrome_58
  • Michael Litvin
    Michael Litvin over 6 years
    To overcome "Subject Alternative Name missing" in Chrome, you could do openssl req -newkey rsa:2048 -x509 -nodes -keyout keytmp.pem -new -out cert.pem -subj /CN=localhost -reqexts SAN -extensions SAN -config <(cat /System/Library/OpenSSL/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:localhost')) -sha256 -days 3650 instead of the first line you suggested. And also this one will ask less questions in the process...
  • Michael Litvin
    Michael Litvin over 6 years
    @AIon, this is only for MacOS, on Windows it's probably much more complicated ;)
  • Belfield
    Belfield almost 6 years
    Amazing, thanks!!! You'll also need to add to the httpsOptions passphrase: 'XXXX'
  • Salyangoz
    Salyangoz over 5 years
    This is the solution ive scoured the internet for over the past 2 hours. For anyone in ubuntu move the cp server.crt /usr/local/share/ca-certificates/. and run sudo update-ca-certificates Then localhost https requests work under NodeJS 8+. Id also increase 1024 to 2048
  • co.zohar
    co.zohar over 5 years
    Hi, when I finish all the steps I open https://localhost:3000 and Chrome is stuck on loading. Anyone can tell what might be the reason?
  • AIon
    AIon over 5 years
    @co.zohar any message in the console? Press crl+shift+i or F12 to open the console.
  • roskelld
    roskelld over 5 years
    If you're doing this for an address on a network I found that setting up the certificate DNS to a hostname such as: DNS.1 = server.local Then on the connecting machine update the HOSTS file to point the server IP address to the hostname, for example: 192.168.0.50 server.local This will allow the certificate and the address to match up and validate the certificate.
  • co.zohar
    co.zohar over 5 years
    @AIon the console doesn't show anything. The page just shows: "Waiting for localhost...". Did you configure anything in the hosts file?
  • AIon
    AIon over 5 years
    @co.zohar "Waiting for localhost..." indeed seems like an DNS resolution problem. I have an uncomented line in the hosts file: 127.0.0.1 localhost #transitCalculator Not sure who put it here. # means a commented line. The line i have typed here is uncomented. Please check you have aprox the same configuration there.
  • co.zohar
    co.zohar over 5 years
    @AIon Hi, I didn't have to have any row in the hosts file. Your initial solution worked perfectly. I didn't include the "app.get('/') and that's why it was waiting for localhost forever. Thank you
  • som
    som over 5 years
    the express code above works, utilising github.com/FiloSottile/mkcert (instead of openSSL) to create a local CA / trusted cert. Green bars all the way.
  • markreyes
    markreyes about 5 years
    I tried this exact exercise on Windows 10 OS and it worked. Thanks!
  • TKoL
    TKoL about 5 years
    @Alon it's unclear which of the fields in req.cnf need to be changed
  • TKoL
    TKoL about 5 years
    @Alon nevermind I think I got it. But what if we want it, instead of being localhost, to be an ip on the network? For example if I was running my node server on 192.168.1.10 and wanted other computers on the network to access it via https, how could i change your advice and have it still work?
  • TKoL
    TKoL about 5 years
    I found a semi-answer to my own question: if you change CN and DNS.1 to something like "local.com" for example, and in each computer that needs access to the server, change the etc/hosts file to point local.com to the ip of the server, this works.
  • zaheer
    zaheer almost 5 years
    Works greatly! With this approach we don't need to register our self-signed certificate, as a CA trusted Certificate Authority, in the chrome/windows certificates store. As mentioned in other answers.
  • clusterBuddy
    clusterBuddy about 4 years
    You just got the same http page but only the UI showing https, this is not correct, unsecure and still not working, can't understand why so many upvotes from people not checking thoroughly and (I guess) forget to remove their upvote only so people wont get confused thinking this is a viable answer,
  • rx2347
    rx2347 almost 4 years
    this only works for safari and chrome, firefox still won't play.
  • Hermenpreet Singh
    Hermenpreet Singh almost 4 years
    Step 2 is creating issue in window. 14148:error:0D07A097:asn1 encoding routines:ASN1_mbstring_ncopy:string too long:.\crypto\asn1\a_mbstr.c:158:maxsize=2
  • AIon
    AIon almost 4 years
    @Hermenpreet Singh not sure what you mean. step 2 is only about creating that file and pasting that content inside it. Is not about validating that file content. But check this for what looks like a simmilar issue: github.com/certbot/certbot/issues/1915
  • sme
    sme over 3 years
    @HermenpreetSingh Its a problem with the country initials, you probably just didn't change the line in the req.cnf C = Country initials like US, RO, GE, it should just be something like C = US
  • Ibra
    Ibra about 2 years
    Can I run this on pipelines ? here is a reference for my question stackoverflow.com/questions/71850480/…
  • som
    som about 2 years
    @Ibra there seems to be some discussion around pipelines usage on the mkcert github repo so assume it's possible .. but keep in mind mkcert isn't intended for production use
  • Ren
    Ren almost 2 years
    for anyone looking to what cryptic drag and drop means find an answer here stackoverflow.com/questions/25940396/…