Image resizing service
Skill almasumdev/awesome-mobile-backend-agent-skills/.github/skills/media/image-resizing-service
Agent skills for the backend-for-mobile layer: APIs, auth, push, sync, and BaaS integrations.
npx -y skills add almasumdev/awesome-mobile-backend-agent-skills --skill image-resizing-serviceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Serve right-sized image variants to mobile via URL-based transforms and a CDN. Use when designing the image delivery path for a mobile app.
SKILL.md
5.3 KB, as published. Nobody here has run it
Image Resizing Service
Instructions
Mobile screens need many sizes: thumbnails, feed cards, full-screen, Retina. Shipping originals is wasteful and slow. A resizing service plus a CDN in front gives you bandwidth savings and good cache hit rates.
1. Two Models
A. Pre-generated variants (batch). At upload time, a worker produces a fixed set of sizes and formats. Clients request specific variant URLs.
Pros: low runtime cost, deterministic storage, easy to cache. Cons: you pay for every size whether it is requested or not; adding a new size requires a backfill.
B. On-demand transforms (URL-based). A single origin; clients append transform params; the edge transforms and caches.
Pros: flexible, no backfill. Cons: first-request latency; requires a transform service (self-hosted imgproxy, thumbor, or managed imgix/Cloudinary/Cloudflare Images).
Most mobile apps benefit from a hybrid: pre-generate the handful of sizes used by the primary screens; enable on-demand for long-tail needs.
2. URL Transforms
Design the URL so the client can ask for exactly what the device needs:
https://cdn.example.com/img/med_01HX7.../w=750,h=750,fit=cover,format=auto,q=80
w,hin CSS pixels (client multiplies by device scale).fit:cover,contain,fill,inside.format=autolets the edge pick AVIF / WebP / JPEG based onAccept.q: quality, default 80; lower for thumbnails.dpr(device pixel ratio) optional alternative to pre-multiplied w/h.
Allowlist permitted combinations -- attackers will otherwise request every size + format to blow up your cache.
3. Format Selection
- AVIF: best compression; slower decode; ship when device supports it.
- WebP: near-universal on modern mobile; excellent default.
- JPEG: fallback; always the floor.
- HEIC from iOS uploads: transcode to JPEG/WebP for delivery unless the client specifically requests HEIC.
Prefer format=auto and let the edge pick based on Accept headers:
Accept: image/avif,image/webp,image/*;q=0.8
4. Service Implementation
Using imgproxy (Go) behind a CDN:
Client -> CDN -> imgproxy -> object storage
cache hit -> serve directly
imgproxy URL example (signed):
https://cdn.example.com/signature/rs:fill:750:750:1/q:80/format:webp/plain/s3://uploads/med_01HX7/original.jpg
Self-hosted alternative (Python/FastAPI + libvips):
from fastapi import FastAPI, Response
import pyvips
app = FastAPI()
@app.get("/img/{key:path}")
def img(key: str, w: int = 0, h: int = 0, q: int = 80, format: str = "webp"):
im = pyvips.Image.thumbnail(f"s3://uploads/{key}", w or h, height=h or None)
buf = im.write_to_buffer(f".{format}[Q={q}]")
return Response(buf, media_type=f"image/{format}",
headers={"Cache-Control": "public, max-age=31536000, immutable"})
Always process in a streaming, memory-bounded way; libvips is the right default for that. Restrict output dimensions (e.g., max 4096x4096) and decoded pixels to prevent DoS via huge inputs.
5. Cache Strategy
- Immutable URLs (include a content hash or upload id) -> set
Cache-Control: public, max-age=31536000, immutable. - CDN cache key includes the full transform path.
- Purge rarely; if a user replaces an image, issue a new id rather than mutating.
6. Client Patterns
iOS (SDWebImage / Nuke):
let url = imageURL(mediaId: id, width: view.bounds.width, fit: .cover)
imageView.nuke.load(url)
Android (Coil):
imageView.load(imageUrl(id, widthPx = view.width, fit = "cover")) {
crossfade(true)
placeholder(R.drawable.ph)
}
The helper imageUrl(...) computes device-scaled pixel dimensions and appends transform params. Keep that logic in one place.
7. Placeholders and LQIP
For feed and list views, ship a tiny placeholder (BlurHash string, or a 16x16 WebP) in the JSON payload. The client renders that instantly while the full image streams:
{
"id": "med_01HX7...",
"w": 3024, "h": 4032,
"blurhash": "LEHV6nWB2yk8pyo0adR*.7kCMdnj"
}
8. Privacy
- Strip EXIF metadata (location, device) on upload or at transform time.
- Private media: sign URLs with a short TTL; do not make buckets public.
9. Cost
- Cache hit rate > 95% is achievable with stable URLs + long max-age.
- Watch egress: mobile image traffic often dominates a backend's total bandwidth bill.
- Compute cost goes up with format=auto + AVIF; measure before broadly enabling.
10. Observability
Track cache hit rate by variant, p95 transform latency on miss, bytes delivered per platform, and format distribution over time. Alert when miss latency degrades -- usually a bad transform service config.
Checklist
- URL transform grammar defined and documented.
- Allowlist of valid size / format combinations.
-
format=autowired toAcceptheader negotiation. - Immutable URLs with long
Cache-Controland CDN in front. - Max input/output pixel limits enforced to prevent DoS.
- EXIF stripped; private media served through signed URLs.
- BlurHash or tiny LQIP shipped in JSON payloads for smooth loading.
- Cache hit rate, miss latency, egress dashboards.