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
Supaffi's Caddy
a certificate per product hostname
Supaffi app
app:3000
External
Affiliates, visitors, Stripe
affiliates.yourapp.com
Your proxy
your certificates
Supaffi app
127.0.0.1:3000
Owner dashboard, in both modes
You
https://<server-ip>:3443
Dashboard Caddy
self-signed certificate
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:
cd /opt/supaffi && sudo SUPAFFI_PROXY_MODE=external bash install.shAdd 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
Hostheader. It selects the product. - Set the visitor's address in
X-Forwarded-Foryourself, 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:
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/:
# docker-compose.override.yml
services:
app:
networks: [default, proxy]
networks:
proxy:
external: true
name: your-proxy-networkReplace your-proxy-network with your proxy's network, then apply it:
cd /opt/supaffi && docker compose up -dUpdates leave this file alone. On a source build, run the installer once after creating it, so it is added to COMPOSE_FILE.