How we use Tailscale and Caddy to develop over HTTPS
Tailscale recently launched a beta version of auto-HTTPS support, and I wanted to quickly document how we are using it on our team here at Shareup. We ❤️ Tailscale and use it everyday, and this auto-HTTPS support has made sharing in-development apps and services with all our devices and between our development machines super easy.
The way Tailscale’s HTTPS feature works is they provide you a custom subdomain of ts.net
which will look similar to [machine-name].[random-words].ts.net
. Since Tailscale owns and operates ts.net
, they can provision real SSL certificates for subdomains using Lets Encrypt.
You can find your machine on the Tailscale dashboard, and it will show your custom subdomain there in the machine info.
One can also find the same info in a local terminal or shell by running
tailscale status --peers=false --json
and looking for theDNSName
property in theSelf
object or looking in theCertDomains
array. We usejq
to find it:tailscale status --peers=false --json | jq -r '.CertDomains[]'
We use Caddy to proxy HTTPS traffic to our HTTP services running on localhost. We like Caddy because it already knows about Tailscale and will automatically pull down your certificates to set up HTTPS endpoints. You can install Caddy for your platform (we use Homebrew).
The easiest way to get going with Caddy is to make a Caddyfile
.
To make things more predictable, we have a convention for translating an HTTPS port to an HTTP port: we add 10,000 to the HTTP port number. For example: if we have a local service running bound to localhost:3000
then we expect to be able to visit that using tailscale’s HTTPS url at[machine-name].[random-words].ts.net:13000
.
Here is an example Caddyfile
that would configure the above port proxying:
[machine-name].[random-words].ts.net:13000 {
reverse_proxy localhost:3000
}
You can then boot caddy
with that configuration in a terminal / shell:
caddy run --config Caddyfile
(Depending on how you install Caddy, it may already be running and have a default Caddyfile
. If you have issues running caddy
then check to make sure it’s not already running or edit its default Caddyfile
instead of making a new one.)
We run a few services locally, so we need to list them all in a config to proxy 13000, 13001, 13002, … to 3000, 3001, 3002, …
We have a script which auto-generates this Caddyfile
and writes out a .env.overrides
file to override our environment variables while the caddy proxy is running so our different services are able to find each other over HTTPS. Something like:
export WEBSOCKET_URL=wss://[machine-name].[random-words].ts.net:3003/
export WEB_ORIGIN=https://[machine-name].[random-words].ts.net:3001/
export METADATA_SERVICE_URL=https://[machine-name].[random-words].ts.net:3002/
This setup has greatly improved testing on all of our devices 🙌 We also are more likely to share a quick link to see an in-development service or web app with each other, since we know it will load over HTTPS without any additional setup, having to share keys, etc.
If you have Tailscale installed, and have the HTTPS feature turned on, you can get Caddy working for you as well with a similar configuration file. 🚀