Document · Network Access · 9 min read

Enable HTTPS over Tailscale for Kaptain

Put Tailscale Serve in front of Kaptain so browser traffic uses a trusted HTTPS URL on your tailnet hostname, while Kaptain keeps listening only on localhost.

What this enables

Kaptain starts on the local machine and is safest when it is reachable only from the same machine or from trusted devices over Tailscale. Tailscale already encrypts traffic between devices in the tailnet, but browsers still treat plain http:// URLs as not HTTPS. Tailscale Serve gives the browser a normal https:// URL for Kaptain without exposing anything beyond the tailnet.

Use the full Tailscale MagicDNS name, such as kaptain-box.example.ts.net. A bare machine name such as https://kaptain-box is not enough for a Tailscale-issued certificate.

Privacy note: Tailscale HTTPS certificates use public Certificate Transparency logs. The certificate name includes the machine name and tailnet DNS name. Rename the machine first if its name contains sensitive information.

Requirements

Kaptain's Network Access mode stays on Localhost only for this setup. Tailscale Serve connects to Kaptain over loopback, and Kaptain's network ACL rejects loopback callers while it is in Tailscale mode.

Find the Tailscale name for the Kaptain machine

  1. Open the Tailscale admin console.
  2. Go to Machines.
  3. Open the Kaptain machine.
  4. Copy its full MagicDNS name, for example kaptain-box.example.ts.net.

You can also find the tailnet DNS name on the DNS page in the admin console. The full name is the machine name plus the tailnet DNS name.

Set up Tailscale Serve

Tailscale Serve is the way to put HTTPS in front of Kaptain. Kaptain keeps listening on 127.0.0.1:8810 in plain HTTP; tailscaled terminates TLS for the tailnet hostname and forwards the request to that local port.

1. Start Kaptain and confirm it answers locally

kaptain start
kaptain status

kaptain status prints the UI URL. Open http://localhost:8810 on the Kaptain machine and confirm the interface loads before going further.

2. Publish it with Tailscale Serve

In a terminal on the Kaptain machine:

tailscale serve 8810

Serve prints the HTTPS URL it is now handling for this machine, for example https://kaptain-box.example.ts.net/. That URL is reachable only from devices in the same tailnet. Keep the command running, or use Tailscale's own background/service option so the proxy survives closing the terminal.

Note the port: the browser now uses the standard HTTPS port on the tailnet hostname. It does not use 8810. Port 8810 stays private to the Kaptain machine.

Scope note: Use Tailscale Serve for tailnet-only access. Do not enable Tailscale Funnel unless you intentionally want to expose a service beyond your tailnet.

Allow the tailnet hostname in Kaptain

Kaptain checks the Host header on every request and rejects values it does not recognise, which is what stops a hostile web page from resolving its own name to 127.0.0.1 and talking to your Kaptain. Through Serve the browser sends the MagicDNS name, so that name has to be on the list or Kaptain answers 421 Misdirected Request.

There is no setting for this in the interface. Edit kaptain.json in the Kaptain data directory:

Add the full MagicDNS name to allowed_hosts, leaving network_mode alone:

{
  "network_mode": "localhost",
  "allowed_hosts": ["kaptain-box.example.ts.net"]
}

Then restart Kaptain so it reloads the file:

kaptain stop
kaptain start
Leave Network Access on Localhost only. Switching Kaptain to Tailscale mode makes it bind the tailnet IP and reject callers that are not in the Tailscale 100.64.0.0/10 range — including tailscaled's own Serve proxy, which connects over loopback. The two setups are alternatives, not layers.

Kaptain itself does not serve TLS

Kaptain's server speaks plain HTTP. It has no certificate configuration, and it does not read cert.pem or key.pem from any folder. Running tailscale cert to produce certificate files and dropping them next to Kaptain has no effect: Kaptain will keep answering on http:// and an https://…:8810 URL will fail to connect.

If you do not want to run Serve, the alternative is plain HTTP over the tailnet: set Network Access to Tailscale in Settings, restart, and open http://<tailscale-ip>:8810 from another device in the tailnet. Traffic is still encrypted device-to-device by Tailscale itself; the browser just does not display an HTTPS indicator, and browser features that require a secure context are unavailable.

Verify the connection

  1. Open the HTTPS URL from another trusted Tailscale device.
  2. Confirm the browser shows HTTPS for the full MagicDNS name.
  3. Enter this Kaptain's access code (chynj_…) when the pairing screen asks for it.
  4. Open Settings and make a harmless change before entering any new provider key.
  5. If the page returns "Host header is not allowed", the MagicDNS name is missing from allowed_hosts or Kaptain has not been restarted since you added it.
  6. If the page does not load at all, check that tailscale serve is still running and that the second device is connected to the tailnet.

Certificate renewal

With Tailscale Serve, Tailscale obtains and renews the certificate for the tailnet hostname itself. There are no certificate files to copy into Kaptain and nothing to re-run before expiry. Renewal requires that HTTPS Certificates stay enabled for the tailnet and that the machine keeps its MagicDNS name.

Sources

  1. Tailscale Docs: Enabling HTTPS
  2. Tailscale Docs: MagicDNS
  3. Tailscale Docs: Tailscale Serve
  4. Tailscale Docs: Tailscale CLI