Skip to main content
Docs

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. If that route is protected, the middleware also checks if the user is authenticated.

middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { geolocation } from '@vercel/functions'
import { NextResponse } from 'next/server'

// Define your protected and blocked routes
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])
const isBlockRoute = createRouteMatcher(['/block(.*)'])

// Define the countries you want to allow
const allowedCountries = ['US']

export default clerkMiddleware(async (auth, req) => {
  if (isBlockRoute(req)) {
    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))
  }

  // Protect routes based on authentication status
  if (isProtectedRoute(req)) await auth.protect()
})

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)(.*)',
  ],
}

The following example shows how to implement geo blocking in your clerkMiddleware() using Render's 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. If that route is protected, the middleware also checks if the user is authenticated.

middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)'])
const isBlockRoute = createRouteMatcher(['/block(.*)'])

const allowedCountries = ['US']

export default clerkMiddleware(async (auth, req) => {
  if (isBlockRoute(req)) {
    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))
  }

  // Protect routes based on authentication status
  if (isProtectedRoute(req)) await auth.protect()
})

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)(.*)',
  ],
}

Feedback

What did you think of this content?

Last updated on