Serve local HTTPS

Trust the lab CA for one command

Verify the local server with an explicit CA file before changing an operating-system trust store.

The last missing piece is trust. curl rejected our server because lab-ca.crt is not one of its trusted roots. There are several ways to fix that, and they differ a lot in blast radius. My advice is to always start with the narrowest one.

The narrowest fix is per command. curl’s --cacert option replaces its default root store with a file you name, for this one transfer. Nothing else on the machine learns to trust the lab CA.

Let’s connect with the explicit root:

curl --cacert lab-ca.crt --resolve app.lab.test:8443:127.0.0.1 https://app.lab.test:8443/
# secure lab

That’s the response body from our Node server. Both checks passed: the chain and the hostname. Everything from the previous lessons is working together now. The name resolves to loopback, the server presents the certificate we issued, and curl walks the chain up to the root we handed it.

Add -v if you want to watch it happen:

curl -v --cacert lab-ca.crt --resolve app.lab.test:8443:127.0.0.1 https://app.lab.test:8443/ 2>&1 | grep -E 'SSL cert|subject|issuer'
# *  subject: CN=app.lab.test
# *  subjectAltName: host "app.lab.test" matched cert's "app.lab.test"
# *  issuer: CN=Practical TLS Lab CA
# *  SSL certificate verify ok.

Four lines, and each one is a check you set up. The subject is our leaf. The SAN matched the hostname we asked for. The issuer is our lab CA. And verification passed.

Now break it on purpose. Controlled failures teach you what each check does. Drop --cacert and you get error 60, unable to get local issuer certificate. That’s the trust check. Keep --cacert but connect to https://127.0.0.1:8443/ and the name check fails instead, because 127.0.0.1 is not in the SAN. Two protections, two different errors.

Notice what we never typed: --insecure, or its short form -k. That flag skips verification entirely. It teaches you nothing except a bad habit. With --cacert, every check still runs. We only told curl which root to measure against.

The wide fix is installing the root system-wide. Every application on the machine would then accept anything your lab CA signs. Sometimes a development workflow needs that, for example when a browser has to open the lab site. If you go that way, write down how to remove the root, and do it when the lab ends. A lab CA should never outlive the lab.

Lesson completed