SiamCafe · Blog
Medusa Commerce CDN Configuration — ตั้งค่า CDN
บทความ

Medusa Commerce CDN Configuration — ตั้งค่า CDN

เผยแพร่ 28 พฤษภาคม 2569

Medusa Commerce CDN

Medusa Commerce CDN Configuration — ตั้งค่า CDN

Medusa Commerce CDN Configuration CloudFront Image Optimization Cache Strategy Edge Computing Headless E-commerce Node.js TypeScript Performance Global

CDNImage OptimizationEdge Computeราคาเหมาะกับ
CloudFrontLambda@EdgeCloudFront FunctionsPay-per-useAWS Users
CloudflarePolish + ResizeWorkersFree / $20/moทั่วไป
FastlyImage OptimizerCompute@EdgePay-per-useEnterprise
VercelBuilt-in next/imageEdge FunctionsFree / $20/moNext.js
Bunny CDNBunny OptimizerEdge Scripting$0.01/GBBudget

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

Medusa Commerce CDN Configuration — ตั้งค่า CDN

=== 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