Marketplace
cdn-media-delivery
Use when configuring CDN for media delivery, implementing cache invalidation, or designing signed URL patterns. Covers CDN configuration, edge caching, origin shielding, and secure media access for headless CMS.
allowed_tools: Read, Glob, Grep, Task, Skill
$ 설치
git clone https://github.com/melodic-software/claude-code-plugins /tmp/claude-code-plugins && cp -r /tmp/claude-code-plugins/plugins/content-management-system/skills/cdn-media-delivery ~/.claude/skills/claude-code-plugins// tip: Run this command in your terminal to install the skill
SKILL.md
name: cdn-media-delivery description: Use when configuring CDN for media delivery, implementing cache invalidation, or designing signed URL patterns. Covers CDN configuration, edge caching, origin shielding, and secure media access for headless CMS. allowed-tools: Read, Glob, Grep, Task, Skill
CDN Media Delivery
Guidance for configuring CDN delivery, cache management, and secure media access for headless CMS architectures.
When to Use This Skill
- Configuring CDN for media delivery
- Implementing cache invalidation strategies
- Setting up signed/secure URLs
- Optimizing edge caching
- Configuring origin shielding
CDN Architecture
Basic CDN Setup
┌─────────────────────────────────────────────────────────────┐
│ Users │
│ (Global, geographically distributed) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CDN Edge Network │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Edge │ │ Edge │ │ Edge │ │ Edge │ │
│ │ US-West │ │ US-East │ │ Europe │ │ Asia │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Origin Shield │
│ (Optional intermediate cache layer) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Origin Server │
│ ┌───────────────┐ ┌────────────────┐ ┌───────────────┐ │
│ │ Media API │ │ Blob Storage │ │ Image │ │
│ │ (transform) │ │ (Azure/S3) │ │ Processor │ │
│ └───────────────┘ └────────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────┘
CDN Configuration
Azure CDN (Front Door)
// appsettings.json
{
"Cdn": {
"Provider": "AzureFrontDoor",
"Endpoint": "https://media.example.com",
"OriginHost": "storage.blob.core.windows.net",
"CacheRules": {
"Images": {
"CacheDuration": "365.00:00:00",
"QueryStringCaching": "IgnoreQueryString"
},
"Transforms": {
"CacheDuration": "30.00:00:00",
"QueryStringCaching": "UseQueryString"
}
}
}
}
CloudFront Configuration
public class CloudFrontConfiguration
{
public string DistributionId { get; set; } = string.Empty;
public string DomainName { get; set; } = string.Empty;
public string OriginId { get; set; } = string.Empty;
public CacheBehavior DefaultCacheBehavior { get; set; } = new()
{
ViewerProtocolPolicy = "redirect-to-https",
CachePolicyId = "658327ea-f89d-4fab-a63d-7e88639e58f6", // CachingOptimized
Compress = true,
AllowedMethods = new[] { "GET", "HEAD", "OPTIONS" },
CachedMethods = new[] { "GET", "HEAD" }
};
public CacheBehavior[] CacheBehaviors { get; set; } =
{
new()
{
PathPattern = "/media/transform/*",
CachePolicyId = "custom-transform-policy",
QueryStringCaching = QueryStringCaching.All
}
};
}
Cloudflare Configuration
public class CloudflareConfiguration
{
public string ZoneId { get; set; } = string.Empty;
public string ApiToken { get; set; } = string.Empty;
public PageRule[] PageRules { get; set; } =
{
new()
{
Targets = new[] { "*example.com/media/*" },
Actions = new PageRuleAction
{
CacheLevel = "cache_everything",
EdgeCacheTtl = 2592000, // 30 days
BrowserCacheTtl = 86400 // 1 day
}
}
};
}
Cache Headers
Setting Cache Headers
public class MediaCacheMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
await next(context);
if (context.Request.Path.StartsWithSegments("/media"))
{
var cacheControl = GetCacheControl(context.Request.Path);
context.Response.Headers["Cache-Control"] = cacheControl;
context.Response.Headers["Vary"] = "Accept, Accept-Encoding";
}
}
private string GetCacheControl(PathString path)
{
// Original media: cache for 1 year (immutable content)
if (path.Value?.Contains("/original/") == true)
{
return "public, max-age=31536000, immutable";
}
// Transformed images: cache for 30 days
if (path.Value?.Contains("/transform/") == true)
{
return "public, max-age=2592000, stale-while-revalidate=86400";
}
// Default: 1 day
return "public, max-age=86400";
}
}
Cache-Control Directives
| Directive | Purpose | Example |
|---|---|---|
public | Allow CDN caching | Images, static assets |
private | Browser only | User-specific content |
max-age | Cache duration (seconds) | max-age=86400 (1 day) |
immutable | Never revalidate | Versioned assets |
stale-while-revalidate | Serve stale while fetching | Background refresh |
no-cache | Always revalidate | Dynamic content |
no-store | Never cache | Sensitive data |
Cache Invalidation
Invalidation Service
public interface ICdnInvalidationService
{
Task InvalidatePathAsync(string path);
Task InvalidatePathsAsync(IEnumerable<string> paths);
Task InvalidatePrefixAsync(string prefix);
Task InvalidateAllAsync();
}
// Azure CDN implementation
public class AzureCdnInvalidationService : ICdnInvalidationService
{
private readonly CdnManagementClient _cdnClient;
public async Task InvalidatePathAsync(string path)
{
await _cdnClient.Endpoints.PurgeContentAsync(
_resourceGroup,
_profileName,
_endpointName,
new PurgeParameters(new[] { path }));
}
public async Task InvalidatePrefixAsync(string prefix)
{
await _cdnClient.Endpoints.PurgeContentAsync(
_resourceGroup,
_profileName,
_endpointName,
new PurgeParameters(new[] { $"{prefix}/*" }));
}
}
// CloudFront implementation
public class CloudFrontInvalidationService : ICdnInvalidationService
{
private readonly AmazonCloudFrontClient _client;
public async Task InvalidatePathAsync(string path)
{
var request = new CreateInvalidationRequest
{
DistributionId = _distributionId,
InvalidationBatch = new InvalidationBatch
{
CallerReference = Guid.NewGuid().ToString(),
Paths = new Paths
{
Items = new List<string> { path },
Quantity = 1
}
}
};
await _client.CreateInvalidationAsync(request);
}
}
Event-Based Invalidation
public class MediaUpdatedHandler : INotificationHandler<MediaUpdatedEvent>
{
private readonly ICdnInvalidationService _cdn;
public async Task Handle(MediaUpdatedEvent notification, CancellationToken ct)
{
// Invalidate original
await _cdn.InvalidatePathAsync($"/media/{notification.MediaId}");
// Invalidate all transformations
await _cdn.InvalidatePrefixAsync($"/media/transform/{notification.MediaId}");
}
}
Signed URLs
Signed URL Generation
public class SignedUrlService
{
public string GenerateSignedUrl(
string path,
TimeSpan validity,
SignedUrlOptions? options = null)
{
options ??= new SignedUrlOptions();
var expiry = DateTime.UtcNow.Add(validity);
var expiryTimestamp = new DateTimeOffset(expiry).ToUnixTimeSeconds();
// Build URL with parameters
var urlBuilder = new UriBuilder($"{_cdnBaseUrl}{path}");
var query = HttpUtility.ParseQueryString(urlBuilder.Query);
query["expires"] = expiryTimestamp.ToString();
if (options.AllowedIp != null)
{
query["ip"] = options.AllowedIp;
}
// Generate signature
var signatureData = $"{path}|{expiryTimestamp}|{options.AllowedIp}";
var signature = ComputeSignature(signatureData);
query["signature"] = signature;
urlBuilder.Query = query.ToString();
return urlBuilder.ToString();
}
private string ComputeSignature(string data)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_signingKey));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(hash)
.Replace("+", "-")
.Replace("/", "_")
.TrimEnd('=');
}
}
public class SignedUrlOptions
{
public string? AllowedIp { get; set; }
public string? AllowedCountry { get; set; }
public int? MaxDownloads { get; set; }
}
Signed URL Validation
public class SignedUrlValidationMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (RequiresSignedUrl(context.Request.Path))
{
var query = context.Request.Query;
// Check expiry
if (!long.TryParse(query["expires"], out var expiry) ||
DateTimeOffset.UtcNow.ToUnixTimeSeconds() > expiry)
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("URL expired");
return;
}
// Validate signature
var expectedSignature = ComputeSignature(
context.Request.Path,
expiry,
query["ip"]);
if (query["signature"] != expectedSignature)
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Invalid signature");
return;
}
// Check IP restriction
if (!string.IsNullOrEmpty(query["ip"]))
{
var clientIp = context.Connection.RemoteIpAddress?.ToString();
if (clientIp != query["ip"])
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("IP not allowed");
return;
}
}
}
await next(context);
}
}
Origin Shielding
Shield Configuration
public class OriginShieldConfiguration
{
public bool Enabled { get; set; } = true;
public string ShieldRegion { get; set; } = "us-east-1";
public int ShieldCacheTtl { get; set; } = 3600; // 1 hour
public int MaxConnectionsToOrigin { get; set; } = 100;
}
Benefits
| Feature | Without Shield | With Shield |
|---|---|---|
| Origin requests | From each edge | From one region |
| Cache efficiency | Per-edge | Shared shield cache |
| Origin load | High | Reduced 90%+ |
| Latency | Variable | Predictable |
CDN URL Generation
URL Service
public class CdnUrlService
{
public string GetMediaUrl(MediaItem media, MediaUrlOptions? options = null)
{
options ??= new MediaUrlOptions();
var path = $"/media/{media.StoragePath}";
// Add transformation query params
if (options.Width.HasValue || options.Height.HasValue)
{
var query = new List<string>();
if (options.Width.HasValue) query.Add($"w={options.Width}");
if (options.Height.HasValue) query.Add($"h={options.Height}");
if (options.Format.HasValue) query.Add($"format={options.Format}");
if (options.Quality.HasValue) query.Add($"q={options.Quality}");
path += "?" + string.Join("&", query);
}
// Generate signed URL if private
if (media.IsPrivate || options.RequireSignature)
{
return _signedUrlService.GenerateSignedUrl(
path,
options.UrlValidity ?? TimeSpan.FromHours(1));
}
return $"{_cdnBaseUrl}{path}";
}
}
public class MediaUrlOptions
{
public int? Width { get; set; }
public int? Height { get; set; }
public ImageFormat? Format { get; set; }
public int? Quality { get; set; }
public bool RequireSignature { get; set; }
public TimeSpan? UrlValidity { get; set; }
}
Performance Monitoring
CDN Metrics
public class CdnMetrics
{
public long TotalRequests { get; set; }
public long CacheHits { get; set; }
public long CacheMisses { get; set; }
public double CacheHitRatio => (double)CacheHits / TotalRequests;
public long BandwidthBytes { get; set; }
public double AverageLatencyMs { get; set; }
public Dictionary<string, long> RequestsByRegion { get; set; } = new();
public Dictionary<int, long> StatusCodeCounts { get; set; } = new();
}
Related Skills
media-asset-management- Media storage and organizationimage-optimization- Image processing before CDNheadless-api-design- Media API endpoints
Repository

melodic-software
Author
melodic-software/claude-code-plugins/plugins/content-management-system/skills/cdn-media-delivery
3
Stars
0
Forks
Updated1d ago
Added6d ago