bunny.net — CDN¶
OWNER: karel
ALSO_USED_BY: stef (DNS/certs)
LAST_VERIFIED: 2026-03-26
GE_STACK_VERSION: bunnynet Terraform provider (BunnyWay/bunnynet) latest
Overview¶
bunny.net CDN is the first layer all client traffic hits.
Pull zones define how content is cached and served from 119+ global PoPs.
GE uses one pull zone per client project for isolation.
Origin servers are UpCloud Managed Load Balancers (Zones 2+3).
Pull Zones¶
A pull zone is a CDN distribution that pulls content from an origin server.
resource "bunnynet_pullzone" "client_project" {
name = "ge-${var.client}-${var.project}"
origin_url = "https://${var.origin_hostname}"
origin_type = 0 # URL origin
enable_geo_zone_eu = true
enable_geo_zone_us = true
enable_geo_zone_asia = false # Enable per client requirements
cache_control_max_age_override = 2592000 # 30 days
cache_control_browser_max_age = 86400 # 1 day
enable_origin_shield = true
origin_shield_zone = "NL" # Amsterdam — closest to UpCloud origin
add_host_header = var.origin_hostname
enable_tls_1_3 = true
enable_smart_cache = true
blocked_countries = [] # Empty = allow all
}
CHECK: every pull zone has enable_origin_shield = true
CHECK: origin shield zone matches UpCloud region (NL for nl-ams1)
CHECK: TLS 1.3 is enabled
CHECK: one pull zone per client project — never shared across clients
IF: creating a new client project
THEN: create a new pull zone via Terraform
THEN: enable Bunny Shield on the pull zone
READ_ALSO: wiki/docs/stack/bunnynet/security.md
Origin Shield¶
Origin Shield adds a caching layer between edge PoPs and the origin server.
Instead of 119 PoPs each requesting from origin, only the shield does.
This dramatically reduces origin load.
IF: origin shield is NL
THEN: cache miss at any PoP → NL shield → origin (UpCloud Amsterdam)
THEN: only one request reaches origin per cache miss globally
CHECK: origin shield zone is NL for all GE pull zones
CHECK: origin server can handle shield traffic (not scaled for per-PoP requests)
ANTI_PATTERN: disabling origin shield to "speed up" cache invalidation
FIX: origin shield reduces origin load by 80-90% — always keep it on
Cache Rules¶
Default Cache Behaviour¶
bunny.net respects origin Cache-Control headers by default.
GE overrides with CDN-level settings for consistency.
IF: static assets (images, CSS, JS, fonts)
THEN: cache for 30 days at edge, 1 day in browser
IF: HTML pages with ISR
THEN: cache based on origin s-maxage header (Next.js ISR revalidation)
IF: API responses
THEN: do not cache — set Cache-Control: no-store at origin
Vary Header¶
CHECK: origin sends Vary: Accept-Encoding for text-based assets
CHECK: do NOT vary on User-Agent — explodes cache cardinality
ANTI_PATTERN: Vary: * — disables caching entirely
FIX: be specific: Vary: Accept-Encoding or Vary: Accept
Query String Caching¶
resource "bunnynet_pullzone" "example" {
# ...
enable_query_string_sort = true # Normalise query order
ignore_query_strings = false # Cache per unique query
}
IF: query strings are used for cache busting (?v=abc123)
THEN: keep ignore_query_strings = false
IF: query strings are irrelevant to content
THEN: set ignore_query_strings = true to improve hit ratio
Perma-Cache¶
Perma-Cache stores content permanently at the edge. Even if origin goes down,
cached content continues to serve. GE enables this for critical client sites.
resource "bunnynet_pullzone" "example" {
# ...
enable_cache_slice = true
cache_error_responses = false # Do not cache 5xx
}
CHECK: cache_error_responses = false — never cache error pages
IF: origin is down
THEN: perma-cache serves stale content with stale-if-error behaviour
Custom Hostnames¶
Each client project has its own domain pointing to the pull zone.
resource "bunnynet_pullzone_hostname" "client_domain" {
pullzone_id = bunnynet_pullzone.client_project.id
hostname = "app.${var.client_domain}"
force_ssl = true
}
CHECK: force_ssl = true on all custom hostnames
CHECK: DNS CNAME points to {pullzone}.b-cdn.net
CHECK: free automatic SSL certificate provisioned by bunny.net
IF: SSL certificate not provisioning
THEN: verify DNS CNAME is correctly pointed
THEN: wait up to 15 minutes for propagation
READ_ALSO: wiki/docs/stack/bunnynet/pitfalls.md
Cache Purge¶
IF: need to invalidate cached content
THEN: purge via Terraform or API, never the dashboard
# Purge entire pull zone
curl -X POST "https://api.bunny.net/pullzone/{id}/purgeCache" \
-H "AccessKey: ${BUNNYNET_API_KEY}"
# Purge specific URL
curl -X POST "https://api.bunny.net/purge?url=https://app.example.com/path" \
-H "AccessKey: ${BUNNYNET_API_KEY}"
CHECK: purge specific URLs when possible, not entire pull zones
CHECK: monitor origin load after purge — cache refill causes traffic spike
Monitoring¶
bunny.net provides per-pull-zone analytics:
- Bandwidth usage
- Cache hit ratio
- Request count by status code
- Geographic distribution
CHECK: cache hit ratio above 90% for static assets
IF: hit ratio below 80%
THEN: review cache rules, check Vary headers, check query string settings
Cross-References¶
READ_ALSO: wiki/docs/stack/bunnynet/index.md
READ_ALSO: wiki/docs/stack/bunnynet/security.md
READ_ALSO: wiki/docs/stack/bunnynet/edge.md
READ_ALSO: wiki/docs/stack/bunnynet/pitfalls.md
READ_ALSO: wiki/docs/stack/bunnynet/checklist.md
READ_ALSO: wiki/docs/stack/kubernetes/networking.md