CaptchaBox / Docs / Node.js
Add a captcha to Node.js and Express
Last updated 6 November 2026 · By Mohammad AlSaraf
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.