Wildcard SSL and Beyond


This is the third article in the Taming Nginx series, where we explore creative ways to configure Nginx. In this article, we focus on securing your web server with SSL, wildcard certificates, and we touch on mutual SSL.
So far, we've covered:
- Smarter Defaults for Subdomains and SSL - where I talk about how you can organise your subdomain configuration in Nginx
- Building the Foundation - where I suggest a folder structure that I use to manage multiple domains and services
It might be worth reading In the Beginning... We Shared (whether we liked it or not!) which is a light-hearted exploration of how self-hosting evolved over the decades.
What in the world is SSL and where did TLS come from?
SSL (Secure Sockets Layer) was introduced in the mid-1990s to secure internet communications but was deprecated in favour of TLS (Transport Layer Security), which is far more secure and efficient. While SSL versions are now obsolete, TLS 1.2 and 1.3 remain widely used, providing encryption, integrity, and authenticity.
Fun Fact: "SSL" is still colloquially used to refer to TLS, even though SSL is no longer in use.
Today, we have services like Cloudflare and Let’s Encrypt that have made securing your site or service so much easier.
But this wasn’t always the case. In the early days of web hosting, you had to purchase certificates from providers at exorbitant prices, ranging from $100 to over $1,500 per year, depending on the reputation of the Certificate Authority!
Thankfully, if you’re looking for the simplest way to secure your site today, Cloudflare offers an outstanding free solution.
Cloudflare
To be fair, when Cloudflare was first introduced, it didn’t yet issue certificates for your domain. That said, it was already a game-changer, excelling at protecting your site from DDoS attacks and offering useful 3rd party integrations like analytics.
SSL wasn’t even part of the picture yet, but Cloudflare still stood out as a critical tool for website owners.
The way it worked was simple: you’d buy a domain from a registrar of your choice, then point your domain’s name servers (NS Records) to Cloudflare.
This allowed Cloudflare to act as your DNS provider and deliver its features seamlessly.
Perhaps the biggest benefit? You didn’t have to pay a cent. And when Cloudflare introduced automated SSL provisioning, it added a layer of security that elevated its value to an entirely new level.
At the time of this article, there’s still no service quite like it that offers so much - for free.
Getting Started with Cloudflare
Setting up Cloudflare for your domain is straightforward:
Sign Up: Create a free Cloudflare account.
Add Your Domain: Register your domain with Cloudflare and follow the step-by-step instructions to configure DNS settings.
Update Your Name Servers: Point your domain’s name servers (NS Records) to Cloudflare as directed during setup.
And that’s it, really! Once you’ve completed the setup, you’ll have DDoS protection and SSL out of the box. However, Cloudflare offers a few options that are worth mentioning to get the most out of your configuration.
DNS Proxied vs. DNS Only
Proxied: This routes traffic through Cloudflare’s network, enabling features like DDoS protection, caching, and SSL termination.
DNS Only: This points traffic directly to your origin server, bypassing Cloudflare. Use this for services like mail servers or anything incompatible with Cloudflare’s proxy.
SSL Options Cloudflare offers several SSL modes, depending on your requirements:
Off: No SSL (not recommended).
Flexible: Encrypts traffic between the browser and Cloudflare but leaves traffic to your origin server unencrypted (not secure).
Full: Encrypts traffic end-to-end but skips validation of the origin server’s certificate.
Full (Strict): The most secure option, encrypting traffic end-to-end while validating the origin server’s certificate.
For a fully secure setup, always aim for Full (Strict) SSL mode if your origin server has a valid certificate (such as one issued by Let’s Encrypt or your own CA). While Cloudflare simplifies SSL for many scenarios, it’s worth knowing how to use Certbot to issue your own certificates, which we’ll dive into next.
Certbot
Let’s Encrypt revolutionized SSL provisioning by making certificates free and accessible to everyone. At the heart of this process is certbot
, a powerful CLI tool that automates certificate issuance and renewal.
Fun Fact: certbot
—the CLI tool—used to be known as letsencrypt
and was really annoying to type out each time! The rebranding was more than welcome, not to mention its expanded capabilities.
There are many tutorials on using Certbot to provision single-domain certificates, so this guide focuses on wildcard certificates. These simplify SSL management by covering all subdomains of a domain, making them ideal for scalable and dynamic setups.
Getting Started with Let's Encrypt
Let’s set up Certbot on Ubuntu with Nginx to automate SSL provisioning. If your configuration differs, the Certbot interactive guide is a great resource. We’ll focus on the basics:
# Update your package list and install snapd (if not already installed)
sudo apt update
sudo apt install snapd
# Install Certbot using snap
sudo snap install --classic certbot
# Verify Certbot installation
certbot --version
# If certbot isn't found, create a symlink to the binary
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Allow Certbot to use plugins with root access
sudo snap set certbot trust-plugin-with-root=ok
# Install Certbot's Cloudflare DNS plugin
sudo snap install certbot-dns-cloudflare
To get a Cloudflare API Token:
→ API Tokens in Cloudflare’s Dashboard → Create Token → Use the Edit zone DNS
template → Select the domain (zone) you want to manage → Copy the token and save it securely.
On your machine, create a cloudflare.ini
file:
# Cloudflare API token used by Certbot
dns_cloudflare_api_token = <Your Token>
Save the file in a secure location and set its permissions:
# use sudo depending on where you save it.
chmod 600 ./cloudflare.ini
And now it's time to generate the wildcard certificate:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
-dns-cloudflare-propagation-seconds 60 \
-d example.com \
-d *.example.com
The command may take a minute due to the DNS propagation delay, but once complete, your certificates will be saved to:
Certificate:
/etc/letsencrypt/live/
example.com/fullchain.pem
Private Key:
/etc/letsencrypt/live/
example.com/privkey.pem
Nginx Integration
Remember the handy ssl_params
file from the first article? That’s where you’ll update the certificate paths with the locations generated above.
To illustrate how this works, here’s a minimal example of an Nginx configuration for a subdomain:
# File: /etc/nginx/sites-available/freebies.example.com
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name freebies.example.com;
root /srv/freebies.example.com/;
index index.html;
# SSL Configuration is the same for every subdomains thanks the
# wildcard certificate
include /etc/nginx/ssl_params;
# Serve files and directories, or return 404
location / {
try_files $uri $uri/ =404;
}
}
This example illustrates how the wildcard certificate makes SSL configuration consistent across subdomains. With this foundation in place, let’s take it a step further and explore enabling Mutual SSL for secure system-to-system communication.
Mutual SSL
Before APIs were everywhere, developers relied on simpler methods like API keys or IP whitelisting to secure system-to-system communication. Mutual SSL improves on this by verifying both the client and server using certificates, ensuring trust at the system level.
It’s important to note that Mutual SSL verifies requests at the transport layer and does not replace user-level authentication methods like OAuth or passwords.
How to Do It with Nginx
Let's take a hypothetical scenario where you want to establish Mutual SSL with two of your subdomains:
api.example.com
: Your API serviceclient.example.com
: Your WebApp that calls the API for data
Since we’ve already created a wildcard certificate earlier for browser-level verification, that setup remains unchanged. Here, we’ll generate additional certificates specifically for Mutual SSL using OpenSSL:
For a production-grade guide on setting this up, be sure to check out Nginx’s Documentation
# Create a custom CA certificate and private key
openssl req -new -x509 -days 365 -nodes -out ca.crt -keyout ca.key
# Generate a private key and CSR (Certificate Signing Request) for the client
openssl req -new -nodes -out client.csr -keyout client.key
# Sign the CSR with the CA to create the client certificate
openssl x509 \
-req \
-in client.csr \ # Input: Client CSR
-CA ca.crt \ # CA certificate
-CAkey ca.key \ # CA private key
-CAcreateserial \ # Generate a serial number for the certificate
-out client.crt \ # Output: Client certificate
-days 365 # Valid for 365 days
Server Configuration for api.example.com
Update the configuration for api.example.com
to include Mutual SSL settings:
# File: /etc/nginx/sites-available/api.example.com
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name api.example.com;
root /srv/api.example.com/;
index index.html;
# Include wildcard certificate
include /etc/nginx/ssl_params;
# Mutual SSL settings
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client on;
location / {
# Enforce client verification by leveraging ssl_verify_client
error_page 495 = /error/missing_or_invalid_client_cert;
try_files $uri $uri/ =404;
}
# Custom error for missing or invalid certificates
location = /error/missing_or_invalid_client_cert {
return 403 "mTLS certificate missing or invalid";
}
}
Client Configuration for client.example.com
To connect securely to the API, configure the client.example.com
subdomain to use the generated certificates:
# File: /etc/nginx/sites-available/client.example.com
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name client.example.com;
root /srv/client.example.com/;
index index.html;
# Include wildcard certificate
include /etc/nginx/ssl_params;
# Configure Mutual SSL for outgoing requests to api.example.com
location /api/ {
proxy_pass https://api.example.com;
proxy_ssl_certificate /path/to/client.crt;
proxy_ssl_certificate_key /path/to/client.key;
proxy_ssl_trusted_certificate /path/to/ca.crt;
}
}
Verification Steps
Let’s make sure everything is configured correctly by testing NGINX and verifying the API endpoint.
# Test the NGINX configuration for syntax errors
sudo nginx -t
# Reload NGINX to apply changes (use restart if significant changes were made)
sudo service nginx reload # or restart
# Verify the API with the client certificate and key
curl --cert /path/to/client.crt --key /path/to/client.key https://api.example.com
If everything is set up correctly, the nginx -t
test should pass, and the curl
command should return a valid response from the API. If something’s off, check Nginx's access.log
or error.log
for SSL-related issues or misconfigurations.
Finally, open client.example.com
in your browser and ensure the WebApp loads without SSL warnings. No red screens? Perfect! Mutual SSL is up and running while keeping browser compatibility intact!
I didn’t anticipate this article being so detailed, but I wanted to share a bit more depth compared to the usual guides out there. This wasn’t meant to be a quick copy-paste style tutorial so I hope you found it informative and useful.
So far, we’ve explored three articles in the Taming Nginx series, and next up, we’ll dive into some Advanced Caching and Scaling Techniques. Stay tuned, and don’t forget to subscribe to the newsletter below to get the latest updates.
Cheers!
Subscribe to my newsletter
Read articles from Jason Joseph Nathan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Jason Joseph Nathan
Jason Joseph Nathan
Yo! I’m J, your go-to geek at Geekist. With nearly two decades under my belt, I craft high-performance software that’s as sleek as it is functional, specialising in JavaScript/TypeScript and modern full-stack solutions. Beyond code, my world revolves around music, mentoring budding developers, and cracking up my two wonderful daughters. Whether jamming out to Punjabi beats with my wife or leading dynamic teams across continents, I’m all about mixing passion with innovation. Here at Geekist, I share top-notch tutorials, tech wisdom, and a bit of humor to spice up your dev journey. So, whether you’re looking to skill up or just hang out, you’re in the right place. Welcome to our community of creators and thinkers!