natbox.io

CaptchaBox / Docs / Node.js

Add a captcha to Node.js and Express

Last updated 6 November 2026 · By Mohammad AlSaraf

About 5 minutes. Node.js 18 or newer (built-in fetch), Express 4 or 5.

You will write one function that checks a token, and an Express middleware to put in front of any route that takes a form. No npm package needed.

1. Get your keys

Sign in to the CaptchaBox client area (a free account is enough) and add your site with its domain. You get a site key for the page and a secret for your server. Keep the secret out of your HTML and out of git: put it in an environment variable.

2. The check

// Asks CaptchaBox whether the token from the form is valid. Each token works once, for 5 minutes.
async function verifyCaptchaBox(token) {
  if (!token) return false;
  try {
    const res = await fetch('https://captcha.natbox.io/v1/siteverify', {
      method: 'POST',
      body: new URLSearchParams({ secret: SECRET, response: token }),
      signal: AbortSignal.timeout(10000),
    });
    const data = await res.json();
    return data.success === true;
  } catch {
    return false; // could not reach CaptchaBox: refuse (or allow, if you prefer to fail open)
  }
}

As middleware, so any route can use it:

// Middleware: put it in front of any POST route that takes a form.
async function requireCaptchaBox(req, res, next) {
  if (await verifyCaptchaBox(req.body['captchabox-response'])) return next();
  res.status(400).send('The captcha check failed. Please go back and try again.');
}
app.use(express.urlencoded({ extended: false }));
app.post('/contact', requireCaptchaBox, (req, res) => { /* the visitor passed */ });

3. The widget in your page

<script src="https://captcha.natbox.io/v1/widget.js" async defer></script>
<form method="post" action="https://natbox.io/contact">
  <input name="email" type="email" required>
  <div class="captchabox" data-sitekey="YOUR_SITE_KEY"></div>
  <button>Send</button>
</form>

JSON APIs and single-page apps

If your front end sends JSON with fetch, get a token from the widget and send it along:

// browser
const token = await captchabox.execute();
await fetch('/api/signup', { method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email, token }) });
captchabox.reset(); // a token works once

// server
app.post('/api/signup', express.json(), async (req, res) => {
  if (!(await verifyCaptchaBox(req.body.token))) return res.status(400).json({ error: 'captcha' });
  // ...
});

The complete example

Run it with npm install express and node server.js:

// CaptchaBox example: Express (Node.js 18+, uses the built-in fetch).
// Run: npm install express && CAPTCHABOX_SITE_KEY=... CAPTCHABOX_SECRET=... node server.js
// Test keys that always pass, on any domain: cb_test_pass / cbs_test_pass
const express = require('express');

const SITE_KEY = process.env.CAPTCHABOX_SITE_KEY || 'cb_test_pass';
const SECRET = process.env.CAPTCHABOX_SECRET || 'cbs_test_pass';

// Asks CaptchaBox whether the token from the form is valid. Each token works once, for 5 minutes.
async function verifyCaptchaBox(token) {
  if (!token) return false;
  try {
    const res = await fetch('https://captcha.natbox.io/v1/siteverify', {
      method: 'POST',
      body: new URLSearchParams({ secret: SECRET, response: token }),
      signal: AbortSignal.timeout(10000),
    });
    const data = await res.json();
    return data.success === true;
  } catch {
    return false; // could not reach CaptchaBox: refuse (or allow, if you prefer to fail open)
  }
}

// Middleware: put it in front of any POST route that takes a form.
async function requireCaptchaBox(req, res, next) {
  if (await verifyCaptchaBox(req.body['captchabox-response'])) return next();
  res.status(400).send('The captcha check failed. Please go back and try again.');
}

const app = express();
app.use(express.urlencoded({ extended: false }));

app.get('/', (req, res) => {
  res.send(`<!doctype html>
<meta charset="utf-8">
<title>Contact</title>
<script src="https://captcha.natbox.io/v1/widget.js" async defer></script>
<form method="post" action="https://natbox.io/contact">
  <input name="email" type="email" placeholder="Your email" required>
  <div class="captchabox" data-sitekey="${SITE_KEY}"></div>
  <button>Send</button>
</form>`);
});

app.post('/contact', requireCaptchaBox, (req, res) => {
  // The visitor passed: send the email, save the message, etc.
  res.send('<p id="result">Thanks, your message was sent.</p>');
});

app.listen(process.env.PORT || 3000, () => console.log('listening on', process.env.PORT || 3000));

Trying it out? The test keys cb_test_pass (site key) and cbs_test_pass (secret) always pass, on any domain including localhost. Swap in your own keys from the client area before going live.

Using Next.js? See the Next.js guide, with a React component.

Other platforms: all CaptchaBox docs. Questions? Open a ticket, we reply within 24 hours.