Reverse proxy and HTTPS

Publish Netstamp securely behind a reverse proxy with HTTPS.

A production Netstamp instance should have one stable HTTPS origin. Terminate TLS at a reverse proxy and forward the complete origin to the controller.

Set the public origin

Configure these values before exposing the service:

dotenv
APP_ENV=production
PUBLIC_BASE_URL=https://netstamp.example.com
NETSTAMP_VERSION=<tested tag or digest>

PUBLIC_BASE_URL is the single origin used for OAuth and OIDC callbacks, email and browser links, OpenAPI, notifications, and controller-served probe installers. It must contain only the HTTPS scheme and host, with no path, query, fragment, or credentials.

With APP_ENV=production, session cookies use the __Host- prefix together with Secure, HttpOnly, SameSite=Strict, and Path=/. Users must access the application over HTTPS.

Forward required headers

Preserve these headers through the reverse proxy:

  • Host
  • X-Forwarded-Proto
  • X-Forwarded-For
  • X-Real-IP when the proxy provides it
  • Authorization

An Nginx server block can look like this:

nginx
server {
    listen 443 ssl http2;
    server_name netstamp.example.com;

    ssl_certificate /etc/letsencrypt/live/netstamp.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/netstamp.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization $http_authorization;
    }
}

Redirect plain HTTP to the same HTTPS origin.

Limit direct controller access

When the reverse proxy runs on the same host, bind the Compose port to loopback:

yaml
services:
  netstamp:
    ports:
      - "127.0.0.1:3000:8080"

If Netstamp must trust client addresses supplied by the proxy, set HTTP_TRUSTED_PROXIES to the proxy’s exact address or CIDR prefixes, separated by commas. Do not trust all addresses unless the controller port is unreachable from untrusted networks.

Network direction

The controller needs:

  • Inbound HTTPS from browsers, API clients, and probes.
  • Outbound access to configured OAuth/OIDC providers and notification endpoints.
  • Outbound SMTP access when email is enabled.
  • Database access to PostgreSQL.

Probes initiate outbound HTTPS connections to the controller. The controller does not need inbound access to probe hosts.

Verify the public deployment

bash
curl --fail https://netstamp.example.com/healthz
curl --fail https://netstamp.example.com/api/v1/healthz
curl --fail https://netstamp.example.com/api/v1/openapi.json >/dev/null
curl --fail https://netstamp.example.com/api/v1/install/agent.sh >/dev/null

Also verify that HTTP redirects to HTTPS, the certificate chain is valid, sign-in sets secure cookies, and a disposable probe can reach the public origin.

Production checklist

  • APP_ENV=production is set.
  • PUBLIC_BASE_URL exactly matches the public HTTPS origin.
  • Every development secret has been replaced independently.
  • Netstamp and TimescaleDB image versions are pinned.
  • The database and controller ports are not publicly exposed.
  • Trusted proxy ranges are no broader than necessary.
  • Both health endpoints are monitored.
  • A database backup has been restored successfully in a test environment.

Continue with Authentication and email before opening account creation broadly.