Tailscale HTTPS shows off and cannot be disabled in Settings when local DNS fails#14746

opened19d agobyRrabesss

What happened

Settings → Connections shows Tailscale HTTPS as off, although it is enabled and Tailscale Serve is exposing the backend on port 443. Turning the switch on and confirming Setup does nothing, and Settings offers no way to turn it off. This happens when the machine can't resolve its own MagicDNS name (tailscale set --accept-dns=false).

Diagnosis

The switch shows the result of a reachability probe instead of the saved setting, and the probe resolves the MagicDNS name through the OS resolver. Line numbers are from main @ 54084ae1; these files haven't changed since 99e08526.

  • apps/desktop/src/backend/tailscaleEndpointProvider.ts:82,93: the endpoint is available only if Serve is enabled and the probe passes.
  • packages/tailscale/src/tailscale.ts:365-381: the probe GETs https://<MagicDNS>/.well-known/t3/environment with the desktop Undici client (apps/desktop/src/main.ts:216), so it goes through the OS resolver. A failure or timeout returns false, and the endpoint becomes unavailable.
  • apps/web/src/components/settings/ConnectionsSettings.tsx:3181-3183: checked={tailscaleHttpsEndpoint.status === "available"}.
  • apps/desktop/src/settings/DesktopAppSettings.ts:326-341: re-enabling with the same port is a no-op, so requiresRelaunch: false (apps/desktop/src/backend/DesktopServerExposure.ts:525-528).
  • Disable is offered only on the switch's checked → unchecked path. The endpoint-row Disable button also requires available (ConnectionsSettings.tsx:1311-1313), and the rendered endpoint list leaves Tailscale HTTPS out anyway (:2568, :2838).
  • Unavailable endpoints are left out of pairing options (ConnectionsSettings.tsx:641), and Authorized clients is shown only for network-accessible || HTTPS available (:3388). Existing client authorizations are not revoked.
  • Graceful backend shutdown tries to remove the mapping (apps/server/src/server.ts:683, release at :717), but the saved setting stays true, so the next launch configures it again.

Suggested fix:

  1. Bind the switch to tailscaleServeEnabled. Keep Disable reachable when discovery or the probe fails, and show probe health as a separate note. Keep Authorized clients visible when Network access or the saved Serve setting is on. Keep endpoint health checks for pairing: a saved true means configured intent, so it should not mark the endpoint available by itself.
  2. Optional: probe a parsed Tailscale IP (tailnetIpv4Addresses is parsed but unused) while keeping the MagicDNS name for SNI, Host and certificate checks, and validate the returned descriptor.
  3. Regression test: saved enabled + failed or missing endpoint → Disable and client management stay reachable.

Steps to reproduce

  1. On Linux, join a tailnet with MagicDNS and HTTPS certificates enabled. Run tailscale set --accept-dns=false and check that the system resolver can't resolve this machine's <host>.<tailnet>.ts.net name.
  2. In the desktop app, with Network access on, enable Tailscale HTTPS on port 443. After the relaunch, check that tailscale serve status proxies HTTPS to http://127.0.0.1:3773.
  3. Reopen Settings → Connections. Tailscale HTTPS shows off.
  4. Turn it on and confirm Setup. The dialog closes without a relaunch and the switch stays off.
  5. Optional: turn Network access off while Tailscale HTTPS stays configured. Authorized clients disappears.

Expected: the switch is on with the MagicDNS URL, and turning it off removes the route (docs/user/remote-access.md: "Turn it off there to remove that route.").

Version

0.0.45-nightly.20261002.2561

Environment

Arch Linux (kernel 7.1), desktop app, Tailscale 1.98.8 with MagicDNS on tailnet-wide and accept-dns=false, Node v24.16.0 for the probe check

Evidence

shell
1# tailscale serve status --json (host redacted)
2{"TCP":{"443":{"HTTPS":true}},"Web":{"<host>.<tailnet>.ts.net:443":{"Handlers":{"/":{"Proxy":"http://127.0.0.1:3773"}}}}}
3
4# desktop-settings.json (window bounds omitted)
5"tailscaleServeEnabled": true
6
7# tailscale dns status
8Tailscale DNS: disabled.
9MagicDNS: enabled tailnet-wide
10
11# resolvectl query <host>.<tailnet>.ts.net
12resolve call failed: '<host>.<tailnet>.ts.net' does not have any RR of the requested type
13
14# dig +short @100.100.100.100 <host>.<tailnet>.ts.net
15100.x.y.z
16
17# curl https://<host>.<tailnet>.ts.net/.well-known/t3/environment
18curl: (6) Could not resolve host: <host>.<tailnet>.ts.net
19
20# same request with --resolve <host>.<tailnet>.ts.net:443:100.x.y.z
21http_code=200
22
23# Node 24: undici fetch (same client stack as the desktop probe) and dns.lookup
24TypeError ENOTFOUND
25dns.lookup: ENOTFOUND
26
27# desktop.trace.ndjson: the probe span reports Success because the failure is recovered to false
28{"name":"resolveTailscaleMagicDnsAdvertisedEndpoint","durationMs":432.4,"exit":{"_tag":"Success"}}

Related issues

No open duplicate. #4919 and #4698 proposed binding the switch to the saved setting (both closed unmerged); this adds a DNS-failure repro. Related: #4028, #2830.

Fix applied or workaround

On this machine, setting "tailscaleServeEnabled": false in desktop-settings.json and running tailscale serve --https=443 off removed the route, and the setting held after restart. Quit T3 Code completely before editing the file, because the running app keeps settings in memory and rewrites the whole file. tailscale serve --https=443 off removes every web handler on port 443, not only T3's.

Untested alternative: temporarily enable Tailscale DNS. If the name resolves and the probe passes, turn HTTPS off in Settings, then restore the DNS setting.

Filed by

Claude Code (claude-opus-5-5), following .github/triage/PLAYBOOK.md but not launched through t3 triage. Findings cross-checked with Codex CLI (gpt-6.1-sol).