Basic geo blocking
While Clerk does not provide a geo blocking feature, deployment platforms, such as Vercel and Render, expose geolocation data through request headers. This allows you to implement custom geo blocking logic by accessing the client's location data at runtime and conditionally allowing or blocking requests based on your requirements.
The following example shows how to implement geo blocking in your clerkMiddleware() using Vercel's geolocation() function. If a user visits any route that is not a /block route, the middleware checks the client's country and redirects to the /block route if the country is not allowed.
import { clerkMiddleware } from '@clerk/nextjs/server'
import { geolocation } from '@vercel/functions'
import { NextResponse } from 'next/server'
// Define the countries you want to allow
const allowedCountries = ['US']
export default clerkMiddleware(async (auth, req) => {
const { pathname } = req.nextUrl
// Don't run on the `/block` route to avoid a redirect loop
if (pathname === '/block' || pathname.startsWith('/block/')) {
return
}
// Use Vercel's `geolocation()` function to get the client's country
const { country } = geolocation(req)
// Redirect if the client's country is not allowed
if (country && !allowedCountries.includes(country)) {
return NextResponse.redirect(new URL('/block', req.url))
}
})
export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
// Always run for API routes
'/(api|trpc)(.*)',
// Always run for Clerk-specific frontend API routes
'/__clerk/(.*)',
],
}The following example shows how to implement geo blocking in your clerkMiddleware()cf-ipcountry header. If a user visits any route that is not a /block route, the middleware checks the client's country and redirects to the /block route if the country is not allowed.
import { clerkMiddleware } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
const allowedCountries = ['US']
export default clerkMiddleware(async (auth, req) => {
const { pathname } = req.nextUrl
// Don't run on the `/block` route to avoid a redirect loop
if (pathname === '/block' || pathname.startsWith('/block/')) {
return
}
// Use Render's `cf-ipcountry` header to get the client's country
const country = req.headers.get('cf-ipcountry')
// Redirect if the client's country is not allowed
if (country && !allowedCountries.includes(country)) {
return NextResponse.redirect(new URL('/block', req.url))
}
})
export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
// Always run for API routes
'/(api|trpc)(.*)',
// Always run for Clerk-specific frontend API routes
'/__clerk/(.*)',
],
}Feedback
Last updated on