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

Next.js中间件重写异常:主域名可访问子域名专属页面

问题分析与解决方案

你的核心问题是主域名能直接访问本该仅子域名可访问的页面,本质是主域名的访问控制逻辑缺失:当前中间件仅对主域名请求做路径重写,但未限制重写后路径的访问范围,导致主域名可通过重写路径或直接访问根路径路由获取子域名页面内容。

修改后的中间件代码

import { env } from '@/lib/env'
import { Database } from '@/types/supabase'
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
  let response = NextResponse.next()

  const url = request.nextUrl
  const originalHost = request.headers.get('host')!
  
  // 处理hostname,但保留原始host用于主域名判断
  let hostname = originalHost.replace('.localhost:3000', `.${process.env.NEXT_PUBLIC_ROOT_DOMAIN}`)

  if (
    hostname.includes('---') &&
    hostname.endsWith(`.${process.env.NEXT_PUBLIC_VERCEL_DEPLOYMENT_SUFFIX}`)
  ) {
    hostname = `${hostname.split('---')[0]}.${
      process.env.NEXT_PUBLIC_ROOT_DOMAIN
    }`
  }

  const searchParams = request.nextUrl.searchParams.toString()
  const path = `${url.pathname}${
    searchParams.length > 0 ? `?${searchParams}` : ''
  }`

  // 主域名访问控制:仅允许访问根路径,其他路径直接拒绝
  if (
    originalHost === 'localhost:3000' ||
    originalHost === process.env.NEXT_PUBLIC_ROOT_DOMAIN
  ) {
    // 仅允许主域名访问根路径,重写到/home;其他路径返回403
    if (path !== '/') {
      return new NextResponse('Access Denied', { status: 403 })
    }
    return NextResponse.rewrite(new URL('/home', request.url))
  }

  const supabase = createServerClient<Database>(
    env.NEXT_PUBLIC_SUPABASE_URL,
    env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) =>
            response.cookies.set(name, value, options),
          )
        },
      },
    },
  )

  const {
    data: { user },
  } = await supabase.auth.getUser()

  if (hostname == `admin.${process.env.NEXT_PUBLIC_ROOT_DOMAIN}`) {
    response = NextResponse.rewrite(
      new URL(`/admin${path === '/' ? '' : path}`, request.url),
    )

    if (!user && path !== '/login') {
      return NextResponse.redirect(new URL('/login', request.url))
    }

    return response
  }

  // 子域名路径重写与权限校验
  response = NextResponse.rewrite(new URL(`/${hostname}${path}`, request.url))

  const domain = hostname.split('.')[0]

  const { data: workspace } = await supabase
    .from('public_workspaces')
    .select('domain')
    .eq('domain', domain)
    .single()

  if (!workspace && path !== '/access-denied') {
    return NextResponse.redirect(new URL('/access-denied', request.url))
  }

  if (!user && path !== '/login' && path !== '/access-denied') {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return response
}

export const config = {
  matcher: ['/((?!api/|_next/|_static/|_vercel|[\w-]+\.\w+).*)'],
}

修改说明

  1. 保留原始Host判断:避免hostname被替换后导致主域名识别失效,确保主域名请求能被正确拦截。
  2. 强化主域名访问控制:仅允许主域名访问根路径(/),其他路径直接返回403拒绝访问,彻底阻断主域名对子域名页面的访问路径。
  3. 精准路径重写:主域名根路径统一重写到/home,符合原有业务逻辑的同时避免路径泄漏。

拓展调整(可选)

如果需要主域名允许访问/home下的特定路径(如/home/profile),可修改主域名控制逻辑为:

if (
  originalHost === 'localhost:3000' ||
  originalHost === process.env.NEXT_PUBLIC_ROOT_DOMAIN
) {
  // 允许的路径列表
  const allowedPaths = ['/', '/home', '/home/profile']
  const cleanPath = path.split('?')[0] // 剔除查询参数
  if (!allowedPaths.includes(cleanPath)) {
    return new NextResponse('Access Denied', { status: 403 })
  }
  const targetPath = cleanPath === '/' ? '/home' : cleanPath
  return NextResponse.rewrite(new URL(targetPath, request.url))
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:55:55