Opcional. Un filtro DNS, un bloqueador de anuncios o una red que bloquea el host de Korzen detiene el tag, su beacon y la página de desafío. Esos visitantes siguen pasando, juzgados solo por lo que envía tu servidor, pero en una ruta que exige el token del tag no pueden pasar. El modo first-party sirve los tres desde una ruta de tu propio dominio, /kz si no eliges otra, y los reenvía a Bot Protection con la dirección del visitante. Nada bloquea una ruta de un sitio sin bloquear el sitio.
Hay dos formas de servir la ruta, y puedes usar las dos:
| La forma | Qué activas | Quién sirve /kz |
|---|---|---|
| Solo el SDK | firstParty: true en Node, FirstParty: true en Go | tu servidor |
| Solo el Worker | KORZEN_FIRST_PARTY = "1" y una ruta para /kz/* | tu Cloudflare Worker |
| El Worker y el SDK | ambos, con la misma ruta | el Worker; tu servidor le envía los visitantes desafiados |
En todas las formas, la página carga el tag desde tu ruta en lugar del host de Korzen. El tag toma su dirección de su propio src, así que cada solicitud que hace también va a /kz:
<script src="/kz/tag.js" data-site-key="YOUR_SITE_KEY" async></script>
El modo está en todos los SDK: Node (y su Worker), Go, Python, PHP, Java y .NET, cada uno con su opción en su README. Un cliente escrito a partir de una guía de framework no tiene forwarder: ahí usa el SDK o, detrás de Cloudflare, el Worker solo, que da el modo a un origen en cualquier lenguaje.
Solo el SDK
Tu servidor sirve la ruta. Pasa la opción al middleware que ya consulta a Bot Protection, y dale a la ruta un handler propio con las mismas opciones. El client debe ser uno creado con createClient, con tu clave de API: el reenviador la envía a Korzen con cada solicitud.
// Express, with korzen.js as the Express guide sets it up const options = { client: korzen, ip: (_get, req) => req.ip, firstParty: true }; app.use("/kz", protect(options)); // before any body parser: it answers /kz/... itself app.use(express.urlencoded({ extended: false })); app.post("/signup", protect(options), signup); // Next.js: app/kz/[...path]/route.js serves the path const kz = withKorzen(() => new Response(null, { status: 404 }), { client, ip: ipFrom.vercel, firstParty: true }); export { kz as GET, kz as POST };
o := korzen.Options{IP: korzen.RemoteIP, FirstParty: true} mux.Handle("/kz/", bot.Protect(http.NotFoundHandler(), o)) // the path: Protect answers it itself mux.Handle("POST /signup", bot.Protect(http.HandlerFunc(signup), o))
- Qué activas.
firstParty: trueenprotectowithKorzen(activo solo cuando estrue); en Go,Options.FirstParty. Luego elsrcdel tag en tus páginas pasa a ser/kz/tag.js. La dirección del visitante se lee como la lee la decisión: la opciónip, en GoOptions.IP. - Una dirección que el visitante no puede escribir. Korzen confía en la dirección que el forwarder envía con tu clave, así que la opción
ipdebe leer una que el visitante no pueda fijar:ipFrom.vercelsolo en Vercel,ipFrom.cloudflare(Go:korzen.CloudflareIP) solo si tu servidor recibe tráfico solo de Cloudflare,ipFrom.lastForwardeddetrás de tu propio load balancer o nginx. Un header que el visitante puede escribir deja que un script elija la dirección que Korzen juzga. - Qué cambia para el visitante. Nada que vea. Su navegador carga el tag y envía su beacon a tu dominio, así que un visitante cuya red bloquea Korzen se juzga con la evidencia del tag y puede hacer una acción que exige su token. Las solicitudes bajo la ruta nunca se juzgan ni llegan a tus handlers; cualquier otra ruta se juzga como antes.
- La página de desafío. Una carga de página desafiada va a
/kz/challenge?c=…&r=…en tu dominio en lugar del de Korzen, y tu servidor reenvía la página, sus archivos y su envío. El mismo desafío, y el visitante vuelve a la página que pidió. - Fail open. Cuando Bot Protection está lento, caído o inalcanzable desde tu servidor, cada solicitud bajo la ruta responde
503con el cuerpo vacío, y el tag descarta esa señal; la página y tus otras rutas no esperan nada. Fail open - Cómo saber que funciona.
curl -I https://shop.example/kz/tag.jsresponde200, y el panel de red del navegador muestra las solicitudes del tag yendo a tu propio dominio. Cómo saber que funciona
Solo el Worker
Tu Cloudflare Worker sirve la ruta, sin cambios en tu servidor, para un origen en cualquier lenguaje. Configura el Worker como muestra Cloudflare Workers, luego activa el modo y añade /kz/* a las rutas del Worker:
# wrangler.toml
routes = [
{ pattern = "shop.example/kz/*", zone_name = "shop.example" },
{ pattern = "shop.example/signup*", zone_name = "shop.example" },
]
[vars]
KORZEN_URL = "https://korzen.dev"
KORZEN_FIRST_PARTY = "1"
# KORZEN_FIRST_PARTY_PATH = "/kz" # the path, /kz unless set
- Qué activas.
KORZEN_FIRST_PARTYcon1otrue(cualquier otro valor salvo0ofalselo deja apagado, con un aviso en el log del Worker), una ruta parashop.example/kz/*, y elsrcdel tag en tus páginas como/kz/tag.js. La dirección del visitante esCF-Connecting-IP, que pone Cloudflare. - Qué cambia para el visitante. Nada que vea. El tag y su beacon van a tu dominio, respondidos en el edge de Cloudflare; tu origen nunca ve esas solicitudes. Las rutas del Worker se juzgan como antes.
- La página de desafío. Una carga de página que el Worker desafía va a
/kz/challengeen tu dominio, que sirve el Worker. - Fail open. Igual que con el SDK: un
503con el cuerpo vacío solo para esa solicitud, y todas las demás siguen hacia tu origen. Fail open - Cómo saber que funciona. Como abajo; y
npx wrangler taillista las solicitudes bajo/kz, mientras el log de acceso de tu origen no muestra ninguna. Cómo saber que funciona
El Worker y el SDK
Cuando hay un Worker delante de tu sitio y el SDK corre en tu aplicación (para el context que solo tu aplicación conoce), activa el modo en los dos, con la misma ruta.
- Qué activas. El Worker como arriba, con su ruta para
/kz/*, yfirstParty: true(Go:FirstParty: true) en el SDK detrás de él. El Worker responde/kz/*por sí mismo, así que el SDK nunca ve esas solicitudes. - Qué cambia para el visitante. Lo mismo que con el Worker solo: el tag, su beacon y la página de desafío vienen de tu dominio, respondidos en el edge.
- La página de desafío. El
firstPartydel SDK envía a los visitantes que desafía a/kz/challengeen lugar del host de Korzen, y el Worker sirve esa página. Sin él, un visitante que el SDK desafía va al host de Korzen, que su red puede bloquear. - Fail open. El que sirve la ruta se rinde por su cuenta y responde
503; ninguno retiene al otro. Si las rutas del Worker dejan fuera/kz/*, o se quita el Worker, el SDK sirve la ruta él mismo: detrás de Cloudflare, dale entoncesip: ipFrom.cloudflare(Go:korzen.CloudflareIP). - Cómo saber que funciona. Como abajo;
npx wrangler taillista las solicitudes bajo/kz, y el log de tu servidor no muestra ninguna.
Qué sirve la ruta
El reenviador, el SDK o el Worker, responde a estas y a nada más:
| En tu dominio | Qué es |
|---|---|
GET /kz/tag.js | el tag, con los headers de caché de Korzen |
GET /kz/n, POST /kz/s | el nonce del tag y su beacon (un cuerpo de 64 KiB como máximo) |
GET /kz/ping | respondido 204 por el propio reenviador, nunca reenviado |
GET y POST /kz/challenge, GET /kz/challenge/wait | la página de desafío y su envío (16 KiB como máximo) |
GET /kz/static/… | el script, el estilo y las fuentes de la página de desafío |
Cualquier otra ruta bajo /kz es un 404, y una conocida con otro método un 405; ninguna llega a Bot Protection, así que la ruta nunca es un proxy abierto. A Korzen el reenviador envía los headers del propio navegador de una lista corta (User-Agent, Accept, Accept-Language, Content-Type, Origin, Sec-Fetch-*, los client hints, If-None-Match), tu clave de API como bearer y la dirección del visitante. Nunca una cookie, el Authorization del navegador ni un X-Forwarded-*. Lo que vuelve conserva los headers de caché y de seguridad de Korzen, nunca un Set-Cookie.
Korzen solo acepta la dirección del visitante de una solicitud en estas rutas que lleve tu clave de API, y en el beacon solo para tu propia site key; cualquier otra cosa se rechaza con 403. Tu clave nunca llega al navegador.
La cookie _kz del tag y su almacenamiento siguen en tu dominio, como antes; los datos que recibe Korzen son los mismos. Algunas evidencias que Korzen solo lee cuando el navegador se conecta directamente a Korzen no están a través de un reenviador; el resto de la evidencia del tag llega, y nada de lo que falta cuenta contra un visitante.
Otra ruta
firstPartyPath (Go: Options.FirstPartyPath; el Worker: KORZEN_FIRST_PARTY_PATH) nombra otra ruta, por ejemplo cuando una lista de bloqueo aprenda /kz. Empieza con /, termina sin ella, y tiene como máximo 64 caracteres en segmentos de A-Z a-z 0-9 . _ ~ -. Una que no se lee se registra una vez en el log, y el modo queda apagado. Usa la misma ruta en el src del tag, en el SDK y en el Worker.
Fail open
La ruta nunca retiene tu página ni tus otras rutas:
- Cada solicitud reenviada se rinde por su cuenta: tras 1 s en un
GET, 2,5 s en unPOST. Un timeout, un error de red, una respuesta de más de 4 MiB o un fallo del reenviador es un503con el cuerpo vacío yCache-Control: no-store; las redirecciones no se siguen. Los4xxy5xxdel propio Bot Protection pasan tal cual. - Sin la dirección del visitante (la opción
ipno lee ninguna), el nonce, el beacon y el desafío responden503; el tag y los archivos de la página de desafío se siguen sirviendo. - El tag descarta entonces esa señal, como hace cuando el host de Korzen está caído. Un tag que no informó nunca es evidencia de un bot: el visitante se juzga por lo que envía tu servidor.
Cómo saber que funciona
curl -I https://shop.example/kz/tag.jsresponde200, ycurl -I https://shop.example/kz/pingresponde204.- En las herramientas de desarrollo del navegador, en una página con el tag, el panel de red muestra
/kz/tag.js,/kz/n,/kz/pingy/kz/sen tu propio dominio, respondidos200o204, y ninguna solicitud al host de Korzen. - Haz una acción protegida y después pulsa Comprueba tu integración en la página Configuración: El token del tag en esa llamada indica Visto. Tus decisiones aparecen como antes en la página Decisiones del panel.
- Con una
Content-Security-Policy, el host de Korzen puede salir de ella:script-src 'self'; connect-src 'self'; worker-src blob:basta para el tag. El tag
Errores comunes
- El tag aún cargado desde el host de Korzen: el modo sirve la ruta, pero solo el
srcdel tag lleva allí al navegador. - Un body parser antes del reenviador en Express: el cuerpo del beacon se lee antes de que el reenviador lo reciba. Móntalo antes de cualquier parser, o solo en la ruta.
- El Worker con
KORZEN_FIRST_PARTYpero sin ruta para/kz/*: las solicitudes llegan a tu origen, que responde404salvo que el SDK sirva la ruta allí. - Dos rutas: el Worker en una y el SDK en otra. El SDK envía entonces a los visitantes desafiados a una ruta que el Worker no sirve.
- Llamar a
/v1/decidea mano: suchallenge.urles siempre el host de Korzen. Solo los SDKs y el Worker envían al visitante a tu ruta.