Repository navigation
Self-host on Cloudflare's free plan: no server or domain needed - #3588
Merged
Merged
Conversation
A Worker on workers.dev serves the widget and forwards the provider API through a Workers VPC binding to a Cloudflare Tunnel, so a provider needs no domain, public IP or certificate. Adds the Worker template with a demo page, a compose add-on that runs cloudflared without Caddy, and generateSelfHostedEnv.ts --cloudflare. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
forgetso
enabled auto-merge (squash)
October 22, 2026 14:14
forgetso
disabled auto-merge
October 22, 2026 14:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
You can now self-host Procaptcha with no server, no domain and no open ports, just a free Cloudflare account and any machine that can run Docker.
How it works: the provider runs in Docker on your machine, connected to Cloudflare through a tunnel that only ever connects outwards. A Worker on your free
workers.devaddress serves the widget and passes the widget's API calls through that tunnel to the provider. Cloudflare handles HTTPS, so Caddy isn't needed.New files:
docker/cloudflare/worker.ts+wrangler.jsonc: the Worker. It also serves a demo page at/, which shows whether the provider is reachable, renders the CAPTCHA and shows the response token with a copy button. It supports light and dark mode.docker-compose.self-hosted.cloudflare.yml: addscloudflaredto the stack and leaves Caddy out.generateSelfHostedEnv.ts --cloudflare: makes.envload both compose files and adds aCLOUDFLARE_TUNNEL_TOKENline to fill in.SELF_HOSTING.md: a new section, "No server? Run it on Cloudflare's free plan". The matching docs page is in Self-Hosting: work from a fresh clone, and add a Cloudflare free-plan page docs#123.One detail that matters: the Worker passes each visitor's real IP address to the provider. Without it, the provider sees every visitor as the tunnel, so per-IP rate limits and rules would apply to everyone at once. I saw this happen with a plain tunnel: every session recorded the tunnel's container address.
Effect
Self-hosting no longer needs a server with a public IP, or a domain. Nothing changes for anyone using the existing guide.
Test coverage
Tested by running it, from a fresh clone of this branch, against real Cloudflare: a tunnel created with wrangler, a VPC service, the Worker on workers.dev, and the published 3.8.38 provider image (no custom build).
docker compose up -dstarted Mongo, Redis, the provider andcloudflared, but not Caddy. The tunnel registered 4 connections./healthzon the Worker reached the provider.@prosopo/serververified it (verified: true).strict. Lint is clean.The one step I couldn't do by hand was copying the tunnel token from the Cloudflare dashboard. I fetched the same token through the API.
🤖 Generated with Claude Code