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

当Next.js重写的源服务器返回404时,如何渲染自定义404页面?

解决Next.js外部URL重写时触发自定义404页面的问题

默认情况下,Next.js的rewrites规则在代理外部URL时,会直接将外部服务器的响应(包括404状态码和对应内容)原封不动返回给客户端,不会触发你创建的pages/404.tsx页面。要实现外部返回404时渲染自定义404,你可以通过以下两种方式解决:

方案一:使用Middleware(Next.js 12+ 推荐)

通过Middleware拦截请求,先向外部URL发起请求,判断响应状态码,若为404则重写请求到自定义404页面。

在项目根目录创建middleware.ts文件:

import { NextRequest, NextResponse } from 'next/server';

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  if (pathname.startsWith('/foo/')) {
    const slug = pathname.split('/foo/')[1];
    const externalUrl = `https://example.com/foo/${slug}`;
    
    try {
      // 复制原请求的方法、头信息和内容,保证请求一致性
      const response = await fetch(externalUrl, {
        method: request.method,
        headers: request.headers,
        body: request.method !== 'GET' ? await request.text() : undefined,
      });

      // 外部返回404时,重写至自定义404页面
      if (response.status === 404) {
        return NextResponse.rewrite(new URL('/404', request.url));
      }

      // 正常响应时,转发外部服务器的内容和状态
      return new NextResponse(response.body, {
        status: response.status,
        headers: response.headers,
      });
    } catch (error) {
      // 请求外部失败时,同样返回自定义404
      return NextResponse.rewrite(new URL('/404', request.url));
    }
  }

  // 非目标路径直接放行
  return NextResponse.next();
}

// 限定Middleware仅匹配/foo/:slug路径
export const config = {
  matcher: '/foo/:slug*',
};

方案二:自定义API路由作为代理

先将rewrites指向内部API路由,在API路由中处理外部请求并判断状态码,404时返回自定义页面。

步骤1:修改next.config.js的重写规则

module.exports = {
  async rewrites() {
    return [
      {
        source: '/foo/:slug',
        destination: '/api/foo/:slug',
      },
    ];
  },
};

步骤2:创建API路由文件pages/api/foo/[slug].ts

import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { slug } = req.query;
  const externalUrl = `https://example.com/foo/${slug}`;

  try {
    const response = await fetch(externalUrl, {
      method: req.method,
      headers: req.headers,
      body: req.method !== 'GET' ? req.body : undefined,
    });

    if (response.status === 404) {
      // 方式1:重定向到自定义404页面
      res.redirect(302, '/404');
      return;

      // 方式2:直接返回404页面的HTML(适合静态生成场景)
      // const html = await fetch(`${req.headers.origin}/404`).then(res => res.text());
      // res.status(404).send(html);
    }

    // 正常转发外部响应
    const content = await response.text();
    res.status(response.status)
       .setHeader('Content-Type', response.headers.get('Content-Type') || 'text/html')
       .send(content);
  } catch (error) {
    res.redirect(302, '/404');
  }
}

注意事项

  • 使用Middleware时,确保你的Next.js版本在12及以上,且已启用对应Router的Middleware支持。
  • 两种方案都需要正确转发原请求的方法、头信息和请求体,避免外部服务器拒绝请求或返回异常内容。
  • 若外部URL存在缓存策略,需注意响应头的转发,避免客户端缓存异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:15:10