A quick guide to free HTTPS with Cloudflare and Nginx
I’ll outline how I usually set up Cloudflare in front of a web app. Cloudflare offers a very generous amount of free functionality, but in this article I’ll just outline how to set up HTTPS.
DNS
Let’s assume you have a web application hosted somewhere, for example on a VM with DigitalOcean. It’s serving some static resources and API requests over HTTP. Further, I’m going to assume you have Nginx set up there. In case you need it, here’s a tutorial for setting up Ningx on Ubuntu.
The next step is to buy a domain name for your application. I use namecheap.com so I’ll use that in my example, but other registrars should have analogous configuration options.
You’ll also need a Cloudflare account, which is free to set up.
After you’ve bought a domain name, log in to Cloudflare. We want to ‘add a site’, which is a button in the toolbar at the time of writing. Provide your domain name in the dialogue, and select the ‘free’ option when prompted.
Now you’ll be prompted to set up Cloudflare’s nameservers. This is something you’ll need to change with whatever service you purchased the domain from. With Namecheap, this configuration is under the ‘Domain’ tab.
So we’ll change the nameservers from ‘Namecheap BasicDNS’ to ‘Custom DNS’ and specify the ones that Cloudflare provided:
This change can take a while to take effect. The Cloudflare ‘Overview’ section for your domain will stop reading ‘Complete your nameserver setup’ once it’s done, and look like this:
Finally, we need to configure DNS with Cloudflare so it points our domain name to our app server. In the ‘DNS’ tab, you can get rid of any existing DNS entries and just configure two A records — one to our domain name, and one as simply ‘www’.
Setting up HTTPS
In the ‘SSL/TLS’ tab, set the encryption mode to ‘Full (strict)’.
This option requires a Cloudflare certificate at the server we’re pointing to. To set this up, we’ll generate a certificate and key with Cloudflare. This option is under the ‘Origin Server’ tab, within ‘SSL/TLS’ settings.
This will provide you with a certificate and a private key, displayed in a popup. Copy the certificate to a file on your app server, at a location like /etc/ssl/your.domain.crt. Similarly, copy the key to a file like /etc/ssl/your.domain.key.
Now we need to add a server block to the Nginx conf on your app server. It will have a server_name corresponding to your domain name, and use the ssl certificate and key we just created. It will listen on port 443 (HTTPS), and proxy_pass the request over HTTP to your application. In my example, I have a single application instance listening on port 5004, but you could load balance between a number of instances. See the docs for the Nginx upstream directive. The following is the relevant config from /etc/nginx/nginx.conf:
upstream galleri-prod {
server 127.0.0.1:5004;
}server {
server_name galleri.fun www.galleri.fun;
listen 443;
ssl on;
ssl_certificate /etc/ssl/certs/galleri.fun.crt;
ssl_certificate_key /etc/ssl/certs/galleri.fun.key;
location / {
proxy_pass http://galleri-prod;
}
}
Check it’s valid, then reload the Nginx config:
liam@webapp-host:~$ sudo nginx -s reload -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
liam@webapp-host:~$ sudo nginx -s reload
liam@webapp-host:~$
Test
That’s it for setting up HTTPS. To test that it’s working as expected, tail the Nginx log on your app host, and make an HTTPS request to your domain.
Tailing the Nginx log (on your app server):
liam@webapp-host:~$ sudo tail -f -n 0 /var/log/nginx/*.log
==> /var/log/nginx/access.log <====> /var/log/nginx/error.log <====> /var/log/nginx/access.log <==
173.245.52.61 - - [29/Jul/2020:18:26:01 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.71.1"
Issuing an HTTPS request (from your laptop). Note the headers added by Cloudflare:
~ % curl -I https://galleri.fun
HTTP/2 200
date: Wed, 29 Jul 2020 18:26:01 GMT
content-type: text/html
set-cookie: __cfduid=dd0108c45c09458146ebc2d1f7ebcecbc1596047160; expires=Fri, 28-Aug-20 18:26:00 GMT; path=/; domain=.galleri.fun; HttpOnly; SameSite=Lax; Secure
last-modified: Sat, 02 May 2020 05:02:36 GMT
cf-cache-status: DYNAMIC
cf-request-id: 043d6c0e960000553ef0a2c200000001
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
cf-ray: 5ba8e2c4293a553e-ORD
That’s it!