Why We Chose BunnyCDN as Ciphera's CDN
A CDN terminates your TLS, sits in the request path of every visitor, and sees every asset request your pages make. Most engineering teams choose one on price and latency. For a privacy company, those are the second questions — the first is jurisdictional: who operates the box that decrypts every request, and which courts can compel them?
We went through that decision for ciphera.net and our product frontends, picked Bunny, and have now run it in production long enough to have opinions with evidence. This is the checklist we used, what the setup actually looks like, and four things running it taught us — including one embarrassing number worth reading about so you don't repeat it.
TL;DR:We chose Bunny because it's EU-jurisdiction (Slovenia), commits to anonymized logging under a GDPR DPA, prices transparently ($0.01/GB in Europe), and performs well (119 PoPs, ~24 ms average global latency). Our setup: Bunny pull zones in front of Swiss object storage (Exoscale, Zurich), serving path-keyed immutable assets. The lessons: CDN caches collapse things you don't expect, measure your real cache-hit rate, push security rules to the edge, and draw a hard line around what never leaves the origin.
Update, 19-07-2026 — rewritten around our current production setup, with all network and pricing figures re-verified against bunny.net on this date.
Why a CDN Is a Privacy Decision
The CDN's entire job is to stand between your users and your servers. That means it observes every request — URL, IP, headers — for every visitor, on every page. You can architect so it holds nothing sensitive (more on that below), but you cannot architect away the fact that it sees traffic. Whoever operates it is inside your trust boundary whether you like it or not.
The asymmetry is easy to miss because static assets feel harmless. An icon request is just an icon request — until you notice that every one arrives with an IP address, a user agent, and a referer header naming the exact page being read. Aggregate those across a browsing session and a CDN's logs reconstruct visitor behavior about as well as an analytics script would. A company that won't run invasive analytics on its own pages has no business ignoring the same data accumulating in a vendor's log pipeline.
That makes jurisdiction the first filter. A US-headquartered CDN operates under the CLOUD Act wherever its edge nodes physically sit — the compulsion follows the company, not the server. Our vendor policy is explicit and ranked: residency, then privacy, then security, then robustness. Applied here, that ruled out the default answers (Cloudflare, CloudFront, Fastly) before performance was even discussed — not because they're bad at CDN-ing, but because they move the request path into a legal system we're deliberately building away from. Ciphera is a Belgian company; our origin storage is Swiss; the CDN needed to keep the chain European.
The Checklist We Used
Five criteria, in the order we applied them:
- EU jurisdiction. Bunny is Slovenian. The request path — visitor, edge, origin — stays inside EU/Swiss legal territory end to end.
- Logging you can live with. Bunny's GDPR commitments include anonymizing personally identifiable data in logging, with a signed DPA available in the panel. "We anonymize" from an EU company under GDPR enforcement is a materially different promise than the same words from elsewhere.
- Content-agnostic serving. The CDN must do its job without interpreting the bytes. We treat the CDN as untrusted infrastructure on principle: it caches and moves opaque objects, whether those are PNGs or encrypted blobs. Any CDN feature that requires inspecting or transforming content gets extra scrutiny before we enable it.
- Transparent pricing. $0.01/GB for European and North American traffic on the standard network, volume tiers from $0.005/GB, and a $1 monthly minimum — all public on the pricing page, no sales call required. (All figures re-checked 19-07-2026.)
- Performance, last. 119 PoPs, 250+ Tbps network capacity, ~24 ms average global latency (bunny.net/network). Comfortably more than a content site needs — but it was the fifth question, not the first.
The order is the point. Plenty of CDNs pass criterion five. Very few pass one and two first.
What Our Setup Actually Looks Like
The architecture is deliberately boring:
- Origin: all static assets — product illustrations, icons, blog OG cards — live in object storage on Exoscale SOS in Zurich (CH-DK-2), under Swiss jurisdiction.
- Edge: Bunny pull zones (fifteen of them, across our sites and services) front that origin at
cdn.ciphera.net. The CDN holds only cache copies; the origin holds truth. Swapping CDN providers would be a DNS change, not a migration. - Assets are path-keyed and immutable. Every object uploads with
Cache-Control: public, max-age=31536000, immutableand is never edited in place — a changed image gets a new path. Edges can cache for a year with zero staleness risk, and there's no revalidation traffic to leak anything about visitors. - Assets are pre-sized at upload. No on-the-fly transformation service sits in the pipeline resizing images per request — what's in storage is exactly what's served.
Belgian company, Swiss storage, EU edge. Each layer's jurisdiction is chosen, not inherited from a default.
Four Things Running It Taught Us
1. Cache keys will collapse things you didn't expect. Next.js ships an image optimizer that serves every image through query-string URLs (/_next/image?url=…&w=…&q=…). Our Bunny zone's cache configuration collapsed query strings — so one cached body was served for every image on the site. The fix is right in our next.config.ts, comment and all: images are served directly from cdn.ciphera.net, path-keyed and pre-sized, unoptimized: true, never through the optimizer. The general lesson: know exactly what your CDN includes in its cache key before you put a URL scheme that depends on query strings behind it.
2. Measure your real cache-hit rate — ours was embarrassing. When we actually audited it in March, our aggregate cache-hit rate was 8.65%. Not 86 — eight point six five. The immutable asset zones were fine; the damage came from zones and paths that were never being cached at all, silently proxying every request to origin. A CDN with a single-digit hit rate is an expensive DNS alias. We fixed the worst offenders the same day with explicit cache rules. Watch the hit rate per zone, too, not just the fleet aggregate — one silently bypassing zone hides comfortably behind nine healthy ones in an average. If you haven't looked at your numbers this quarter, they are probably lower than you think.
3. Push security rules to the edge. Internal paths — metrics endpoints, health checks, anything under /internal/ — shouldn't rely on application middleware alone to stay private. Bunny's edge rules let us block those routes at the CDN, before requests ever reach origin. Defense in depth costs one API call per rule.
4. Decide what never goes on the CDN — and write it down. Not everything belongs behind cdn.ciphera.net. Favicons, robots.txt, sitemap.xml, and PWA manifests stay at the origin, because browsers and crawlers expect them there and some flatly require it. Everything else follows a written rule: images are never committed to a frontend repo's public/ directory — they're uploaded to the bucket under a per-project prefix (website/, pulse/, id/) and referenced through a small cdnUrl() helper that falls back to local paths in development. The payoff of that discipline shows up in purging: because assets are immutable and path-versioned, cache purges are rare, deliberate, single-URL API calls — not panicked zone-wide flushes. If your team purges the whole zone more than a couple of times a year, the asset pipeline is telling you something.
What We'd Tell Another EU Team
Choose the CDN like you'd choose a payment processor — as a counterparty, not a utility. Concretely:
- Write the jurisdiction requirement down first, before benchmarks make you fall in love with someone's latency map.
- Read the DPA and the logging policy, not the marketing page. "GDPR-compliant" on a landing page is decoration; anonymization commitments in a signed DPA are a contract.
- Keep origin and edge separable. The CDN should be replaceable in an afternoon; if it isn't, you've built a dependency, not a cache.
- Treat the CDN as untrusted. Serve it opaque, immutable, path-keyed objects and let it do the one thing CDNs are great at.
- One honest caveat: Bunny's software is proprietary. We verify behavior through its DPA, its GDPR commitments, and what we can observe operationally — the same "verify, don't trust" standard we apply elsewhere is only partially available here, and pretending otherwise would be exactly the kind of claim we tell you to distrust.
Jurisdiction is also why this choice sits inside a bigger pattern for us — the same reasoning that puts our origin storage in Zurich puts our CDN in the EU. That argument, with the receipts, is in why Swiss infrastructure matters for data privacy; and the full inventory of what our stack can and can't see about you is in what we see about you, what we don't.
FAQ
Frequently Asked Questions
A CDN necessarily sees requests — that's what serving traffic means. What matters is what gets retained. Bunny's GDPR commitments include anonymized logging, and a signed DPA governs the relationship. On our side, the assets we serve through the CDN are static and identical for every visitor: no personalization, no cookies, nothing per-user in the cached content itself.
Capability-wise, Cloudflare would work. Jurisdiction-wise, it's a US company, which places it under the CLOUD Act regardless of where its servers sit. Our vendor policy ranks residency above features: Bunny is EU-based (Slovenia), which keeps the request path inside EU jurisdiction. That's a policy call, stated as such — not a claim that Cloudflare mishandles data.
The origin (Exoscale SOS in Zurich) holds the source of truth under Swiss jurisdiction; the CDN (Bunny) is a disposable cache in front of it. Either can be replaced without touching the other — the CDN never holds the only copy of anything, and swapping CDN providers is a DNS change plus a cache warm-up, not a migration.
Yes — and it's a useful mental model even for unencrypted assets. A CDN caches bytes; it doesn't need to understand them. Encrypted blobs cache exactly like images do. We treat the CDN as untrusted infrastructure on principle: it should be able to do its job — moving bytes close to users — without needing to interpret any of them.
Related Articles
Get started
Put this into practice.
Ciphera builds privacy-first infrastructure — analytics, identity, bot protection, and email that don’t surveil. The tools this article describes are the ones we run.


