usercontent.abtestly.com. Deliberately a
separate hostname from app.abtestly.com and cdn.abtestly.com.
customer content is isolated from the app and the snippet.
The URL shape
<namespace>, a per-site 32-character hex string. Namespaces are isolated from each other; site A cannot enumerate site B’s files.<hash>, the SHA-256 of the file contents. Content-addressed. any change to bytes produces a new URL.<filename>, the canonical filename. A request with a different filename returns a308 Permanent Redirectto the canonical one.
Cache
Every response includes:CORS
Access-Control-Allow-Origin: *.
Assets are safe to reference from any domain. <img>, <video>, CSS
background-image, <a href> for PDFs, all work anywhere.
Content type
Set from the file’s sniffed type at upload, not from the URL extension.image/png, image/jpeg, image/gif, image/webp,
application/pdf.
HEAD
HEAD /a/… returns the same headers as GET /a/… with no body.
Useful for programmatic size checks.
Rate limits
None on serving. The URL is a static-file URL cached at Cloudflare’s edge, traffic is essentially free for us. The upload endpoint on the dashboard side is rate-limited (100 requests per minute per user), but that only matters if you are batch-uploading.Takedown
Two ways an asset can stop serving:- You deleted it. Soft-delete first, then permanent-delete. See Recycle bin.
- We took it down. Admin action, only in response to a legal request (DMCA, GDPR erasure) or an abuse report. When this happens, the R2 object is deleted and the URL is purged from Cloudflare cache within minutes.
Why a separate hostname
Two reasons:- Cookie isolation.
usercontent.abtestly.comshares no cookies withapp.abtestly.comorcdn.abtestly.com. A malicious file (a crafted PDF with active content, for example) cannot read auth cookies. - Cache scope. Cache-Control headers for user content are different from the app and the snippet. Separating hostnames keeps the cache behavior clean.