Local HTTPS Using Nginx Proxy Manager, Cloudflare, & Let’s Encrypt

Local SSL certificates have always been a pain. You have to create them, deploy them, renew them, and don’t get me started on trying to get them to work on iOS. I have found a pretty easy way to get a Let’s Encrypt certificate on a local domain that is not exposed to the internet. You do need to own an actual routable domain name for this to work which can cost as little as $10 to $20 a year. We will be taking advantage of the .local internal only domain name to allow our internal servers to be trusted by an external certificate. Nginx Proxy Manager will be making our lives much easier, allowing us the ability to within a few clicks get a wildcard SSL certificate. This type of certificate will allow us to have an infinite number of internal server subdomains without needing a certificate for each one of them. Additionally, you don’t need to install a certificate on each internal computer you want to trust the certificate, because Let’s Encrypt is an internet-wide Certificate Authority.

Nginx Proxy Manager

First off, you need Nginx Proxy Manager (I will be referring to this as NPM from now on for briefness). You can spin up a docker container using docker-compose on their official site. The NPM admin panel will be on port 81 by default to be able to access it over your network. This proxy manager will stand in front of our services and give us easy access to Let’s Encrypt certificates. We will create a single certificate and have HTTPS for all of our internal services.

Cloudflare

Cloudflare is a company who specializes in all sorts of web infrastructure, website security, DDoS protection, and a ton of other things. We will be using it to manage our domain name and get an API token to connect to Let’s Encrypt that proves that we own our public domain name. I got this idea from a reddit user by the name of “Sunsparc” who explains this whole wildcard certificate process we are about to do. If you need more guidance, take a look at their post as they provide a picture step-by-step guide. I will be briefly shortening his post for our specific use.

Getting NPM Ready

  • Go to your Nginx Proxy Manager admin page and navigate to “Add SSL Certificate” => “Let’s Encrypt”
  • Add the subdomain “local” to your public domain name with a wildcard (*) preceding “local”. An example would look like: *.local.yourdomain.net (The asterisk means that this SSL certificate can be used for any subdomain of local.yourdomain.net).
  • Put in your email address
  • Click on the “Use a DNS Challenge” box, along with the “I Agree to the Let’s Encrypt Terms of Service”
  • When you click on the “Use a DNS Challenge” it will come up with a DNS provider box which we are going to choose “Cloudflare”
  • A new box named “Credentials File Content” should show up with a dummy API token

Getting the Cloudflare API Token

  • We now need to go to our Cloudflare account, which if you do not have one, sign up here
  • Make sure if you did not buy your domain name through Cloudflare, you connect it to Cloudflare’s DNS servers through your domain name provider’s management panel. Cloudflare will walk you through the process when you click “Add site” once you create an account
  • Click on the site you want to use as your domain name
  • Scroll all the way down the page until you see “Get your API token” and click on it
  • On the navigation bar near the top click on “API Tokens”
  • Click “Create Token” under the API Tokens box
  • Scroll down and click on “Get started” on the “Create Custom Token” box
  • Give your API token a name (this is only for your use, it does not matter what it is)
  • Under the permissions box you will see three boxes from left to right
  • Click on the first left-most box and choose “Zone”
  • Click on the second middle box and choose “DNS”
  • Click on the third right-most box and choose “Edit”
  • Scroll to the bottom of the page and click the blue “Continue to summary” box
  • Click “Create Token”
  • Your API token will now show up, copy it and then go to your NPM
  • In the “Credentials File Content” box, after “dns_cloudflare_api_token=” you will want to remove anything that is currently after the equals sign, and put your token into that place.
  • Leave the propagation seconds empty and click “Save”
  • After a moment it should say that it was successful in creating the certificate

This is what your setup should look like.

Creating A Proxy Host in NPM

Now that we have a certificate, we have to figure out how to use it. Go into NPM and go to the “Proxy Hosts” page and click on “Add Proxy Host”.

  • Put the desired subdomain preceding your “local.yourdomain.net”. Example: portainer.local.yourdomain.net
  • Put your IP or hostname of the service you want to connect to, and the port number it is accessed on
  • Under the “SSL” tab on the top, click on the box below “SSL Certificate”
  • Click on our Let’s Encrypt certificate we created
  • Force SSL if you want only an HTTPS connection to your service
  • Click “Save”

We are now done with all the configuring on NPM, the last thing to do is add our local domain and subdomain to our DNS server.

Local DNS Records

I use Pi-hole as my DNS server, but you may use another service or just your router if it supports an integrated DNS server. Under your local DNS records in the DNS records tab, associate your local domain with the IP of the server hosting NPM. Here I added our local.yourdomain.net and associated that with my NPM server 192.168.20.10

The last step to do is add a CNAME record for our local service. For example, I will be adding the domain name of “portainer.local.yourdomain.net” and targeting the domain of “local.yourdomain.net” so the DNS server knows where to direct the traffic when it gets a request for the portainer subdomain.

Last Remarks

Now that you have an easy way to secure your internal servers without having to deal with annoying self-signed certificates, you can easily host whatever service you want without complication. There is no need to trust your own Certificate Authority, because Let’s Encrypt deals with all of that. There is an easy way to renew certificates by going to your NPM, SSL Certificates, and then click “Refresh Now” on the cert you need to renew. Since we chose to go with a wildcard cert, we do not need to deal with multiple SSL certificates, we only have to deal with one. This lessens the administration time and the overall time it takes to do local HTTPS. For me this has been the easiest way that I have found to do local SSL. Now each time you want a local service over HTTPS, you go into NPM, make a proxy host, put a CNAME record in your DNS server, and you’re done. I saw a video from Techno Tim on YouTube doing a similar thing but with Traefik instead of Nginx Proxy Manager. I found my way of doing things to be a lot simpler because there is much less to configure in the command line, and NPM provides an easy and automated way with integrated SSL within the GUI. However, if you are interested in Traefik, Tim provides a great in-depth walkthrough of how to set things up much more granularly if that is what you are looking for here.