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

如何在Next.js 13的_app.js中安全使用Storyblok私有访问令牌?

在Next.js 13的_app.js中隐藏Storyblok访问令牌的可行方案

问题背景

我正在用Storyblok (v2) + Next.js (v13)搭博客,清楚带NEXT_PUBLIC前缀的环境变量会暴露给浏览器,所以不想把访问令牌泄露出去。之前在组件里通过API路由实现过令牌隐藏,但不确定能不能在_app.js这类页面文件里这么做。查了Next.js文档还是没搞明白怎么在_app.js里实现,之前项目里成功做过类似的隐藏操作。

我已经试过这几种方法,但都没成功:

  • 在根目录.env里加不带NEXT_PUBLIC前缀的访问令牌,然后在next.config.js里配置env字段,接着在_app.js里用process.env.ACCESS_TOKEN调用,结果令牌还是暴露在页面里;
  • 只在.env里加无NEXT_PUBLIC前缀的变量,不修改next.config.js,尝试在_app.js里通过getStaticProps传递token,报错“token未定义”;
  • 调整MyApp组件参数后尝试获取props.token,报错“Cannot read properties of undefined (reading 'token')”。

想请教:在_app.js里实现隐藏访问令牌可行吗?如果可行,正确的做法是什么?也可以提供相关参考要点。

解决方案

在Next.js 13的Pages Router中,_app.js默认是客户端组件,直接在里面使用服务端环境变量会导致变量被打包到浏览器端,所以必须把令牌的使用逻辑放到服务端。下面是两种可行的方案:

方案1:通过API路由代理所有Storyblok请求

核心思路是:用Next.js的API路由作为中间层,所有客户端到Storyblok的请求都先经过这个API路由,令牌只在服务端的API路由里使用,不会暴露给浏览器。

  1. 创建API路由文件pages/api/storyblok/[...path].js,用来代理所有Storyblok请求:
// pages/api/storyblok/[...path].js
export default async function handler(req, res) {
  const { path } = req.query;
  // 拼接Storyblok的官方接口地址,带上服务端环境变量里的令牌
  const storyblokApiUrl = `https://api.storyblok.com/v2/cdn/${path.join('/')}?token=${process.env.STORYBLOK_ACCESS_TOKEN}`;

  try {
    const response = await fetch(storyblokApiUrl);
    const data = await response.json();
    res.status(response.status).json(data);
  } catch (err) {
    res.status(500).json({ error: '请求Storyblok数据失败' });
  }
}
  1. 在_app.js里调用这个API路由,而不是直接请求Storyblok的官方接口:
// pages/_app.js
import { useEffect, useState } from 'react';

function MyApp({ Component, pageProps }) {
  const [storyData, setStoryData] = useState(null);

  useEffect(() => {
    async function fetchStoryData() {
      // 示例:获取首页内容,根据你的需求调整API路径
      const res = await fetch('/api/storyblok/stories/home');
      const data = await res.json();
      setStoryData(data);
    }
    fetchStoryData();
  }, []);

  return <Component {...pageProps} storyData={storyData} />;
}

export default MyApp;

方案2:用getServerSideProps在服务端处理令牌(仅SSR场景)

如果你的博客是服务器端渲染(SSR)模式,可以在_app.js中添加getServerSideProps,在服务端用令牌请求数据后,把数据传递给组件——注意绝对不要把令牌本身传给客户端,只传处理后的数据。

// pages/_app.js
function MyApp({ Component, pageProps, storyData }) {
  // 直接使用服务端获取的数据,不用碰令牌
  return <Component {...pageProps} storyData={storyData} />;
}

export async function getServerSideProps() {
  // 服务端环境下直接使用无NEXT_PUBLIC前缀的变量,不会暴露
  const storyblokRes = await fetch(`https://api.storyblok.com/v2/cdn/stories/home?token=${process.env.STORYBLOK_ACCESS_TOKEN}`);
  const storyData = await storyblokRes.json();

  return {
    props: {
      storyData // 传递数据而非令牌
    }
  };
}

export default MyApp;

注意:_app.js添加getServerSideProps后,所有页面都会变成SSR模式,如果你需要静态生成(SSG)的页面,这个方案不适用。

为什么之前的方法失败?

  • 方法1:next.config.js里的env字段会把变量注入到客户端代码中,所以令牌会暴露,这是Next.js的设计逻辑;
  • 方法2:_app.js不支持getStaticProps,这个方法只能在具体的页面文件(如pages/index.js)中使用;
  • 方法3:因为getStaticProps在_app.js里不生效,所以props.token自然不存在,导致报错。

关键参考要点

  • Next.js中,不带NEXT_PUBLIC前缀的环境变量只能在服务端代码中使用(API路由、getServerSideProps、getStaticProps等);
  • _app.js默认是客户端组件,直接在里面使用服务端环境变量会导致变量被打包到客户端,从而暴露;
  • 敏感令牌必须只在服务端代码中使用,客户端只通过API路由获取处理后的业务数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 15:21:02