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
- Upload: User uploads file → NestJS backend receives via multer
- Processing: If image, Sharp generates 3 variants (thumb 150×150 WebP, medium 600×600 WebP, original optimized WebP)
- Storage: All variants stored in Azure Blob Storage (
uploadscontainer) with SAS URL access - Delivery: Frontend requests images via CDN URL → Front Door serves from edge cache or fetches from Blob origin
- Audio: Audio files follow same path but without Sharp processing (direct blob → CDN)
CDN Configuration
Front Door Resources
| Component | Name | Details |
|---|---|---|
| Profile | sonnance-cdn | SKU: Standard_AzureFrontDoor |
| Endpoint | sonnance-images | Host: sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net |
| Origin Group | sonnance-blob-origin | Health probe: HEAD every 120s |
| Origin | sonnance-blob-storage | Host: sonnancewebappstorage00.blob.core.windows.net |
| Route | default-route | Pattern: /*, HTTPS redirect enabled |
Caching Policy
| Setting | Value |
|---|---|
| Caching | Enabled |
| Query String Behavior | IncludeSpecifiedQueryStrings |
| Cached Query Params | sv, sp, se, sr, sig, spr (SAS token params) |
| Compression | Enabled 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:
| Variant | Size | Format | Use Case |
|---|---|---|---|
| Thumbnail | 150×150 px | WebP | Icons, list views, player avatar |
| Medium | 600×600 px | WebP | Card images, grid views |
| Original | Full resolution | WebP (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>.webpFrontend Integration
The AzureImage component (azure-image.tsx) handles CDN URL construction and fallback logic:
- Prefixes blob paths with the CDN hostname
- Handles responsive sizing (
sizesprop) - Provides fallback to direct blob URL if CDN is unavailable
In next.config.mjs, the CDN hostname is whitelisted for next/image optimization:
images: {
remotePatterns: [
{ hostname: 'sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net' }
]
}Environment Variables
| Variable | Backend | Frontend |
|---|---|---|
CDN_URL | sonnance-images-hvcsepc8dkhrbjfp.z01.azurefd.net | — |
AZURE_STORAGE_ACCOUNT_NAME | sonnancewebappstorage00 | — |
AZURE_STORAGE_CONTAINER_NAME | uploads | — |
The CDN URL is set on both prod and dev Container Apps.
Cache Management
Purge CDN Cache
If you need to invalidate cached content:
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 mingcTime: 30 minutes— unused data stays in cache for 30 min
Setup & Maintenance
Initial Setup
cd infrastructure
chmod +x setup-cdn.sh
./setup-cdn.shVerify CDN is Working
# 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.