Secure local HTTPS for Your Bitcoin Node
Learn how to Encrypt your local node’s web apps with real HTTPS
Encrypt your local node’s web apps with real HTTPS. This walkthrough shows exactly how to serve mempool.space at https://nodebox.local and how to add other apps either on https://nodebox.local:PORT (recommended when an app cannot live under a subpath) or https://nodebox.local/appname (when the app supports subpaths). You’ll see why each step matters, what threats this setup mitigates, and how to troubleshoot it.
If you followed the Sovereign Stacker’s Node Guide, you already have:
- mempool.space running locally (default example uses port 4080)
- Avahi (mDNS) available
- Ubuntu Server with systemd
If not, no problem - this guide is self-contained. Where helpful, we reference the ultimate guide sections for more context.
Assumptions: Your server’s hostname is nodebox.local
- If your server uses a different hostname (e.g., myserver.local), replace every instance of nodebox.local below with your actual hostname.
- If you want nodebox.local but your machine hostname is different, you can:
- Change the machine hostname (not necessary), or
- Configure Caddy to serve nodebox.local and ensure nodebox.local resolves to your server’s IP via mDNS/Avahi or client /etc/hosts entries. Steps below show both.
Goal - what we will achieve and the threat model
Goal: Terminate TLS locally with Caddy so browsers and apps connect to your node via HTTPS instead of HTTP. Caddy will reverse proxy to local backends (like mempool on 127.0.0.1:4080).
What this achieves
- Confidentiality and integrity between client ↔ Caddy via TLS: prevents passive sniffing on the LAN and blocks common in-LAN tampering.
- Protection against many local MITM attacks, provided client devices trust your local Root CA. If clients do not trust the CA, they will get certificate warnings.
- Simple, maintainable reverse proxy in one place (Caddy), with clear routing to backends.
What this does not solve
- If a client device or the server is already compromised, TLS cannot help.
- If your Root CA private key leaks, an attacker could impersonate services on your LAN. Keep the CA private key offline and safe.
- This guide does not secure services you publish to the internet; that requires a public CA, hardened reverse proxy, and firewalling.
Overview of components and roles
- Avahi (mDNS): Optional but convenient - makes nodebox.local resolvable by name on your LAN without editing each client’s hosts file.
- Caddy: TLS terminator and reverse proxy. Listens on 80/443, serves https://nodebox.local, and forwards requests to local backends like mempool.
- Root CA and server cert: A local Root CA you create and trust on client devices, and a server certificate for nodebox.local signed by your Root CA (valid e.g., 5 years).
- Backends: Your local apps listening on localhost (e.g., mempool on 127.0.0.1:4080). Caddy proxies to them.
Preparation
- Ubuntu Server 20.04+ (commands target apt + systemd; works fine on Ubuntu 24.04 from the other guide).
- You have sudo on the server.
- mempool is already accessible at http://localhost:4080 (this is the default in our ultimate guide section).
- Clients are on the same LAN. macOS/Linux typically support mDNS out of the box; Windows may need Bonjour or use hosts file entries.
Step 1 - Install Avahi (mDNS) so .local names resolve
Skip if nodebox.local already resolves on your LAN.
1
2
3
sudo apt update
sudo apt install -y avahi-daemon avahi-utils
sudo systemctl enable --now avahi-daemon
Test from another device on the LAN:
1
ping nodebox.local
- If ping resolves to your server IP, you’re set.
- If not, either fix mDNS or add a temporary entry to the client’s /etc/hosts:
- 192.168.x.y nodebox.local
Note: Avahi advertises the machine hostname (from /etc/hostname) as hostname.local. This guide uses nodebox.local.
Step 2 - Install Caddy (official package)
1
2
3
4
5
6
7
8
9
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Caddy runs as a systemd service and binds to :80 and :443.
Step 3 - Create a local Root CA and a server certificate for nodebox.local
We create a Root CA you trust on client devices, then a server certificate for nodebox.local signed by that Root CA. This avoids mDNS subdomain issues and keeps it simple.
1
2
mkdir -p ~/.caddy-certs
cd ~/.caddy-certs
3.1 Generate Root CA (keep the private key safe)
1
2
3
openssl genrsa -out my-root-ca.key 4096
openssl req -x509 -new -nodes -key my-root-ca.key -sha256 -days 1825 \
-out my-root-ca.crt -subj "/CN=My Local Caddy Root CA"
3.2 Generate server key and CSR for nodebox.local
1
2
3
openssl genrsa -out nodebox.local.key 2048
openssl req -new -key nodebox.local.key -out nodebox.local.csr \
-subj "/CN=nodebox.local"
3.3 Create extension file with SAN (required)
Create (nano) nodebox.local.ext containing:
1
2
3
4
5
6
7
8
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = nodebox.local
3.4 Sign the server cert with your Root CA
1
2
3
openssl x509 -req -in nodebox.local.csr -CA my-root-ca.crt -CAkey my-root-ca.key \
-CAcreateserial -out nodebox.local.crt -days 1825 -sha256 \
-extfile nodebox.local.ext
Now you have:
- my-root-ca.key (private — keep safe, never distribute)
- my-root-ca.crt (Root CA to install/trust on clients)
- nodebox.local.crt and nodebox.local.key (server certificate and key for Caddy)
Step 4 - Install the server cert into Caddy and set permissions
1
2
3
4
sudo mkdir -p /etc/caddy/certs
sudo cp nodebox.local.crt nodebox.local.key /etc/caddy/certs/
sudo chown -R caddy:caddy /etc/caddy/certs
sudo chmod 600 /etc/caddy/certs/*
Step 5 - Simple Caddyfile to serve mempool at https://nodebox.local
Create or replace /etc/caddy/Caddyfile with:
1
sudo nano /etc/caddy/Caddyfile
Paste:
1
2
3
4
5
6
nodebox.local {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
# Serve Mempool at root
reverse_proxy / 127.0.0.1:4080
}
Notes:
- reverse_proxy / 127.0.0.1:4080 routes the site root (/) to mempool’s backend.
- This puts mempool at https://nodebox.local/ to avoid subpath issues (mempool is not designed to live under /mempool by default AFAIK).
Apply and check logs:
1
2
sudo systemctl reload caddy
sudo journalctl -u caddy -n 200 --no-pager
Step 6 - Trust the Root CA on client devices (one-time per device)
Clients must trust my-root-ca.crt for browsers to accept the server certificate without warnings.
macOS
On your mac:
1
scp user@nodebox.local:~/.caddy-certs/my-root-ca.crt /your-desired-path
Then follow this guide but with my-root-ca.crt
Linux (Debian/Ubuntu)
On your Linux machine:
1
2
3
4
scp user@nodebox.local:~/.caddy-certs/my-root-ca.crt /home/user/Documents/
cd ~/Documents
sudo cp my-root-ca.crt /usr/local/share/ca-certificates/my-local-caddy-root.crt
sudo update-ca-certificates
Configuring Firefox to respect your CA
Configuring Chrome to Respect Your Root CA
Windows
Ensure you have downloaded your Root CA just like above.
Install Bonjour
Then follow this guide but with my-root-ca.crt
After trusting the Root CA, restart your browser and visit:
- https://nodebox.local
- You should see a padlock and mempool’s UI.
Step 7 - Adding additional apps: two approaches
You can expose more apps in two common ways:
A) Subpaths: https://nodebox.local/appname
- Single certificate, single port (443), cleaner URLs.
- Only use if the backend app properly supports being served under a subpath (base path, relative asset URLs, etc.).
Caddyfile example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nodebox.local {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
# Mempool served at root
reverse_proxy / 127.0.0.1:4080
# Another app served under /otherapp (if it supports subpaths)
handle_path /otherapp* {
reverse_proxy 127.0.0.1:5000
}
# Media app that supports subpath
handle_path /media* {
reverse_proxy 127.0.0.1:32400
}
}
Why handle_path?
- handle_path /foo* strips /foo and proxies the remainder to the backend (so the backend sees /, /index.html, etc.).
- Many apps are not subpath-aware and hard-code absolute URLs (/app.js), causing missing CSS/JS. If your app supports setting a base URL/root path, configure it there too.
When subpaths fail
- If you see a blank page, missing assets, or 404s for JS/CSS, the app likely assumes it is at /.
- Use the port-based approach if subpaths don’t work.
B) Port-based listeners: https://nodebox.local:PORT
- Always works for apps that require root (/).
- No app reconfiguration needed.
- Still encrypted with the same TLS certificate at Caddy.
Caddyfile example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# main site at 443
nodebox.local {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
# Mempool at root
reverse_proxy / 127.0.0.1:4080
# Optional subpath (only if supported)
handle_path /media* {
reverse_proxy 127.0.0.1:32400
}
}
# Separate listener at 8443 for another app at root
https://nodebox.local:8443 {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
reverse_proxy 127.0.0.1:5000
}
Then visit https://nodebox.local:8443/ for that app.
When to choose which
- App supports base path → Use https://nodebox.local/appname
- App requires root → Use https://nodebox.local:PORT
- Prefer unique hostnames → Use SAN certs and ensure each name resolves (mDNS or hosts). This adds complexity; stick to nodebox.local unless you need it.
Security notes - why TLS here helps
- Eavesdropping prevention: On shared/hostile LANs, TLS keeps data private.
- Integrity: Prevents in-LAN tampering of HTTP responses (e.g., injected scripts).
- Authentication: With the Root CA trusted on client devices, the browser can authenticate nodebox.local to your server (attackers cannot easily impersonate without your CA key).
- Limits: If the client is compromised or the CA key leaks, this protection is undermined. Keep the CA key offline and secured.
Example setups (copy/paste)
Minimal - mempool at root
1
2
3
4
5
# /etc/caddy/Caddyfile
nodebox.local {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
reverse_proxy / 127.0.0.1:4080
}
Multiple apps - mempool at root, photos under /photos, another app on port 8443
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nodebox.local {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
# mempool
reverse_proxy / 127.0.0.1:4080
# photos app that supports subpath
handle_path /photos* {
reverse_proxy 127.0.0.1:8001
}
}
https://nodebox.local:8443 {
tls /etc/caddy/certs/nodebox.local.crt /etc/caddy/certs/nodebox.local.key
reverse_proxy 127.0.0.1:5000
}
Where this fits in your stack
- If you followed the Node Guide, you already run mempool, Fulcrum, and Bitcoin Core. Caddy gives you private, authenticated HTTPS to these web services without relying on public CAs or exposing them to the internet.
- You can front more apps (e.g. Ride the Lightning, Albyhub, Ashigaru Terminal) using the same approach:
- For Tor-only UIs, keep them onion-only (strong privacy).
- For LAN-only UIs you trust and use often, put Caddy in front with your local CA.
P.S. - Add banner to Fulcrum
Add “banner = /home/satoshi/fulcrum/banner.txt” to your fulcrum conf and create banner.txt with:
1
2
3
4
5
6
______ _ ___ ___
| ____| | | |__ \ / _ \
| |__ _ _| | ___ _ __ _ _ _ __ ___ ) | | | | |
| __| | | | |/ __| '__| | | | '_ ` _ \ / / | | | |
| | | |_| | | (__| | | |_| | | | | | | / /_ _ | |_| |
|_| \__,_|_|\___|_| \__,_|_| |_| |_| |____| (_) \___/
