[]previous and next page

Clerk: sessions, redirects and webhooks

The Clerk helpers sit in front of the router, so the rules about what they let through are part of your deployment.

For agentsUpdated

The Clerk helpers sit in front of the router, so the rules about what they let through are part of your deployment. The Clerk guide has the full list; these are the parts that change what you write or run. They replace the "Clerk route rules" and "Clerk webhooks" lines of the internal audit above.

  • Protect sections with the matcher, not with text. createRouteMatcher compares the spelling the router serves a page under: segments decoded once, ASCII case folded, one trailing slash ignored. /DASHBOARD and /%64ashboard are the dashboard, /tasks%2F7 is not /tasks/7. A rule written as pathname.split("/")[1] === "dashboard" misses the first two. If your router is case-sensitive, pass { caseSensitive: true }. /dashboard/* does not cover /dashboard; write /dashboard(.*). Prefer clerkMiddleware({ protect }), which also checks event.url after a rewrite.
  • A pending session is signed out. A user who has not finished the sign-in tasks Clerk requires has no userId, and requireAuth and protect treat the request as signed out.
  • Handshakes go to Clerk and nowhere else. The redirect is sent only to the frontend API of your publishable key, and only for a return origin in authorizedParties. Set authorizedParties to a list in every environment, and make your proxy set X-Forwarded-Host and X-Forwarded-Proto itself instead of passing the client's. The development-instance redirect to a __clerk_redirect_url of the request's choosing, which carried the dev-browser token, is refused.
  • authorizedParties: [] is an error. Use a list, or false to accept any application of the instance.
  • Set jwtKey. Without it a request with a made-up kid makes the server call Clerk before the signature is checked. There is a limit (4 unknown kids per minute), but jwtKey removes the call. Rate-limit requests that carry __clerk_handshake_nonce at the edge: each one costs a call to Clerk.
  • Webhooks. verifyWebhook signs the raw bytes, reads at most 16 signatures, throws on a toleranceSeconds or maxBodyBytes that is not a finite number, and drops __proto__ and constructor.prototype keys from the event. A request that verified verifies again inside the tolerance (300 seconds): deduplicate by svix-id.
  • The browser side. HandleSSOCallback with its default navigation follows only destinations on the page's origin; clerkJSUrl and clerkUIUrl must be https (http for localhost); the publishable key and those URLs are configuration, never request data.