How to Add CAPTCHA to Fastly CDN
Learn how to integrate Procaptcha with Fastly CDN to protect your website from bot attacks while maintaining performance and user privacy.

As cyber threats evolve, protecting your website from malicious bots and automated attacks has become essential. If you're using Fastly CDN, you can integrate Procaptcha to intercept suspicious traffic and display a privacy-friendly CAPTCHA challenge. Unlike Google's reCAPTCHA, Procaptcha is designed with privacy at its core and provides a GDPR-compliant solution that respects user privacy.
In this guide, we'll walk you through how to implement Procaptcha with Fastly to protect high-risk requests while maintaining excellent performance and user experience.
What is Fastly CDN?
Fastly is a modern content delivery network (CDN) and edge cloud platform that helps websites deliver content faster to users worldwide. It provides powerful features including:
- Edge computing - Run custom code at the edge using Fastly VCL (Varnish Configuration Language) or Compute services
- Real-time analytics - Monitor traffic patterns and security threats
- DDoS protection - Defend against distributed denial of service attacks
- WAF (Web Application Firewall) - Filter malicious traffic before it reaches your origin servers
By combining Fastly's edge capabilities with Procaptcha's privacy-focused bot detection, you create a powerful bot defense system that operates at the network edge.
Prerequisites
Before you begin, make sure you have:
- A Fastly account with an active service
- A Procaptcha account - Sign up for free
- Your Procaptcha
SITE_KEYandSECRET_KEYfrom the portal - Basic familiarity with Fastly VCL or Compute services
Implementation Approaches
There are two main ways to integrate Procaptcha with Fastly:
Approach 1: Client-Side Implementation with Edge Detection
This approach uses Fastly to detect suspicious traffic and inject the Procaptcha challenge on the client side.
Approach 2: Edge Compute Implementation
This approach uses Fastly Compute to handle the entire CAPTCHA flow at the edge, including verification.
We'll cover both approaches in this guide.
Approach 1: Client-Side Implementation with VCL
This method uses Fastly VCL to detect high-risk requests and inject Procaptcha into your web pages.
Step 1: Configure Risk Detection in VCL
First, we'll set up VCL logic to identify suspicious traffic patterns. Add this to your Fastly VCL service:
sub vcl_recv {
# Initialize risk score
declare local var.risk_score INTEGER;
set var.risk_score = 0;
# Check for suspicious user agents
if (req.http.User-Agent ~ "(?i)(bot|crawler|spider|scraper)") {
set var.risk_score = var.risk_score + 50;
}
# Check request rate (requires Fastly rate limiting)
if (ratelimit.check_rate("client_" + client.ip,
"100",
"60s",
"100",
"100",
"",
"client_" + client.ip)) {
set var.risk_score = var.risk_score + 30;
}
# Check for missing common headers
if (!req.http.Accept-Language) {
set var.risk_score = var.risk_score + 20;
}
# Check for suspicious referrers or missing referrer on sensitive paths
if (req.url ~ "^/(login|register|checkout)" && !req.http.Referer) {
set var.risk_score = var.risk_score + 25;
}
# If risk score is high, flag for CAPTCHA
if (var.risk_score > 50) {
set req.http.X-Needs-Captcha = "true";
}
}Step 2: Inject Procaptcha for High-Risk Requests
When a high-risk request is detected, inject the Procaptcha script and challenge:
sub vcl_deliver {
# Only inject for HTML responses flagged as high-risk
if (req.http.X-Needs-Captcha == "true" &&
resp.http.Content-Type ~ "text/html") {
# Inject Procaptcha bundle script into <head>
set resp.http.Content-Type = "text/html; charset=utf-8";
# Use synthetic response with CAPTCHA challenge
if (resp.status == 200) {
return(deliver(captcha));
}
}
}
# Define CAPTCHA challenge page
sub vcl_synth {
if (obj.status == 950) {
set resp.status = 200;
set resp.http.Content-Type = "text/html; charset=utf-8";
# Generate HTML with Procaptcha widget
# The synthetic response includes:
# - Procaptcha bundle script in the head
# - A centered container with the captcha form
# - Auto-submit JavaScript when captcha completes
# - Styling for a clean, centered verification page
synthetic({"<!DOCTYPE html>..."}); # Full HTML omitted for brevity
return(deliver);
}
}Important: Replace YOUR_SITE_KEY with your actual Procaptcha site key.
Step 3: Verify Procaptcha Response
When the user completes the CAPTCHA, verify the response by calling the Procaptcha API from VCL. First, configure a backend for the Procaptcha API in your Fastly service settings:
Backend Configuration:
- Name:
procaptcha_api - Address:
api.prosopo.io - Port:
443 - Enable SSL/TLS
Then add this VCL code to verify the token:
sub vcl_recv {
# Check if CAPTCHA response is present
if (req.http.X-Needs-Captcha == "true" &&
req.request == "POST" &&
req.http.procaptcha-response) {
# Store the original request
set req.http.X-Original-URL = req.url;
set req.http.X-Captcha-Token = req.http.procaptcha-response;
# Prepare verification request to Procaptcha API
set req.backend = procaptcha_api;
set req.url = "/siteverify";
set req.http.Host = "api.prosopo.io";
set req.http.Content-Type = "application/json";
# Build JSON body for verification
# Note: Replace YOUR_SECRET_KEY with your actual secret key
set req.http.X-Verify-Body = {"secret":"YOUR_SECRET_KEY","token":"} + req.http.X-Captcha-Token + {""};
return(pass);
}
}
sub vcl_pass {
# Send the verification request with JSON body
if (req.url == "/siteverify" && req.http.X-Verify-Body) {
set bereq.method = "POST";
set bereq.body = req.http.X-Verify-Body;
}
}
sub vcl_deliver {
# Handle verification response
if (req.url == "/siteverify" && req.http.X-Captcha-Token) {
# Parse the verification result
# The API returns JSON like: {"verified": true/false}
if (resp.body ~ "\"verified\":\s*true") {
# Verification successful - set cookie and redirect to original URL
set resp.http.Set-Cookie = "captcha_verified=true; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600";
set resp.http.Location = req.http.X-Original-URL;
set resp.status = 302;
return(deliver);
} else {
# Verification failed - show error
error 403 "CAPTCHA verification failed";
}
}
}Important Notes:
- Replace
YOUR_SECRET_KEYwith your actual Procaptcha secret key - Store your secret key securely (consider using Fastly's edge dictionary for sensitive values)
- The verification API endpoint is:
https://api.prosopo.io/siteverify - The API expects a POST request with JSON body:
{"secret": "YOUR_SECRET", "token": "PROCAPTCHA_RESPONSE"} - A successful verification returns:
{"verified": true} - For production use, consider implementing error handling for network failures
Approach 2: Edge Compute Implementation (JavaScript)
For more sophisticated control, use Fastly Compute to handle the entire CAPTCHA flow at the edge.
Step 1: Set Up Fastly Compute Service
Create a new Fastly Compute service using JavaScript:
$ fastly compute init
# Choose JavaScript as your languageStep 2: Implement Bot Detection and CAPTCHA Challenge
Create your main Compute handler (src/index.js):
/// <reference types="@fastly/js-compute" />
import { env } from "fastly:env";
// Procaptcha configuration
const PROCAPTCHA_SITE_KEY = env('PROCAPTCHA_SITE_KEY') || 'YOUR_SITE_KEY';
const PROCAPTCHA_SECRET_KEY = env('PROCAPTCHA_SECRET_KEY') || 'YOUR_SECRET_KEY';
const PROCAPTCHA_VERIFY_URL = 'https://api.prosopo.io/siteverify';
// Risk scoring function
function calculateRiskScore(request) {
let score = 0;
const userAgent = request.headers.get('User-Agent') || '';
const referer = request.headers.get('Referer') || '';
const acceptLanguage = request.headers.get('Accept-Language') || '';
// Check user agent
if (/bot|crawler|spider|scraper/i.test(userAgent)) {
score += 50;
}
// Check for missing headers
if (!acceptLanguage) {
score += 20;
}
// Check sensitive paths without referrer
const url = new URL(request.url);
if (/\/(login|register|checkout)/.test(url.pathname) && !referer) {
score += 25;
}
return score;
}
// Generate CAPTCHA challenge page
function getCaptchaChallenge(request) {
const url = new URL(request.url);
const returnUrl = url.pathname + url.search;
// Build HTML page with Procaptcha widget
// Includes: script bundle, responsive styling, auto-submit form
const html = buildCaptchaHTML(PROCAPTCHA_SITE_KEY, returnUrl);
return new Response(html, {
status: 200,
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'no-store, no-cache, must-revalidate',
}
});
}
// Helper function to build the CAPTCHA challenge HTML
function buildCaptchaHTML(siteKey, returnUrl) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verification Required</title>
<script src="https://js.prosopo.io/js/procaptcha.bundle.js" async defer><\/script>
</head>
<body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<div style="background: white; padding: 2.5rem; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); max-width: 500px; text-align: center;">
<h1 style="color: #333; margin-bottom: 1rem; font-size: 1.8rem;">🔒 Security Verification</h1>
<p style="color: #666; margin-bottom: 2rem; line-height: 1.6;">We've detected unusual activity. Please verify to continue.</p>
<form id="captcha-form" action="/verify-captcha" method="POST">
<input type="hidden" name="return_url" value="${returnUrl}">
<div class="procaptcha" data-sitekey="${siteKey}"></div>
</form>
<p style="font-size: 0.85rem; color: #999; margin-top: 1.5rem;">
This protects our site while respecting your privacy.
<a href="https://prosopo.io/" target="_blank">Learn more</a>
</p>
</div>
<script>
document.addEventListener('procaptcha-success', function() {
document.getElementById('captcha-form').submit();
});
<\/script>
</body>
</html>`;
}
// Verify CAPTCHA token with Procaptcha API
async function verifyCaptchaToken(token) {
try {
const response = await fetch(PROCAPTCHA_VERIFY_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
secret: PROCAPTCHA_SECRET_KEY,
token: token,
}),
backend: 'procaptcha_api', // Configure this backend in fastly.toml
});
if (!response.ok) {
return { verified: false, error: 'Verification request failed' };
}
const result = await response.json();
return { verified: result.verified === true, data: result };
} catch (error) {
console.error('CAPTCHA verification error:', error);
return { verified: false, error: error.message };
}
}
// Main request handler
async function handleRequest(event) {
const request = event.request;
const url = new URL(request.url);
// Handle CAPTCHA verification endpoint
if (url.pathname === '/verify-captcha' && request.method === 'POST') {
const formData = await request.formData();
const token = formData.get('procaptcha-response');
const returnUrl = formData.get('return_url') || '/';
if (!token) {
return new Response('Missing CAPTCHA response', { status: 400 });
}
const verification = await verifyCaptchaToken(token);
if (verification.verified) {
// Set a cookie to remember verified users
const response = Response.redirect(url.origin + returnUrl, 302);
response.headers.set('Set-Cookie',
'captcha_verified=true; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600'
);
return response;
} else {
return new Response('CAPTCHA verification failed', { status: 403 });
}
}
// Check if user already verified
const cookies = request.headers.get('Cookie') || '';
if (cookies.includes('captcha_verified=true')) {
// User already verified, pass through to origin
return fetch(request, {
backend: 'origin_server',
});
}
// Calculate risk score
const riskScore = calculateRiskScore(request);
// If high risk, show CAPTCHA challenge
if (riskScore > 50) {
return getCaptchaChallenge(request);
}
// Low risk, pass through to origin
return fetch(request, {
backend: 'origin_server',
});
}
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));Step 3: Configure Backend Connections
Update your fastly.toml to include the Procaptcha API backend:
# fastly.toml
manifest_version = 2
name = "procaptcha-protection"
description = "Fastly Compute service with Procaptcha integration"
authors = ["your-email@example.com"]
language = "javascript"
[local_server]
[local_server.backends]
[local_server.backends.origin_server]
url = "https://your-origin-server.com"
[local_server.backends.procaptcha_api]
url = "https://api.prosopo.io"
override_host = "api.prosopo.io"
[setup]
[setup.backends]
[setup.backends.origin_server]
address = "your-origin-server.com"
port = 443
[setup.backends.procaptcha_api]
address = "api.prosopo.io"
port = 443
override_host = "api.prosopo.io"Step 4: Set Environment Variables
Configure your Procaptcha keys as environment variables:
$ fastly compute publish
# During setup, add environment variables:
# PROCAPTCHA_SITE_KEY=your_site_key_here
# PROCAPTCHA_SECRET_KEY=your_secret_key_hereStep 5: Deploy Your Compute Service
$ fastly compute publishYour Fastly Compute service will now:
- Analyze incoming requests for suspicious patterns
- Challenge high-risk users with Procaptcha
- Verify CAPTCHA responses at the edge
- Set a cookie for verified users to avoid repeated challenges
- Pass legitimate traffic to your origin server
Best Practices
1. Tune Risk Scoring
Adjust your risk score thresholds based on your traffic patterns:
// Conservative (more challenges)
if (riskScore > 30) { showCaptcha(); }
// Moderate (balanced)
if (riskScore > 50) { showCaptcha(); }
// Lenient (fewer challenges)
if (riskScore > 70) { showCaptcha(); }Monitor your false positive rate and adjust accordingly.
2. Implement Rate Limiting
Combine Procaptcha with Fastly's rate limiting to prevent abuse:
if (ratelimit.check_rate("client_" + client.ip,
"100", # bucket size
"60s", # time window
"100", # requests allowed
"100", # penalty
"",
"client_" + client.ip)) {
error 950 "Rate limit exceeded";
}3. Use Adaptive Challenges
Don't challenge all users equally. Consider:
- Returning users - Use cookies to identify verified users
- Trusted IPs - Whitelist known good actors
- User behavior - Reduce challenges for users with positive history
- Geographic patterns - Adjust based on regional traffic patterns
4. Monitor and Optimize
Track key metrics:
- Challenge rate - Percentage of users challenged
- Pass rate - Percentage of users who complete the CAPTCHA
- False positives - Legitimate users incorrectly challenged
- Bot block rate - Percentage of actual bots blocked
Use Fastly's real-time analytics to monitor these metrics and adjust your configuration.
5. Provide User Feedback
Always explain why users see a CAPTCHA challenge:
<p>We've detected unusual activity from your connection...</p>
<p>This helps protect our site from automated attacks.</p>
<p>Your privacy is respected - no personal data is collected.</p>Clear communication reduces user frustration and abandonment.
Troubleshooting Common Issues
CAPTCHA Not Displaying
Problem: The Procaptcha widget doesn't appear on the challenge page.
Solutions:
- Verify the bundle script URL is correct:
https://js.prosopo.io/js/procaptcha.bundle.js - Check browser console for JavaScript errors
- Ensure your
SITE_KEYis correct - Verify the
procaptchadiv has the correctdata-sitekeyattribute
Verification Always Fails
Problem: CAPTCHA verification endpoint always returns false.
Solutions:
- Verify your
SECRET_KEYis correct - Check that the token is being properly extracted from the form data
- Ensure the backend connection to
api.prosopo.iois configured correctly - Check for network connectivity issues between Fastly edge and Procaptcha API
Users Challenged Too Frequently
Problem: Legitimate users see CAPTCHA challenges too often.
Solutions:
- Lower your risk score threshold
- Implement longer-lived verification cookies
- Add trusted IP ranges to a whitelist
- Review and adjust your risk scoring logic
Performance Impact
Problem: CAPTCHA integration slows down your site.
Solutions:
- Cache the CAPTCHA challenge page at the edge
- Use asynchronous verification where possible
- Minimize the number of API calls by using cookies effectively
- Consider using Fastly's edge computing for faster verification
Conclusion
Integrating Procaptcha with Fastly CDN provides a powerful, privacy-focused solution for protecting your website from bot attacks. By implementing bot detection at the edge, you reduce server load, improve performance, and create a better experience for legitimate users.
Unlike traditional CAPTCHA solutions that track users and collect data, Procaptcha respects user privacy while providing effective protection against:
- Credential stuffing attacks
- Account takeover attempts
- Web scraping and content theft
- DDoS attacks
- Click fraud
- Ticket scalping bots
Whether you choose the VCL approach for simplicity or the Compute approach for flexibility, Procaptcha integrates seamlessly with Fastly's edge platform to deliver security without compromising user experience or privacy.
Ready to get started? Sign up for a free Procaptcha account and start protecting your website today.
Additional Resources
- Procaptcha Documentation
- Fastly Compute Documentation
- Fastly VCL Documentation
- How to Deploy Procaptcha
- What is CAPTCHA?
- Procaptcha vs hCaptcha Comparison
Need Help Adding CAPTCHA to Fastly?
If you need assistance implementing Procaptcha with Fastly CDN, please get in touch below to arrange a demo or discuss your requirements.
Related Posts to How to Add CAPTCHA to Fastly CDN

How to deploy Procaptcha on your website or app
Wed, 20 Sept 2023

Stop giving your website data away!
Thu, 22 Feb 2024

How Much Does Prosopo Procaptcha Cost Compared to reCAPTCHA?
Tue, 09 Apr 2024

What is the best value CAPTCHA in 2024?
Sat, 13 Apr 2024

reCAPTCHA is Changing Its Terms of Service
Mon, 10 Nov 2025

Cloudflare Turnstile Alternatives 2026: Free & Privacy-First
Wed, 15 Apr 2026

Best CAPTCHA 2026: 7 Top Solutions Compared
Thu, 11 Jun 2026

Forrester Wave Bot and Agent Trust Management Q2 2026: How Prosopo Compares
Thu, 25 Jun 2026

Google reCAPTCHA Pricing: How the Price Hike Affects You
Sun, 18 Feb 2024

Why am I clicking traffic lights? The reason Google reCAPTCHA frustrates users
Fri, 15 Mar 2024

Google reCAPTCHA is a privacy nightmare - Questions over privacy promises and cookie use
Mon, 18 Mar 2024

Google reCAPTCHA Switches to Data Processor - Too Little, Too Late?
Mon, 09 Feb 2026

reCAPTCHA vs Cloudflare Turnstile 2026: Which One (and When Procaptcha Beats Both)
Thu, 02 Jul 2026

reCAPTCHA vs hCaptcha 2026: Which One (and Why Procaptcha Often Wins Both)
Thu, 02 Jul 2026
