Toda la documentación

Empezar

  1. 01Primeros pasos
  2. 02Instalación
  3. 03Seguimiento

Funciones

  1. 04El panel
  2. 05Objetivos
  3. 06Enlaces cortos
  4. 07Fuentes y agentes de IA
  5. 08Informes por email
  6. 09Comprobación programada
  7. 10Pregunta a tu IA

Referencia

  1. 11Configuración
  2. 12API HTTP

Plataformas

  1. 13Servidor independiente
  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

Confianza

  1. 26Privacidad

Funciones

Enlaces cortos

Los enlaces cortos en tu propio dominio se cuentan como visitas y se pueden importar desde Umami, Dub, Bitly, Short.io, Rebrandly o un CSV.

Runlight crea enlaces cortos que redirigen a donde quieras. Cada clic se cuenta como una visita (fuente, país, dispositivo, navegador y cualquier etiqueta UTM del enlace corto) y se mantiene aparte de tus cifras de visitantes y páginas vistas. Los bots se redirigen sin contarse.

De serie, los enlaces viven bajo /go en tu aplicación, como en example.com/go/sale. Sírvelos con una ruta más, que aquí se muestra para Next.js.

app/go/[slug]/route.ts

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

export const GET = rl.linkHandler();

Cambia la ruta con runlight({ linkPath: "/l" }). En Express, Connect, NestJS, Fastify y Koa, el middleware shortLinks que se muestra en Dominios personalizados también sirve /go. En los demás lenguajes, /go se sirve junto con el resto de rutas de Runlight, como muestra la página de cada lenguaje.

Nuevo enlace en el recuadro Enlaces recibe el destino y mantiene el identificador aleatorio o usa uno que escribas. Gestionar lista todos los enlaces para que puedas buscar, ordenar por más recientes, clics o nombre, y editar, copiar o eliminar cada uno. Los identificadores son únicos en todos tus dominios, y miles de enlaces funcionan sin problema.

Haz clic en un enlace para ver sus clics a lo largo del tiempo y de dónde vinieron.

Dominios personalizados

Los enlaces también pueden vivir en un dominio propio, como t.example.com/sale. Configura uno en Ajustes, Dominios personalizados.

  1. Apunta el dominio a tu aplicación con un registro CNAME para un subdominio, o con un registro A o ALIAS para un dominio sin subdominio.
  2. Haz que tu hosting acepte el dominio y lo sirva con HTTPS (en Vercel o Netlify, añádelo a los dominios del proyecto).
  3. Añade el dominio en Ajustes. Runlight comprueba que llega a tu aplicación y te lo indica.

Un dominio de enlaces responde a todas sus rutas, así que nunca puede ser el dominio de tu propia aplicación ni uno de tus sitios. También tiene que ser un nombre público, así que se rechazan los nombres reservados para redes privadas, como .internal o .local. Pasa origin a routes() con la dirección de tu aplicación, y la primera regla se cumple sea cual sea la cabecera Host que indique una petición. Un hub conectado solo puede añadir dominios de enlaces cuando origin está definido, ya que no puede saber en qué nombres responde tu aplicación. Las rutas bajo el panel, como /runlight, siempre llegan a tu aplicación, así que puedes abrir el panel en cualquiera de sus nombres para quitar un dominio.

Quitar un dominio conserva sus enlaces junto con sus clics y estadísticas. Pasan a /go/<slug> hasta que vuelvas a añadir el dominio, y entonces regresan a él.

Las peticiones a ese dominio tienen que llegar a Runlight antes de que las vean las rutas de tu aplicación. rl.linkDomainResponse(request) responde a una petición en uno de tus dominios de enlaces y devuelve null para todo lo demás, así que el resto de tu aplicación no se toca. Llámalo primero en lo que se ejecute antes de tus rutas. Los demás lenguajes tienen la misma comprobación, a menudo integrada en el middleware que sirve Runlight, y la página de cada lenguaje muestra dónde va.

Next.js

Next.js lo ejecuta en proxy.ts a partir de Next.js 16.

proxy.ts

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

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

En Next.js 15, llama al archivo middleware.ts y cambia el nombre de la función a middleware. A partir de Next.js 15.5, añade también export const config = { runtime: "nodejs" }; para que pueda cargarse el driver de tu base de datos. Antes de Next.js 15.5, el middleware solo funciona en el runtime Edge, así que necesita un almacén que funcione allí, como Turso por HTTP. Sin uno, usa enlaces /go/<slug>, que ya sirve el route handler.

SvelteKit hace lo mismo en el hook handle de src/hooks.server.ts, con (await rl.linkDomainResponse(event.request)) ?? resolve(event), y Astro en src/middleware.ts, con (await rl.linkDomainResponse(context.request)) ?? next().

Express, Connect y NestJS

shortLinks de @runlight/sdk/node responde a los dominios de enlaces y a /go/<slug>, y deja pasar cualquier otra petición sin leer su cuerpo. Ponlo antes de tus rutas.

server.ts

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

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

Un identificador sin enlace en tu propio dominio pasa a tu aplicación, así que responde tu propia página 404. En un servidor http sencillo, llámalo sin next, y se resuelve a true cuando ha respondido.

ts

const links = shortLinks(rl);

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

Fastify

Ejecútalo en un hook onRequest y entrega la respuesta en cuanto haya respondido.

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 y h3

Un middleware de servidor se ejecuta antes de cada ruta. Construye la petición solo con la dirección y las cabeceras, porque toWebRequest de h3 empezaría a leer el cuerpo de una petición destinada a tu aplicación.

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 y Cloudflare Workers

Haz la comprobación antes que nada en fetch.

ts

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

Un Worker necesita que se le enrute el dominio de enlaces además de /runlight/*.

Ajustes, Importar, Enlaces cortos trae enlaces de otro servicio, con su historial de clics cuando el servicio lo comparte.

Desde Qué se trae
Umami Cada enlace y cada clic, con país, dispositivo, navegador y referente. Inicia sesión con una clave de API, o con tu usuario y contraseña en Umami autoalojado.
Dub Cada clic en los planes Business; clics por día en Pro; solo enlaces en Free.
Bitly Clics por día, tan atrás como los conserve tu plan.
Short.io Clics por día. Short.io permite 60 peticiones de estadísticas por minuto, así que las cuentas grandes tardan un rato.
Rebrandly Solo enlaces, porque su API no da clics con fecha.
Cualquier CSV Columnas name, url y, opcionalmente, slug y domain.

Tu clave o contraseña se envía con cada paso de la importación y nunca se guarda. Las importaciones procesan unos pocos enlaces cada vez, así que funcionan en hostings serverless, y se pueden repetir sin riesgo porque los enlaces que ya están aquí se omiten. Los enlaces en el dominio propio del servicio anterior (bit.ly, dub.sh, rebrand.ly, short.gy) conservan su identificador y pasan a tu propia ruta de enlaces; los enlaces en tus dominios de marca conservan el dominio, que se añade en Dominios personalizados.