fix: proxy Notion images to avoid expired S3 URLs#72
Merged
Conversation
…3 URLs
Notion serves block images via pre-signed S3 URLs with X-Amz-Expires=3600.
After one hour the URL returns 403 + Content-Type: application/xml, which
Chrome blocks via ORB (net::ERR_BLOCKED_BY_ORB). Since fetchArticleById
uses "use cache" + cacheLife("max"), the dead URL is frozen and served to
every visitor. The S3 host is external so neither Cloudflare nor Vercel
can cache it either.
The new route resolves a fresh signed URL via notion.blocks.retrieve at
request time and streams the image with Cache-Control: public, s-maxage,
keeping the URL stable on qraft.tech so both CDNs cache it at the edge.
Closes #71
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
app/api/notion-image/route.tsqui résout une URL S3 fraîche vianotion.blocks.retrieve({ block_id })et stream le binaire avecCache-Control: public, s-maxage=86400, stale-while-revalidate=604800.NotionRendererpointe les images Notion-hosted (typefile) sur cette route au lieu de l'URL S3 signée. Les imagesexternal(Unsplash, etc.) restent directes.lib/notion.ts:notionclient exporté pour réutilisation ;databaseIdrendu lazy pour ne plus crasher au load du module dans la route.Pourquoi
Les URLs S3 signées de Notion expirent au bout d'1h (
X-Amz-Expires=3600). Après expiration, S3 répond 403 +Content-Type: application/xml→ Chrome bloque (ORB/CORB). AveccacheLife("max")surfetchArticleById, l'URL morte est gelée et servie à tous les visiteurs (cf. https://qraft.tech/blog/350218ed-56d7-814c-88a2-def1a2800fd2). Aucun CDN ne peut intervenir parce que le domaine S3 est externe.En proxifiant via
qraft.tech, l'URL devient stable → Cloudflare + Vercel cachent l'image à l'edge ; un seul appel Notion API par image par jour au pire.Closes #71
Test plan
external(si présente dans un article) charge toujourscf-cache-statusau 2ème hit de/api/notion-image?blockId=…(sera DYNAMIC tant qu'aucune Cache Rule Cloudflare n'est ajoutée — pas bloquant, le cache Vercel s'occupe déjà du gros)?blockId=foo→ 400,?blockId=<UUID inexistant>→ 404/api/notion-image*🤖 Generated with Claude Code