Supaffi

Self-hosting/Reverse proxy and HTTPS

Reverse proxy and HTTPS

Let Supaffi handle HTTPS, or put it behind a proxy you already run.

Each product's affiliate portal, tracking script and Stripe webhook are served on its own hostname, such as affiliates.yourapp.com, over ports 80 and 443. Supaffi either runs the proxy for those ports itself, or leaves them to one you already run.

Bundled (default)

Affiliates, visitors, Stripe

affiliates.yourapp.com

ports 80, 443

Supaffi's Caddy

a certificate per product hostname

internal network

Supaffi app

app:3000

External

Affiliates, visitors, Stripe

affiliates.yourapp.com

ports 80, 443

Your proxy

your certificates

Host, X-Forwarded-*

Supaffi app

127.0.0.1:3000

Owner dashboard, in both modes

You

https://<server-ip>:3443

Dashboard Caddy

self-signed certificate

Product hostnames go through ports 80 and 443. The owner dashboard has its own port in both modes.

Bundled proxy

The default when ports 80 and 443 are free at install time. Supaffi's Caddy takes both ports and handles HTTPS with nothing for you to configure:

  • Each product hostname gets a certificate the first time it is visited, usually within a minute of its DNS record pointing at the server.
  • Caddy asks Supaffi before issuing one and only gets an answer for hostnames that belong to a product, so pointing an unrelated domain at your server gets no certificate.
  • SUPAFFI_DOMAIN, if set, gets an ordinary certificate as soon as its DNS record resolves.

The dashboard address

In both modes, the owner dashboard is served at https://<server-ip>:3443 by a second Caddy, with a certificate your server signs itself. It needs no DNS, so the browser shows a warning once. The connection is encrypted either way.

To serve the dashboard on a domain with a normal certificate, set SUPAFFI_DOMAIN. The owner pages never answer on a product hostname.

Use your own proxy

If port 80 or 443 is in use when you first install, Supaffi leaves both alone and your proxy forwards to the app on 127.0.0.1:3000. To switch modes later:

Terminal
cd /opt/supaffi && sudo SUPAFFI_PROXY_MODE=external bash install.sh

Add SUPAFFI_KEEP_VERSION=yes to stay on your current version, and use SUPAFFI_PROXY_MODE=bundled to switch back. Then route each product hostname to the app. Your proxy must:

  • Pass the Host header. It selects the product.
  • Set the visitor's address in X-Forwarded-For yourself, replacing anything the visitor sent. Supaffi reads the last entry, and rate limits and the certificate check rely on it.
  • Keep /api/internal/ closed to outside visitors.
  • Serve a valid certificate for every product hostname. Supaffi issues none in this mode.

nginx example

Inside the server block for a product hostname:

nginx
location /api/internal/ {
    return 404;
}

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Proxy in a Docker network

A proxy running in a container cannot reach the host's loopback. Share a network and forward to http://supaffi-app-1:3000, with the same headers and the same closed /api/internal/:

/opt/supaffi/docker-compose.override.yml
# docker-compose.override.yml
services:
  app:
    networks: [default, proxy]
networks:
  proxy:
    external: true
    name: your-proxy-network

Replace your-proxy-network with your proxy's network, then apply it:

Terminal
cd /opt/supaffi && docker compose up -d

Updates leave this file alone. On a source build, run the installer once after creating it, so it is added to COMPOSE_FILE.