# Scoping Clerk auth in Fastify
`fastify.register(clerkPlugin)` at the top level applies auth state attachment to every route in that context, including `/health`, `/metrics`, and webhook endpoints that must stay public.
## The pattern
Use Fastify's encapsulation: register the plugin inside a scoped context for the routes that need auth, and leave public routes outside it.
```ts
// public: no auth
fastify.post('/api/webhooks', webhookHandler)
fastify.get('/health', async () => ({ ok: true }))
// authenticated area
fastify.register(async (instance) => {
instance.register(clerkPlugin)
instance.get('/protected', async (request, reply) => {
const { isAuthenticated, userId } = getAuth(request)
if (!isAuthenticated) {
return reply.code(401).send({ error: 'User not authenticated' })
}
// ...
})
})
```
## Why it matters
- Webhook routes must never require auth state: incoming webhooks come from Clerk's servers, not from a signed-in user, and they carry no session. Gating them breaks delivery and triggers retries.
- Health and readiness probes from your platform get no session either; auth overhead on them is pure latency.
- `getAuth()` outside a plugin-registered context has no auth state to read. If `isAuthenticated` is mysteriously always false on a route, check that the route lives inside the context where `clerkPlugin` was registered.
## Checklist
- Decide per route group: public, session-authenticated, or machine-authenticated, and register accordingly.
- Webhook verification (Svix) is its own check, separate from session auth; do not mix the two on one route.
- Keep the raw-body content-type parser (needed for webhook signature verification) on the webhook route only, not global.