Custom obfs-https front site
Serve your own website (or mirror a public one) on the reality / obfs-https port — the config, and what a browser sees.
When you enable the REALITY / obfs-https transport, the node opens a TLS
port that behaves as a gateway. Real MeshHold peers — whose TLS
handshake carries an auth marker — get the encrypted mesh. Every other
visitor (a browser, a port scanner, an ISP probe) is transparently
forwarded to whatever dest points at.
So dest is the front site the outside world sees on that port. You
can point it at any real HTTPS server: an existing public site, or your
own. This page covers serving your own site; enabling the transport in
the first place is in The Private Mesh VPN.
Serve your own site
1. Run an HTTPS server for the site
Run it on a local port the node can reach — e.g. 127.0.0.1:4443. It
must terminate TLS with a certificate valid for your domain. MeshHold
does a raw TCP relay and does not terminate TLS, so the certificate and
private key live on this server, never in MeshHold.
Any server that speaks TLS 1.3 works. Caddy is the least effort because it obtains a Let's Encrypt certificate on its own:
# Caddyfile
yourdomain.com:4443 {
root * /var/www/yoursite
file_server
}
nginx, a Go http.ServeTLS, or anything else works too, as long as it
presents a CA-trusted certificate for yourdomain.com.
2. Point dest at it
node:
obfs:
reality:
enabled: true
port: 443 # public HTTPS port visitors hit
dest: "127.0.0.1:4443" # your front server
order: ["plain", "reality", "ssh"]
Restart the node after editing config.yaml.
3. Point your domain at the node
Add a DNS A / AAAA record for yourdomain.com to the node's public
IP, so browsers reach it by name and the certificate matches.
4. Verify
# dest reachable + TLS-1.3 / X25519 / ALPN compatible
meshhold check-reality-dest 127.0.0.1:4443
# the full path, exactly as a browser would take it
curl -I --resolve yourdomain.com:443:<node-public-ip> https://yourdomain.com/
The curl line should return HTTP/… 200 with no certificate error.
Then open https://yourdomain.com/ in a browser — your site loads with a
valid padlock.
What a visitor sees
- By domain (
https://yourdomain.com/, DNS → node) → the site loads with a valid padlock. The TLS session is end-to-end between the browser and your front server; the node only relays bytes. - By raw IP (
https://<node-ip>/) → a certificate name-mismatch warning. That is normal for any HTTPS site hit by IP instead of by its hostname — the certificate is issued for a domain, not an address. - MeshHold peers on the same port still get the encrypted mesh. A
browser can never reach it: without the peer auth marker it is always
forwarded to
dest.
Requirements & gotchas
- The front server must terminate TLS itself with a CA-trusted certificate (Let's Encrypt, etc.). A plain-HTTP server won't work; a self-signed certificate shows a browser warning.
- SNI passes through untouched — the front server picks its virtual host and certificate from the SNI the client sends.
- The public
portreality binds (e.g.443) and the front server's port (e.g.4443) must differ. destcan also point at an external site —dest: "example.com:443"— instead of a local one. The node then relays visitors straight to that site, with no local server to run. Runmeshhold check-reality-dest <host:port>on any candidate first.