Gesamte Doku

Start

  1. 01Erste Schritte
  2. 02Installation
  3. 03Tracking

Funktionen

  1. 04Das Dashboard
  2. 05Ziele
  3. 06Kurzlinks
  4. 07KI-Quellen und Agenten
  5. 08E-Mail-Berichte
  6. 09Stündlicher Check
  7. 10Frag deine KI

Referenz

  1. 11Konfiguration
  2. 12HTTP-API

Plattformen

  1. 13Eigenständiger Server
  2. 14PHP
  3. 15WordPress
  4. 16Drupal
  5. 17Craft CMS
  6. 18Python
  7. 19Rails
  8. 20Ruby
  9. 21Go
  10. 22Java
  11. 23.NET
  12. 24Elixir
  13. 25Rust

Vertrauen

  1. 26Datenschutz

Funktionen

Kurzlinks

Kurzlinks auf deiner eigenen Domain werden wie Besuche gezählt und lassen sich aus Umami, Dub, Bitly, Short.io, Rebrandly oder einer CSV importieren.

Runlight erstellt Kurzlinks, die auf ein beliebiges Ziel weiterleiten. Jeder Klick wird wie ein Besuch gezählt (Quelle, Land, Gerät, Browser und alle UTM-Tags am Kurzlink) und getrennt von deinen Zahlen für Besucher und Seitenaufrufe gehalten. Bots werden weitergeleitet, ohne gezählt zu werden.

Von Haus aus liegen Links unter /go in deiner App, etwa example.com/go/sale. Liefere sie mit einer weiteren Route aus, hier für Next.js gezeigt.

app/go/[slug]/route.ts

import { rl } from "@/lib/runlight";

export const GET = rl.linkHandler();

Den Pfad änderst du mit runlight({ linkPath: "/l" }). In Express, Connect, NestJS, Fastify und Koa liefert die Middleware shortLinks, die unter Eigene Domains gezeigt wird, auch /go aus. In den anderen Sprachen wird /go zusammen mit den übrigen Routen von Runlight ausgeliefert, wie die Seite jeder Sprache zeigt.

Neuer Link in der Box Links nimmt das Ziel und behält entweder das zufällige Kürzel oder nutzt eines, das du eintippst. Verwalten listet jeden Link auf, damit du suchen, nach Neueste, Klicks oder Name sortieren und jeden Link bearbeiten, kopieren oder löschen kannst. Kürzel sind über alle deine Domains hinweg eindeutig, und Tausende von Links funktionieren problemlos.

Klick auf einen Link, um seine Klicks im Zeitverlauf zu sehen und woher sie kamen.

Eigene Domains

Links können auch auf einer eigenen Domain liegen, etwa t.example.com/sale. Richte eine unter Einstellungen, Eigene Domains ein.

  1. Lass die Domain auf deine App zeigen, mit einem CNAME-Eintrag für eine Subdomain oder einem A- oder ALIAS-Eintrag für eine Domain ohne Subdomain.
  2. Sorge dafür, dass dein Host die Domain annimmt und sie per HTTPS ausliefert (bei Vercel oder Netlify fügst du sie zu den Domains des Projekts hinzu).
  3. Füge die Domain in den Einstellungen hinzu. Runlight prüft, ob sie deine App erreicht, und sagt es dir.

Eine Link-Domain beantwortet jeden Pfad auf ihr, deshalb kann sie nie die eigene Domain deiner App oder eine deiner Websites sein. Sie muss außerdem ein öffentlicher Name sein, Namen für private Netzwerke wie .internal oder .local werden also abgelehnt. Übergib origin mit der Adresse deiner App an routes(), dann gilt die erste Regel, egal welchen Host-Header eine Anfrage nennt. Ein verbundener Hub kann Link-Domains erst hinzufügen, wenn origin gesetzt ist, weil er nicht wissen kann, unter welchen Namen deine App antwortet. Pfade unter dem Dashboard, etwa /runlight, erreichen immer deine App, du kannst das Dashboard also unter jedem seiner Namen öffnen, um eine Domain zu entfernen.

Wenn du eine Domain entfernst, bleiben ihre Links samt Klicks und Statistiken erhalten. Sie ziehen nach /go/<slug> um, bis du die Domain wieder hinzufügst, und kehren dann dorthin zurück.

Anfragen auf dieser Domain müssen Runlight erreichen, bevor die eigenen Routen deiner App sie sehen. rl.linkDomainResponse(request) beantwortet eine Anfrage auf einer deiner Link-Domains und gibt für alles andere null zurück, der Rest deiner App bleibt also unberührt. Ruf es als Erstes in dem auf, was vor deinen Routen läuft. Die anderen Sprachen haben dieselbe Prüfung, oft eingebaut in die Middleware, die Runlight ausliefert, und die Seite jeder Sprache zeigt, wo sie hingehört.

Next.js

Next.js führt es ab Next.js 16 in proxy.ts aus.

proxy.ts

import { rl } from "@/lib/runlight";

export async function proxy(request: Request) {
  return (await rl.linkDomainResponse(request)) ?? undefined;
}

Unter Next.js 15 nennst du die Datei middleware.ts und benennst die Funktion in middleware um. Ab Next.js 15.5 fügst du außerdem export const config = { runtime: "nodejs" }; hinzu, damit dein Datenbanktreiber laden kann. Vor Next.js 15.5 läuft Middleware nur in der Edge-Runtime und braucht daher einen Store, der dort funktioniert, etwa Turso über HTTP. Ohne einen solchen nutzt du Links unter /go/<slug>, die der Route Handler schon ausliefert.

SvelteKit macht dasselbe im handle-Hook von src/hooks.server.ts, mit (await rl.linkDomainResponse(event.request)) ?? resolve(event), und Astro in src/middleware.ts, mit (await rl.linkDomainResponse(context.request)) ?? next().

Express, Connect und NestJS

shortLinks aus @runlight/sdk/node beantwortet Link-Domains und /go/<slug> und reicht jede andere Anfrage mit ungelesenem Body weiter. Setze es vor deine Routen.

server.ts

import { shortLinks, toNodeHandler } from "@runlight/sdk/node";

app.use(shortLinks(rl));
app.use(toNodeHandler(rl.routes().handler));

Ein Kürzel ohne Link auf deiner eigenen Domain geht weiter an deine App, sodass deine eigene 404-Seite darauf antwortet. In einem einfachen http-Server rufst du es ohne next auf, und es löst zu true auf, wenn es geantwortet hat.

ts

const links = shortLinks(rl);

createServer(async (req, res) => {
  if (await links(req, res)) return;
  // ...your app
});

Fastify

Führe es in einem onRequest-Hook aus und übergib die Antwort, sobald es geantwortet hat.

ts

const links = shortLinks(rl);

app.addHook("onRequest", async (req, reply) => {
  if (await links(req.raw, reply.raw)) reply.hijack();
});

Koa

ts

const links = shortLinks(rl);

app.use(async (ctx, next) => {
  if (await links(ctx.req, ctx.res)) ctx.respond = false;
  else await next();
});

Hono

ts

app.use(async (c, next) => (await rl.linkDomainResponse(c.req.raw)) ?? next());

Nuxt und h3

Eine Server-Middleware läuft vor jeder Route. Sie baut die Anfrage nur aus Adresse und Headern, weil toWebRequest von h3 anfangen würde, den Body einer Anfrage zu lesen, die für deine App bestimmt ist.

server/middleware/links.ts

export default defineEventHandler(async (event) => {
  const link = await rl.linkDomainResponse(new Request(getRequestURL(event), { method: event.method, headers: event.headers }));
  if (link) return link;
});

Bun, Deno und Cloudflare Workers

Prüfe in fetch vor allem anderen.

ts

async fetch(request) {
  const link = await rl.linkDomainResponse(request);
  if (link) return link;
  // ...the routes and your app
}

An einen Worker muss neben /runlight/* auch die Link-Domain geleitet werden.

Einstellungen, Import, Kurzlinks holt Links von einem anderen Dienst herüber, mit ihrem Klickverlauf, sofern der Dienst ihn teilt.

Von Was herüberkommt
Umami Jeder Link und jeder Klick, mit Land, Gerät, Browser und Verweis. Melde dich mit einem API-Schlüssel an, oder bei selbst gehostetem Umami mit deinem Benutzernamen und Passwort.
Dub Jeder Klick bei Business-Plänen, Klicks pro Tag bei Pro, nur Links bei Free.
Bitly Klicks pro Tag, so weit zurück, wie dein Plan sie aufbewahrt.
Short.io Klicks pro Tag. Short.io erlaubt 60 Statistikabfragen pro Minute, große Konten brauchen daher eine Weile.
Rebrandly Nur Links, weil seine API keine datierten Klicks liefert.
Beliebige CSV Die Spalten name und url, optional slug und domain.

Dein Schlüssel oder Passwort wird mit jedem Schritt des Imports gesendet und nie gespeichert. Importe laufen jeweils mit ein paar Links, funktionieren also auch auf Serverless-Hosts, und du kannst sie gefahrlos erneut starten, weil vorhandene Links übersprungen werden. Links auf der eigenen Domain des alten Dienstes (bit.ly, dub.sh, rebrand.ly, short.gy) behalten ihr Kürzel und ziehen auf deinen eigenen Link-Pfad um. Links auf deinen eigenen Marken-Domains behalten die Domain, die unter Eigene Domains hinzugefügt wird.