Skip to content

CDN & Front Door Setup

Status: ✅ Deployed (February 2026) Profile: sonnance-cdn (Standard_AzureFrontDoor) Endpoint: sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net


Architecture

┌──────────────────┐         ┌─────────────────────────┐         ┌──────────────────────┐
│   NestJS Backend │ ──────▶ │   Azure Blob Storage    │ ──────▶ │   Azure Front Door   │
│   (file upload)  │         │   sonnancewebappstorage00│         │   (CDN edge caching) │
│                  │         │   Container: uploads     │         │                      │
│  Sharp processes │         └─────────────────────────┘         └──────────┬───────────┘
│  images into:    │                                                        │
│  • thumb (150px) │                                                        ▼
│  • medium (600px)│                                             ┌──────────────────────┐
│  • original      │                                             │   Client (Next.js)   │
└──────────────────┘                                             │   <AzureImage />     │
                                                                 │   <img>, <audio>     │
                                                                 └──────────────────────┘

Request Flow

  1. Upload: User uploads file → NestJS backend receives via multer
  2. Processing: If image, Sharp generates 3 variants (thumb 150×150 WebP, medium 600×600 WebP, original optimized WebP)
  3. Storage: All variants stored in Azure Blob Storage (uploads container) with SAS URL access
  4. Delivery: Frontend requests images via CDN URL → Front Door serves from edge cache or fetches from Blob origin
  5. Audio: Audio files follow same path but without Sharp processing (direct blob → CDN)

CDN Configuration

Front Door Resources

ComponentNameDetails
Profilesonnance-cdnSKU: Standard_AzureFrontDoor
Endpointsonnance-imagesHost: sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net
Origin Groupsonnance-blob-originHealth probe: HEAD every 120s
Originsonnance-blob-storageHost: sonnancewebappstorage00.blob.core.windows.net
Routedefault-routePattern: /*, HTTPS redirect enabled

Caching Policy

SettingValue
CachingEnabled
Query String BehaviorIncludeSpecifiedQueryStrings
Cached Query Paramssv, sp, se, sr, sig, spr (SAS token params)
CompressionEnabled for image/webp, image/jpeg, image/png, image/gif, image/svg+xml

Why SAS params as cache key? Each SAS-signed URL is unique (different expiry, signature). By caching with these params, each unique SAS URL gets its own cached copy at the edge, with automatic expiry matching the SAS token lifetime.


Image Processing Pipeline (Sharp)

The backend uses Sharp to process uploaded images into multiple variants:

VariantSizeFormatUse Case
Thumbnail150×150 pxWebPIcons, list views, player avatar
Medium600×600 pxWebPCard images, grid views
OriginalFull resolutionWebP (optimized)Full-screen, detail views

Typical compression results: ~95% reduction (e.g., 5MB → 247KB).

Backend Integration

The FileService stores 3 URLs per image:

thumbnailUrl → /uploads/<artistId>/<projectId>/<trackId>/thumb_<filename>.webp
mediumUrl    → /uploads/<artistId>/<projectId>/<trackId>/medium_<filename>.webp
blobUrl      → /uploads/<artistId>/<projectId>/<trackId>/<filename>.webp

Frontend Integration

The AzureImage component (azure-image.tsx) handles CDN URL construction and fallback logic:

  • Prefixes blob paths with the CDN hostname
  • Handles responsive sizing (sizes prop)
  • Provides fallback to direct blob URL if CDN is unavailable

In next.config.mjs, the CDN hostname is whitelisted for next/image optimization:

js
images: {
  remotePatterns: [
    { hostname: 'sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net' }
  ]
}

Environment Variables

VariableBackendFrontend
CDN_URLsonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net
AZURE_STORAGE_ACCOUNT_NAMEsonnancewebappstorage00
AZURE_STORAGE_CONTAINER_NAMEuploads

The CDN URL is set on both prod and dev Container Apps.


Cache Management

Purge CDN Cache

If you need to invalidate cached content:

bash
az afd endpoint purge \
  --profile-name sonnance-cdn \
  --resource-group Sonnance-WebApp \
  --endpoint-name sonnance-images \
  --content-paths "/*"

Browser Caching

ReactQuery on the frontend uses:

  • staleTime: 5 minutes — data is fresh for 5 min
  • gcTime: 30 minutes — unused data stays in cache for 30 min

Setup & Maintenance

Initial Setup

bash
cd infrastructure
chmod +x setup-cdn.sh
./setup-cdn.sh

Verify CDN is Working

bash
# Test with any blob + SAS params
curl -I "https://sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net/uploads/<blob-path>?<sas-params>"

# Look for:
# x-cache: TCP_HIT (served from edge)
# or x-cache: TCP_MISS (fetched from origin, now cached)

Cost

Azure Front Door Standard is usage-based:

  • Base fee: ~$5/month per profile
  • Data transfer: First 5GB/month free, then ~$0.08/GB
  • Requests: First 10M/month included

For current dev-stage usage, expect ~$5–10/month.

Ctrl-Audio Platform Documentation