Next.js图片优化未缓存Notion图片求助(Next 13 appDir)
解决方案:Notion 托管图片在 Next.js 13 App Dir 中的持久化处理
针对你遇到的 Notion 媒体链接1小时过期、ISR 和 next/image 缓存不生效的问题,给你几个可行的本地解决方案(无需外部图片托管):
1. 自建图片代理 Route Handler
在 Next.js 中创建专门的图片代理接口,通过服务器中转请求并缓存图片,绕过 Notion 的临时链接限制:
- 在
app/api/proxy-image/[...path]/route.ts编写代理逻辑:import { NextResponse } from 'next/server'; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const notionUrl = searchParams.get('url'); if (!notionUrl) return new NextResponse('Missing URL', { status: 400 }); const response = await fetch(notionUrl); if (!response.ok) return new NextResponse('Failed to fetch image', { status: response.status }); // 设置缓存头,强制浏览器和CDN缓存图片30天 const headers = new Headers(response.headers); headers.set('Cache-Control', 'public, max-age=2592000, s-maxage=2592000'); headers.delete('expires'); headers.delete('pragma'); return new NextResponse(response.body, { headers }); } // 启用Edge Runtime提升代理性能(可选) export const runtime = 'edge'; - 在页面中用代理链接替换原始 Notion 链接:
import Image from 'next/image'; export default function Post({ post }) { const proxyImageUrl = `/api/proxy-image?url=${encodeURIComponent(post.imageUrl)}`; return <Image src={proxyImageUrl} alt={post.title} width={800} height={600} />; }
核心是通过自有的服务器中转图片请求,把临时的 Notion 链接转换成长期可访问的代理链接,同时通过缓存头锁定图片内容。
2. 优化 ISR 重验证时机 + 客户端兜底
调整 ISR 周期并配合图片加载失败处理,避免显示过期图片:
- 在页面组件中设置小于60分钟的重验证时间,确保链接过期前完成页面更新:
export const revalidate = 3300; // 55分钟,单位秒 - 给图片添加错误回调,加载失败时主动拉取最新链接:
import Image from 'next/image'; import { useState } from 'react'; export default function Post({ initialImageUrl, postId }) { const [imageUrl, setImageUrl] = useState(initialImageUrl); const handleImageError = async () => { const res = await fetch(`/api/get-latest-image?postId=${postId}`); const data = await res.json(); setImageUrl(data.latestImageUrl); }; return <Image src={imageUrl} alt={post.title} width={800} height={600} onError={handleImageError} />; }
这里的 /api/get-latest-image 接口需要直接从 Notion API 拉取对应文章的最新数据,返回有效图片链接。
3. Edge 层缓存图片元数据
利用 Next.js 的 Edge Runtime 缓存图片链接,每次渲染前先检查链接是否即将过期:
- 在获取 Notion 数据的函数中添加缓存逻辑:
import { cache } from 'react'; export const getNotionPost = cache(async (postId: string) => { const cacheKey = `notion-post-${postId}`; const cachedData = await caches.default.match(cacheKey); if (cachedData) { const data = await cachedData.json(); // 检查链接是否还在有效期内(提前5分钟触发更新) const now = Date.now(); if (now - data.fetchTime < 55 * 60 * 1000) { return data; } } // 从Notion API重新拉取数据 const res = await fetch(`https://api.notion.com/v1/pages/${postId}`, { headers: { Authorization: `Bearer ${process.env.NOTION_TOKEN}` }, }); const data = await res.json(); const dataWithFetchTime = { ...data, fetchTime: Date.now() }; // 缓存数据55分钟,同时允许后台异步更新 const response = new Response(JSON.stringify(dataWithFetchTime), { headers: { 'Cache-Control': 's-maxage=3300, stale-while-revalidate=300' }, }); await caches.default.put(cacheKey, response); return dataWithFetchTime; });
通过 Edge 层缓存优先使用未过期的链接,同时在后台异步更新数据,规避 stale-while-revalidate 带来的旧链接问题。
内容的提问来源于stack exchange,提问作者lw_101
相关产品推荐
相关产品推荐

