Dev HTTPS with .test domains
Trusted local TLS in five minutes — no browser warnings, no Let’s Encrypt rate limits.
Why .test? RFC 2606 reserves it for testing — it will never resolve on the public internet, so you can hand out names like crm.solisoft.test to anything on your LAN with zero risk of collision.
Why mkcert? It installs a development root CA into your OS trust store. Browsers and curl trust certs it signs, so HTTPS “just works” locally without the --insecure dance.
Install mkcert
Pick the recipe for your client machine — the laptop you’ll be browsing from. The CA needs to be installed on every device that talks to the proxy.
# Homebrew
brew install mkcert nss
mkcert -install
nss is needed for Firefox.
# Debian/Ubuntu
sudo apt install libnss3-tools
brew install mkcert # or build from source
mkcert -install
Linuxbrew works; or grab a release binary from the mkcert repo.
# Chocolatey or Scoop
choco install mkcert
mkcert -install
Run the install once with admin rights.
mkcert -CAROOT
# prints the directory holding rootCA.pem — remember it,
# the same path will be used as --cacert by curl below.
Generate a wildcard cert
One cert per parent domain covers every subdomain. The proxy will pick it for any SNI one label deep (per RFC 6125).
mkcert \
-cert-file _wildcard.solisoft.test.cert.pem \
-key-file _wildcard.solisoft.test.key.pem \
# SAN list — cover the parent and one label deep
"*.solisoft.test" "solisoft.test"
Naming matters. The proxy looks for files literally named _wildcard.<parent>.cert.pem + _wildcard.<parent>.key.pem. Don’t rename them, don’t put them in a subdirectory.
Install on the proxy
Copy the pair into the proxy’s tls.cache_dir (default ./certs/) and restart the proxy.
scp _wildcard.solisoft.test.{cert,key}.pem proxy-host:/path/to/proxy/certs/
# Restart — cert files are scanned ONCE at startup.
# SIGUSR1 and POST /api/v1/reload only refresh routing.
ssh proxy-host 'kill "$(cat /path/to/proxy/proxy.pid)" && soli-proxy --dev -d'
Cert files are not hot-reloaded. Adding, replacing, or removing a cert file requires a full proxy restart. The reload endpoints (SIGUSR1, POST /api/v1/reload) re-parse config.toml and routing only.
On startup the proxy log should now contain:
Loaded wildcard certificate for *.solisoft.test
Resolve .test to the proxy
Pick whichever fits your setup — the proxy doesn’t care which you use.
# /etc/hosts (macOS, Linux)
192.168.1.30 soli.solisoft.test \
crm.solisoft.test \
www.delupay.test
Good for one or two devices, painful at scale.
# /etc/dnsmasq.d/test.conf
address=/.test/192.168.1.30
Resolves any *.test name — no per-host edits.
--resolvecurl --resolve soli.solisoft.test:443:192.168.1.30 \
https://soli.solisoft.test/
Verify
curl -v \
--cacert "$(mkcert -CAROOT)/rootCA.pem" \
--resolve soli.solisoft.test:443:192.168.1.30 \
https://soli.solisoft.test/
# Look for:
# * Server certificate: ...
# * SSL certificate verify ok.
In the browser, hit the URL directly — you should see a normal padlock with no warning. If you see “Not Secure”, your client doesn’t trust the mkcert CA: re-run mkcert -install and restart the browser.
Troubleshooting
How the proxy picks a cert
On every TLS handshake the resolver tries, in order:
- Exact match:
certs/<sni>.cert.pem - Wildcard, one label deep:
certs/_wildcard.<parent>.cert.pem - Self-signed fallback:
certs/self-signed.cert.pem(auto-generated)
curl says bad signature
Almost always means mkcert’s root CA was regenerated (e.g. you ran mkcert -uninstall && mkcert -install, or reinstalled the OS) after you generated the cert. The new CA has the same subject DN as the old one but a different key, so leaf certs signed by the old key no longer verify.
Run this on the client to confirm:
echo | openssl s_client -servername soli.solisoft.test \
-connect 192.168.1.30:443 -showcerts 2>/dev/null \
| awk '/BEGIN CERT/,/END CERT/' > /tmp/live.pem
openssl verify -CAfile "$(mkcert -CAROOT)/rootCA.pem" /tmp/live.pem
If it prints certificate signature failure, regenerate the wildcard cert (step 2), copy it back, and restart the proxy.
Helper scripts
| scripts/diag-mkcert-mac.sh | Three-step diagnostic: live cert, local CA, openssl verify. Run on the client. |
| scripts/regen-mkcert-and-deploy.sh | Regenerates the wildcards listed in PARENTS=(), scps to the proxy host, restarts the daemon, and verifies. Run on the client. |
Other gotchas
-
Two-label SNI. Wildcard match is one label deep.
a.b.solisoft.testwill not match_wildcard.solisoft.test— either flatten the name or generate_wildcard.b.solisoft.test. -
Cert covers parent too. Don’t forget the bare parent in the SAN list (
"*.solisoft.test" "solisoft.test") if you also servehttps://solisoft.test/. -
Multiple devices. Each device that connects to the proxy needs the mkcert CA in its trust store. Generate certs on one machine and copy
$(mkcert -CAROOT)/rootCA.pemto the others, then trust it locally. -
CI / Docker. Bake
rootCA.peminto the image and trust it at build time — mkcert is for development trust, not for shipping.