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

Next.js调用notFound()未返回universal目录下的404页面问题

Next.js 多域名重写后调用notFound()未加载对应目录的not-found.tsx问题

我在Next.js服务器中通过Middleware处理多域名路由:当请求来自“通用域名”时,动态修改nextUrl.pathname,将请求路由到app目录下的universal/[...urlParts]页面。但遇到一个问题:在该页面中调用next/navigation的notFound()函数时,返回的是app根目录的not-found.tsx,而非universal目录下对应的not-found.tsx。

为设置通用页面的locale,我在page.tsx中先从服务获取页面信息,再通过unstable_setRequestLocale设置locale,之后调用notFound()。


依赖版本

next: 14.2.6
eslint-config-next: 14.2.6
react: 18.3.1
react-dom: 18.3.1
typescript: 5.0.4

Middleware代码

const withUniversal: MiddlewareFactory = (next: NextMiddleware) => {
    return async (request: NextRequest, _next: NextFetchEvent) => {
        if (request.nextUrl.pathname === "/robots.txt") {
            return NextResponse.next();
        }

        const hostname = getHostNameFromRequest(request);
        if (
            request.nextUrl.pathname.startsWith("/api/") ||
            request.nextUrl.pathname.startsWith("/assets/") ||
            request.nextUrl.pathname.startsWith("/favicon/") ||
            request.nextUrl.pathname.startsWith("/~partytown/") ||
            request.nextUrl.pathname.startsWith("/_next/static") ||
            request.nextUrl.pathname.startsWith("/_next/image") ||
            request.nextUrl.pathname.startsWith("/universal")
        ) {
            return await next(request, _next);
        }

        if (
            Object.values(UniversalDomains).includes(
                hostname as UniversalDomains
            )
        ) {
            const newPath = "/universal/" + hostname + request.nextUrl.pathname;
            request.nextUrl.pathname = newPath;
            
            return NextResponse.rewrite(new URL(newPath, request.url));
        }
        const response = await next(request, _next);
        return response;
    };
};

项目目录结构

.
├── [locale]
│   ├── (published_apps)
│   ├── demo
│   ├── not-found.tsx
│   └── page-renderer
├── api
│   ├── auth
│   ├── embeddable
│   ├── hello
│   ├── newsletter
│   ├── preview-deployments
├── layout.tsx
├── not-found.tsx
├── test.txt
└── universal
    ├── [...urlParts]
    │   ├── layout.tsx
    │   └── page.tsx
    ├── layout.tsx
    └── not-found.tsx

日志信息

✓ Ready in 1341ms
 ✓ Compiled /middleware in 203ms (123 modules)
 ○ Compiling /universal/[...urlParts] ...
 ✓ Compiled /universal/[...urlParts] in 8.1s (7501 modules)

Unable to find `next-intl` locale because the middleware didn't run on this request. See https://next-intl-docs.vercel.app/docs/routing/middleware#unable-to-find-locale. The `notFound()` function will be called as a result.

 GET /dbaizuvd 404 in 10150ms
 ○ Compiling /_not-found ...
 ✓ Compiled /_not-found in 3.8s (7504 modules)
 GET /favicon/favicon.co 404 in 3938ms

已尝试方法

  • 在layout中手动设置带locale的NextIntlClientProvider
  • 直接访问http://localhost:3000/universal//ezfbaizeu,仍返回根目录的404页面

解决方案

这个问题的核心在于Next.js的notFound()函数会根据当前请求的逻辑路由层级查找对应not-found.tsx,但重写后的请求可能未正确保留universal目录的层级上下文,或unstable_setRequestLocale的调用干扰了路由上下文。

1. 确保重写请求的路由上下文正确

检查getHostNameFromRequest函数是否正确获取域名,避免生成的newPath出现格式错误(如多余斜杠)。同时,在使用NextResponse.rewrite时,不要覆盖请求的原始locale相关参数,确保路由层级上下文完整。

2. 提前在universal目录的layout中设置locale

不要在page.tsx中调用unstable_setRequestLocale,而是提前在universal的根layout中完成locale初始化,确保路由上下文在调用notFound()前已正确建立:

// universal/layout.tsx
import { unstable_setRequestLocale } from 'next/server';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from './messages';

export async function generateMetadata({ params }: { params: { urlParts: string[] } }) {
  // 从urlParts中提取实际hostname和locale信息
  const hostname = params.urlParts[0];
  const locale = 'en'; // 替换为业务逻辑获取的locale
  unstable_setRequestLocale(locale);
  return {};
}

export default async function UniversalLayout({ children }: { children: React.ReactNode }) {
  const locale = 'en'; // 替换为业务逻辑获取的locale
  const messages = await getMessages(locale);
  
  return (
    <NextIntlClientProvider locale={locale} messages={messages}>
      {children}
    </NextIntlClientProvider>
  );
}

3. 手动渲染universal/not-found.tsx(替代方案)

如果上述方法无效,可以放弃调用notFound(),直接在page.tsx中渲染universal目录下的404组件:

// universal/[...urlParts]/page.tsx
import NotFound from '../../universal/not-found';

export default async function UniversalPage({ params }: { params: { urlParts: string[] } }) {
  const pageInfo = await fetchPageInfo(params);
  
  if (!pageInfo) {
    // 直接渲染universal下的not-found组件
    return <NotFound />;
  }
  
  // 正常渲染页面逻辑
  return <div>...</div>;
}

4. 调整next-intl的Middleware顺序

日志提示next-intl找不到locale,说明你的next-intl Middleware可能未正确处理重写后的请求。确保withUniversal Middleware在next-intl Middleware之前执行,且覆盖所有需要处理的路径:

// middleware.ts
import createMiddleware from 'next-intl/middleware';
import { withUniversal } from './withUniversal';

const intlMiddleware = createMiddleware({
  locales: ['en', 'zh'],
  defaultLocale: 'en'
});

// 确保withUniversal先执行,处理域名重写后再交给next-intl处理locale
export default withUniversal(intlMiddleware);

export const config = {
  matcher: ['/((?!api|assets|favicon|~partytown|_next).*)']
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:27:04