Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 86 additions & 59 deletions docs/guides/dashboard/dns-domains/proxy-fapi.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,91 @@ When using a proxy, all requests to the Frontend API will be made through your d
- `Clerk-Secret-Key`: Your Clerk [Secret Key](!secret-key).
- `X-Forwarded-For`: The IP address of the original client making the request.

> [!NOTE]
> If you're using **Next.js** or **Express**, you can use the built-in `frontendApiProxy` option in [`clerkMiddleware()`](/docs/reference/nextjs/clerk-middleware#frontend-api-proxy) instead of manually configuring a proxy server. This handles all the proxy requirements automatically. See the **Next.js (clerkMiddleware)** and **Express (clerkMiddleware)** tabs below.

#### Example configuration

<Tabs items={["Nginx", "Cloudflare Workers", "Next.js"]}>
<Tabs items={["Next.js (clerkMiddleware)", "Express (clerkMiddleware)", "Next.js (App Router route handler)", "Nginx", "Cloudflare Workers"]}>
<Tab>
The `frontendApiProxy` option in `clerkMiddleware()` handles all proxy requirements automatically, including header forwarding, body streaming, and redirect rewriting. The `proxyUrl` for authentication handshake is also auto-derived.

> [!IMPORTANT]
> Ensure your middleware matcher includes the proxy path (default `__clerk`) so proxy requests are handled by the middleware.

```ts {{ filename: 'proxy.ts' }}
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware({
frontendApiProxy: {
enabled: true,
},
})

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 and the proxy path
'/(api|trpc|__clerk)(.*)',
],
}
```

For multi-domain setups, you can pass a function to `enabled` to conditionally enable proxying based on the request URL:

```ts {{ filename: 'proxy.ts' }}
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware({
frontendApiProxy: {
enabled: (url) => url.hostname !== 'myapp.com',
},
})

export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc|__clerk)(.*)',
],
}
```

See the [`clerkMiddleware()` reference](/docs/reference/nextjs/clerk-middleware#frontend-api-proxy) for all available options.
</Tab>

<Tab>
The `frontendApiProxy` option in `clerkMiddleware()` handles all proxy requirements automatically for Express applications.

```ts {{ filename: 'index.ts' }}
import express from 'express'
import { clerkMiddleware } from '@clerk/express'

const app = express()

app.use(clerkMiddleware({
frontendApiProxy: {
enabled: true,
},
}))
```

See the [`clerkMiddleware()` reference](/docs/reference/express/clerk-middleware#frontend-api-proxy) for all available options.
</Tab>

<Tab>
As an alternative to the middleware approach, you can use `createFrontendApiProxyHandlers()` in a Next.js App Router route handler:

```ts {{ filename: 'app/api/__clerk/[[...path]]/route.ts' }}
import { createFrontendApiProxyHandlers } from '@clerk/nextjs/server'

export const { GET, POST, PUT, DELETE, PATCH } = createFrontendApiProxyHandlers()
```

> [!NOTE]
> When using route handlers instead of middleware, you must still set the `proxyUrl` option on `clerkMiddleware()` or set the `NEXT_PUBLIC_CLERK_PROXY_URL` environment variable so that the authentication handshake uses the proxy.
</Tab>

<Tab>
```nginx {{ filename: 'nginx.conf' }}
http {
Expand Down Expand Up @@ -105,64 +187,6 @@ When using a proxy, all requests to the Frontend API will be made through your d
```
</CodeBlockTabs>
</Tab>

<Tab>
<Include src="_partials/nextjs/nextjs-15-callout" />

```ts {{ filename: 'proxy.ts' }}
import { NextResponse } from 'next/server'
import { clerkMiddleware } from '@clerk/nextjs/server'

function proxyMiddleware(req) {
if (req.nextUrl.pathname.match('__clerk')) {
const proxyHeaders = new Headers(req.headers)
proxyHeaders.set('Clerk-Proxy-Url', process.env.NEXT_PUBLIC_CLERK_PROXY_URL || '')
proxyHeaders.set('Clerk-Secret-Key', process.env.CLERK_SECRET_KEY || '')
if (req.ip) {
proxyHeaders.set('X-Forwarded-For', req.ip)
} else {
proxyHeaders.set('X-Forwarded-For', req.headers.get('X-Forwarded-For') || '')
}

const proxyUrl = new URL(req.url)
proxyUrl.host = 'frontend-api.clerk.dev'
proxyUrl.port = '443'
proxyUrl.protocol = 'https'
proxyUrl.pathname = proxyUrl.pathname.replace('/__clerk', '')

return NextResponse.rewrite(proxyUrl, {
request: {
headers: proxyHeaders,
},
})
}

return null
}

const clerkHandler = clerkMiddleware()

export default function middleware(req) {
// First check if it's a proxy request
const proxyResponse = proxyMiddleware(req)
if (proxyResponse) {
return proxyResponse
}

// Otherwise, use Clerk's middleware
return clerkHandler(req)
}

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 AND anything passed through the proxy
'/(api|trpc|__clerk)(.*)',
],
}
```
</Tab>
</Tabs>

> [!NOTE]
Expand Down Expand Up @@ -197,6 +221,9 @@ When using a proxy, all requests to the Frontend API will be made through your d

### Configure your proxy setup

> [!NOTE]
> If you used the `frontendApiProxy` option in `clerkMiddleware()` (Next.js or Express), the server-side `proxyUrl` is auto-derived from the proxy configuration. You only need to set the client-side environment variable (e.g., `NEXT_PUBLIC_CLERK_PROXY_URL`) so that ClerkJS in the browser routes requests through the proxy.

You can configure your proxy setup by either:

- Setting environment variables
Expand Down
54 changes: 54 additions & 0 deletions docs/reference/express/clerk-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,60 @@ app.listen(PORT, () => {
- `boolean`

A flag to enable Clerk's handshake flow, which helps verify the session state when a session JWT has expired. It issues a `307` redirect to refresh the session JWT if the user is still logged in. Defaults to `true`.

---

- `frontendApiProxy?`
- [`FrontendApiProxyOptions`](#frontend-api-proxy)

Configure Frontend API proxy handling. When enabled, requests to the proxy path are forwarded to Clerk's Frontend API, and the `proxyUrl` is automatically derived for authentication handshake.
</Properties>

### Frontend API proxy

The `frontendApiProxy` option enables built-in proxying of Clerk Frontend API requests through your Express application. This is useful when direct communication with Clerk's API is blocked or needs to go through your application server.

When enabled, requests matching the proxy path (default: `/__clerk`) are intercepted and forwarded to Clerk's Frontend API before authentication runs. The `proxyUrl` for authentication handshake is automatically derived.

> [!NOTE]
> You must also [enable proxying](/docs/guides/dashboard/dns-domains/proxy-fapi#enable-proxying) in the Clerk Dashboard and configure the client-side proxy URL so that ClerkJS in the browser routes requests through the proxy.

```js
import { clerkMiddleware } from '@clerk/express'
import express from 'express'

const app = express()

// Enable proxy with defaults (path: '/__clerk')
app.use(clerkMiddleware({
frontendApiProxy: {
enabled: true,
},
}))

// Or with a custom proxy path
app.use(clerkMiddleware({
frontendApiProxy: {
enabled: true,
path: '/custom-clerk-proxy',
},
}))
```

#### `FrontendApiProxyOptions`

<Properties>
- `enabled`
- `boolean`

Enable Frontend API proxy handling. When `true`, requests to the proxy path are proxied to Clerk's Frontend API and the `proxyUrl` is auto-derived for authentication handshake.

---

- `path?`
- `string`

The path prefix for proxy requests. Defaults to `'/__clerk'`. Must be unique and not conflict with other routes in your application.
</Properties>

#### `OrganizationSyncOptions`
Expand Down
122 changes: 122 additions & 0 deletions docs/reference/nextjs/clerk-middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,128 @@ export const config = {
}
```

## Frontend API proxy

The `frontendApiProxy` option enables built-in proxying of Clerk Frontend API requests through your application. This is useful when direct communication with Clerk's API is blocked or needs to go through your application server.

When enabled, requests matching the proxy path (default: `/__clerk`) are intercepted by the middleware and forwarded to Clerk's Frontend API. The `proxyUrl` for authentication handshake is automatically derived from the proxy configuration.

> [!NOTE]
> You must also [enable proxying](/docs/guides/dashboard/dns-domains/proxy-fapi#enable-proxying) in the Clerk Dashboard and set the client-side `NEXT_PUBLIC_CLERK_PROXY_URL` environment variable so that ClerkJS in the browser routes requests through the proxy.

### Basic usage

> [!IMPORTANT]
> Ensure your middleware matcher includes the proxy path (default `__clerk`) so proxy requests are handled by the middleware.

```ts {{ filename: 'proxy.ts' }}
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware({
frontendApiProxy: {
enabled: true,
},
})

export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc|__clerk)(.*)',
],
}
```

### Multi-domain support

For applications that serve multiple domains (e.g., preview deployments, staging environments), you can pass a function to `enabled` that receives the request URL and returns a boolean:

```ts {{ filename: 'proxy.ts' }}
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware({
frontendApiProxy: {
// Only proxy on non-production domains
enabled: (url) => url.hostname !== 'myapp.com',
},
})

export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc|__clerk)(.*)',
],
}
```

### Custom proxy path

```ts {{ filename: 'proxy.ts' }}
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware({
frontendApiProxy: {
enabled: true,
path: '/custom-clerk-proxy',
},
})

export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc|custom-clerk-proxy)(.*)',
],
}
```

### `frontendApiProxy` properties

<Properties>
- `enabled`
- `boolean | ((url: URL) => boolean)`

Enable proxy handling. Can be `true` to enable for all requests, `false` to disable, or a function that receives the request URL and returns a boolean to conditionally enable proxying.

---

- `path?`
- `string`

The path prefix for proxy requests. Defaults to `'/__clerk'`. Must be unique and not conflict with other routes in your application.
</Properties>

### App Router route handlers

As an alternative to handling the proxy in middleware, you can use route handlers in the Next.js App Router. This is useful if you prefer to keep proxy logic separate from your middleware.

#### `createFrontendApiProxyHandlers()`

Returns an object with `GET`, `POST`, `PUT`, `DELETE`, and `PATCH` handlers that can be directly exported from a route file.

```ts {{ filename: 'app/api/__clerk/[[...path]]/route.ts' }}
import { createFrontendApiProxyHandlers } from '@clerk/nextjs/server'

export const { GET, POST, PUT, DELETE, PATCH } = createFrontendApiProxyHandlers()
```

> [!NOTE]
> When using route handlers instead of the `frontendApiProxy` middleware option, the `proxyUrl` is **not** auto-derived. You must set the `proxyUrl` option on `clerkMiddleware()` or set the `NEXT_PUBLIC_CLERK_PROXY_URL` environment variable for the authentication handshake to work correctly.

#### `clerkFrontendApiProxy()`

For more control, you can use `clerkFrontendApiProxy()` directly in individual route handlers:

```ts {{ filename: 'app/api/__clerk/[[...path]]/route.ts' }}
import { clerkFrontendApiProxy } from '@clerk/nextjs/server'

export async function GET(request: Request) {
return clerkFrontendApiProxy(request)
}

export async function POST(request: Request) {
return clerkFrontendApiProxy(request)
}
```

## `clerkMiddleware()` options

<Include src="_partials/clerk-middleware-options" />
Expand Down