HomeTechOps

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.

Operator snapshotEvidence first
First proof

Read the exact error code in the browser.

Screen to open

openssl s_client -connect <host>:443 -servername <host> </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates

Expected signal

A clear NET::ERR_CERT_AUTHORITY_INVALID / SEC_ERROR_UNKNOWN_ISSUER.

Stop boundary

Don't treat thisisunsafe / exceptions as an operator solution.

Layer path

1A self-signed certificate triggers a full-page warning because its issuer isn't in the device's trusted root store — the browser can't verify who it's talking to, regardless of whether encryption is happening.
2There are two legitimate fixes: a publicly-trusted cert for a real domain via Let's Encrypt DNS-01 (no inbound port needed, so it works for internal-only hosts), or a private CA whose root you install into every device's trust store.
3Clicking through exceptions is fragile and, under HSTS, impossible — the browser removes the bypass entirely. .local/mDNS names and bare IPs can't get a public cert, forcing the private-CA path.
4With CA/Browser Forum max cert lifetimes dropping toward 47 days by 2029, automated renewal (ACME via a proxy, or an internal ACME CA) is mandatory, not optional.
Runbook

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.

1

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.

2

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.

3

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.

4

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.

5

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.

6

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

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

Evidence table

SymptomEvidence to collectLikely layerNext action
NET::ERR_CERT_AUTHORITY_INVALID on a LAN serviceThe cert's issuer chainSelf-signed / untrusted issuerIssue a trusted cert (public DNS-01 or private CA).
Can't get a public cert for the hostThe hostname (.local / IP / real domain?)Non-issuable nameUse a real domain, or go private-CA.
No inbound 80/443 availableWhich ACME challenge is configuredHTTP-01/TLS-ALPN needs inboundSwitch to DNS-01.
Interstitial with no bypassHSTS state (chrome://net-internals/#hsts)HSTS enforcedClear HSTS or fix the cert.
Cert keeps expiringWhether renewal is automatedManual cert handlingAutomate ACME renewal; lifetimes are shrinking.
Reference

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 boundary

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 troubleshooter

Related 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.