fix(staging): completely resolve phantom 403 imgproxy caching loops via base64, traefik routing precedence, and variable mapping
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 12s
Build & Deploy / 🧪 QA (push) Successful in 1m54s
Build & Deploy / 🏗️ Build (push) Successful in 7m44s
Build & Deploy / 🚀 Deploy (push) Successful in 30s
Build & Deploy / 🧪 Smoke Test (push) Successful in 1m2s
Build & Deploy / ⚡ Lighthouse (push) Successful in 3m17s
Build & Deploy / 🔔 Notify (push) Successful in 1s

This commit is contained in:
2026-02-19 20:06:55 +01:00
parent 8a87318b12
commit e3e0a7670c
2 changed files with 32 additions and 10 deletions

View File

@@ -55,10 +55,18 @@ export function getImgproxyUrl(src: string, options: ImgproxyOptions = {}): stri
`g:${gravity}`,
].join('/');
// Using /unsafe/ with plain/ source URL format
// plain/ format works reliably with imgproxy URL mapping
// Format: <base_url>/unsafe/<options>/plain/<source_url>[@<extension>]
const suffix = extension ? `@${extension}` : '';
// Using Base64 encoding for the source URL.
// This completely eliminates any risk of intermediate proxies (Traefik/Next.js)
// URL-decoding the path, which corrupts the double-slash (// to /) and causes 403 errors.
// Imgproxy expects URL-safe Base64 (RFC 4648) without padding.
const b64 =
typeof window === 'undefined'
? Buffer.from(absoluteSrc).toString('base64')
: btoa(unescape(encodeURIComponent(absoluteSrc)));
return `${baseUrl}/unsafe/${processingOptions}/plain/${encodeURIComponent(absoluteSrc)}${suffix}`;
const urlSafeB64 = b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
const suffix = extension ? `.${extension}` : '';
return `${baseUrl}/unsafe/${processingOptions}/${urlSafeB64}${suffix}`;
}