Website and API for Tail.WTF. Contributions, corrections & requests can be made on GitHub.
Find more details on project homepage.
Tail.WTF minimizes link parameters — it does not merely strip known trackers. Every link goes through this pipeline, and the response reports honestly what happened to it:
- Expand — short links (
b23.tv,a.co, …) are dereferenced by following redirects (HEAD, falling back to GET). - Rules — per-site allowlist rules decide which path segments and parameters survive. Rules live in Tail-WTF/Rules; the build fetches the assembled ruleset and bundles it into the worker.
- Verification (opt-in via
deep=true) — for sites without rules, the worker compares the page's identity metadata (<title>,og:title,og:description, all of which must match) across candidate URLs, then binary-searches for the minimal set of parameters that still shows the same content. Before trusting any "same" verdict it first checks a control page that must differ — if the site serves identical metadata everywhere (e.g. a client-rendered shell), verification refuses to conclude anything. Parameters are removed only when the pages look identical without them — a heuristic: it can miss trackers and cannot guarantee the sanitized link still works. Unverifiable links are reported as not sanitized; there is deliberately no "striputm_*and hope" fallback.
One API for everyone: the web UI, iOS Shortcuts, clipboard tools, and bots all use the same endpoints. CORS is open; no authentication.
| Query param | Description |
|---|---|
text (required) |
Text containing one or more links |
deep |
true to enable live-page verification for unknown domains |
format |
text to get back only the sanitized text as text/plain |
JSON body: { "text": "...", "deep": true }. Same query params apply.
{
"text": "look https://example.com/item/42",
"links": [
{
"original": "https://example.com/item/42?share_token=abc",
"sanitized": "https://example.com/item/42",
"method": "rule"
}
]
}method is one of:
| Method | Meaning |
|---|---|
rule |
Cleaned by a per-site rule |
expanded |
Short link dereferenced; no rule for the target, parameters untouched |
canonical |
Replaced by the page's own canonical URL (verified) |
verified |
Parameters removed after live-page comparison proved them removable |
none |
Nothing could be done safely; the link is unchanged |
# JSON
curl -G "https://tail.wtf/api/sanitize" --data-urlencode "text=https://example.com/item/42?share_token=abc"
# Clean the clipboard in place (macOS)
pbpaste | curl -sG "https://tail.wtf/api/sanitize" --data-urlencode "text@-" --data-urlencode "format=text" | pbcopyFor iOS Shortcuts, use "Get Contents of URL" with the GET form and either
parse the JSON or pass format=text.
POST /api/bot/telegram— Telegram webhook (messages and inline queries)
frontend/— SvelteKit static site (UI only; talks to the public API)api/— Cloudflare Worker (Hono). Serves/api/*and the built frontend via Workers Assets from a single deployment (api/wrangler.jsonc,run_worker_first: ["/api/*"])
Prerequisites: Node.js 24+, pnpm 9.
pnpm install
pnpm dev # build UI once + wrangler dev on http://localhost:8787
pnpm run dev:ui # UI hot reload on :5173, /api proxied to :8787| Command | Description |
|---|---|
pnpm run check |
Typecheck worker and frontend |
pnpm test |
Worker unit tests (vitest + @cloudflare/vitest-pool-workers) |
pnpm run test:e2e |
Playwright e2e against wrangler dev (real API, no mocks) |
pnpm run build |
Build the static site and bundle rules |
pnpm run deploy |
Build and wrangler deploy |
Rules live in Tail-WTF/Rules, whose CI
publishes the assembled ruleset as a release asset. api/scripts/build-rules.mjs
converts it to JSON before dev/test/deploy, reading from the first available
source: the TAIL_WTF_RULES_FILE env var, a Link-Sanitization-Rules checkout
next to this repo, or the release asset.
A single Cloudflare Worker serves both the API and the static site, deployed by Workers Builds: the Cloudflare GitHub App pulls the repository on push, builds it, and authenticates with an API token it provisions and manages itself. Build settings on the Worker:
| Setting | Value |
|---|---|
| Production branch | main |
| Build command | pnpm run build |
| Deploy command | pnpm --dir api run deploy |
| Non-production branch deploy command | pnpm --dir api exec wrangler versions upload |
| Root directory | / |
.github/workflows/ci.yml runs typecheck, unit tests, lint, and e2e on every
push and PR.

