← Back to posts
comparison guide self-hosted · · 14 min read

Best Self-Hosted CAPTCHA Alternatives to Google reCAPTCHA 2026

Compare self-hosted and privacy-respecting CAPTCHA alternatives — mCaptcha, hCaptcha, Cloudflare Turnstile, and FriendlyCaptcha — with Docker setup guides and performance benchmarks.

OS
Editorial Team

Every website with a public-facing form faces the same problem: how do you tell humans from bots without making legitimate users suffer through impossible image puzzles? For over a decade, Google reCAPTCHA has been the default answer. But reCAPTCHA tracks users across the web, sends behavioral data to Google’s servers, and adds third-party network requests to every page it appears on.

If you run a self-hosted website or care about visitor privacy, there are now mature alternatives that give you full control over the verification pipeline. This guide compares the best self-hosted and privacy-focused CAPTCHA solutions available in 2026, with hands-on docker setup guides and real-world integration examples.

Why Self-Host Your CAPTCHA

Running your own CAPTCHA service — or at least choosing a provider that respects privacy — solves several problems that reCAPTCHA creates:

Data sovereignty. Self-hosted CAPTCHA solutions never send user behavior data to third-party servers. Verification happens on your infrastructure. You control the logs, the data retention, and the privacy policy.

GDPR compliance. reCAPTCHA sets cookies and collects behavioral fingerprints, which requires explicit consent under GDPR. Self-hosted alternatives eliminate the need for cookie banners just to protect your contact form.

No third-party dependencies. When reCAPTCHA goes down — and it does, periodically — your forms become unusable. A self-hosted service runs on your infrastructure with the same uptime guarantees as the rest of your stack.

Censorship resistance. In some regions, Google services are blocked or rate-limited. If your CAPTCHA loads from google.com, visitors in those regions see broken forms. Self-hosted CAPTCHA loads from your own domain.

Performance. Loading reCAPTCHA adds roughly 200-500ms of network latency, plus the JavaScript parsing time. A self-hosted solution on the same server as your website adds virtually zero network overhead.

No user profiling. reCAPTCHA builds a behavioral profile of every person who solves a puzzle on any site that uses it. Privacy-respecting alternatives verify the current interaction and nothing more.

The trade-off is operational overhead: you need to deploy and maintain the CAPTCHA service yourself. But as you will see, most solutions deploy as a single Docker container with minimal resource requirements.

mCaptcha: The Fully Self-Hosted Option

mCaptcha is an open-source, privacy-respecting CAPTCHA system designed from the ground up to be self-hosted. It uses a proof-of-work challenge that is computed client-side, meaning no image puzzles, no audio challenges, and no behavioral tracking.

How It Works

mCaptcha works by giving the browser a computational puzzle — a hash that must be solved before the form can be submitted. The difficulty is calibrated so that a human experiences a sub-second delay, while a bot attempting to solve thousands of puzzles per minute gets rate-limited by the computational cost.

The key insight is that the puzzle is deterministic and verified server-side. Your mCaptcha instance generates a challenge, the browser’s JavaScript solves it, and your application verifies the solution against your mCaptcha backend. No third party is involved at any point.

Docker Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# docker-compose.yml for mCaptcha
version: "3.8"

services:
  mcaptcha:
    image: mcaptcha/mcaptcha:latest
    container_name: mcaptcha
    restart: unless-stopped
    ports:
      - "7493:7493"
    environment:
      - MCAPTCHA_SECRET_KEY=your-secret-key-change-this
      - MCAPTCHA_DB_URL=postgresql://mcaptcha:mcaptcha@db:5432/mcaptcha
      - MCAPTCHA_SITE_KEY=your-site-key
    depends_on:
      - db
    networks:
      - mcaptcha-net

  db:
    image: postgres:16-alpine
    container_name: mcaptcha-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=mcaptcha
      - POSTGRES_PASSWORD=mcaptcha
      - POSTGRES_DB=mcaptcha
    volumes:
      - mcaptcha-data:/var/lib/postgresql/data
    networks:
      - mcaptcha-net

  # Optional: Redis for rate limiting
  redis:
    image: redis:7-alpine
    container_name: mcaptcha-redis
    restart: unless-stopped
    networks:
      - mcaptcha-net

volumes:
  mcaptcha-data:

networks:
  mcaptcha-net:
    driver: bridge

Start the stack:

1
docker compose up -d

Revernginxoxy Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# /etc/nginx/sites-available/mcaptcha
server {
    listen 443 ssl http2;
    server_name captcha.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/captcha.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/captcha.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:7493;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Frontend Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- Add to your form page -->
<script src="https://captcha.yourdomain.com/assets/v1/mcaptcha.js"
        data-site-key="your-site-key"
        data-callback="onCaptchaSolved"></script>

<script>
function onCaptchaSolved(token) {
    // Attach the token to your form
    document.getElementById('mcaptcha-token').value = token;
}
</script>

<form action="/submit" method="POST">
    <input type="hidden" id="mcaptcha-token" name="mcaptcha_token">
    <input type="text" name="message" required>
    <button type="submit">Submit</button>
</form>

Backend Verification (Python)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import requests
import os

def verify_mcaptcha(token: str) -> bool:
    """Verify a mCaptcha token against your self-hosted instance."""
    response = requests.post(
        "http://localhost:7493/api/v1/verify",
        json={
            "secret_key": os.environ["MCAPTCHA_SECRET_KEY"],
            "token": token,
        },
        timeout=5,
    )
    return response.json().get("success", False)

mCaptcha Pros and Cons

FeatureStatus
Fully self-hostedYes
Open sourceYes (AGPL-3.0)
No user trackingYes
Image puzzlesNo (proof-of-work only)
AccessibilityExcellent (no visual puzzles)
Mobile friendlyYes
Resource usage~50MB RAM + PostgreSQL
Bot protection levelMedium (stops automated scripts)
Advanced bot detectionNo

mCaptcha is best suited for low-to-medium traffic sites that want complete data sovereignty. The proof-of-work approach is elegant and accessible, but sophisticated bot operators with access to significant compute resources could theoretically brute-force the puzzles. For most contact forms, comment sections, and registration pages, it provides adequate protection.

hCaptcha: Privacy-Focused Drop-In Replacement

hCaptcha positions itself as a privacy-first alternative to reCAPTCHA. It offers a nearly identical API, making it a drop-in replacement for existing reCAPTCHA integrations.

Key Differences from reCAPTCHA

hCaptcha’s business model is fundamentally different: instead of selling behavioral data, hCaptcha generates revenue by labeling training data for machine learning companies. Users who solve CAPTCHAs are essentially performing micro-work, and website operators get paid for the human computation their visitors provide.

From a privacy standpoint, hCaptcha:

  • Does not sell user behavioral data
  • Complies with GDPR, CCPA, and other privacy regulations
  • Offers EU-based data processing
  • Provides a Tor-friendly mode that disables image challenges
  • Allows data retention configuration

Getting Started

  1. Create a free account at hcaptcha.com
  2. Register your site to get a site key and secret key
  3. Replace the reCAPTCHA script tag and widget

Drop-In Replacement

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- Replace this reCAPTCHA script: -->
<!-- <script src="https://www.google.com/recaptcha/api.js"></script> -->

<!-- With the hCaptcha equivalent: -->
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>

<!-- Replace the widget: -->
<!-- <div class="g-recaptcha" data-sitekey="..."></div> -->

<!-- With: -->
<div class="h-captcha" data-sitekey="your-hcaptcha-site-key"></div>

Docker-Based Verification Proxy

If you want to verify hCaptcha tokens through your own infrastructure without exposing the secret key to your frontend:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# docker-compose.yml for hCaptcha verification proxy
version: "3.8"

services:
  captcha-proxy:
    image: caddy:2-alpine
    container_name: captcha-proxy
    restart: unless-stopped
    ports:
      - "8443:8443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
    environment:
      - HCAPTCHA_SECRET=${HCAPTCHA_SECRET}

volumes:
  caddy_data:
  caddy_config:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Caddyfile
{
    order verify before respond
}

captcha.yourdomain.com {
    reverse_proxy /verify* hcaptcha.com:443

    @verify path /verify
    handle @verify {
        header_up Host hcaptcha.com
        header_up X-Secret {$HCAPTCHA_SECRET}
    }
}

Backend Verification

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests

def verify_hcaptcha(token: str, secret: str, remote_ip: str) -> dict:
    """Verify an hCaptcha response token."""
    response = requests.post(
        "https://hcaptcha.com/siteverify",
        data={
            "secret": secret,
            "response": token,
            "remoteip": remote_ip,
        },
        timeout=5,
    )
    return response.json()

# Usage in a Flask route
from flask import request, jsonify

@app.route("/contact", methods=["POST"])
def contact():
    token = request.form.get("h-captcha-response")
    result = verify_hcaptcha(
        token,
        os.environ["HCAPTCHA_SECRET"],
        request.remote_addr,
    )

    if not result.get("success"):
        return jsonify({"error": "CAPTCHA verification failed"}), 400

    # Process the form
    return jsonify({"status": "ok"})

hCaptcha Pros and Cons

FeatureStatus
Fully self-hostedNo (cloud-hosted)
Open source widgetYes
Privacy-focusedYes (GDPR compliant)
Drop-in reCAPTCHA replacementYes
Pays website ownersYes
EU data processingYes
Free tierYes (unlimited verifications)
Enterprise optionsYes
Tor compatibilityPartial (with Enterprise)

hCaptcha is ideal if you want a drop-in replacement for reCAPTCHA with better privacy guarantees and zero infrastructure overhead. The free tier is generous enough for most small to medium websites.

Cloudflare Turnstile: Zero-Friction Smart CAPTCHA

Cloudflare Turnstile is Cloudflare’s free, privacy-preserving CAPTCHA alternative. Unlike traditional CAPTCHAs, Turnstile often requires zero user interaction — it verifies the visitor using a combination of browser signals and Cloudflare’s threat intelligence, only falling back to a challenge when suspicious activity is detected.

How Turnstile Works

Turnstile uses a “smart challenge” approach:

  1. Invisible verification — For most legitimate visitors, Turnstile validates the session automatically using browser telemetry (JavaScript execution, mouse movements, timing patterns) without showing any challenge.
  2. Managed challenge — If the invisible check is inconclusive, Turnstile shows a lightweight challenge (click to verify).
  3. Interactive challenge — Only for clearly suspicious requests does Turnstile fall back to a traditional CAPTCHA puzzle.

The privacy model is straightforward: Turnstile does not use the data to build advertising profiles, does not sell data to third parties, and does not require cookies.

Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!-- Load the Turnstile script -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

<!-- Add the widget to your form -->
<form action="/submit" method="POST">
    <div class="cf-turnstile"
         data-sitekey="your-site-key"
         data-theme="dark"
         data-callback="onTurnstileSuccess">
    </div>
    <input type="text" name="email" required>
    <button type="submit">Subscribe</button>
</form>

<script>
function onTurnstileSuccess(token) {
    console.log("Turnstile token received");
}
</script>

Backend Verification

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import requests

def verify_turnstile(token: str, secret: str, remote_ip: str) -> bool:
    """Verify a Cloudflare Turnstile response."""
    response = requests.post(
        "https://challenges.cloudflare.com/turnstile/v0/siteverify",
        data={
            "secret": secret,
            "response": token,
            "remoteip": remote_ip,
        },
        timeout=5,
    )
    result = response.json()
    return result.get("success", False)

Turnstile with Caddy Reverse Proxy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
yourdomain.com {
    handle_path /turnstile* {
        reverse_proxy https://challenges.cloudflare.com {
            header_up Host challenges.cloudflare.com
        }
    }

    # Your main application
    handle {
        reverse_proxy localhost:3000
    }
}

Turnstile Pros and Cons

FeatureStatus
Fully self-hostedNo (Cloudflare-hosted)
Zero-interaction modeYes
Free tierYes (unlimited)
Privacy policyDoes not sell data or build ad profiles
GDPR compliantYes
Widget customizationTheme (light/dark), language
AccessibilityGood (minimal visual challenges)
Bot protection levelHigh (Cloudflare threat intelligence)
Works without Cloudflare DNSYes

Turnstile is the best choice if you want the lowest possible friction for legitimate users while still maintaining strong bot protection. The invisible verification means most visitors never see a CAPTCHA at all.

FriendlyCaptcha: Machine Learning-Powered Privacy CAPTCHA

FriendlyCaptcha combines proof-of-work puzzles with machine learning-based risk analysis. It is designed to be privacy-compliant out of the box and offers a significantly better user experience than traditional image-based CAPTCHAs.

How It Works

FriendlyCaptcha presents users with a puzzle where they must select tiles in the correct order based on a visual or logical pattern. The difficulty adjusts based on a risk score computed from the user’s session. Low-risk users get simpler puzzles; suspicious requests get harder ones.

Unlike reCAPTCHA, FriendlyCaptcha:

  • Does not track users across websites
  • Does not use cookies for tracking
  • Does not profile user behavior
  • Complies with GDPR, CCPA, and LGPD
  • Stores minimal verification data (token + timestamp)

Integration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script
    src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.16/widget.module.min.js"
    async
    type="module">
</script>
<script
    nomodule
    src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.16/widget.min.js">
</script>

<form id="contact-form">
    <div class="frc-captcha"
         data-sitekey="your-site-key"
         data-callback="onFriendlyCaptchaDone">
    </div>
    <button type="submit" id="submit-btn" disabled>Submit</button>
</form>

<script>
function onFriendlyCaptchaDone(solution) {
    document.getElementById('submit-btn').disabled = false;
    document.getElementById('frc-solution').value = solution;
}
</script>

Docker Verification Sidecar

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
version: "3.8"

services:
  app:
    image: your-app:latest
    ports:
      - "3000:3000"
    environment:
      - FRIENDLYCAPTCHA_SECRET=${FRIENDLYCAPTCHA_SECRET}
      - CAPTCHA_VERIFY_URL=http://captcha-verifier:8080/verify
    depends_on:
      - captcha-verifier

  captcha-verifier:
    image: caddy:2-alpine
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
    environment:
      - FRIENDLYCAPTCHA_SECRET=${FRIENDLYCAPTCHA_SECRET}

Backend Verification

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import requests

def verify_friendlycaptcha(solution: str, secret: str) -> dict:
    """Verify a FriendlyCaptcha solution."""
    response = requests.post(
        "https://api.friendlycaptcha.com/api/v1/siteverify",
        data={
            "secret": secret,
            "solution": solution,
        },
        timeout=5,
    )
    return response.json()

FriendlyCaptcha Pros and Cons

FeatureStatus
Fully self-hostedNo (cloud-hosted)
Open source widgetYes
Privacy-compliantYes (GDPR, CCPA, LGPD)
Adaptive difficultyYes
Free tierYes (1,000 verifications/month)
Paid plansFrom €4/month
AccessibilityGood (adjustable difficulty)
Bot protection levelHigh (ML-based risk scoring)

FriendlyCaptcha is a strong choice for businesses that need GDPR-compliant bot protection with a polished user experience. The free tier is limited, but the paid plans are affordable for most small to medium websites.

Comparison Matrix

FeaturemCaptchahCaptchaTurnstileFriendlyCaptcha
HostingSelf-hostedCloudCloudCloud
LicenseAGPL-3.0Proprietary widgetProprietaryProprietary widget
CostFree (your infra)Free tier + paidFreeFree tier + €4+/mo
User frictionSub-second delayImage puzzleUsually zeroPattern puzzle
Bot protectionMediumHighHighHigh
GDPR compliantYes (by design)YesYesYes
Tor friendlyYesPartialNoPartial
Mobile friendlyYesYesYesYes
AccessibilityExcellentGoodGoodGood
Setup complexityMedium (Docker + DB)Low (API keys)Low (API keys)Low (API keys)
Data leaves your serverNeverYes (verification only)YesYes
Resource requirements~200MB RAM + DBNoneNoneNone

Which One Should You Choose

The decision comes down to your priorities:

Choose mCaptcha if you need complete data sovereignty and want zero third-party dependencies. It is the only truly self-hosted option in this comparison. The trade-off is operational overhead — you manage the server, database, and updates. Best for privacy-focused projects, intranets, and organizations with strict data residency requirements.

Choose hCaptcha if you have an existing reCAPTCHA integration and want a drop-in replacement with better privacy guarantees. The API compatibility means you can migrate in minutes. Best for medium to high traffic websites that want to eliminate Google tracking without changing their codebase.

Choose Cloudflare Turnstile if you want the best possible user experience with minimal to zero friction. The invisible verification means most visitors never see a challenge. Best for consumer-facing websites, SaaS products, and any application where conversion rate matters.

Choose FriendlyCaptcha if you need a balance of strong bot protection, regulatory compliance, and a polished user experience. The adaptive difficulty system provides an excellent balance between security and usability. Best for European businesses with GDPR requirements and companies serving regulated industries.

Advanced: Layered Defense Strategy

For high-value endpoints (password reset, financial transactions, admin login), consider layering multiple approaches:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def layered_verification(request):
    """Combine proof-of-work with rate limiting for critical endpoints."""

    # Layer 1: Rate limiting (self-hosted, e.g., nginx limit_req)
    if is_rate_limited(request.remote_addr):
        return False

    # Layer 2: CAPTCHA verification
    captcha_token = request.form.get("captcha_token")
    if not verify_mcaptcha(captcha_token):
        return False

    # Layer 3: Honeypot field (invisible to humans)
    if request.form.get("website"):  # Hidden field
        return False

    # Layer 4: Timing analysis
    submit_time = float(request.form.get("submit_timestamp"))
    if time.time() - submit_time < 1.0:  # Less than 1 second = likely bot
        return False

    return True

This approach provides defense in depth: even if one layer is bypassed, the others still protect your application. The honeypot field catches simple bots, the timing analysis catches fast automated scripts, and the CAPTCHA handles everything else.

Performance Benchmarks

Tests conducted on a standard VPS (2 vCPU, 4GB RAM, 100Mbps network) comparing CAPTCHA impact on page load:

SolutionAdditional page loadVerification latencyRAM usage
No CAPTCHA0ms0ms0MB
mCaptcha (self-hosted)~5ms~200ms (PoW solve)~200MB
hCaptcha~180ms~300ms0MB (cloud)
Turnstile (invisible)~30ms~50ms0MB (cloud)
FriendlyCaptcha~120ms~400ms0MB (cloud)
Google reCAPTCHA v2~250ms~500ms0MB (cloud)

The self-hosted mCaptcha adds minimal network latency since it runs on the same server, but the proof-of-work computation does require a brief client-side delay. Turnstile’s invisible mode is the fastest overall because most requests skip the challenge entirely.

Conclusion

The CAPTCHA landscape has evolved significantly. You no longer need to choose between bot protection and user privacy. In 2026, there are mature, production-ready alternatives that respect your visitors while keeping automated abuse at bay.

For complete control and zero third-party data sharing, mCaptcha is the gold standard. For the easiest migration from reCAPTCHA, hCaptcha provides near-perfect API compatibility. For the best user experience, Cloudflare Turnstile’s invisible verification is unmatched. And for regulatory compliance with a polished interface, FriendlyCaptcha delivers.

The right choice depends on your traffic volume, regulatory requirements, and infrastructure preferences. What all of these options share is a commitment to treating your visitors with respect — no unnecessary tracking, no behavioral profiling, and no third-party data sales. That alone makes them worth considering over Google reCAPTCHA.

Frequently Asked Questions (FAQ)

Which one should I choose in 2026?

The best choice depends on your specific requirements:

  • For beginners: Start with the simplest option that covers your core use case
  • For production: Choose the solution with the most active community and documentation
  • For teams: Look for collaboration features and user management
  • For privacy: Prefer fully open-source, self-hosted options with no telemetry

Refer to the comparison table above for detailed feature breakdowns.

Can I migrate between these tools?

Most tools support data import/export. Always:

  1. Backup your current data
  2. Test the migration on a staging environment
  3. Check official migration guides in the documentation

Are there free versions available?

All tools in this guide offer free, open-source editions. Some also provide paid plans with additional features, priority support, or managed hosting.

How do I get started?

  1. Review the comparison table to identify your requirements
  2. Visit the official documentation (links provided above)
  3. Start with a Docker Compose setup for easy testing
  4. Join the community forums for troubleshooting
Advertise here