Docs / Content

Gallery & images

How and why mustr handles images the way it does — R2 storage, edge caching, per-album audiences, and upload limits.

Every image in mustr — avatars, medal art, event banners, gallery photos — is stored the same way, and understanding that mechanism explains a lot of behavior you'll notice elsewhere (why uploads have size limits, why images load instantly on a second view, why an SVG upload gets rejected).

Where images live

Uploads go to R2, Cloudflare's object storage, not the database. When you upload, the file is stored under a category (avatars, medals, ranks, events, pages, branding, gallery, …) with a content-addressed key — a name derived from the file itself, never reused, so two different uploads can never collide or overwrite each other.

Why only PNG, JPEG, GIF, and WebP

SVG uploads are deliberately rejected. An SVG can contain a <script> tag, and if it were served from mustr's own domain and opened directly, that script would run with the site's own permissions — a classic stored-XSS vector. Raster formats (PNG/JPEG/GIF/WebP) can't do that, so the upload endpoint only accepts them. It's a small restriction that closes a real hole.

Upload size limits

Why images load fast

Images are served from /media/<key>, a path that's deliberately outside the API layer so it can be cached hard at Cloudflare's edge — once an image has been requested once from a given edge location, later requests for it don't touch storage at all. Combined with the size caps and lazy-loading on the roster, this is what keeps image traffic essentially free even as a community grows into the thousands of members.

Replacing or deleting an image (a new avatar, a re-uploaded medal icon) automatically deletes the old file from storage in the background, so nothing orphaned accumulates and eats into your free-tier allowance for no reason.

Admin → Content → Gallery manages albums. Each album carries its own audience — public, members, or a specific role — checked on every request, so a members-only album never leaks through a direct link or an anonymous page view. Deleting the role an album is gated to closes the album rather than opening it, so a data mistake never accidentally widens access.

An album holds photos (uploaded, downscaled as above) and videos — but videos are always embedded from YouTube or Twitch, never self-hosted, so there's no video-storage bill and no bandwidth cost, ever.

Ordering albums and photos uses explicit up/down buttons rather than drag-and-drop, on purpose — it stays usable on a phone, with a keyboard, and mid-upload, when a drag gesture would be awkward.