Medusa Commerce CDN
Medusa Commerce CDN Configuration CloudFront Image Optimization Cache Strategy Edge Computing Headless E-commerce Node.js TypeScript Performance Global
| CDN | Image Optimization | Edge Compute | ราคา | เหมาะกับ |
|---|---|---|---|---|
| CloudFront | Lambda@Edge | CloudFront Functions | Pay-per-use | AWS Users |
| Cloudflare | Polish + Resize | Workers | Free / $20/mo | ทั่วไป |
| Fastly | Image Optimizer | Compute@Edge | Pay-per-use | Enterprise |
| Vercel | Built-in next/image | Edge Functions | Free / $20/mo | Next.js |
| Bunny CDN | Bunny Optimizer | Edge Scripting | $0.01/GB | Budget |
CloudFront Setup
# === CloudFront CDN for Medusa ===
# AWS CDK — CloudFront Distribution
# import * as cdk from 'aws-cdk-lib';
# import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
# import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
# import * as acm from 'aws-cdk-lib/aws-certificatemanager';
# import * as s3 from 'aws-cdk-lib/aws-s3';
#
# // S3 Bucket for product images
# const imageBucket = new s3.Bucket(this, 'ProductImages', {
# bucketName: 'medusa-product-images',
# cors: [{
# allowedMethods: [s3.HttpMethod.GET],
# allowedOrigins: ['https://shop.example.com'],
# allowedHeaders: ['*'],
# }],
# });
#
# // CloudFront Distribution
# const distribution = new cloudfront.Distribution(this, 'CDN', {
# defaultBehavior: {
# origin: new origins.HttpOrigin('api.medusa.example.com', {
# protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
# }),
# cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
# viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
# },
# additionalBehaviors: {
# '/images/*': {
# origin: new origins.S3Origin(imageBucket),
# cachePolicy: new cloudfront.CachePolicy(this, 'ImageCache', {
# defaultTtl: cdk.Duration.days(365),
# maxTtl: cdk.Duration.days(365),
# queryStringBehavior: cloudfront.CacheQueryStringBehavior.allowList('w', 'h', 'q', 'f'),
# }),
# viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
# },
# '/static/*': {
# origin: new origins.S3Origin(imageBucket),
# cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
# },
# },
# domainNames: ['shop.example.com'],
# certificate: acm.Certificate.fromCertificateArn(this, 'Cert', 'arn:aws:acm:...'),
# });
# Nginx Reverse Proxy — Medusa Origin
# server {
# listen 443 ssl http2;
# server_name api.medusa.example.com;
#
# location /images/ {
# proxy_pass http://s3-bucket.s3.amazonaws.com/;
# proxy_cache_valid 200 365d;
# add_header Cache-Control "public, max-age=31536000, immutable";
# add_header X-Cache-Status $upstream_cache_status;
# }
#
# location /store/ {
# proxy_pass http://localhost:9000;
# proxy_cache_valid 200 60s;
# add_header Cache-Control "public, max-age=60, s-maxage=300";
# }
# }
from dataclasses import dataclass
@dataclass
class CacheRule:
path: str
cache_ttl: str
cache_control: str
invalidation: str
compression: bool
rules = [
CacheRule("/images/*", "365 days", "public, max-age=31536000, immutable", "On upload", True),
CacheRule("/static/*", "365 days", "public, max-age=31536000, immutable", "On deploy", True),
CacheRule("/store/products", "5 min", "public, s-maxage=300", "On product update", True),
CacheRule("/store/collections", "15 min", "public, s-maxage=900", "On collection update", True),
CacheRule("/store/cart", "No cache", "private, no-cache", "N/A", False),
CacheRule("/admin/*", "No cache", "private, no-store", "N/A", False),
]
print("=== CDN Cache Rules ===")
for r in rules:
gz = "Brotli+Gzip" if r.compression else "None"
print(f" [{r.path}] TTL: {r.cache_ttl}")
print(f" Cache-Control: {r.cache_control}")
print(f" Invalidation: {r.invalidation} | Compression: {gz}")
Image Optimization
# === Image Optimization Pipeline ===
# CloudFront Function — Image Resize URL
# // viewer-request function
# function handler(event) {
# var request = event.request;
# var params = request.querystring;
#
# // /images/product.jpg?w=400&h=300&q=80&f=webp
# if (params.w || params.h) {
# request.uri = '/resize' + request.uri;
# request.headers['x-resize-width'] = { value: params.w?.value || '0' };
# request.headers['x-resize-height'] = { value: params.h?.value || '0' };
# request.headers['x-resize-quality'] = { value: params.q?.value || '80' };
# request.headers['x-resize-format'] = { value: params.f?.value || 'webp' };
# }
# return request;
# }
# Sharp — Server-side Image Processing
# const sharp = require('sharp');
#
# async function resizeImage(buffer, width, height, quality, format) {
# let pipeline = sharp(buffer);
#
# if (width || height) {
# pipeline = pipeline.resize(width || null, height || null, {
# fit: 'inside',
# withoutEnlargement: true,
# });
# }
#
# switch (format) {
# case 'webp': pipeline = pipeline.webp({ quality }); break;
# case 'avif': pipeline = pipeline.avif({ quality }); break;
# default: pipeline = pipeline.jpeg({ quality }); break;
# }
#
# return pipeline.toBuffer();
# }
@dataclass
class ImageFormat:
format_name: str
compression: str
quality_range: str
size_vs_jpeg: str
browser_support: str
formats = [
ImageFormat("JPEG", "Lossy", "60-90", "Baseline", "100%"),
ImageFormat("WebP", "Lossy/Lossless", "70-85", "25-35% smaller", "97%"),
ImageFormat("AVIF", "Lossy/Lossless", "50-75", "40-50% smaller", "92%"),
ImageFormat("PNG", "Lossless", "N/A", "2-5x larger", "100%"),
ImageFormat("SVG", "Vector", "N/A", "Tiny for icons", "100%"),
]
print("\n=== Image Formats ===")
for f in formats:
print(f" [{f.format_name}] {f.compression}")
print(f" Quality: {f.quality_range} | Size: {f.size_vs_jpeg}")
print(f" Browser: {f.browser_support}")
Performance Metrics
# === E-commerce Performance ===
@dataclass
class PerfMetric:
metric: str
before_cdn: str
after_cdn: str
improvement: str
target: str
metrics = [
PerfMetric("TTFB", "850ms", "120ms", "86% faster", "<200ms"),
PerfMetric("LCP (Product Image)", "3.5s", "1.2s", "66% faster", "<2.5s"),
PerfMetric("FCP", "2.1s", "0.8s", "62% faster", "<1.8s"),
PerfMetric("Page Weight", "4.2MB", "1.1MB", "74% smaller", "<2MB"),
PerfMetric("Image Load Time", "2.8s", "0.6s", "79% faster", "<1s"),
PerfMetric("API Response (cached)", "200ms", "15ms", "93% faster", "<50ms"),
PerfMetric("Lighthouse Score", "52", "94", "+42 points", ">90"),
PerfMetric("Bounce Rate", "45%", "22%", "-23%", "<30%"),
PerfMetric("Conversion Rate", "1.8%", "3.2%", "+78%", ">3%"),
]
print("E-commerce Performance:")
for m in metrics:
print(f" [{m.metric}]")
print(f" Before: {m.before_cdn} | After: {m.after_cdn}")
print(f" Improvement: {m.improvement} | Target: {m.target}")
cdn_costs = {
"CloudFront Data Transfer": "$85/mo (1TB)",
"S3 Storage": "$23/mo (100GB images)",
"Lambda@Edge": "$12/mo (Image resize)",
"ACM Certificate": "Free",
"Route 53": "$0.50/mo",
"Total CDN Cost": "$120.50/mo",
"Revenue Increase (est)": "+$5,000/mo from better conversion",
"ROI": "4,050%",
}
print(f"\n\nCDN Cost Analysis:")
for k, v in cdn_costs.items():
print(f" {k}: {v}")
เคล็ดลับ
- WebP: ใช้ WebP เป็น Default Format ลดขนาด 30%
- Cache: Static Assets Cache 1 ปี ใช้ Hash ใน Filename
- Lazy Load: Lazy Load ภาพที่อยู่ Below the Fold
- srcset: ใช้ srcset ส่งภาพขนาดพอดีกับ Device
- Monitor: ดู Core Web Vitals LCP FCP CLS ทุกสัปดาห์
Medusa Commerce คืออะไร
Open Source Headless E-commerce Node.js TypeScript REST GraphQL Admin Payment Inventory Next.js Multi-region Multi-currency Plugin Shopify Alternative
CDN สำคัญกับ E-commerce อย่างไร
Latency Edge Server Images เร็ว 50-80% Server Load Global Coverage Cache SEO Page Speed DDoS Protection SSL Edge
ตั้งค่า CloudFront กับ Medusa อย่างไร
CloudFront Distribution Origin Medusa Cache Policy Static 1 ปี Dynamic สั้น Image Lambda@Edge Resize WebP AVIF Custom Domain ACM Invalidation
Image Optimization ทำอย่างไร
CDN Transform Resize Device Width WebP AVIF 30-50% Lazy Loading srcset sizes BlurHash Compression Quality 75-85% Cache
สรุป
Medusa Commerce CDN Configuration CloudFront Image Optimization WebP AVIF Cache Strategy Edge Computing Performance Core Web Vitals Conversion E-commerce
