6 min read
Accelerating Content Delivery with Azure CDN
Azure Content Delivery Network (CDN) delivers content to users globally with low latency by caching content at strategically placed edge locations. It integrates seamlessly with Azure services and supports advanced features like custom domains, HTTPS, and rules engines.
CDN Offerings
| Provider | Features | Use Case |
|---|---|---|
| Standard Microsoft | Basic caching, rules engine | General purpose |
| Standard Verizon | Advanced analytics | Media streaming |
| Standard Akamai | Large file optimization | Software distribution |
| Premium Verizon | Advanced rules, real-time analytics | Enterprise |
Creating a CDN Profile and Endpoint
from azure.mgmt.cdn import CdnManagementClient
from azure.mgmt.cdn.models import (
Profile,
Sku,
Endpoint,
DeepCreatedOrigin,
QueryStringCachingBehavior
)
from azure.identity import DefaultAzureCredential
class CDNManager:
def __init__(self, subscription_id: str, resource_group: str):
self.credential = DefaultAzureCredential()
self.client = CdnManagementClient(self.credential, subscription_id)
self.resource_group = resource_group
def create_profile(self, profile_name: str, sku: str = "Standard_Microsoft") -> Profile:
"""Create a CDN profile."""
profile = Profile(
location="global",
sku=Sku(name=sku)
)
return self.client.profiles.begin_create(
self.resource_group,
profile_name,
profile
).result()
def create_endpoint(self, profile_name: str, endpoint_name: str,
origin_hostname: str,
origin_path: str = None) -> Endpoint:
"""Create a CDN endpoint."""
endpoint = Endpoint(
location="global",
origins=[
DeepCreatedOrigin(
name="origin1",
host_name=origin_hostname,
http_port=80,
https_port=443,
origin_host_header=origin_hostname
)
],
origin_path=origin_path,
is_http_allowed=True,
is_https_allowed=True,
query_string_caching_behavior=QueryStringCachingBehavior.IGNORE_QUERY_STRING,
is_compression_enabled=True,
content_types_to_compress=[
"text/plain",
"text/html",
"text/css",
"text/javascript",
"application/javascript",
"application/json",
"application/xml"
]
)
return self.client.endpoints.begin_create(
self.resource_group,
profile_name,
endpoint_name,
endpoint
).result()
def purge_content(self, profile_name: str, endpoint_name: str,
paths: list):
"""Purge cached content."""
return self.client.endpoints.begin_purge_content(
self.resource_group,
profile_name,
endpoint_name,
{"content_paths": paths}
).result()
def load_content(self, profile_name: str, endpoint_name: str,
paths: list):
"""Pre-load content into cache."""
return self.client.endpoints.begin_load_content(
self.resource_group,
profile_name,
endpoint_name,
{"content_paths": paths}
).result()
# Usage
cdn_manager = CDNManager("subscription-id", "cdn-rg")
# Create profile
cdn_manager.create_profile("mycdn-profile")
# Create endpoint for blob storage
cdn_manager.create_endpoint(
"mycdn-profile",
"static-content",
"mystorageaccount.blob.core.windows.net",
"/public"
)
Configuring Custom Domains
from azure.mgmt.cdn.models import (
CustomDomain,
CustomDomainHttpsParameters,
CdnManagedHttpsParameters,
CertificateType,
ProtocolType,
MinimumTlsVersion
)
def add_custom_domain(cdn_manager: CDNManager, profile_name: str,
endpoint_name: str, custom_domain: str):
"""Add a custom domain to CDN endpoint."""
# Custom domain name (without protocol)
domain_name = custom_domain.replace(".", "-")
custom_domain_obj = CustomDomain(
host_name=custom_domain
)
# Create custom domain
result = cdn_manager.client.custom_domains.begin_create(
cdn_manager.resource_group,
profile_name,
endpoint_name,
domain_name,
custom_domain_obj
).result()
return result
def enable_https(cdn_manager: CDNManager, profile_name: str,
endpoint_name: str, domain_name: str):
"""Enable HTTPS with CDN-managed certificate."""
https_params = CdnManagedHttpsParameters(
certificate_source="Cdn",
protocol_type=ProtocolType.SERVER_NAME_INDICATION,
minimum_tls_version=MinimumTlsVersion.TLS12,
certificate_source_parameters={
"certificate_type": CertificateType.DEDICATED
}
)
return cdn_manager.client.custom_domains.begin_enable_custom_https(
cdn_manager.resource_group,
profile_name,
endpoint_name,
domain_name,
https_params
).result()
# Add custom domain
add_custom_domain(cdn_manager, "mycdn-profile", "static-content", "cdn.example.com")
# Enable HTTPS
enable_https(cdn_manager, "mycdn-profile", "static-content", "cdn-example-com")
Rules Engine Configuration
from azure.mgmt.cdn.models import (
DeliveryRule,
DeliveryRuleCondition,
UrlFileExtensionMatchConditionParameters,
UrlPathMatchConditionParameters,
DeliveryRuleCacheExpirationAction,
CacheExpirationActionParameters,
HeaderAction,
HeaderActionParameters,
UrlRedirectAction,
UrlRedirectActionParameters
)
def configure_caching_rules(cdn_manager: CDNManager, profile_name: str,
endpoint_name: str):
"""Configure caching rules for different content types."""
rules = []
# Rule 1: Cache static assets for 30 days
rules.append(DeliveryRule(
name="CacheStaticAssets",
order=1,
conditions=[
{
"name": "UrlFileExtension",
"parameters": UrlFileExtensionMatchConditionParameters(
operator="Equal",
match_values=["css", "js", "png", "jpg", "jpeg", "gif", "svg", "woff", "woff2"]
)
}
],
actions=[
DeliveryRuleCacheExpirationAction(
parameters=CacheExpirationActionParameters(
cache_behavior="Override",
cache_type="All",
cache_duration="30.00:00:00"
)
)
]
))
# Rule 2: Don't cache API responses
rules.append(DeliveryRule(
name="NoCacheAPI",
order=2,
conditions=[
{
"name": "UrlPath",
"parameters": UrlPathMatchConditionParameters(
operator="BeginsWith",
match_values=["/api/"]
)
}
],
actions=[
DeliveryRuleCacheExpirationAction(
parameters=CacheExpirationActionParameters(
cache_behavior="BypassCache"
)
)
]
))
# Rule 3: Add security headers
rules.append(DeliveryRule(
name="SecurityHeaders",
order=3,
conditions=[],
actions=[
HeaderAction(
parameters=HeaderActionParameters(
header_action="Append",
header_name="X-Content-Type-Options",
value="nosniff"
)
),
HeaderAction(
parameters=HeaderActionParameters(
header_action="Append",
header_name="X-Frame-Options",
value="DENY"
)
),
HeaderAction(
parameters=HeaderActionParameters(
header_action="Append",
header_name="Strict-Transport-Security",
value="max-age=31536000; includeSubDomains"
)
)
]
))
# Rule 4: Redirect HTTP to HTTPS
rules.append(DeliveryRule(
name="HttpsRedirect",
order=4,
conditions=[
{
"name": "RequestScheme",
"parameters": {"operator": "Equal", "match_values": ["HTTP"]}
}
],
actions=[
UrlRedirectAction(
parameters=UrlRedirectActionParameters(
redirect_type="Found",
destination_protocol="Https"
)
)
]
))
# Update endpoint with rules
endpoint = cdn_manager.client.endpoints.get(
cdn_manager.resource_group,
profile_name,
endpoint_name
)
endpoint.delivery_policy = {"rules": rules}
return cdn_manager.client.endpoints.begin_update(
cdn_manager.resource_group,
profile_name,
endpoint_name,
endpoint
).result()
CDN Integration with Storage
from azure.storage.blob import BlobServiceClient, generate_blob_sas, BlobSasPermissions
from datetime import datetime, timedelta
class CDNStorageIntegration:
def __init__(self, storage_connection: str, cdn_hostname: str):
self.blob_service = BlobServiceClient.from_connection_string(storage_connection)
self.cdn_hostname = cdn_hostname
def get_cdn_url(self, container: str, blob_name: str) -> str:
"""Get CDN URL for a blob."""
return f"https://{self.cdn_hostname}/{container}/{blob_name}"
def upload_with_cache_headers(self, container: str, blob_name: str,
data: bytes, content_type: str,
cache_control: str = "public, max-age=86400"):
"""Upload blob with cache-control headers."""
container_client = self.blob_service.get_container_client(container)
blob_client = container_client.get_blob_client(blob_name)
blob_client.upload_blob(
data,
content_settings={
"content_type": content_type,
"cache_control": cache_control
},
overwrite=True
)
return self.get_cdn_url(container, blob_name)
def upload_versioned(self, container: str, blob_name: str,
data: bytes, version: str) -> str:
"""Upload with version in filename for cache busting."""
# Add version to filename
name_parts = blob_name.rsplit('.', 1)
if len(name_parts) == 2:
versioned_name = f"{name_parts[0]}.{version}.{name_parts[1]}"
else:
versioned_name = f"{blob_name}.{version}"
return self.upload_with_cache_headers(
container,
versioned_name,
data,
"application/octet-stream",
"public, max-age=31536000, immutable" # 1 year, immutable
)
# Usage
cdn_storage = CDNStorageIntegration(
"your-storage-connection",
"mycdn.azureedge.net"
)
# Upload static assets
with open("styles.css", "rb") as f:
url = cdn_storage.upload_versioned(
"static",
"styles.css",
f.read(),
"v1.2.3"
)
print(f"CDN URL: {url}")
Monitoring and Analytics
from azure.monitor.query import MetricsQueryClient, LogsQueryClient
from datetime import datetime, timedelta
def get_cdn_metrics(metrics_client: MetricsQueryClient,
endpoint_resource_id: str,
hours: int = 24) -> dict:
"""Get CDN endpoint metrics."""
end_time = datetime.utcnow()
start_time = end_time - timedelta(hours=hours)
metrics = metrics_client.query_resource(
endpoint_resource_id,
metric_names=[
"ByteHitRatio",
"RequestCount",
"ResponseSize",
"OriginLatency"
],
timespan=(start_time, end_time),
granularity=timedelta(hours=1)
)
results = {}
for metric in metrics.metrics:
values = []
for series in metric.timeseries:
for data in series.data:
values.append({
"timestamp": data.timestamp,
"average": data.average,
"total": data.total
})
results[metric.name] = values
return results
def generate_cdn_report(metrics: dict) -> str:
"""Generate CDN performance report."""
report = []
report.append("=== CDN Performance Report ===\n")
if "ByteHitRatio" in metrics:
avg_hit_ratio = sum(v["average"] or 0 for v in metrics["ByteHitRatio"]) / max(len(metrics["ByteHitRatio"]), 1)
report.append(f"Cache Hit Ratio: {avg_hit_ratio:.1f}%")
if "RequestCount" in metrics:
total_requests = sum(v["total"] or 0 for v in metrics["RequestCount"])
report.append(f"Total Requests: {total_requests:,}")
if "ResponseSize" in metrics:
total_bytes = sum(v["total"] or 0 for v in metrics["ResponseSize"])
report.append(f"Data Transferred: {total_bytes / (1024**3):.2f} GB")
if "OriginLatency" in metrics:
avg_latency = sum(v["average"] or 0 for v in metrics["OriginLatency"]) / max(len(metrics["OriginLatency"]), 1)
report.append(f"Average Origin Latency: {avg_latency:.0f} ms")
return "\n".join(report)
Best Practices
- Enable Compression: Compress text-based content
- Set Cache Headers: Use appropriate cache-control headers
- Version Assets: Include version in filenames for cache busting
- Use HTTPS: Always enable HTTPS with modern TLS
- Monitor Hit Ratio: Aim for >90% cache hit ratio
- Purge Strategically: Purge only what’s necessary
Azure CDN significantly improves application performance by reducing latency and offloading traffic from origin servers.