Skip to content

Instantly share code, notes, and snippets.

@ribtoks
Created March 14, 2026 19:42
Show Gist options
  • Select an option

  • Save ribtoks/c59a9fed190532bde91b947e97a5e80c to your computer and use it in GitHub Desktop.

Select an option

Save ribtoks/c59a9fed190532bde91b947e97a5e80c to your computer and use it in GitHub Desktop.
Private Captcha Local e2e tutorial (Node.js version)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Private Captcha Example</title>
<style>
body {
display:flex;
flex-direction: column;
min-height: 100vh;
}
form {
max-width: 32rem;
margin: auto;
display: flex;
flex-direction: column;
gap: 20px;
border: 1px #ccc solid;
padding: 20px;
}
</style>
<script defer src="https://cdn.privatecaptcha.com/widget/js/privatecaptcha.js"></script>
<script type="text/javascript">
function onCaptchaSolved() {
const submitButton = document.querySelector('#formSubmit');
submitButton.disabled = false;
}
</script>
</head>
<body>
<div style="display: flex; flex: 1 1 0%">
<form action='/submit' method="POST">
<label> Email: </label>
<input type="email" name="email" placeholder="Email address" required />
<div class="private-captcha" data-sitekey="your-sitekey" data-finished-callback="onCaptchaSolved"></div>
<button id="formSubmit" type="submit" disabled> Submit </button>
</form>
</div>
</body>
</html>
const http = require('node:http');
const fs = require('node:fs');
async function checkSolution(solution, apiKey) {
const response = await fetch('https://api.privatecaptcha.com/verify', {
method: 'POST',
headers: { 'X-Api-Key': apiKey },
body: solution
});
const data = await response.json();
if (!data.success || data.code !== 0) {
throw new Error('solution is not correct');
}
}
const resultPage = (color) => `<!DOCTYPE html><html><body style="background-color: ${color};"></body></html>`;
const server = http.createServer(async (req, res) => {
if (req.url === '/submit' && req.method === 'POST') {
let body = '';
for await (const chunk of req) body += chunk;
const formData = new URLSearchParams(body);
const captchaSolution = formData.get('private-captcha-solution') ?? '';
try {
await checkSolution(captchaSolution, 'your-api-key');
res.end(resultPage('green'));
} catch (error) {
res.end(resultPage('red'));
}
return;
}
if (req.url === '/' && req.method === 'GET') {
fs.readFile('index.html', (err, html) => {
if (err) {
res.statusCode = 500;
res.end('Internal Server Error');
return;
}
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(html);
});
return;
}
res.statusCode = 404;
res.end('Not found');
});
server.listen(8081, () => {
console.log('Listening on http://localhost:8081/');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment