Before I get started with the how, I assume some of you may be asking, "Why do I care about running SSL on my localhost?" Well, there are some specific situations that you may care. Here are just a few:
- You are enforcing SSL in production and want to ensure that you can test for errors that may pop up in production when working locally. For example, errors related to non-secure resources being loaded and causing security warnings or errors related to potentially broken links when redirecting to SSL.
- You are making an ajax call to an API that uses SSL and are running into a same origin policy error due to the different protocol. This should be solvable via CORS if you control the endpoint, but you may not.
- You are developing a PWA, which requires SSL. While you should be able to ignore warnings related to SSL for local testing, you may prefer to more closely replicate your production app locally for testing.
There may be others, but these are some that I have run into myself.
Now that we have some reasons why you might want to run your localhost on SSL, let's look at how to do it. In this post, I'll look at some examples when using a simple Node web server, running Jekyll and running Wordpress. All of my examples are running on MacOS. Obviously, there are a ton of other local web server setups that I won't get to cover.
SSL with localhost on a Node Web Server
For many local web development tasks, I rely on simple HTTP servers built on Node. There are a ton of them available in npm. It turns out that many of them support SSL. One of the options that I have installed, local-web-server, even comes with a certificate that you can use to automatically launch localhost on SSL with just a command line option.
ws --https
The problem is that, by default, you'll get this lovely error.
You can, of course, proceed to localhost but you won't see the "secure" icon in Chrome, which may obscure other security issues (like insecure resources on a secure page). If you want the secure checkmark, they offer detailed instructions on how to do this for Mac OS.
The instructions generally work for me running MacOS Sierra (yep, I'm still avoiding High Sierra until someone convinces me of a compelling reason to switch). I will note that I could not easily navigate to the installation folder that contains their built-in certificate (in my case, this is /usr/local/lib/node_modules/local-web-server/node_modules/lws
) from within Keychain Access. Instead, I located it via Finder and then simply dragged the certificate into my "login" keychain.Once you do that, click on it to open it.
Expand the "Trust" section and set "Secure Sockets Layer (SSL)" to "Always Trust."
If you prefer not to use the web server's included certificate but want to use your own, the instructions also demonstrate how to do that, though it wasn't a critical issue for me, personally.
SSL with localhost with Jekyll
I have used Jekyll to build a number of sites, including my blog which enforces SSL. When building Jekyll locally, you'll typically use the built-in web server that builds the pages and allows you to test them in the browser. The good news is that it is fairly easy if you want to do this and test the page locally using SSL.
The first step would be to generate a certificate for your localhost. This guide from Let's Encrypt offers good instructions that you can just copy/paste into the Terminal.
openssl req -x509 -out localhost.crt -keyout localhost.key \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
I placed the generated certificate and key file into a folder named ssl in my documents root. However, as this issue points out, Jekyll's SSL support expects the certificate and key to be inside the site's files. That makes sense, and you could easily just place them there if you like. Since I have a number of projects that would potentially use the same certificate, I didn't do that and instead created a symlink to the ssl folder in Documents from within the project root.
ln -s /Users/brinaldi/Documents/ssl /Users/brinaldi/Documents/projects/remotesynth.github.io/ssl
As a side note, you'll want to make sure you don't check this symlink in with your project, so you may want to add it to your .gitignore.
Now you can launch the Jekyll server specifying the certificate and key locations using the symlink.
jekyll serve --ssl-key ssl/localhost.key --ssl-cert ssl/localhost.crt
Of course, you'll need to go through the process I discussed earlier to avoid the security warning in Chrome. Drag the certificate into Keychain Access, click on it and then set the "Secure Sockets Layer (SSL)" setting to "Always Trust."
Now you'll get the "Secure" icon.
SSL with localhost using ngrok
There are a number of other scenarios that I personally run into where I may potentially need to test SSL locally. For example, Hugo, the other static site server I build locally, does not support SSL via its built-in web server. Or I also still occasionally work on a Wordpress site, which involve going through a long list of instructions to update the Apache config.
A quick and easy solution to this is to use a service like ngrok. For basic local testing, the free account should suffice, but it is a paid service if you are looking for more options.
The first step, of course, is to download ngrok. Once you run it, you’ll need to connect it to your ngrok account – the tool will walk you through the process. I find it easier to add ngrok to my PATH variable as well so that I can access it via the command line from anywhere.
Once you are all set up (and assuming you have it on your PATH), you can launch the HTTP port forwarding service. For example, to port forward my built-in Hugo server (which, by default, uses port 1313), I just use the following:
ngrok http 1313
Now I can access the site running locally using SSL via the provided URLs.
If you're looking for a quick and easy way to test SSL locally, and are ok with signing up for an ngrok account, then this is definitely the simplest option.
Top comments (12)
Other method:
foobar.com
*.foobar.com
app1.foobar.com
is a proxy tolocalhost:4000
127.0.0.1 app1.foobar.com
to your/etc/hosts
Let's Encrypt makes this pretty easy, and free. What I usually do is have
localhost.foobar.com
resolve to127.0.0.1
, and either have a Let's Encrypt wildcard cert, or a cert specifically forlocalhost.foobar.com
Now this is a nerdy, overcomplicated solution that I like.
Note that ngrok goes over the internet, so it's pretty excessive/slow for local development. The request will go over the internet to ngrok's servers, then back to your system.
What I usually do for local development is configure
localdev.example.com
(whereexample.com
is the site for whatever project I'm working on) to resolve to127.0.0.1
on my DNS server, and then use Let's Encrypt to create a real cert forlocaldev.example.com
.I'm so interested to learn more about this.
You can use Let's Encrypt DNS validation to get certificates for servers even if they're not accessible over the internet (for example, for internal servers or local development). Certbot and acme.sh both support this and integrate with various DNS APIs to handle the validation, or you could manually set the required TXT record.
I use acme-dns to handle the validation, but it takes a bit of setup.
I like where you're going. Wanted to offer some two other options to skin that particular cat, addressing at least the three motivations you identified: VMs and Containers.
If you want me to support the suggestions, let me know in a comment. Otherwise, I'll just consider it out-of-scope or beyond the intent of this post.
Go for it. I barely skimmed the surface of possible solutions...just covering the use-cases that I needed for my particular work.
Sure.
I prefer the VM (or container) approach because, in addition to addressing the three items you identified, it generally allows me to nail down a test environment as close to the production server as I care to make it -- without unnecessarily constraining my dev machine.
By offloading the test environment to a VM, I'm free to upgrade my dev machine through various OS/distro upgrades, without compromising the test environment integrity -- the VM continues to run CentOS 6 server, while my dev machine matures from Ubuntu 12->14->16(->18 next weekend) LTS versions.
Similar advantages may be had with containers - with the added advantage that they can also leverage shared OS for a smaller footprint if multiple servers are needed.
With later versions of apache, getting SSL up and running is as simple as linking the sites-available conf to the sites-enabled directory. The provided snakeoil certs make it a breeze.
There's a bit of a learning curve for the VMs, but its been well worth it.
Good luck and please keep blogging.
LetsEncrypt have revoked around 3 million certs last night due to a bug that they found. Are you impacted by this, Check out ?
DevTo
[+] dev.to/dineshrathee12/letsencrypt-...
GitHub
[+] github.com/dineshrathee12/Let-s-En...
LetsEncryptCommunity
[+] community.letsencrypt.org/t/letsen...
Hi Brian nice article.
I found localtunnel.github.io/www/ as alternative to ngrok... just care to
lt
alias for lsI use Valet on Linux which allows you to quickly secure any of the folders its currently serving.