You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在集成Clerk Auth的NextJS 14中使用动态子域名

解决Next.js 14 App Router中Clerk Auth与动态子域名共存的问题

要同时实现Clerk认证和动态子域名路由,你需要自定义中间件函数,将子域名处理逻辑与Clerk的中间件逻辑结合,而不是直接导出Clerk的默认中间件。以下是具体实现步骤:

1. 修改根目录的middleware.ts

替换原代码为以下内容,我们会先解析子域名并重写路径,再交由Clerk处理认证:

import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import { NextResponse, type NextRequest } from "next/server";

// 定义需要跳过子域名处理的路径(比如Clerk的认证路由)
const isClerkRoute = createRouteMatcher([
  "/sign-in(.*)",
  "/sign-up(.*)",
  "/clerk(.*)",
]);

export default async function middleware(request: NextRequest) {
  // 1. 处理动态子域名
  const host = request.headers.get("host");
  if (!host) return NextResponse.next();

  // 配置你的主域名(生产环境)和本地开发域名
  const mainDomain = "mysite.app";
  const localDomain = "localhost:3000";
  let subdomain: string | null = null;

  // 解析子域名
  if (host.includes(mainDomain)) {
    subdomain = host.split(".")[0];
    // 排除主域名本身(比如mysite.app)
    if (subdomain === mainDomain.split(".")[0]) subdomain = null;
  } else if (host.includes(localDomain)) {
    // 本地开发环境,比如thomas-bistro.localhost:3000
    subdomain = host.split(".")[0];
  }

  // 如果存在有效子域名,重写路径为动态路由格式
  if (subdomain && !isClerkRoute(request)) {
    // 重写路径:将 /xxx 转为 /org/[subdomain]/xxx
    const newUrl = new URL(`/org/${subdomain}${request.nextUrl.pathname}`, request.url);
    // 保留原查询参数
    newUrl.search = request.nextUrl.search;
    request.nextUrl.pathname = newUrl.pathname;
  }

  // 2. 调用Clerk中间件处理认证
  const clerkResponse = await clerkMiddleware()(request);
  return clerkResponse;
}

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

2. 配置App Router的动态路由

在app目录下创建对应的动态路由结构,比如:

app/
  org/
    [orgId]/
      page.tsx       // 子域名对应的主页
      layout.tsx     // 子域名的布局
      // 其他页面组件...
  // 原有的根路由组件...

这样,当用户访问thomas-bistro.mysite.app时,中间件会将路径重写为/org/thomas-bistro,自动匹配到app/org/[orgId]/page.tsx,你可以在组件中通过params.orgId获取子域名:

// app/org/[orgId]/page.tsx
export default function OrgPage({ params }: { params: { orgId: string } }) {
  return <div>欢迎访问 {params.orgId} 的专属页面</div>;
}

3. Clerk配置注意事项

  • 登录Clerk Dashboard,在Settings > Domain中,将你的主域名(比如mysite.app)添加为允许的域名,并勾选"Allow subdomains"选项,确保Clerk在子域名下能正常处理认证。
  • 本地开发时,你需要修改hosts文件(Windows: C:\Windows\System32\drivers\etc\hosts,Mac/Linux: /etc/hosts),添加子域名映射:
    127.0.0.1 thomas-bistro.localhost
    
    这样就能在本地访问thomas-bistro.localhost:3000测试子域名功能。

常见问题排查

  • 如果你之前出现"无middleware导出"错误,是因为你没有正确导出自定义的async函数,确保export default async function middleware(request: NextRequest)的写法正确。
  • 子域名解析失败:检查mainDomain和localDomain是否与你的实际域名匹配,注意不要包含协议(http/https)。
  • Clerk认证失效:确保Clerk的instance domain配置正确,且子域名在允许列表中。

内容的提问来源于stack exchange,提问作者Thomas Price

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 06:16:19