export default {
async fetch(request, env) {
const url = new URL(request.url)
// Upstream B2 (S3 endpoint) — change if you rename buckets/regions
const originHostname = env.originHostname || 'eventchypublicassets.s3.us-west-004.backblazeb2.com'
// Rewrite hostname to B2 and preserve the path/query
const upstreamUrl = new URL(url.toString())
upstreamUrl.hostname = originHostname
// Clone incoming headers and force correct Host for S3 routing
const headers = new Headers(request.headers)
headers.set('Host', originHostname)
// Only proxy safe methods
if (request.method !== 'GET' && request.method !== 'HEAD') {
return new Response('Method Not Allowed', { status: 405 })
}
// Build upstream request (pass through Range/If-None-Match/etc.)
const upstreamReq = new Request(upstreamUrl.toString(), {
method: request.method,
headers,
redirect: 'follow'
})
// Cloudflare cache hints (tweak TTLs as you like)
const cf = {
cacheEverything: true,
cacheTtlByStatus: { '200-299': 86400, '404': 300, '500-599': 0 }
}
const upstreamRes = await fetch(upstreamReq, { cf })
// Clone so we can adjust headers if needed
const res = new Response(upstreamRes.body, upstreamRes)
// Optional: add CORS for browsers pulling from a different site
// If you need it, set env.corsOrigin to '*' or a specific origin.
if (env.corsOrigin) {
res.headers.set('Access-Control-Allow-Origin', env.corsOrigin)
res.headers.set('Access-Control-Expose-Headers', 'ETag, Content-Length')
res.headers.set('Vary', 'Origin')
}
// Optional sane defaults if origin sends no cache headers
if (!res.headers.has('Cache-Control')) {
res.headers.set('Cache-Control', 'public, max-age=86400, s-maxage=86400, immutable')
}
// Keep content-type/length/etag/range as-is (important for images/video)
return res
}
}