Reverse Proxy & Domains
This page describes how a self-hosted Lombok instance expects to sit behind a reverse proxy, and the domain model it relies on. It is intentionally conservative: it documents the contract the application actually enforces, not a specific vendor configuration.
It does not prescribe a canonical Caddy, Traefik, or Nginx config. Lombok already ships its own internal Nginx inside the container; what an external proxy or load balancer must do is comparatively small and is described as a set of requirements below.
[!NOTE] If you only need a single-host demo on plain HTTP, you can run the demo compose files directly without an external proxy. This page is for operators putting Lombok behind their own TLS-terminating proxy or load balancer.
The domain model
Section titled “The domain model”A Lombok instance is reached through a single base host — its
PLATFORM_HOST — plus a set of subdomains of that host. Both the apex host
and its subdomains must resolve to, and be routable to, the same Lombok
container.
The container’s bundled Nginx routes incoming requests by their Host header
(packages/api/nginx/nginx.conf). There are four request families:
| Host pattern | Purpose |
|---|---|
PLATFORM_HOST (apex) | The platform UI, its API (/api/), Socket.IO, and the bridge paths (/_bridge/…). |
app-server--<app>.PLATFORM_HOST | A running app’s UI. |
api-server--<worker>--<app>.PLATFORM_HOST | An app worker’s API endpoint (called from the app UI iframe). |
<label>--tn-<id>--<app>.PLATFORM_HOST | Tunnel subdomains (for example dev-server or live-reload tunnels). |
These subdomain shapes are generated and parsed by the application itself. For
example, getAppRequestWorkerHostname() in packages/types/src/util.ts builds
api-server--<worker>--<app>.<platformHost>, and the browser SDK
(packages/app-browser-sdk/src/app-browser-sdk.ts) derives the app identifier
from the leading label of window.location.hostname. Operators do not construct
these names; they only need to make sure traffic for all of them reaches Lombok.
[!NOTE] All subdomains are a single DNS label under
PLATFORM_HOST(the--separators are part of one label, not nested subdomains). So routing and certificates only need to cover one wildcard level:*.PLATFORM_HOST.
Why wildcard subdomains are required
Section titled “Why wildcard subdomains are required”Because app UIs, app-worker APIs, and tunnels each live on their own subdomain of
PLATFORM_HOST, an instance cannot function with only the apex host routed.
You need both:
- DNS —
PLATFORM_HOSTand*.PLATFORM_HOSTresolving to the instance. - Reverse-proxy routing — the apex host and all of its subdomains forwarded to the Lombok container.
This requirement also shows up in the platform’s own Content-Security-Policy, which frames app subdomains explicitly:
frame-src 'self' *.{{PLATFORM_HOST}}:*;(from packages/api/nginx/nginx.conf, where {{PLATFORM_HOST}} is substituted
at startup).
If you terminate TLS in front of Lombok, the certificate must therefore cover
both the apex and the wildcard — typically a SAN/wildcard certificate for
PLATFORM_HOST and *.PLATFORM_HOST.
[!WARNING] A certificate or proxy rule that only covers the apex host will leave app UIs, app-worker APIs, and tunnels unreachable, even if the main platform page loads.
How Lombok decides its own URLs
Section titled “How Lombok decides its own URLs”Lombok builds the public URLs it advertises to clients and workers from
configuration, not from inbound proxy headers. The relevant variables are read
in packages/api/src/core/config/core.config.ts and combined by
buildPlatformOrigin() in packages/api/src/core/utils/platform-origin.util.ts:
| Variable | Required | Default | Effect |
|---|---|---|---|
PLATFORM_HOST | Yes | — | Public base host. The container exits on startup if this is unset (packages/api/cmd/entrypoint.sh). |
PLATFORM_HTTPS | No | true | Selects https vs http in generated URLs. |
PLATFORM_PORT | No | unset | Port included in generated URLs; omitted automatically when it is 80 or 443. |
The resulting origin is "{protocol}://{PLATFORM_HOST}{:PORT?}". So:
- A standard HTTPS deployment on
:443→PLATFORM_HTTPS=true,PLATFORM_PORTunset →https://your.host - A deployment exposed on a non-standard public port → set
PLATFORM_PORTto the port users actually connect to, so generated links and subdomain URLs are correct
[!IMPORTANT] Set
PLATFORM_HOST,PLATFORM_HTTPS, andPLATFORM_PORTto match how users reach the instance through your proxy, not how the container is reached internally. These values drive the URLs embedded in the UI, app iframes, and worker calls. See Environment Variables.
The application validates inbound hosts against this configuration. For tunnel
requests, for example, the tunnel-auth controller rejects any request whose
Host does not end in PLATFORM_HOST
(packages/api/src/docker/controllers/tunnel-auth.controller.ts). This is why
the external proxy must preserve the original Host.
Forwarded headers and the proxy contract
Section titled “Forwarded headers and the proxy contract”The container’s bundled Nginx already sets forwarded headers when it proxies
to Lombok’s internal services (Host, X-Real-IP, X-Forwarded-For,
X-Forwarded-Proto) and performs WebSocket upgrades for Socket.IO, app servers,
bridge attach, and tunnels (packages/api/nginx/nginx.conf). That behavior is
internal to the image and you do not configure it.
For an external proxy in front of the container, the requirements clearly supported by the source are:
- Preserve the original
Hostheader. Routing is done byHost, and the app validates inbound hosts againstPLATFORM_HOST. RewritingHostto the upstream/container name will break subdomain routing and tunnel auth. - Forward the apex and all
*.PLATFORM_HOSTsubdomains to the container’s HTTP port. The image listens on port8080; confirm the actual published port against your compose or runtime configuration. - Support WebSocket upgrades end-to-end. Live features such as Socket.IO, bridge session attach, and tunnels rely on upgrade passthrough.
[!NOTE] Lombok derives its own scheme and port from
PLATFORM_HTTPSandPLATFORM_PORT, not fromX-Forwarded-Proto. We did not find anytrust proxy/ forwarded-protocol handling in the API. In practice, this means the headers your external proxy sets do not change the URLs Lombok generates — settingPLATFORM_*correctly is what matters.
Where TLS is expected to terminate
Section titled “Where TLS is expected to terminate”The bundled Nginx serves plain HTTP on port 8080 and contains no TLS configuration. TLS is therefore expected to terminate in front of the container — at your reverse proxy or load balancer — which then forwards plain HTTP to the container.
When TLS terminates in front:
- set
PLATFORM_HTTPS=trueso generated URLs usehttps - ensure your certificate covers both
PLATFORM_HOSTand*.PLATFORM_HOST
[!NOTE] When
PLATFORM_HTTPSis true, the tunnel-auth cookie is issued asSameSite=None; Secure(packages/api/src/docker/controllers/tunnel-auth.controller.ts). ASecurecookie is only stored by browsers over HTTPS, so cross-subdomain tunnel auth depends on real HTTPS to the browser.
Variables that are not part of the external proxy contract
Section titled “Variables that are not part of the external proxy contract”APP_UI_HOST— an internal upstream that the bundled Nginx proxies app UI traffic to. It is not an external-facing setting and most operators never change it.BRIDGE_TUNNEL_DOMAIN— this variable is accepted by the configuration schema, but in the inspected source it is not returned by the config object and is not read elsewhere. Treat it as having no observable effect today, and do not rely on it to host tunnels on a domain different fromPLATFORM_HOST.
What this page does not prescribe
Section titled “What this page does not prescribe”To stay grounded in the source, this page deliberately does not provide:
- a canonical Caddy, Traefik, or public Nginx configuration
- generic reverse-proxy hardening or caching advice not specifically required by Lombok’s source
- any claim that Lombok consumes
X-Forwarded-*headers to compute its own origin
If a behavior here matters for your deployment and you need certainty beyond
what this page states, the authoritative sources are
packages/api/nginx/nginx.conf, packages/api/cmd/entrypoint.sh, and the
config modules under packages/api/src/**/config/.