Self-Hosting
Browser 'Not Secure' on a home service
Fix NET::ERR_CERT_AUTHORITY_INVALID on a self-hosted service — why a self-signed cert fails, the two real fixes (Let's Encrypt DNS-01 for an internal host, or a private CA you trust), the HSTS no-bypass trap, and why automated renewal is now mandatory.
Problem summary
A self-hosted service on a self-signed certificate throws a full-page browser warning (Chrome `NET::ERR_CERT_AUTHORITY_INVALID`, Firefox `SEC_ERROR_UNKNOWN_ISSUER`) because the issuer isn't in the device's trusted root store — the browser can't prove who it's talking to, regardless of encryption. There are two legitimate fixes: a publicly-trusted cert for a real domain via Let's Encrypt's DNS-01 challenge (which needs no inbound port 80/443, so it works for an internal-only host) terminated at a reverse proxy; or your own private CA whose root you install into every device's trust store. Clicking through exceptions is fragile and, under HSTS, impossible — and with cert lifetimes dropping toward 47 days, automated renewal is mandatory.
Read the exact error code in the browser.
openssl s_client -connect <host>:443 -servername <host> </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates
A clear NET::ERR_CERT_AUTHORITY_INVALID / SEC_ERROR_UNKNOWN_ISSUER.
Don't treat thisisunsafe / exceptions as an operator solution.
Layer path
Step-by-step runbook
Start here. Do each check in order, compare it to the expected result, and stop when the evidence explains the failure or the safe stop point applies.
Identify the cause
Check: Read the error and inspect the cert chain.
Expected result: Confirmed untrusted-issuer (not expiry/name).
If not: If it's expiry/name, fix that specific issue instead.
Choose the path
Check: Real domain → public DNS-01; .local/IP/few devices → private CA.
Expected result: A path that matches your naming and device count.
If not: Many varied devices favor a public cert.
Issue via DNS-01 on a proxy
Check: Configure Caddy/nginx/Traefik to solve DNS-01 for the internal host.
Expected result: A publicly-trusted cert with no inbound port exposed.
If not: If DNS-01 isn't possible, go private CA.
Or stand up a private CA
Check: Run step-ca/mkcert and install the root on each device.
Expected result: Devices trust your internal certs.
If not: Track which devices have the root installed.
Clear HSTS if stuck
Check: If there's no bypass, clear the HSTS entry or fix the cert.
Expected result: The browser loads (with a now-trusted cert).
If not: Don't leave a self-signed cert under HSTS — it's unusable.
Automate renewal
Check: Use ACME (proxy or internal CA) for issuance/renewal.
Expected result: Certs renew themselves as lifetimes shrink.
If not: Manual renewal won't survive 47-day lifetimes.
Decision tree
If: You own a real domain and want broad device trust
Then: Public cert is the low-friction path.
Action: Issue via Let's Encrypt DNS-01 on a reverse proxy (e.g. Caddy with a DNS plugin).
If: Internal-only host, can't expose 80/443
Then: DNS-01 is the only ACME challenge that fits.
Action: Use DNS-01 (TXT record) so no inbound port is required.
If: Only .local/mDNS names or bare IPs
Then: Public CAs can't issue.
Action: Run a private CA (step-ca/mkcert) and install its root, or use Caddy tls internal.
If: Warning has no 'proceed anyway' button
Then: HSTS is blocking the bypass.
Action: Clear the HSTS entry or fix the certificate; don't fight the interstitial.
If: You're relying on manual click-through / exceptions
Then: Fragile and short-lived as lifetimes shrink.
Action: Move to automated ACME issuance/renewal.
Safe stop: Don't treat thisisunsafe / exceptions as an operator solution.
Evidence table
| Symptom | Evidence to collect | Likely layer | Next action |
|---|---|---|---|
| NET::ERR_CERT_AUTHORITY_INVALID on a LAN service | The cert's issuer chain | Self-signed / untrusted issuer | Issue a trusted cert (public DNS-01 or private CA). |
| Can't get a public cert for the host | The hostname (.local / IP / real domain?) | Non-issuable name | Use a real domain, or go private-CA. |
| No inbound 80/443 available | Which ACME challenge is configured | HTTP-01/TLS-ALPN needs inbound | Switch to DNS-01. |
| Interstitial with no bypass | HSTS state (chrome://net-internals/#hsts) | HSTS enforced | Clear HSTS or fix the cert. |
| Cert keeps expiring | Whether renewal is automated | Manual cert handling | Automate ACME renewal; lifetimes are shrinking. |
Commands and settings paths
Inspect the served certificate chain
openssl s_client -connect <host>:443 -servername <host> </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates
Where: From a client machine
Expected: Issuer/subject/validity print; issuer is a CA the device trusts.
Failure means: A self-signed issuer (issuer == subject) explains the warning.
Safe next step: Replace with a trusted cert (public DNS-01 or private CA).
Check whether HSTS is blocking the bypass
chrome://net-internals/#hsts (Query domain)
Where: In Chrome
Expected: Shows whether an HSTS policy exists for the host.
Failure means: A present policy is why there's no 'proceed' button.
Safe next step: Delete the HSTS entry or fix the cert; don't rely on bypassing.
Install a private CA root into the system trust store
step certificate install root_ca.crt
Where: On each client device (with step CLI)
Expected: The device now trusts certs issued by your CA.
Failure means: Browsers/devices without the root still warn.
Safe next step: Repeat per device, or prefer a public cert for many devices.
Hardware and platform boundary
Change only when
- Adopt a reverse proxy with automatic HTTPS once you run more than one internal service.
- Stand up an internal ACME CA when you have many internal-only hostnames and controlled devices.
Evidence that matters
- A trusted issuer (public via DNS-01, or a private CA root installed on devices).
- DNS-01 for internal hosts so no inbound port is exposed.
- Automated ACME renewal ahead of shrinking cert lifetimes.
Evidence that does not matter
- Whether the connection is 'encrypted' — the warning is about trust, not encryption.
- Per-browser click-through tricks as a long-term plan.
Avoid
- Manually clicking through cert warnings on a real service.
- Trying to get a public cert for .local/mDNS or a bare IP.
- Manual certs when lifetimes are heading to 47 days.
Related tool
Use the linked tool to turn this runbook into a guided check for your exact setup.
Device setup troubleshooterRelated problems
Last reviewed
2026-06-03 · Reviewed by HomeTechOps. Built from 2026-06 research verified against Let's Encrypt challenge-type docs, Caddy automatic-HTTPS docs, Smallstep's private-ACME guidance, and the CA/Browser Forum Baseline Requirements lifetime schedule (≤398d now → 200d 2026-03-15 → 100d 2027-03-15 → 47d 2029-03-15). The operator differentiators are DNS-01 for internal hosts, the HSTS no-bypass trap, and treating automated renewal as mandatory.
Sources/assumptions
- Assumes a home service served over HTTPS behind a reverse proxy (Caddy/nginx/Traefik) reachable on the LAN, with a goal of removing the browser certificate warning rather than suppressing it.
- The DNS-01 (no inbound port) and private-CA (step-ca/mkcert + install root) paths are from Let's Encrypt, Caddy, and Smallstep docs; public CAs won't issue for .local/mDNS names or bare IPs, forcing the private-CA route there.
- The CA/Browser Forum lifetime schedule (≤398 days now → 200 days 2026-03-15 → 100 days 2027-03-15 → 47 days 2029-03-15) is first-party from the Baseline Requirements; dates are time-sensitive — re-verify.
Source-backed checks
HomeTechOps turns official docs and conservative safety rules into a shorter runbook. These links are the source trail for the page direction.