跳至正文
来两杯美式
返回

Next.js 子域名、反向代理与分享链路最佳实践

By 来两杯美式
发布于更新于

一、适用场景

适用于这类 Web 项目:

典型例子:


二、先说结论

长期最好维护的方案,不是把所有页面都塞进同一套域名逻辑里,而是明确分层:

  1. 主站 host 只承载应用能力
  2. 用户子域名只承载公开内容
  3. 内部回源地址只给 nginx 和服务端使用
  4. 域名配置只保留一套真相源

如果这四件事混在一起,后期一定会出现这些问题:


三、推荐的职责拆分

1. APP_PUBLIC_ORIGIN

主站规范入口。

例子:

APP_PUBLIC_ORIGIN=https://jianli.live

用途:

2. APP_ROOT_DOMAIN

根域名,只包含 hostname。

例子:

APP_ROOT_DOMAIN=jianli.live

用途:

3. APP_INTERNAL_ORIGIN

Next.js 实际监听地址,仅用于内部回源。

例子:

APP_INTERNAL_ORIGIN=http://127.0.0.1:3000

用途:

4. AUTH_TRUST_HOST

反向代理场景必须开启。

AUTH_TRUST_HOST=true

用途:


四、最佳实践架构

1. 主站和子域名必须职责分离

推荐约定:

不推荐:

原因:

补充:主站公开路径也要跳回主站

不仅是 /auth/dashboard/api 这类后台路径,像 /privacy/terms 这类主站公开页,也不应该在租户 host 上承载。访问时应跳回主站:

https://alice.jianli.live/terms → 302 → https://jianli.live/terms

补充:APP_PUBLIC_ORIGIN 为子域名时的根域请求

如果 APP_PUBLIC_ORIGIN 本身是根域名的子域名(如 APP_PUBLIC_ORIGIN=https://app.jianli.live),那么裸根域名请求(jianli.live)应该跳回主站,而不是作为”root host”处理。

2. 公开分享和后台预览必须分离

推荐:

不推荐:

原因:


五、推荐的请求流

1. 主站访问

浏览器访问:

https://jianli.live/dashboard

流程:

  1. nginx 收到请求
  2. 保留 HostX-Forwarded-*
  3. 转发到 127.0.0.1:3000
  4. Next.js 识别为主站 host
  5. 正常渲染后台页面

2. 子域名访问

浏览器访问:

https://alice.jianli.live/?code=abc123

流程:

  1. nginx 收到请求
  2. 转发原始 Host
  3. Next.js Proxy 识别 alice 是租户子域名
  4. 内部 rewrite 到租户内部路由
  5. 页面读取分享码并查询公开内容

3. 子域名误访问后台

浏览器访问:

https://alice.jianli.live/dashboard

推荐处理:

https://jianli.live/dashboard

而不是继续在租户 host 上渲染。


六、Next.js 中的推荐实现方式

1. 域名配置统一放服务端

推荐建立一个集中模块,例如:

src / config / domain.ts;

职责:

不要把这些逻辑分散在:

更不要每个地方各写一份校验。

2. 客户端不要直接依赖 NEXT_PUBLIC_* 域名变量

这是一个非常容易踩的坑。

原因:

推荐做法:

3. Proxy 只负责 Host 分类和 rewrite / redirect

推荐让 Proxy 只做这几件事:

不推荐让 Proxy 参与:

4. 内部租户路由要单独命名,且必须禁止直达

推荐:

/tenant/[subdomain]

安全约束(两层防护)

  1. Proxy 层拦截:禁止直接访问内部租户路由。任何直达 /tenant/* 的请求都会被重定向到主站首页(proxy.ts 拦截)。

  2. 页面层校验:内部租户页面必须校验请求来自正常的 proxy rewrite。页面读取 x-tenant-rewritex-tenant-subdomain 请求头,只有两者匹配才正常渲染,否则 404。

// 页面侧校验示例
if (requestHeaders.get("x-tenant-rewrite") !== "1") {
  notFound();
}
if (requestHeaders.get("x-tenant-subdomain") !== subdomain) {
  notFound();
}

这样即使有人绕过 proxy 直接请求内部路由,页面层也会拒绝渲染。

不推荐直接 rewrite 到:

/[username]

原因:


七、Auth.js 最佳实践

1. 反向代理场景开启 AUTH_TRUST_HOST

这是必须项。

AUTH_TRUST_HOST=true

除非你非常确定需要自定义,否则优先使用 Auth.js 默认 cookie 行为。

不推荐一开始就手写:

原因:

3. 如果子域名不承载后台,就没必要强依赖跨子域登录态

这是一个非常重要的简化点。

如果你的产品策略是:

那么 Auth 就只需要把主站登录做好,不需要为了“子域名 owner 视图”强行拉高复杂度。


八、SEO 与分享卡片最佳实践

1. 根布局设置 metadataBase

在根布局设置:

用于统一生成 canonical、Open Graph、相对 metadata URL。

2. 租户公开页生成自己的 metadata

租户页应该根据实际子域名生成:

3. 分享码不要进入 metadata URL

不推荐把:

?code=abc123

写进 metadata 或 canonical。

原因:


九、nginx 最佳实践

推荐配置:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:3000;

关键点:

这样本地与线上行为才一致。


十、本地开发最佳实践

推荐本地也模拟真实访问方式:

APP_PUBLIC_ORIGIN=http://jianli.localhost
APP_ROOT_DOMAIN=jianli.localhost
APP_INTERNAL_ORIGIN=http://127.0.0.1:3000
AUTH_TRUST_HOST=true

推荐访问方式:

不推荐:

原因:


十一、推荐的代码组织方式

src/
├── app/
│   ├── [username]/                       # 兼容跳转到子域名
│   ├── tenant/[subdomain]/[[...slug]]   # 内部租户公开页
│   ├── auth/                            # 登录注册
│   ├── dashboard/                       # 后台页面
│   └── api/                             # API 路由
├── components/
│   ├── providers/PublicDomainProvider.tsx
│   └── dashboard/
├── config/
│   └── domain.ts                        # 域名真相源
├── lib/
│   ├── subdomain.ts                     # Host 分类和 URL 拼接
│   └── subdomain.schemas.ts             # 子域名校验 schema(所有子域名校验共用此文件)
├── proxy.ts                             # Host 路由分发
└── server/
    ├── auth/
    └── api/

十二、推荐的设计原则

原则 1:域名配置只保留一套真相源,子域名校验也必须共用一份 schema

域名配置不要同时存在:

子域名校验也必须共用同一份 schema(src/lib/subdomain.schemas.ts),禁止在多个路由或 API 里重复定义校验逻辑。所有注册、检查、更新接口必须引用同一份 schema,确保校验规则始终一致。

原则 2:Host 路由和业务权限分层

原则 3:主站能力和租户展示能力分离

原则 4:本地开发尽量模拟生产链路


十三、反面示例

以下做法不推荐:

1. 用 NEXT_PUBLIC_* 作为域名核心配置

问题:

2. 子域名继续承载 /auth/dashboard

问题:

3. 后台预览直接复用分享链接

问题:

4. 在多个文件里重复校验域名合法性

问题:

5. 本地只用 localhost:3000

问题:


十四、上线前检查清单


十五、一句话总结

多域名、多子域名项目的核心,不是“把所有页面都支持任意 host 访问”,而是:

把主站、租户展示、内部回源、Auth、分享链路的职责边界切干净。

边界一旦清晰,配置会少很多,问题会少很多,后期加自定义域名也会顺很多。


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.T3 Stack 开发规范
  2. 02.Next.js 并行路由完全指南
  3. 03.Web 会话与身份验证完整指南
  4. 04.Auth Guard 与 tRPC 中间件
  5. 05.T3 Stack 会话认证指南
  6. 06.Next.js 子域名、反向代理与分享链路最佳实践
  7. 07.CORS 与 Web 安全实战白皮书
  8. 08.Web 多主题架构最佳实践
  9. 09.Vercel AI SDK 自定义 OpenAI 模型接入
  10. 10.AI 驱动动态看板架构指南
  11. 11.VitePress 动态渲染全栈落地方案

上一篇
CORS 与 Web 安全实战白皮书
下一篇
T3 Stack 会话认证指南