use case · webflow

Webflow form spam protection.

Updated May 12, 2026

Webflow ships zero spam filtering on its native forms. The default response — drop reCAPTCHA on the page — adds friction, hurts conversion, and still misses the headless-browser bots. Route the form-submission webhook through Siftfy instead and you get a calibrated probability per submission with no UX change.

Why webhook-fronting beats on-page CAPTCHA

Every Webflow plan supports a server-side form webhook (Project Settings → Forms → Form Submissions URL). Anything you put behind that URL runs after the user submits but before the data lands in your CRM or inbox — which is exactly where spam classification belongs. The user sees the same confirmation message either way; you just decide whether the lead is real before paying it any attention.

On-page CAPTCHA, by contrast, is a tax on every legitimate visitor and is bypassed by any bot running a real headless browser. Modern spam isn't dumb form-fillers — it's content. Classify the content.

Drop-in Cloudflare Worker

The pattern below is a 30-line Cloudflare Worker that sits between Webflow and your real destination. Same shape works on Vercel Functions, Netlify Functions, AWS Lambda, or a tiny Express app you already run. Three thresholds — definitely-spam (drop), maybe (queue), clean (deliver) — and a 2-second timeout so a slow Siftfy call never makes the form-submission UX feel broken.

javascript
// Cloudflare Worker — fronts Webflow's form webhook with Siftfy.
// Configure Webflow Project Settings > Forms > Form Submissions URL to
// https://your-worker.workers.dev/webflow

const SPAM_THRESHOLD = 0.85;   // hard drop above this
const QUEUE_THRESHOLD = 0.50;  // human review between

export default {
  async fetch(req, env) {
    if (req.method !== "POST") return new Response("method", { status: 405 });

    // Webflow posts form fields as application/x-www-form-urlencoded.
    const form = await req.formData();
    const message = String(form.get("message") ?? form.get("description") ?? "");
    const email = String(form.get("email") ?? "");

    let probability = 0;
    try {
      const resp = await fetch("https://api.siftfy.io/v1/predict", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-API-Key": env.SIFTFY_KEY,
        },
        body: JSON.stringify({ text: message }),
        signal: AbortSignal.timeout(2000),
      });
      if (resp.ok) {
        ({ spam_probability: probability } = await resp.json());
      }
    } catch {
      // Fall open on transport failure — don't drop a real lead.
    }

    if (probability >= SPAM_THRESHOLD) {
      // Always 200 to Webflow, even on a hard block. The form sees "thanks"
      // and the spammer doesn't learn the threshold.
      return new Response("ok", { status: 200 });
    }

    // Forward clean / borderline submissions to your real handler
    // (CRM, email service, Airtable, n8n, whatever).
    await env.DESTINATION.fetch(`https://hooks.example.com/lead?score=${probability}`, {
      method: "POST",
      body: JSON.stringify({ email, message, probability }),
      headers: { "Content-Type": "application/json" },
    });

    return new Response("ok", { status: 200 });
  },
};

Wiring it up in Webflow

  1. Deploy the worker (or function) to a public HTTPS URL.
  2. In Webflow, open Project Settings → Forms → Form Submissions URL and paste the public URL.
  3. Test with a clean submission and a junk submission — junk should be silently dropped, clean should land at your destination.
  4. Set a SIFTFY_KEY secret on the worker (Cloudflare Dashboard → Workers → Variables → Encrypt).

Webflow's native "Email me on form submission" still fires for clean and borderline submissions because the worker forwards them to your email destination as the last step.

Edge cases worth handling

  • Multiple form types. Webflow sends a name field identifying the form. Branch on it if you classify a contact form differently from a newsletter signup (newsletters tolerate higher false positives).
  • Long message bodies. Siftfy truncates input above its model context — for long bug reports or product feedback, you don't need the entire body, just the first 500 words. Slice client-side.
  • Multilingual forms. The model is trained primarily on English. Raise the block threshold to ~0.92 if you serve other languages until coverage improves.
  • Don't reveal the score. Always return 200 to Webflow. A 4xx makes Webflow show a generic error to the user and tells the spammer their content was flagged.
Try it free

10,000 submissions / month free. Read the /v1/predict reference, or peek at related use cases: contact forms, static sites, headless CMS.