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:

  1. A Fastly account with an active service
  2. A Procaptcha account - Sign up for free
  3. Your Procaptcha SITE_KEY and SECRET_KEY from the portal
  4. 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_KEY with 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 language

Step 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_here

Step 5: Deploy Your Compute Service

$ fastly compute publish

Your Fastly Compute service will now:

  1. Analyze incoming requests for suspicious patterns
  2. Challenge high-risk users with Procaptcha
  3. Verify CAPTCHA responses at the edge
  4. Set a cookie for verified users to avoid repeated challenges
  5. 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_KEY is correct
  • Verify the procaptcha div has the correct data-sitekey attribute

Verification Always Fails

Problem: CAPTCHA verification endpoint always returns false.

Solutions:

  • Verify your SECRET_KEY is correct
  • Check that the token is being properly extracted from the form data
  • Ensure the backend connection to api.prosopo.io is 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:

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


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.

Tell us about your needs

We'll get back to you within 24 hours

By submitting this form, you agree to our Privacy Policy and Terms of Service