Serve a domain and its subdomains over HTTPS with DNS-01

HTTPS with a Wildcard Certificate

This guide serves example.com and every subdomain over HTTPS. r3v3rs3 orders one wildcard certificate from Let's Encrypt with the DNS-01 challenge, redirects HTTP to HTTPS, and sends HSTS.

DNS-01 is the only challenge that gets a wildcard certificate. It also needs no open port for the validation, so it works on a server behind a firewall.

Before You Start

Step 1: Create the API Credential

Create a credential that can write the _acme-challenge TXT records of the zone. r3v3rs3 creates each record before the validation and deletes it after.

ProviderCredentialPermissions
CloudflareAPI TokenZone:Read and DNS:Edit for the zone
Route 53Access Key ID, Secret Access Keyroute53:ListHostedZones and route53:ChangeResourceRecordSets

The DNS-01 table lists the other 13 providers with their credentials. Two of them need no hosting provider: the webhook provider calls a service of your own, and the exec provider runs a program on the r3v3rs3 host.

Step 2: Bind the HTTP and HTTPS Ports

The wildcard certificate needs no open port, but your site does.

  1. Click Ports in the menu, then Add.
  2. Select the interface 0.0.0.0, the port 443 and the protocol HTTPS. Click Create.
  3. Add a second port with the port 80 and the protocol HTTP. Step 5 redirects it.
  4. Check that both ports show the state Listening.

On Linux a port below 1024 needs root or the CAP_NET_BIND_SERVICE capability.

Step 3: Create the ACME Entry

  1. Click Certificates in the menu, then the ACME tab, then Add.
  2. Select the provider Let's Encrypt. The page uses its directory URL. ACME Provider offers Google Trust Services, ZeroSSL and a custom server too.
  3. Write your address in Email Address. The certificate authority sends the expiry warnings there.
  4. Write example.com, *.example.com in Domain Names. The certificate holds every name as a Subject Alternative Name.
  5. Select DNS-01 in Challenge.
  6. Select your provider in DNS Provider and fill in the credential fields of Step 1.
  7. Click Create. The preset providers order again 60 days after each order. A custom server has a Renewal Interval (days) field.

r3v3rs3 orders the certificate at once, and then at each renewal check. example.com and *.example.com share one TXT record name, _acme-challenge.example.com, so the record set holds two values during the validation.

r3v3rs3 stores the entry in acme.toml:

[abc-def]
provider = "Let's Encrypt"
renewal_days = 60
identifiers = ["example.com", "*.example.com"]
challenge_type = "dns-01"

[abc-def.dns_provider]
provider = "cloudflare"
api_token = "<token>"

Create the entry in the WebUI and not in this file, because r3v3rs3 creates the ACME account when you add the entry. The file holds the account key and the credential in plain text with the mode 0600. The admin API never returns them.

Step 4: Check the Certificate

The Server Certs tab shows the certificate after the order. It names the issuer, the subject names and the Renews on date.

A failed order writes the reason to the server log, and r3v3rs3 orders again one hour later. Two causes are common:

Step 5: Create the Proxy

  1. Click Proxies in the menu, then Add.
  2. Select the protocol HTTP / HTTPS.
  3. Select both ports of Step 2. The proxy needs the HTTP port for the redirect and the HTTPS port for the traffic.
  4. Write app.example.com in Virtual Hosts. The wildcard certificate covers every subdomain, so each subdomain can get its own proxy.
  5. Write the address of your application in Target, for example http://127.0.0.1:3000.
  6. Turn on Automatically Redirect HTTP to HTTPS.
  7. Click Create.

r3v3rs3 selects the certificate from the SNI name of the TLS handshake, so the HTTPS port needs no certificate setting.

Check both protocols:

$ curl -i http://app.example.com/
HTTP/1.1 301 Moved Permanently
location: https://app.example.com/

$ curl -i https://app.example.com/
HTTP/2 200

The redirect keeps the path and the query. It runs before the redirect rules and before authentication.

Step 6: Send HSTS

HSTS tells a browser to use HTTPS for the next request without a redirect. Add it as a response header rule:

  1. Open the proxy and find the Header Rules section.
  2. Write this line in Response Headers:
set Strict-Transport-Security: max-age=31536000; includeSubDomains
  1. Save.
$ curl -sI https://app.example.com/ | grep -i strict
strict-transport-security: max-age=31536000; includeSubDomains

Step 7: Add HTTP/3

HTTP/3 runs over QUIC, which is UDP. It needs a second port next to the HTTPS port; it does not replace it.

  1. Open Ports and add a port.
  2. Set the protocol to HTTP over QUIC (HTTP/3).
  3. Listen on 0.0.0.0:443. This is UDP 443, so it does not collide with the TCP 443 of the HTTPS port.
  4. Set TLS Termination with the same server names as the HTTPS port, so both ports use the same certificate.
  5. Open the proxy and add the new port next to the HTTPS port.

The listen address of the port then reads:

/ip4/0.0.0.0/udp/443/quic/https

Now every response of the proxy carries an alt-svc header:

$ curl -sI https://app.example.com/
HTTP/2 200
alt-svc: h2=":443", h3=":443", h3-25=":443"

r3v3rs3 writes this header itself, from the HTTPS port and the QUIC port of the proxy. It removes an alt-svc header that the upstream server sent. A browser reads it, remembers the QUIC port and uses HTTP/3 for the next request. The first request always uses TCP, so nothing breaks when UDP is blocked.

Three things to check:

HTTP/3 is available for incoming connections only. The upstream connection uses HTTP/2 or HTTP/1.1, and WebTransport is not supported.

Renewal

r3v3rs3 orders a new certificate renewal_days after the last order, 60 days with a preset provider. A Let's Encrypt certificate is valid for 90 days, so the renewal has 30 days of margin. The order repeats the DNS-01 challenge, so the credential must stay valid. In a cluster only the leader orders certificates.

The Settings page has a notification webhook. It sends a certificate_expiring event before an expiry and an acme_order_failed event after a failed order, so a broken credential does not stay silent. Notifications describes the events.

Next Steps