HTTPS & Domain Lock
Out of the box Pinchy accepts requests on any host over plain HTTP. That's fine for local development but unsafe in production. This guide walks you through two steps that secure your instance: HTTPS via a reverse proxy (so all traffic is encrypted) and Domain Lock (so Pinchy only responds to your chosen domain).
Set up HTTPS with Caddy
Section titled “Set up HTTPS with Caddy”HTTPS encrypts all traffic between browsers and your server. Pinchy doesn't handle TLS itself — a reverse proxy sits in front of it and takes care of certificates. We recommend Caddy because it automatically gets and renews Let's Encrypt certificates with zero configuration.
Install Caddy
Section titled “Install Caddy”apt-get install -y caddyConfigure your domain
Section titled “Configure your domain”Make sure your domain's DNS A record points to your server's IP address. Then edit the Caddy configuration:
nano /etc/caddy/CaddyfileReplace the contents with (use your actual domain):
pinchy.example.com { reverse_proxy localhost:7777}Save the file (Ctrl+O, Enter, Ctrl+X in nano) and restart Caddy:
systemctl restart caddyCaddy automatically provisions Let's Encrypt certificates for your domain. After a few seconds, visit https://pinchy.example.com — you should see Pinchy with a valid HTTPS certificate.
What's a reverse proxy and why do I need one?
A reverse proxy sits between the internet and Pinchy. When someone visits your domain:
- Their browser connects to Caddy on port 443 (HTTPS)
- Caddy handles the SSL certificate and encryption
- Caddy forwards the request to Pinchy on port 7777 (internal only)
- Pinchy sends the response back through Caddy
This way, Pinchy never needs to deal with SSL certificates directly.
See the Hardening Guide for nginx examples and advanced configuration.
What domain lock does
Section titled “What domain lock does”Once HTTPS is working, the next step is locking Pinchy to that domain. Domain Lock pins your instance to exactly one HTTPS domain and turns on the rest of the production security profile as a package:
- Only requests whose
Hostheader matches the locked domain reach the app. Everything else gets a403 Access Deniedpage. - Cookies are issued with
SecureandSameSite=Lax. - HSTS is advertised so modern browsers refuse to connect over plain HTTP.
- Origin checks are enforced on state-changing requests.
You don't have to configure any of these individually — locking the domain flips them all on.
Two kinds of request are deliberately let through on any host: the unauthenticated health and status endpoints (/api/health, /api/setup/status, /api/version), which monitoring needs to reach before a domain even exists, and everything under /api/internal/. The latter is how the OpenClaw container talks to Pinchy — over the Docker network as pinchy:7777, a hostname that can never match your public domain — and every one of those routes authenticates with the gateway token instead.
A rejected API request is recorded as an auth.host_blocked audit event with the offending Host, so a component that has been turned away shows up in the audit trail rather than only in a container log. Rejected page requests are not audited: a locked instance answers every crawler that finds its IP address, and those rows would bury the ones worth reading.
For the same reason the event is throttled to one row per minute. Reaching a locked instance at the wrong address takes no credential, so an unbounded stream of these rows is something any visitor could produce — and it would drown the rejection you actually need to see. The row that is written names its own host and path, and its suppressedSinceLastEntry field reports how many rejections it stands in for.
Lock the domain
Section titled “Lock the domain”- Sign in as an admin on the HTTPS domain you want to lock to
- Go to Settings → Security
- The page shows your current host and whether the connection is HTTPS
- Click Lock to this domain
- Pinchy restarts briefly to apply the new security profile — the page will reload automatically
From this point on, only the locked domain can reach the app. Domain Lock is the single source of truth for your public origin — there is no separate URL env var to keep in sync.
Remove the lock
Section titled “Remove the lock”If the business needs change (different domain, temporary maintenance URL, etc.):
- Go to Settings → Security
- Click Remove domain lock
- Confirm — the restart happens the same way as on lock
Recover from a lockout
Section titled “Recover from a lockout”If HTTPS goes down (expired certificate, proxy misconfiguration, DNS change) you can find yourself unable to reach the admin UI at all. Pinchy includes a last-resort CLI to clear the lock from inside the container:
docker exec pinchy pnpm domain:resetThe command needs shell access to the Pinchy container, so protect it like any other production admin credential. After it runs, Pinchy accepts any host again and you can reach the UI on the non-HTTPS or fallback URL to diagnose the underlying problem.
What about local development?
Section titled “What about local development?”Don't use domain lock in development. It is designed for a single deployed instance on a known domain, not for localhost or Docker dev stacks. The Security tab is still visible but it refuses to lock when the current connection is not HTTPS.