VI: Vì sao & cách proxy video. Spec máy-đọc:
spec/proxy.yaml. Code:proxy/. EN: Why & how the video proxy works. Machine-readable:spec/proxy.yaml. Code:proxy/.
Data đọc THẲNG R2. Chỉ byte nặng ở host cần CORS/Range (gd/src) mới qua proxy. r2/b2 custom domain phát thẳng — đừng tốn CPU proxy.
client <video src=resolveBlob(token)>
│
├── r2:key / b2:key ──────────────► cdn.example/key (THẲNG: Range+CORS+egress$0)
│
└── gd:id / src:url ──► px.example/p/<token> ──► upstream (PROXY: Range/CORS/confirm/hide)
| Vấn đề | Không proxy | Có proxy |
|---|---|---|
Range seek (<video> tua) |
Drive trả 200 nguyên file / không Accept-Ranges → không seek |
forward Range, giữ 206 Partial Content |
CORS (<video crossorigin>, <track> sub) |
thiếu Access-Control-Allow-Origin → trình duyệt chặn |
proxy gắn CORS + expose Content-Range |
| Confirm token (Drive file lớn) | trả HTML "virus scan" thay vì byte | proxy bóc confirm= và gọi lại |
| Giấu origin | lộ URL Drive/B2, dễ bị khoá/hotlink | client chỉ thấy domain proxy |
| Cache | mỗi host khác header → cache loạn | 1 domain → Cloudflare cache từng byte-range |
GET|HEAD|OPTIONS /p/<host>:<ref>
/p/gd:1A2b3C4d5E
/p/src:https%3A%2F%2Fhost.example%2Fmovie%2F1.mp4 (ref percent-encoded)
r2/b2→ 400 (phải phát thẳng).- method khác GET/HEAD/OPTIONS → 405.
srcvi phạm SSRF/allowlist → 403.
- Client gửi
Range: bytes=N-khi tua. - Proxy forward
Range(+If-Range/If-None-Match) lên upstream. - Upstream trả
206+Content-Range: bytes N-M/Total+Accept-Ranges: bytes. - Proxy giữ nguyên status + các header range, thêm CORS, strip hop-by-hop, set
Cache-Control: immutable.
<video>chỉ seek mượt khi thấyAccept-Ranges: bytesvà server tôn trọng206. Worker mẫu đảm bảo cả hai.
File lớn → Drive trả HTML "Google Drive can't scan this file for viruses". Proxy:
GET uc?export=download&id=ID
└─ Content-Type: text/html → match /confirm=([\w-]+)/ → GET ...&confirm=TOKEN[&uuid=UUID]
└─ Content-Type: video/* → stream thẳng
PROD nên dùng GD_API_KEY → resolver googleapis.com/drive/v3/files/{id}?alt=media (Range ổn định, không trang confirm). Drive vẫn dính quota/ban theo IP+ngày → bám cache CF cứng; nghiêm túc thì đẩy phim lên R2/B2.
.m3u8+ segment.ts/.m4sđều immutable → đặt trên R2/B2 custom domain, phát thẳng là rẻ + nhanh nhất, không cần proxy.- Nếu buộc proxy (host gd): dùng URI tuyệt đối
/p/gd:<segment>ngay lúc ingest (ghi vào playlist), không rewrite lúc đọc. Set CORS cho.m3u8và.vtt.
- Byte phim/segment/sub không đổi →
Cache-Control: public, max-age=31536000, immutable. - Bật range object caching ở Cloudflare → cache từng byte-range (206→200) → tua lại không tốn egress upstream.
- Worker dùng
cf: { cacheEverything: true, cacheTtl: 31536000 }.
- Chỉ
GET/HEAD/OPTIONS; không forward cookie/Server upstream. src:— chặn IP nội bộ (10./127./169.254./192.168./172.16-31.) + allowlist host (SRC_ALLOWLIST) chống open-proxy/SSRF.- (tuỳ chọn) chặn
Content-Lengthquá ngưỡng chosrclạ.
# Worker
cd proxy && npm i
npx wrangler secret put GD_API_KEY # (tuỳ chọn)
npx wrangler deploy # route: px.example/*
# Pages Function: bỏ proxy/functions/ vào repo Astro -> /p/* tự nhận khi deploy Pagescurl -I -H 'Range: bytes=0-1023' https://px.example/p/gd:FILEID # 206 + Content-Range
curl -I -X OPTIONS https://px.example/p/gd:FILEID # 204 + Access-Control-Allow-Origin
curl -I https://px.example/p/r2:key # 400 (phải phát thẳng)