当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
相关产品推荐
相关产品推荐

