Skip to main content

Environment Variables

Configuration reference for self-hosting LinkForty Core. Set these in your .env file (or your platform's environment configuration). Only DATABASE_URL is strictly required; everything else has a sensible default or is optional.

Database

VariableRequiredDefaultDescription
DATABASE_URLYesPostgreSQL connection string: postgresql://user:password@host:5432/dbname.
POSTGRES_DBNoDatabase name (Docker Compose convenience; used to compose DATABASE_URL).
POSTGRES_USERNoDatabase user (Docker Compose).
POSTGRES_PASSWORDNoDatabase password (Docker Compose).
POSTGRES_PORTNo5432Database port (Docker Compose).

Caching (optional)

VariableRequiredDefaultDescription
REDIS_URLNoRedis connection string for link caching (redis://host:6379). Omit to disable — Core falls back to direct database queries.
REDIS_PORTNo6379Redis port (Docker Compose).

Server

VariableRequiredDefaultDescription
NODE_ENVNodevelopmentSet to production in production (enables SSL behavior and hides internal error details).
PORTNo3000Port the server listens on.
HOSTNo0.0.0.0Bind address.
CORS_ORIGINNo*Allowed CORS origin(s). Set to your frontend domain in production.
SERVICE_DOMAINNoCustom domain used when generating short links.
JWT_SECRETNoSecret for signing JWTs (if you use Core's authentication). Generate with openssl rand -hex 32.

Attribution behind a CDN or proxy

VariableRequiredDefaultDescription
TRUSTED_CLIENT_IP_HEADERNounsetName of the header carrying the authoritative client IP, e.g. cf-connecting-ip. When unset, Core uses the connection IP (request.ip).

When LinkForty runs behind a CDN or reverse proxy that terminates the connection (Cloudflare, Fastly, a load balancer), the connection IP Core sees is the edge or NAT hop, not the real visitor. Because fingerprint attribution keys on the client IP, this degrades match accuracy — clicks from many visitors collapse onto one proxy IP.

If your proxy injects an authoritative client-IP header, set TRUSTED_CLIENT_IP_HEADER to that header name so Core reads the real client IP instead. Common values:

# Cloudflare
TRUSTED_CLIENT_IP_HEADER=cf-connecting-ip

# Akamai / some CDNs
TRUSTED_CLIENT_IP_HEADER=true-client-ip

Comma-separated header values are handled automatically (Core takes the first entry), and IPv6-mapped IPv4 addresses are normalized.

Only enable behind a trusted proxy

Set this only when your origin is reachable exclusively through the proxy that sets the header. If clients can reach your server directly, they could spoof the header and forge their IP. When in doubt, leave it unset.

LinkForty Cloud

On LinkForty Cloud this is already configured (cf-connecting-ip). You only need this variable for self-hosted deployments behind a CDN.

These power the .well-known files Core serves for iOS Universal Links and Android App Links. See iOS Universal Links and Android App Links for setup.

VariableRequiredDefaultDescription
IOS_TEAM_IDNoApple Developer Team ID. Required to serve apple-app-site-association.
IOS_BUNDLE_IDNoYour iOS app bundle identifier (e.g. com.yourcompany.yourapp).
ANDROID_PACKAGE_NAMENoYour Android package name. Required to serve assetlinks.json.
ANDROID_SHA256_FINGERPRINTSNoComma-separated SHA-256 signing-certificate fingerprints.

When the iOS pair is unset, GET /.well-known/apple-app-site-association returns a 404 with a configuration message. The same applies to the Android pair and assetlinks.json.

Example .env

# Database
DATABASE_URL=postgresql://linkforty:your-strong-password@localhost:5432/linkforty

# Caching (optional)
REDIS_URL=redis://localhost:6379

# Server
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
CORS_ORIGIN=https://yourdomain.com
SERVICE_DOMAIN=yourdomain.com

# Attribution behind Cloudflare
TRUSTED_CLIENT_IP_HEADER=cf-connecting-ip

# iOS Universal Links
IOS_TEAM_ID=ABC123XYZ
IOS_BUNDLE_ID=com.yourcompany.yourapp

# Android App Links
ANDROID_PACKAGE_NAME=com.yourcompany.yourapp
ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:...