āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/clerk/clerk-docs/guides/dashboard/dns-domains/proxy-fapi ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
[!WARNING] This guide is for users who need to proxy the Frontend API for production (proxying does not work for Clerk development instances). If your application already uses a CNAME subdomain that is required for deploying with Clerk, then you must proxy the Frontend API using a different subdomain. Refer to the deployment guide on how to configure DNS records for deployment.
Clerk supports two configuration methods for connecting to the Clerk Frontend API: CNAME and Proxy.
The recommended way to connect to the Clerk Frontend API is to set up CNAME records and use DNS. However, if you're unable to use this approach, or would like more control over your integration with Clerk, you can use a proxy.
When using a proxy, all requests to the Frontend API will be made through your domain. This allows you to use your own SSL certificate, and gives you more control over how you configure your application.
To get started, you need to create an application from the Clerk Dashboard. Once you create an instance via the Clerk Dashboard, you will be prompted to choose a domain. For the purposes of this guide, the domain will be app.dev.
[!NOTE] For more information on creating a Clerk application, see the setup guide.
For this example, /__clerk is used as the path for the proxy. Your proxy server must be on the same domain as your application.
You can choose any path you'd like, but it must be unique and not conflict with any other routes in your application.
Requests to https://app.dev/__clerk/* must be forwarded to https://frontend-api.clerk.dev/* with the body and all headers intact.
Three additional headers must be set
Clerk-Proxy-Url: Needs to have the full proxy URL.Clerk-Secret-Key: The Secret Key for your Clerk instance.X-Forwarded-For: The IP address of the original client making the request.<Tabs items={["Nginx", "Cloudflare Workers", "Next.js"]}>
<Tab>
nginx {{ filename: 'nginx.conf' }} http { # ... server { # ... location /__clerk/ { rewrite ^/__clerk/(.*)$ /$1 break; proxy_pass https://frontend-api.clerk.dev; proxy_set_header Clerk-Proxy-Url https://app.dev/__clerk; proxy_set_header Clerk-Secret-Key sk_live_***; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; } } }
</Tab>
<Tab>
<CodeBlockTabs options={["Handler", "Wrangler Config", "Environment", "Types"]}>
```ts {{ filename: 'src/index.ts' }}
export default {
async fetch(req: Request, env: Env, _ctx: ExecutionContext): Promise<Response> {
const url = req.url.replace(env.CLERK_PROXY_URL, env.CLERK_FAPI)
const proxyReq = new Request(req, {
redirect: 'manual',
})
proxyReq.headers.set('Clerk-Proxy-Url', env.CLERK_PROXY_URL)
proxyReq.headers.set('Clerk-Secret-Key', env.CLERK_SECRET_KEY)
proxyReq.headers.set('X-Forwarded-For', req.headers.get('CF-Connecting-IP') || '')
return fetch(url, proxyReq)
},
}
```
```toml {{ filename: 'wrangler.toml' }}
name = "cloudflare-proxy"
main = "src/index.ts"
compatibility_date = "2023-10-02"
[vars]
CLERK_FAPI="https://frontend-api.clerk.dev"
```
```env {{ filename: '.dev.vars' }}
# Do not commit this file to source control
CLERK_PROXY_URL="https://app.dev/__clerk"
CLERK_SECRET_KEY="sk_live_xxxxx"
```
```ts {{ filename: 'worker-configuration.d.ts' }}
interface Env {
CLERK_FAPI: string
CLERK_PROXY_URL: string
CLERK_SECRET_KEY: string
}
```
</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] Every proxy configuration will be different and we're here to help. Contact support{{ target: '_blank' }} if there's a specific use-case you're looking to solve.
In order to enable proxying, you need to set a proxy URL for your Clerk instance's domain. This can be done through the Clerk Dashboard or through the Backend API.
[!NOTE] To avoid downtime, your proxy must be set up according to the above configuration before it can be enabled for your instance. Make sure your proxy forwards requests to the Clerk Frontend API correctly and includes the required headers.
<Tabs items={["Dashboard", "Backend API"]}> <Tab> 1. In the Clerk Dashboard, navigate to the Domains page. 1. In the Frontend API section, select the Advanced dropdown. 1. In the Proxy URL field, enter your proxy URL. The proxy URL must be a valid URL and resolve correctly. </Tab>
<Tab>
The request below will update the domain to use the proxy URL `https://app.dev/__clerk`. In doing so, it will trigger checks to validate the proxy URL.
```bash
curl -X PATCH https://api.clerk.com/v1/domains/{{DOMAIN ID}} \
-H "Authorization: Bearer {{SECRET KEY}}" \
-H "Content-Type: application/json" \
-d '{"proxy_url": "https://app.dev/__clerk"}}'
```
</Tab>
</Tabs>
You can configure your proxy setup by either:
To configure your proxy setup using environment variables, your .env file should look like this:
<Tabs items={["Next.js", "Remix", "JavaScript"]}> <Tab> ```env {{ filename: '.env' }} NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY={{pub_key}} CLERK_SECRET_KEY={{secret}}
NEXT_PUBLIC_CLERK_PROXY_URL=https://app.dev/__clerk/
```
</Tab>
<Tab>
```env {{ filename: '.env' }}
CLERK_PUBLISHABLE_KEY={{pub_key}}
CLERK_SECRET_KEY={{secret}}
CLERK_PROXY_URL=https://app.dev/__clerk
```
</Tab>
<Tab>
You will only need to set environment variables in your JavaScript application if you are using a bundler (the `NPM module` method for ClerkJS installation). If you are using the `<script>` method, configure your proxy setup using [properties in your application](#properties-in-your-application) instead.
```env {{ filename: '.env' }}
CLERK_PUBLISHABLE_KEY={{pub_key}}
CLERK_SECRET_KEY={{secret}}
CLERK_PROXY_URL=https://app.dev/__clerk
```
</Tab>
</Tabs>
<Tabs items={["Next.js", "Remix", "JavaScript"]}>
<Tab>
To configure your proxy setup using properties in your Next.js application, set the proxyUrl property on the <ClerkProvider> component.
```tsx {{ filename: 'app/layout.tsx', mark: [5] }}
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider proxyUrl="https://app.dev/__clerk/">
<html lang="en">
<body>{children}</body>
</html>
</ClerkProvider>
)
}
```
</Tab>
<Tab>
To configure your proxy setup using properties in your Remix application, set the `proxyUrl` property on the [`ClerkApp`](/docs/reference/remix/clerk-app) wrapper.
```tsx {{ filename: 'root.tsx' }}
export const loader = (args) => {
return rootAuthLoader(
args,
({ req }) => {
const { userId, sessionId, getToken } = req.auth
return json({
message: `Hello from the root loader :)`,
ENV: getBrowserEnvironment(),
})
},
{
loadUser: true,
proxyUrl: 'https://app.dev/__clerk',
} as const,
)
}
export default ClerkApp(App, {
proxyUrl: 'https://app.dev/__clerk',
})
```
</Tab>
<Tab>
To configure your proxy setup using properties in your JavaScript application, pass the `proxyUrl` option to the [`load()`](/docs/reference/javascript/clerk#load) method.
```js {{ filename: 'main.js' }}
import { Clerk } from '@clerk/clerk-js'
// Initialize Clerk with your Clerk Publishable Key
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const clerk = new Clerk(clerkPubKey, {
proxyUrl: 'https://app.dev/__clerk',
})
await clerk.load()
```
</Tab>
</Tabs>
Your application should now be able to access the Frontend API from your proxy! </Steps>
If you have any questions about proxying, or you're having any trouble setting this up, contact support@clerk.com.
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā