Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Signal Redirect (Self-Hosted Tracked Redirect)

This project is a self-hosted redirect + analytics service with:

  • Unique tracking links
  • Per-hit server data capture
  • Optional client-side telemetry capture
  • SQLite storage
  • Admin portal with simple username/password auth from env vars
  • Bot preview metadata passthrough so chat previews reflect the destination URL

Important Usage Note

Use this only where you have legal basis and consent as required by your jurisdiction and platform rules.

Captured Data

Server-side

  • IP address
  • IP version (IPv4/IPv6)
  • Timestamp
  • User-Agent
  • Browser / OS / device (from User-Agent parse)
  • Referrer
  • Accept-Language
  • Approximate location (GeoIP lookup)
  • ASN / ISP (GeoIP lookup)

Client-side (JS)

  • Screen resolution
  • Viewport size
  • Time zone
  • Language
  • Color depth
  • Touch support
  • Hardware concurrency
  • Device memory (supported browsers)
  • Platform

Setup

  1. Install dependencies:
npm install express better-sqlite3 dotenv express-session ua-parser-js ipaddr.js zod cheerio maxmind
  1. Download local GeoLite2 database files (City + ASN):
  • Create a MaxMind account and generate a license key.
  • Download and extract these files into ./data:
    • GeoLite2-City.mmdb
    • GeoLite2-ASN.mmdb
  1. Copy env file and update secrets:
cp .env.example .env
  1. Start app:
npm run dev
  1. Open admin panel:
  • http://localhost:3000/admin

Redirect Page Theme

  • Set REDIRECT_PAGE_THEME=black for a black redirect interstitial.
  • Set REDIRECT_PAGE_THEME=white (or light) for a white interstitial.

GeoIP Modes

  • GEO_LOOKUP_MODE=local
    • Uses local MaxMind .mmdb files only.
  • GEO_LOOKUP_MODE=local-then-remote
    • Uses local .mmdb, falls back to remote API when local files are unavailable/no match.
  • GEO_LOOKUP_MODE=remote
    • Uses remote API only.

For fully local operation, use GEO_LOOKUP_MODE=local and keep both GeoLite2 files in ./data.

Routes

  • GET /admin - dashboard
  • POST /admin/links - create new tracked link
  • POST /admin/links/:id/delete - delete tracker and all hit data
  • POST /admin/links/:id/hits/delete - delete all hits for a tracker
  • GET /admin/links/:id - list hits for one link
  • GET /admin/hits/:id - full hit details
  • POST /admin/hits/:id/delete - delete a single hit
  • GET /t/:slug - tracked redirect endpoint
  • POST /c/:slug/:hitId - client telemetry collector
  • GET /health - health check

Chat Preview Passthrough

When bots/crawlers visit GET /t/:slug, the app fetches the destination page metadata (og:*, title, description, image), caches it in SQLite, and serves preview tags based on the destination. This keeps most chat/social preview cards aligned with the final redirect target.

On link creation, metadata is also prefetched by default so preview tags are usually ready before first share.

  • PREFETCH_METADATA_ON_CREATE=true (default): prefetch on create
  • PREFETCH_METADATA_ON_CREATE=false: fetch on first bot/crawler hit only

Production Notes

  • Put behind HTTPS reverse proxy (Nginx/Caddy/Cloudflare Tunnel).
  • Keep SESSION_SECRET strong and unique.
  • Set BASE_URL to your public domain.
  • Restrict network access to admin route if possible.

Docker

Build image:

docker build -t signal-redirect .

Run container:

docker run --rm -p 3000:3000 --env-file .env -v $(pwd)/data:/app/data signal-redirect

Notes:

  • Mount ./data so SQLite and GeoLite2 files persist and remain accessible.
  • Ensure your GeoLite2-City.mmdb and GeoLite2-ASN.mmdb files exist in ./data on the host.
  • For fully deterministic Docker builds, keep package-lock.json in sync by running npm install after dependency changes and committing the updated lock file.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages