Detén los bots.
Sin que tus
usuarios se enteren.
Un checkbox. Cero API keys. Cero tracking. Tus usuarios marcan y siguen. Los bots se chocan contra un muro criptográfico que tu servidor valida en una sola línea de código.
Invisible para humanos.
Imposible para bots.
Tres pasos. Ni uno más. Sin librerías que cargar antes de tiempo, sin scripts de terceros, sin claves que rotar cada año.
El usuario marca un checkbox
Hasta ese clic, mCaptcha duerme. Ni un fetch, ni un ciclo de CPU, ni una petición DNS. Tu Lighthouse no se entera de que existe.
Reto criptográfico en ~50 ms
El navegador resuelve un Proof-of-Work con SHA-256. Si la IP ha disparado >30 retos en una hora, pasamos a un slider puzzle que ningún bot atraviesa sin tiempo real de humano.
Tu backend recibe un token
Un GET a /api/captcha/verify con el token. Si ok=true, es humano y procesas el form. Single-use, 2 minutos de vida. Una línea de código en cualquier lenguaje.
Mismo objetivo.
Mejores consecuencias.
Todo lo que reCAPTCHA hace por ti, también lo hace mCaptcha. Lo que reCAPTCHA hace a tus usuarios, no.
Dos líneas. Listo.
Sin npm install. Sin dashboard al que registrarse. Sin claves que copiar. Pega el snippet, despliega y cierra el ticket.
<form action="/contact" method="POST">
<input name="email" required>
<textarea name="message" required></textarea>
<div class="mcaptcha"></div>
<button type="submit">Enviar</button>
</form>
<script src="https://medel.es/captcha.js" async defer></script>
Antes de procesar el formulario,
valida el token.
Tu backend recibe mcaptcha_token. Hace un GET, mira si ok=true, y sigue. Cinco lenguajes, mismo flujo, ni una librería que instalar.
<?php
function mcaptcha($token) {
$url = 'https://medel.es/api/captcha/verify?token=' . urlencode($token);
$response = json_decode(file_get_contents($url));
return $response->ok ?? false;
}
// Uso:
$token = $_POST['mcaptcha_token'] ?? '';
if (!mcaptcha($token)) {
http_response_code(400);
exit('Captcha fallido');
}
// ✅ Captcha verificado. Procesa el formulario.
// Validación desde el cliente — útil en SPAs antes de enviar al backend.
// Para protección real, verifica SIEMPRE también en tu servidor.
async function mcaptcha(token) {
const url = 'https://medel.es/api/captcha/verify?token=' + encodeURIComponent(token);
const data = await fetch(url).then(r => r.json());
return data.ok === true;
}
// Uso en un formulario:
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const token = fd.get('mcaptcha_token');
if (!(await mcaptcha(token))) {
alert('Captcha fallido');
return;
}
// ✅ Captcha verificado. Envía al backend.
await fetch('/api/contact', { method: 'POST', body: fd });
});
async function mcaptcha(token) {
const url = 'https://medel.es/api/captcha/verify?token=' + encodeURIComponent(token);
const data = await fetch(url).then(r => r.json());
return data.ok === true;
}
// Uso (Express):
const token = req.body.mcaptcha_token;
if (!(await mcaptcha(token))) {
return res.status(400).send('Captcha fallido');
}
// ✅ Captcha verificado. Procesa el formulario.
import urllib.request, urllib.parse, json
def mcaptcha(token):
url = 'https://medel.es/api/captcha/verify?' + urllib.parse.urlencode({'token': token})
with urllib.request.urlopen(url, timeout=5) as r:
return json.loads(r.read()).get('ok') is True
# Uso (Flask):
token = request.form.get('mcaptcha_token', '')
if not mcaptcha(token):
abort(400, 'Captcha fallido')
# ✅ Captcha verificado. Procesa el formulario.
# Solo necesitas un GET con el token como query param:
curl 'https://medel.es/api/captcha/verify?token=YOUR_TOKEN'
# OK: {"ok":true,"verified_at":"2026-06-22 12:24:56"}
# FAIL: {"ok":false,"error":"already_used"}
¿Migras desde Google reCAPTCHA?
El widget también rellena g-recaptcha-response y acepta class="g-recaptcha". Si tu backend ya lo lee, sólo cambia la URL de verify:
// Antes: $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret . '&response=' . $token; // Después (sin secret): $url = 'https://medel.es/api/captcha/verify?response=' . $token;
Endpoints
Cuatro endpoints REST. Sin SDK. Sin auth. Sin versionado. Solo el que necesitas y los demás los llama el widget.
GET
https://medel.es/api/captcha/verify?token=…
Lo llamas tú
Verifica el token. Single-use: tras el primer verify exitoso queda invalidado, así nadie lo replay-ataca.
200 OK:{"ok": true, "verified_at": "2026-06-22 12:24:56"}
400 KO:
{"ok": false, "error": "already_used"}
Códigos: missing_token · unknown_token · already_used · expired · origin_mismatch
GET
https://medel.es/api/captcha/challenge
Lo llama el widget
Emite un reto (PoW por defecto, slider puzzle si la IP es sospechosa). El widget lo llama solo.
POST
https://medel.es/api/captcha/solve
Lo llama el widget
El widget envía la solución (nonce del PoW o posición X del slider) y recibe el token verificable.
GET
https://medel.es/api/captcha/stats
Público
Estadísticas agregadas del día. Cero datos personales, cero IPs, cero tracking.
Funciona en el 98%
sin tocar nada.
Funciona automáticamente en
- Sitios HTTPS
- Chrome / Edge / Firefox / Safari (2017+)
- localhost en desarrollo
- SPA (React / Vue / Svelte)
- Formularios server-rendered
Si tienes CSP estricta
Añade el dominio a 3 directivas:
script-src 'self' https://medel.es; connect-src 'self' https://medel.es; style-src 'self' https://medel.es;
Preguntas honestas, respuestas honestas
¿Cuándo aparece el slider puzzle?
¿Es gratis para siempre?
¿Funciona sin JavaScript?
¿Hace tracking de mis usuarios?
¿Por qué un slider en vez de "selecciona los semáforos"?
¿Y si Medel Captcha se cae?
¿Es compatible con reCAPTCHA?
¿Cómo lo paro si quiero auto-hostarlo?
Pega el snippet.
Olvídate de los bots.
Integra en 30 segundos. Sin tarjeta. Sin claves. Sin contratos. Sin letra pequeña. Cuando un bot intente entrar, será problema suyo, no tuyo.