How To Secure Multiple Domains with One Certificate Using Certbot and SAN
Tricknowtech Team 15 min
Goal
Set up one Let's Encrypt certificate whose Subject Alternative Name list covers multiple distinct domains, serve every domain correctly from Nginx using that shared certificate, and understand the renewal/revocation tradeoffs of sharing a certificate versus issuing one per domain.
Prerequisites
An Ubuntu 22.04 LTS server with a non-root sudo user
Nginx installed and running
Two or more domains with DNS A/AAAA records already pointing at the server
Ports 80 and 443 open on the server's firewall
Familiarity with editing Nginx server block files
Let an AI agent do this for you
Copy a ready-made prompt for an AI coding assistant with terminal access to your server (Claude Code, Cursor, or similar) — it can carry out the steps below for you. Review what it plans to run before it executes anything.
By default, one TLS certificate secures one apex domain and whatever subdomains you list for it (or all subdomains, with a wildcard). That breaks down the moment you run several unrelated domains — example.com and example.net, say — off the same server and don't want to juggle a separate certificate, a separate renewal job, and a separate Nginx SSL stanza for each one.
Certbot can request a single certificate that covers a list of arbitrary domains at once by putting each one in the certificate's Subject Alternative Name (SAN) field. This tutorial walks through requesting one SAN certificate for multiple distinct domains, wiring up Nginx to serve all of them from that one certificate, confirming renewal still works, and understanding what you give up operationally by sharing a certificate across domains instead of issuing one per domain.
Prerequisites
An Ubuntu 22.04 LTS server with a non-root user configured with sudo privileges.
Nginx installed and running (`sudo apt install nginx`).
Two or more registered domain names, each with an A record (and AAAA record, if you use IPv6) already pointing at this server's public IP address. This tutorial uses example.com and example.net as stand-ins for two unrelated domains.
Ports 80 and 443 reachable from the internet, e.g. `sudo ufw allow 'Nginx Full'` if you use ufw.
Comfort editing Nginx server block files and reloading the service.
Step 1 — How a SAN Certificate Differs from a Wildcard Certificate
A wildcard certificate (for example one issued for `*.example.com`) covers any number of subdomains, but only subdomains of that one apex domain. It cannot cover `example.net` — a completely different registered domain — no matter how the wildcard is written, and issuing one requires a DNS-01 challenge (publishing a TXT record) rather than the simpler HTTP-01 challenge, because there's no single web-reachable path that proves control of every possible subdomain.
A Subject Alternative Name (SAN) certificate takes the opposite approach: instead of one wildcard name, the certificate lists an explicit set of names in its SAN extension, and each name can be from a different registered domain entirely. Browsers and other TLS clients accept the certificate for any name that appears in that list, so one certificate object can legitimately present itself for `example.com`, `www.example.com`, and `example.net` at once, each validated individually (usually over HTTP-01) when the certificate is issued.
Tricknowtech Domain Registration
50+ TLDs with free WHOIS privacy protection and free DNS management on every domain.
“Every certificate Let's Encrypt issues is technically a SAN certificate, including ones with only a single name — a wildcard is just a SAN certificate whose one name happens to start with `*.`. "SAN certificate" in common usage just means one where you deliberately listed multiple, unrelated names.”
Step 2 — Installing Certbot
Install Certbot via snap, which is the method the Certbot project itself recommends for Ubuntu, since it keeps the client on the latest release independent of the distro's package cadence.
If an older `certbot` package from `apt` is already installed, remove it first so the two don't conflict, then install the snap and symlink it onto your `PATH`.
Step 3 — Preparing DNS and a Shared Webroot for the ACME Challenge
Confirm both domains already resolve to this server before requesting anything — Certbot will fail validation for any domain that doesn't point here yet.
bash
dig +short example.com
dig +short example.net
This tutorial uses the webroot plugin, which proves domain control by dropping a file under `/.well-known/acme-challenge/` and having Let's Encrypt fetch it over plain HTTP — it works cleanly across several unrelated server blocks because they can all point the same challenge path at one shared directory. Create that directory and give ownership to the Nginx worker user.
Add a plain HTTP server block for each domain that serves the challenge path from that shared directory. Create `/etc/nginx/sites-available/example.com`:
Create the matching block for the second domain at `/etc/nginx/sites-available/example.net`, changing only `server_name` and `root`, then enable both, test the configuration, and reload Nginx.
Step 4 — Requesting a Single Certificate for Multiple Domains
Request the certificate with `certonly` (so Certbot only fetches the certificate and does not try to rewrite your Nginx config) and pass every name with its own `-d` flag. `-d` can be repeated as many times as you have names, mixing apex domains, `www` subdomains, and other unrelated domains freely in the same command.
Certbot validates each name against the webroot in turn and, on success, writes the certificate, chain, and private key under `/etc/letsencrypt/live/`. The directory is named after the first `-d` domain given — here that's `example.com` — regardless of how many other domains the certificate covers; pass `--cert-name` explicitly if you want a different directory name.
“The order of `-d` flags only affects the certificate's storage name, not which names are protected — every domain listed is an equal SAN entry in the resulting certificate.”
Step 5 — Configuring Nginx to Serve the Domains from the Shared Certificate
Point every domain's HTTPS server block at the same two files, `/etc/letsencrypt/live/example.com/fullchain.pem` and `/etc/letsencrypt/live/example.com/privkey.pem` — the directory is still named `example.com` from Step 4 even though `example.net` is served from it too. How you split the server blocks depends on whether the domains share content.
Option A — one server block for all domains
If every covered domain should serve identical content, list them all in a single `server_name` directive and skip the duplication entirely:
Option B — separate server blocks sharing the same certificate files
More commonly the domains serve different content, so keep a distinct server block per domain and just repeat the same `ssl_certificate`/`ssl_certificate_key` paths in each. Update `/etc/nginx/sites-available/example.com`:
Test the configuration and reload once both files are in place.
bash
sudo nginx -t
sudo systemctl reload nginx
Step 6 — Verifying the Certificate's SAN List
Confirm each domain now serves HTTPS and that the certificate presented actually lists every domain you requested. `openssl s_client` connects and dumps the certificate; piping through `openssl x509` prints the SAN extension.
Run the same command with `-servername example.net`, or just load `https://example.net` in a browser and inspect the certificate — both should show the identical SAN list covering all four names. A quick `curl -vI https://example.net` also confirms Nginx is answering that domain over TLS with a valid chain.
Step 7 — Confirming Automatic Renewal
The Certbot snap installs a systemd timer that runs `certbot renew` twice a day; renewal itself only acts on certificates within 30 days of expiry, so nothing renews immediately. Confirm the timer is present and do a dry run to exercise the whole renewal path without touching the live certificate.
Certbot re-reads the full list of names and the webroot path from the renewal configuration it saved at `/etc/letsencrypt/renewal/example.com.conf` — you don't need to repeat the `-d` flags yourself. A successful dry run revalidates every domain in the SAN list against the shared webroot exactly as the real renewal will.
Step 8 — Weighing Shared vs. Per-Domain Certificates
Bundling domains into one certificate cuts the number of certificates, renewal timers, and Nginx `ssl_certificate` paths you manage down to one — convenient when the domains are related and administered by the same person. It comes with real coupling, though, and it's worth deciding deliberately rather than by default.
Renewal is all-or-nothing: `certbot renew` revalidates every name in the certificate, so if any single domain's DNS record changes, its webroot becomes unreachable, or its Nginx block breaks, the renewal for the entire certificate fails — leaving even the healthy domains riding an expiring certificate.
Revocation is all-or-nothing too: `sudo certbot revoke --cert-name example.com` invalidates the certificate for every domain it covers, not just the one that had a problem.
Every name in a SAN certificate is publicly readable in the certificate itself and in public Certificate Transparency logs, so grouping domains together publicly discloses that association — a consideration if any of the domains are meant to stay unlinked from the others.
Adding a domain later means re-issuing with `--expand` and the full name list, not just provisioning something new alongside the existing certificate.
A separate certificate per domain isolates all of the above — one domain's renewal or revocation never touches another's — at the cost of one more certificate, one more `/etc/letsencrypt/live/` directory, and one more renewal config to keep track of.
To add a domain to the existing certificate later, rerun `certonly` against the same certificate name with `--expand` and the complete name list, old and new together:
To go the other direction — split a domain back out onto its own certificate — request it independently with its own `-d` list and `--cert-name`, then update that domain's Nginx block to point at the new certificate's files instead of the shared ones.
Conclusion
You installed Certbot, requested a single certificate covering multiple unrelated domains by repeating the `-d` flag, and configured Nginx server blocks to serve those domains over HTTPS from the same shared certificate files. You confirmed the certificate's SAN list matches what you requested and that renewal picks up the full domain list automatically. You also walked through what a shared certificate costs operationally — coupled renewal, coupled revocation, and public association of the bundled names in Certificate Transparency logs — versus the isolation a separate certificate per domain would give you instead.