Using portless.sh
portless.sh replaces localhost:6174 with a stable https://<name>.localhost hostname over HTTPS. ciderpress works behind portless via a static alias — portless proxies your hostname to the local port the dev server binds. No env-var dance, no portless-managed child process required.
1. Install portless and trust its CA — once per machine
portless trust adds the local CA to your system trust store (security on macOS, update-ca-certificates / update-ca-trust on Linux, certutil on Windows). After that the CA stays trusted; you never re-run unless you portless clean first. Verify with portless doctor.
2. Configure your project — once per project
The dev server binds to 127.0.0.1:${devServer.port} (defaults to 127.0.0.1 — explicitly IPv4, because localhost on macOS can resolve to IPv6 first and trip reverse proxies pointed at the IPv4 loopback). devServer.url is purely a display + auto-open hint.
3. Register the alias — once per machine per project
This tells portless: "when someone hits https://acme.localhost, proxy to 127.0.0.1:7174." The alias persists in portless's state directory and survives proxy restarts.
--force overwrites any prior entry, so re-running after a port change just refreshes the route.
4. Run it
Then visit https://acme.localhost. The dev server doesn't know about portless at all — portless intercepts the HTTPS request, terminates TLS with its trusted CA, and forwards to the local HTTP port.
See it working
The examples/custom example ships pre-configured. From its directory:
The predev lifecycle hook runs the same setup script in --quiet mode before every pnpm dev, so misconfiguration surfaces as a clear failure with a Fix: command before the dev server starts.
zx --install: the setup script usesmassamanfor pattern matching but neithermassamannor a config file lives inexamples/custom'sdevDependencies.zx --installreads the script's imports at runtime and installs them on demand.
How it works under the hood
ciderpress runs on top of Rsbuild, which does not validate the Host header on incoming requests. Portless's Host: acme.localhost header lands without any allowlist configuration — no allowedHosts, no CORS adjustment needed for normal docs browsing.
Gotchas
- IPv4 vs IPv6 binding. ciderpress binds
127.0.0.1by default specifically so reverse proxies pointed at the IPv4 loopback can reach it. If you overridedevServer.host, keep it on an IPv4 address that portless can reach.host: 'localhost'will bind IPv6-only on some platforms and produce 502 Bad Gateway through portless. - Pin the port. The alias points at a specific port. If your dev server picks a different port (e.g. the configured port is occupied and ciderpress falls forward), the alias goes stale. Set
devServer.portto a value you don't use anywhere else and keep portless aligned viapnpm setup:portless. - HMR over a portless HTTPS origin. Rsbuild's HMR uses a WebSocket. Portless proxies WebSockets, so this works as long as the page connects to its own origin. If HMR drops, check devtools for a mixed-content or origin-mismatch warning.
- Don't set
corsunless you actually need it. A docs site is static. Only configure CORS if you embed live demos thatfetch()back to the dev server from the portless origin. basestays a path.devServer.urlis the origin (https://acme.localhost);baseis the URL path the site mounts at (/,/docs/). Different concerns.
References
- Configuration reference —
devServer— full field reference - CLI reference —
dev—--url,--host,--portoverrides
Resources
- portless.sh — install + project docs
- vercel-labs/portless — source repo +
portless aliassemantics