6.1 KiB
Turnstile Configuration Guide
Cloudflare Turnstile is optional and can be completely disabled for development or self-hosted deployments.
Quick Config
Disable Turnstile (Recommended for Development)
PUBLIC_ENABLE_TURNSTILE=false
What happens:
- ✅ No Turnstile widget loads
- ✅ No captcha required to log in
- ✅ Login buttons work immediately
- ✅ OAuth URLs don't include
tokenparameter - ✅ No external script loaded from Cloudflare
- ✅ Completely works offline
Perfect for:
- Local development
- Self-hosted deployments
- Testing without internet
Enable Turnstile (Production)
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAABpqJe8FO0N84q0F
What happens:
- 🔒 Turnstile widget loads on login page
- 🔒 User must complete captcha before login
- 🔒 OAuth URLs include
?token=...parameter - 🔒 Script loaded from
challenges.cloudflare.com
Perfect for:
- Production deployments
- Bot protection
- Public-facing instances
How It Works
Frontend Behavior
When ENABLE_TURNSTILE=false:
// Turnstile.svelte
if (!ENABLE_TURNSTILE) {
captcha.set('turnstile-disabled'); // Dummy token
}
// LoginForm.svelte
function getOAuthUrl(provider) {
let url = `${API_URL}/auth/${provider}`;
// No token parameter added
return url;
}
When ENABLE_TURNSTILE=true:
// Turnstile.svelte loads script
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" />
// LoginForm.svelte
function getOAuthUrl(provider) {
let url = `${API_URL}/auth/${provider}?token=${captchaToken}`;
return url;
}
Backend Requirements
Your backend should handle both cases:
// Example: src/routes/auth.ts
app.post("/auth/google", async (req, res) => {
const turnstileToken = req.query.token;
// If Turnstile is enabled in your backend, validate token
if (process.env.ENABLE_TURNSTILE === 'true') {
if (!turnstileToken) {
return res.status(400).json({ error: "Turnstile token required" });
}
// Validate with Cloudflare API
const isValid = await validateTurnstileToken(turnstileToken);
if (!isValid) {
return res.status(403).json({ error: "Invalid captcha" });
}
}
// Continue with OAuth flow...
});
Getting a Turnstile Site Key
If you want to enable Turnstile:
- Go to https://dash.cloudflare.com
- Select "Turnstile" from the sidebar
- Create a new site
- Copy the Site Key (starts with
0x4) - Add to
.env:PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAXXXXXXXXXXXXXXXX
Note: The example key 0x4AAAAAABpqJe8FO0N84q0F is from the original compiled frontend and may not work for you. Get your own key.
Build-Time vs Runtime
Important: Turnstile config is build-time only. It's embedded into the JavaScript bundle.
To change Turnstile settings:
- Update
.env - Rebuild:
pnpm build - Restart backend
You cannot change it at runtime without rebuilding.
Security Considerations
With Turnstile Disabled
Pros:
- Simpler development
- Works offline
- No external dependencies
- No privacy concerns
Cons:
- No bot protection on login
- Vulnerable to automated account creation
- Anyone can spam OAuth endpoints
Recommendation: Fine for development and private deployments. Not recommended for public production.
With Turnstile Enabled
Pros:
- Bot protection
- Rate limiting
- Industry standard (Cloudflare)
Cons:
- Requires internet connection
- External dependency
- Cloudflare can track users (privacy concern)
- Adds friction to login flow
Recommendation: Good for public production deployments with high traffic.
Environment Variable Reference
| Variable | Type | Default | Description |
|---|---|---|---|
PUBLIC_ENABLE_TURNSTILE |
'true' | 'false' |
'false' |
Enable/disable Turnstile |
PUBLIC_TURNSTILE_SITE_KEY |
string | '0x4AAAAAABpqJe8FO0N84q0F' |
Cloudflare site key |
Note: Must be strings 'true' or 'false', not booleans.
Testing
Test with Turnstile Disabled
# .env
PUBLIC_ENABLE_TURNSTILE=false
# Build and test
pnpm build
cd .. && pnpm start
# Open http://localhost:3000/join
# Should see login buttons immediately clickable
Test with Turnstile Enabled
# .env
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=your_key_here
# Build and test
pnpm build
cd .. && pnpm start
# Open http://localhost:3000/join
# Should see Turnstile widget before buttons work
Troubleshooting
"Turnstile failed to load"
- Check
PUBLIC_TURNSTILE_SITE_KEYis valid - Ensure internet connection
- Check browser console for errors
- Verify CSP allows
challenges.cloudflare.com
"Login buttons don't work"
- If Turnstile is enabled, complete captcha first
- If disabled, check that
captchastore has value - Check browser console for errors
"Token parameter missing"
- Set
PUBLIC_ENABLE_TURNSTILE=falseif you don't want Turnstile - Rebuild after changing
.env
CSP Configuration
If you enable Turnstile, your CSP must allow:
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://challenges.cloudflare.com blob:" />
This is already configured in src/app.html.
Alternative: reCAPTCHA
Want to use Google reCAPTCHA instead? You'll need to:
- Create a new
Recaptcha.sveltecomponent - Update
LoginForm.svelteto use it - Load reCAPTCHA script instead of Turnstile
- Update backend to validate reCAPTCHA tokens
The architecture supports swapping captcha providers easily.
Summary
For Development:
PUBLIC_ENABLE_TURNSTILE=false
→ No captcha, login works immediately
For Production (with bot protection):
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=your_cloudflare_key
→ Captcha required before login
For Production (without bot protection):
PUBLIC_ENABLE_TURNSTILE=false
→ Same as development, but publicly accessible
Choose based on your needs!