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

Nuxt3代理/api/**路由后如何隐藏该路由并返回404?

问题描述

我有一个Nuxt3服务端插件myPlugin.server.js,通过await Promise.allSettled(apiRoutes.map(async (route) => {const data = await $fetch(route.url)...})映射所有外部后端的API路由,并在服务端填充Pinia状态。

同时配置了服务端中间件代理请求:

export default defineEventHandler(async (event) => {
    const url = event.node.req.url;

    if (url.includes('/api/') && url !== '/api/health' && url !== '/api/_sitemap-urls') {
        const pathRewrite = url.replace('/api/', '/');

        await proxyRequest(event, (process.env.WP_API_URL || 'http://localhost:1222/wp-json') + pathRewrite);
    }
});

现在需要实现:代理请求后,隐藏浏览器端的/api/**路由并返回404,但遇到以下问题:

  • 代理后/api/**路由不可访问,使用sendRedirect()会出现Can't send headers after they are sent错误;
  • 若在代理前返回404则会导致代理失效;
  • 尝试创建server/api/路由返回重定向、客户端路由及路由中间件使用navigateTo()均无效,因为这些路由已被代理,客户端无法访问。

请问该如何实现需求?


解决方案

核心思路是区分请求来源是服务端内部调用还是浏览器端发起,只允许服务端内部的/api/**请求走代理,浏览器端发起的直接返回404。

方法1:利用Nuxt内部请求头判断

Nuxt的$fetch在服务端内部调用时会自动添加x-nuxt-internal请求头,可直接用这个标识区分请求来源:

export default defineEventHandler(async (event) => {
    const url = event.node.req.url;
    const isInternalRequest = event.node.req.headers['x-nuxt-internal'];

    if (url.includes('/api/') && url !== '/api/health' && url !== '/api/_sitemap-urls') {
        // 仅服务端内部请求走代理逻辑
        if (isInternalRequest) {
            const pathRewrite = url.replace('/api/', '/');
            await proxyRequest(event, (process.env.WP_API_URL || 'http://localhost:1222/wp-json') + pathRewrite);
        } else {
            // 浏览器端直接访问时返回404
            setResponseStatus(event, 404);
            return { status: 404, message: 'Not Found' };
        }
    }
});

方法2:自定义内部请求标识

如果担心默认头的兼容性,可在服务端插件的$fetch调用中添加自定义请求头,再在中间件校验:

  1. 修改服务端插件的$fetch调用:
// myPlugin.server.js
await Promise.allSettled(apiRoutes.map(async (route) => {
    const data = await $fetch(route.url, {
        headers: {
            'x-internal-api-call': 'true' // 自定义内部请求标识
        }
    });
    // 填充Pinia状态的逻辑
}));
  1. 更新服务端中间件:
export default defineEventHandler(async (event) => {
    const url = event.node.req.url;
    const isInternalCall = event.node.req.headers['x-internal-api-call'] === 'true';

    if (url.includes('/api/') && url !== '/api/health' && url !== '/api/_sitemap-urls') {
        if (isInternalCall) {
            const pathRewrite = url.replace('/api/', '/');
            await proxyRequest(event, (process.env.WP_API_URL || 'http://localhost:1222/wp-json') + pathRewrite);
        } else {
            setResponseStatus(event, 404);
            return { status: 404, message: 'Not Found' };
        }
    }
});

关键说明

  • 两种方法都不会影响服务端插件内部的$fetch调用(会携带标识头),同时拦截浏览器端直接访问/api/**的请求;
  • 不要在proxyRequest之后尝试修改响应头,因为代理已经发送了响应,这就是你遇到Can't send headers after they are sent错误的核心原因;
  • 保留的/api/health和/api/_sitemap-urls可根据需求调整逻辑,比如同样限制为内部访问或保持公开。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:40:09