nest-cloudflare-turnstile checks Cloudflare Turnstile tokens in a NestJS guard. Add one decorator and requests without a valid token never reach your code.
Sign in
1import { Controller, Post } from '@nestjs/common';2import { TurnstileCaptcha } from 'nest-cloudflare-turnstile';3 4@Controller('auth')5export class AuthController {6 @Post('login')7 @TurnstileCaptcha()8 login() {9 // Runs only after Cloudflare accepted the token.10 }11}[Nest] 4821 - LOG [NestApplication] Nest application successfully started
Put @TurnstileCaptcha() on a route or a controller. Or guard every route and mark the exceptions with @SkipTurnstile().
Express and Fastify, GraphQL resolvers on Apollo or Mercurius, and WebSocket gateways, on NestJS 10, 11 and 12.
Verifies the hostname and action, retries a failed call once with an idempotency key, and keeps the secret key out of every error.
Turnstile runs a challenge in the browser and hands out a token. Your server asks Cloudflare whether that token is valid. This package does the server half.
Turnstile runs in the browser and puts a token into your form.
<div class="cf-turnstile"
data-sitekey="YOUR_SITE_KEY"></div>Import it once with the secret key from the Cloudflare dashboard.
TurnstileModule.forRoot({
secretKey: process.env.TURNSTILE_SECRET_KEY,
})Requests without a valid token get a 400 and never reach the handler.
@Post('login')
@TurnstileCaptcha()
login() {}Hooks for metrics, logs you can leave on, and checks that catch setup mistakes before your users do.
@Controller('newsletter')
@TurnstileCaptcha({ widget: 'newsletter' })
export class NewsletterController {}Give a form its own widget and secret key, then name it on the routes that accept its tokens.
Learn moreDEBUG[TurnstileModule] siteverify success: 1 attempt, 92 ms
DEBUG[TurnstileModule] siteverify rejected [timeout-or-duplicate]: 1 attempt, 84 ms
DEBUG[TurnstileModule] siteverify success for widget "newsletter": 2 attempts, 311 ms
onVerify reports every check with its outcome and duration. Debug logs never contain tokens or secrets.
Learn moreERROR[ExceptionHandler] Cloudflare refused the TurnstileModule secretKey [invalid-input-secret]. Check the key in the Cloudflare dashboard.
With verifySecretOnStartup, the app checks each secret key with Cloudflare before it accepts requests.
Learn moreTest.createTestingModule({ imports: [AppModule] })
.overrideProvider(TurnstileService)
.useValue(createTurnstileServiceStub('fail'))Turn the guard off in tests, or swap in a stub that passes, rejects or spends every token without a network call.
Learn moreOne guard for every transport Nest supports, and examples for the frontend frameworks people use most.
Free and open source under the MIT License. The guide goes from install to your first protected route.