Skip to content

iamroylim/auto-redirect

Repository files navigation

auto-redirect

CI License: MIT

A small, config-driven redirect service for growth links on Cloudflare Workers. It makes campaign-attribution rules explicit at the edge and can emit privacy-minimized click events to Cloudflare Analytics Engine.

Why this exists

Short links often start as a spreadsheet and quietly become production infrastructure. auto-redirect makes the important behavior explicit:

  • links live in a typed configuration file;
  • only absolute HTTPS destinations are accepted;
  • campaign defaults are applied without overwriting destination or visitor data;
  • redirect semantics are selected per link (302 or 307);
  • click analytics are optional and do not automatically collect request-derived identifiers such as IP address, user agent, cookie, or referrer.

How it works

GET /launch?utm_source=partner
        |
        v
match "launch" in src/links.ts
        |
        v
validate destination + merge query parameters
        |
        +----> optional Analytics Engine event
        |
        v
307 https://example.com/product?plan=pro&utm_medium=redirect&utm_campaign=product-launch&utm_source=partner

The merge order is:

  1. destination query parameters;
  2. configured defaults, only when a key is still missing;
  3. incoming visitor query parameters, which replace configured defaults but cannot replace parameters already fixed by the destination URL.

This lets a link lock a parameter such as plan=pro, provide sensible UTM defaults, and still preserve a partner-provided utm_source. Repeated incoming values are forwarded in order, and an explicit empty incoming value replaces a configured default.

Quick start

npm install
npm test
npm run typecheck
npm run build
npm run dev

Local development requires Node.js 22 or newer.

Then try:

curl -i 'http://localhost:8787/launch?utm_source=partner'
curl -i 'http://localhost:8787/health'

Edit src/links.ts to define your own links:

export const links = defineLinks({
  launch: {
    destination: "https://example.com/product?plan=pro",
    status: 307,
    defaults: {
      utm_source: "shortlink",
      utm_medium: "redirect",
      utm_campaign: "product-launch",
    },
  },
});

Slugs must contain at most 96 ASCII letters, numbers, _, or -; health is reserved. Destinations must use HTTPS. Invalid configuration fails fast when the Worker starts without echoing the destination URL.

HTTP behavior

Request Response
GET /health 200 JSON health response
GET /:slug configured 302 or 307 redirect
HEAD /:slug same redirect without analytics
unknown slug 404 JSON error
unsupported method 405 JSON error with Allow: GET, HEAD
nested path, duplicate slash, or trailing slash 404 JSON error

All responses include Cache-Control: no-store. Redirects also include Referrer-Policy: no-referrer.

Analytics and privacy

Analytics are disabled unless the optional CLICKS Analytics Engine binding is configured. Each successful GET redirect writes one data point containing:

  • slug;
  • destination hostname;
  • redirect status;
  • timestamp.

The service deliberately does not record IP addresses, full destination URLs, query strings, user agents, cookies, or referrers. The recorded slug and destination hostname are operator-controlled metadata, so do not encode email addresses, account IDs, or other personal data in either. HEAD requests are excluded so health checks and link unfurlers do not inflate click counts.

To enable events, create an Analytics Engine dataset binding named CLICKS in wrangler.jsonc, then deploy. The checked-in configuration leaves the binding commented out so the Worker runs without additional Cloudflare resources.

Development

The core URL and routing logic is pure and is tested independently from the Cloudflare runtime. The Worker adapter only resolves bindings and schedules the optional analytics write.

npm test             # Vitest suite
npm run typecheck    # TypeScript, including Workers types
npm run build        # Wrangler dry-run bundle

CI runs all three checks on pushes and pull requests.

Deploy

  1. Authenticate Wrangler with your Cloudflare account.
  2. Change the Worker name in wrangler.jsonc if needed.
  3. Optionally add the CLICKS Analytics Engine binding.
  4. Run npm run deploy.
  5. Add a custom domain or route in Cloudflare.

No secrets are required by the application.

License

MIT

About

Config-driven short links and privacy-minimized click attribution on Cloudflare Workers.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages