Protección antispam
Honeypot, marcas de tiempo firmadas, puntuación de spam, verificación humana, dominios permitidos y límites de frecuencia.
Cada formulario tiene protección por capas sin configurar nada: un honeypot, una marca de tiempo de renderizado firmada, puntuación de spam y límites de frecuencia. Además, puedes añadir una verificación humana y limitar el formulario a tus propios dominios.
| Capa | Activa por defecto | Resultado |
|---|---|---|
| Límites de frecuencia | Sí, preset Standard | 429 rate_limited |
| Dominios permitidos | No | Spam o 403 origin_not_allowed |
| Verificación humana | No | 403 challenge_required o una página de verificación |
| Honeypot | Sí | +5 a la puntuación |
| Puntuación de spam | Sí | Spam desde 5 |
Cómo se gestiona el spam
Un envío que se considera spam se guarda con el estado spam y aun así recibe una respuesta de éxito normal, para que los bots no aprendan nada. El spam no envía webhooks ni emails, no cuenta para tu uso mensual y se elimina a los 30 días. Revísalo en el panel o con la API REST; al marcar un envío como no spam, se envían entonces sus webhooks.
Honeypot
Añade un input oculto que las personas nunca rellenan. El nombre predeterminado es _gotcha; puedes cambiarlo por formulario en Honeypot field, en los ajustes del formulario. Las técnicas para ocultarlo están en Formularios HTML.
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />Puntuación de spam
Cada envío recibe una puntuación según estas señales. Con 5 o más, se guarda como spam.
| Señal | Motivo en el panel | Puntuación |
|---|---|---|
| El honeypot está rellenado | honeypot | +5 |
| Se envió menos de 2 segundos después de renderizar el formulario | timestamp_fast | +3 |
| La marca de tiempo de renderizado firmada es falsa o tiene más de 24 horas | timestamp_invalid | +3 |
| Un enlace en un campo de nombre o de asunto | url_in_name | +3 |
| Se enviaron los mismos campos a este formulario en las últimas 24 horas | duplicate | +3 |
| Tres o más enlaces en total | many_urls | +2 |
| Una frase de spam conocida (22 incluidas) | keyword | +2 |
| Una dirección de email de un dominio de email desechable | disposable_email | +2 |
- La marca de tiempo de renderizado solo existe cuando el formulario lo renderiza el SDK o el componente de shadcn/ui, que envían un
_ikiform_tsfirmado. Los envíos HTML sin ella no se penalizan. - Un campo de nombre es cualquier campo cuyo nombre contiene
name,firstname,lastname,fullname,surnameosubject, pero no palabras comocompany,websiteodomain. - Dos motivos fuerzan el spam sea cual sea la puntuación:
origin_mismatch, de los dominios permitidos, ycaptcha_unavailablecuando no se puede conectar con Turnstile.
Verificación humana
Cada formulario tiene un ajuste Spam check con tres opciones. Está desactivado por defecto, así que las integraciones existentes con fetch y curl siguen funcionando cuando creas un formulario.
| Opción | Qué hace |
|---|---|
| Desactivada | Sin desafío. El honeypot, la marca de tiempo y la puntuación siguen funcionando. |
| Ikiform (recomendada) | Cloudflare Turnstile ejecutado por Ikiform en ikiform.com. No necesitas cuenta ni claves de Cloudflare. |
| Tus propias claves de Turnstile | Tu widget de Turnstile y tu clave secreta. El hostname del token se compara con tus dominios permitidos. |
Cómo funciona la verificación de Ikiform
Formulario HTML
- El navegador envía el formulario
- Aún no hay token: ikiform.com muestra una breve página de verificación
- Turnstile se ejecuta y la página vuelve a enviar los mismos campos con un token
- 303 a tu redirección o a la página de agradecimiento
JavaScript
- challenge(formId) monta un frame oculto de ikiform.com
- Turnstile se ejecuta y solo se muestra si necesita un clic
- submit(formId, data, { turnstileToken })
- 201 con el id del envío
Servidor con clave secreta
- POST con Authorization: Bearer ik_sk_...
- Se omite la verificación de Ikiform
- 201 con el id del envío
import { challenge, submit } from "@ikiform/sdk";
const form = document.querySelector("form");
form.addEventListener("submit", async (event) => {
event.preventDefault();
const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx");
const result = await submit("frm_xxxxxxxxxxxxxxxx", new FormData(form), { turnstileToken });
if (result.ok) form.replaceWith("Thanks, we got it.");
});Los tokens son de un solo uso y duran 300 segundos, así que obtén uno por envío. Un envío JSON sin token recibe 403 challenge_required con un challenge_url; un token que no pasa la verificación recibe 403 captcha_failed.
{
"ok": false,
"error": {
"code": "challenge_required",
"message": "Complete the human check, then send the form again.",
"challenge_url": "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx/challenge"
},
"request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}captcha_unavailable, para que puedas recuperarlo en lugar de perderlo.Tus propias claves de Turnstile
Pega tu clave de sitio y tu clave secreta en los ajustes del formulario. El secreto se guarda cifrado. Renderiza el widget en tu formulario para que añada cf-turnstile-response al envío, o pasa el token a submit como turnstileToken. Los envíos sin un token válido reciben 403 captcha_failed, incluidos los envíos con una clave de API secreta.
Dominios permitidos
Indica los dominios donde está tu formulario en Allowed domains. A partir de ahí, un envío debe venir de uno de ellos o de un subdominio, según la cabecera Origin, o el Referer cuando no hay origen. Deja la lista vacía para aceptar envíos desde cualquier sitio.
| Dominios permitidos | Envío desde | Coincide |
|---|---|---|
example.com | https://example.com | Sí |
example.com | https://www.example.com | Sí, subdominio |
example.com | http://localhost:3000 | No, añade localhost mientras desarrollas |
example.com | https://example.co | No |
app.example.com | https://example.com | No, un dominio padre no coincide |
When a post comes from another site decide qué pasa cuando no coincide: Mark as spam (por defecto) o Reject con 403 origin_not_allowed. Los envíos con una clave de API secreta se saltan esta comprobación. Los dominios permitidos también deciden qué destinos de _redirect se siguen y dónde se puede incrustar el frame de verificación de Ikiform.
Límites de frecuencia
Cada formulario tiene un preset de Rate limit, contado con exactitud por IP de visitante y formulario. Standard permite 5 envíos por minuto, Relaxed 20 por minuto y Strict 2 cada 10 minutos. Las reglas completas están en Límites de frecuencia.