İçeriğe atla

AWS ile Edge Computing: CloudFront Functions vs Lambda@Edge

Global uygulamalar için AWS edge computing çözümlerini seçme ve uygulama üzerine pratik örnekler ve maliyet optimizasyonu stratejileri içeren kapsamlı teknik rehber.

Ayhan Sipahi Ayhan Sipahi

Edge computing, kod yürütmeyi merkezi data center’lardan kullanıcılara yakın konumlara taşıyor. AWS CloudFront dünya çapında geniş bir edge ağı işletiyor ve bu ağa kod yerleştirmek için iki yol sunuyor: CloudFront Functions ve Lambda@Edge. Varsayılan tercih CloudFront Functions olmalı: header işleri, redirect’ler ve cache key normalizasyonu burada çok daha düşük maliyetle çözülüyor. Lambda@Edge ise ancak request network erişimi, request body veya origin tarafındaki event’ler gerektirdiğinde bu maliyeti hak ediyor.

Edge Kodunun Çalıştığı Noktalar#

Her iki servis de request’i origin yerine edge’de karşılayarak latency’yi düşürüyor, ancak request yaşam döngüsünde farklı noktalarda duruyor ve farklı sınırlar taşıyorlar.

CloudFront Functions, network erişimi olmayan kısıtlı bir JavaScript runtime’ı çalıştırıyor; cache key normalizasyonu ve header manipülasyonu gibi yüksek hacimli dönüşümler için ölçeklendirilmiş. Lambda@Edge ise tam bir Node.js veya Python süreci çalıştırıyor; harici API çağrılarını, request body incelemesini ve origin tarafındaki logic’i mümkün kılan da bu.

Execution Point’leri#

CloudFront, edge function’ların çalışabileceği dört execution point sağlıyor:

1. Request

2. Viewer Request

3. Origin Request

4. Forward

5. Response

6. Origin Response

7. Response

8. Cached Response

Kullanici

CloudFront Edge

CloudFront Function veya Lambda at Edge

Sadece Lambda at Edge

Origin Server

Sadece Lambda at Edge

CloudFront Function veya Lambda at Edge

  • Viewer Request: CloudFront request’i aldıktan sonra, cache’i kontrol etmeden önce
  • Viewer Response: Viewer’a response dönmeden önce
  • Origin Request: Origin’e forward etmeden önce (sadece cache miss) - Sadece Lambda@Edge
  • Origin Response: Origin’den response alındıktan sonra - Sadece Lambda@Edge

Servis Karşılaştırması#

Aşağıdaki satırlar, hangi işin hangi servise ait olduğunu belirleyen kısıtlar.

Özellik Karşılaştırması#

ÖzellikCloudFront FunctionsLambda@Edge
Execution LocationCloudFront edge location’larıCloudFront edge location’ları
RuntimeSadece JavaScriptNode.js 22.x, Python 3.13
Execution Time< 1 milisaniye5s (viewer), 30s (origin)
Memory2 MB128 MB - 10 GB
Max Package Size10 KB1 MB (viewer), 50 MB (origin)
Network AccessHayırEvet
Event Type’larıViewer request/responseTüm 4 event type
Request Body AccessHayırEvet (origin event’lerde)
Response Size40 KB1 MB
Fiyatlandırma (1M başına)$0.10 invocation$0.60 invocation + compute
Cold StartYokVar
KeyValueStoreEvetHayır

Maliyet Etkisi#

Aylık 10 milyar request için maliyet farkı önemli:

  • CloudFront Functions: 10,000M × $0.10 = $1,000
  • Lambda@Edge: 10,000M × $0.60 + compute = $6,000-$8,000

CloudFront Functions basit kullanım senaryolarında 5-8x daha ucuz.

Karar Çerçevesi#

Evet

Hayir

Evet

Hayir

Evet

Hayir

Evet

Hayir

Evet

Hayir

Edge Computing Ihtiyaci

Network erisimi veya harici API gerekli?

Lambda at Edge

Execution time > 1ms?

> 2MB memory gerekli?

Request body okumak gerekli?

Origin request/ response event'ler gerekli?

CloudFront Functions

CloudFront Functions Üretim Ortamında#

CloudFront Functions, CloudFront edge location’larında sub-milisaniye latency ile çalışıyor. Network erişimi gerektirmeyen yüksek hacimli, basit operasyonlar için ideal.

Cache Key’ler ve Security Header’ları#

Query parameter’ları normalize ederek cache hit ratio’yu optimize etmek, origin load’u önemli ölçüde azaltabilir.

// Cache key normalizasyonu için CloudFront Function
function handler(event) {
    var request = event.request;
    var querystring = request.querystring;

    // Device indicator'larını standart formata normalize et
    if (querystring.device) {
        var deviceValue = querystring.device.value.toLowerCase();
        if (deviceValue === 'm' || deviceValue === 'mobile') {
            querystring.device.value = 'mobile';
        } else if (deviceValue === 'd' || deviceValue === 'desktop') {
            querystring.device.value = 'desktop';
        }
    }

    // Query parameter'ları alfabetik sırala - tutarlı cache key için
    var sortedQuerystring = {};
    Object.keys(querystring)
        .sort()
        .forEach(function(key) {
            sortedQuerystring[key] = querystring[key];
        });

    request.querystring = sortedQuerystring;

    // Accept-Encoding header'ını normalize et
    if (request.headers['accept-encoding']) {
        var acceptEncoding = request.headers['accept-encoding'].value;
        if (acceptEncoding.includes('br')) {
            request.headers['accept-encoding'].value = 'br,gzip';
        } else if (acceptEncoding.includes('gzip')) {
            request.headers['accept-encoding'].value = 'gzip';
        }
    }

    return request;
}

Normalizasyon olmadan ?device=m&size=l ile ?size=l&device=mobile tek bir object için iki ayrı cache kaydı oluyor. Bu varyantları tek kayda indirmek hit ratio’yu yükseltiyor ve karşılık gelen request’leri origin’e hiç göndermiyor.

Security header’lar da viewer response aşamasında aynı deseni izliyor:

// Security header'ları için CloudFront Function (viewer response)
function handler(event) {
    var response = event.response;
    var headers = response.headers;

    // Strict-Transport-Security (HSTS)
    headers['strict-transport-security'] = {
        value: 'max-age=31536000; includeSubDomains; preload'
    };

    // Content-Security-Policy (CSP)
    headers['content-security-policy'] = {
        value: "default-src 'self'; img-src 'self' https: data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
    };

    // X-Content-Type-Options
    headers['x-content-type-options'] = {
        value: 'nosniff'
    };

    // X-Frame-Options
    headers['x-frame-options'] = {
        value: 'DENY'
    };

    // X-XSS-Protection
    headers['x-xss-protection'] = {
        value: '1; mode=block'
    };

    // Referrer-Policy
    headers['referrer-policy'] = {
        value: 'strict-origin-when-cross-origin'
    };

    // Permissions-Policy
    headers['permissions-policy'] = {
        value: 'geolocation=(), microphone=(), camera=()'
    };

    return response;
}

Aylık 5 milyar request için bu CloudFront Functions ile $500’a mal olurken Lambda@Edge ile $3,000+ tutabiliyor.

KeyValueStore ile A/B Testing#

CloudFront Functions, yeniden deploy olmadan dinamik konfigürasyon için KeyValueStore’u destekliyor.

// A/B testing için KeyValueStore kullanan CloudFront Function
import cf from 'cloudfront';

const kvsId = 'a1b2c3d4-5678-90ab-cdef-example12345';
const kvsHandle = cf.kvs(kvsId);

async function handler(event) {
    var request = event.request;
    var uri = request.uri;

    // Kullanıcının zaten experiment ataması var mı kontrol et
    var cookies = request.cookies;
    var experimentCookie = cookies['experiment_variant'];

    var variant;
    if (experimentCookie) {
        variant = experimentCookie.value;
    } else {
        // Experiment konfigürasyonunu KeyValueStore'dan al
        var experimentConfig = await kvsHandle.get('experiment_homepage');
        var config = JSON.parse(experimentConfig);

        // Traffic split'e göre variant ata
        var random = Math.random() * 100;
        if (random < config.variantA_percentage) {
            variant = 'A';
        } else if (random < (config.variantA_percentage + config.variantB_percentage)) {
            variant = 'B';
        } else {
            variant = 'C';
        }

        // Gelecekteki request'ler için cookie set et
        request.cookies['experiment_variant'] = { value: variant };
    }

    // URL'i variant'a göre yeniden yaz
    if (uri === '/') {
        request.uri = `/variants/home-${variant.toLowerCase()}.html`;
    }

    return request;
}

Tip

KeyValueStore güncellemeleri birkaç dakika içinde yayılıyor. Development sırasında versioning kullan ve production değişiklikleri için gradual rollout uygula.

Origin Tarafındaki Logic İçin Lambda@Edge#

Lambda@Edge, network erişimi, harici API’ler veya origin tarafındaki logic gerektiren operasyonları ele alıyor. Trade-off olarak daha yüksek maliyet ve critical path üzerinde olası bir cold start var.

Geo-Targeting ve Lokalizasyon#

CloudFront, Lambda@Edge’in intelligent routing için kullanabileceği coğrafi header’lar sağlıyor.

// Geo-targeting için Lambda@Edge function (viewer request)
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // CloudFront geo header'ları sağlıyor
    const country = headers['cloudfront-viewer-country']
        ? headers['cloudfront-viewer-country'][0].value
        : 'US';

    // Ülkeleri dil tercihlerine map et
    const countryToLocale = {
        'DE': '/de',
        'AT': '/de',
        'CH': '/de',
        'TR': '/tr',
        'FR': '/fr',
        'ES': '/es',
        'IT': '/it',
        'US': '/en',
        'GB': '/en',
        'CA': '/en'
    };

    // Sadece root path'i redirect et
    if (request.uri === '/') {
        const locale = countryToLocale[country] || '/en';

        // Kullanıcının locale tercihi cookie'si var mı kontrol et
        const cookies = headers.cookie || [];
        let localePreference = null;

        for (let cookie of cookies) {
            const matches = cookie.value.match(/locale=([^;]+)/);
            if (matches) {
                localePreference = matches[1];
                break;
            }
        }

        // Lokalize edilmiş path'e redirect et
        const targetUri = localePreference || locale;

        const response = {
            status: '302',
            statusDescription: 'Found',
            headers: {
                'location': [{
                    key: 'Location',
                    value: targetUri
                }],
                'cache-control': [{
                    key: 'Cache-Control',
                    value: 'max-age=3600'
                }]
            }
        };

        callback(null, response);
    } else {
        callback(null, request);
    }
};

JWT Authentication#

JWT token’ları edge’de doğrulamak, unauthorized request’lerin origin’e ulaşmasını önlüyor.

// JWT validation için Lambda@Edge function (viewer request)
'use strict';

const jwt = require('jsonwebtoken');

// Production'da AWS Secrets Manager'dan çek
const JWT_SECRET = process.env.JWT_SECRET;

exports.handler = async (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // Protected path'ler
    const protectedPaths = ['/api/', '/dashboard/', '/admin/'];
    const isProtected = protectedPaths.some(path => request.uri.startsWith(path));

    if (!isProtected) {
        callback(null, request);
        return;
    }

    // Authorization header'ını çıkar
    const authHeader = headers.authorization || headers.Authorization;

    if (!authHeader || authHeader.length === 0) {
        callback(null, unauthorizedResponse('Missing authorization header'));
        return;
    }

    const token = authHeader[0].value.replace('Bearer ', '');

    try {
        // JWT token'ı doğrula
        const decoded = jwt.verify(token, JWT_SECRET, {
            algorithms: ['HS256'],
            maxAge: '24h'
        });

        // Origin için custom header'lara kullanıcı bilgisi ekle
        request.headers['x-user-id'] = [{
            key: 'X-User-Id',
            value: decoded.userId
        }];
        request.headers['x-user-email'] = [{
            key: 'X-User-Email',
            value: decoded.email
        }];

        callback(null, request);
    } catch (error) {
        console.error('JWT validation failed:', error.message);
        callback(null, unauthorizedResponse('Invalid or expired token'));
    }
};

function unauthorizedResponse(message) {
    return {
        status: '401',
        statusDescription: 'Unauthorized',
        headers: {
            'www-authenticate': [{
                key: 'WWW-Authenticate',
                value: 'Bearer realm="Access to protected resources"'
            }],
            'content-type': [{
                key: 'Content-Type',
                value: 'application/json'
            }]
        },
        body: JSON.stringify({
            error: message
        })
    };
}

Tip

Lambda@Edge function’larında asla secret’ları hardcode etme. API call’larını ve cold start etkisini minimize etmek için caching ile AWS Secrets Manager kullan.

Origin Selection ve Failover#

Health check’lerle dinamik origin routing, dayanıklı mimariler sağlıyor.

// Origin selection için Lambda@Edge function (origin request)
'use strict';

const https = require('https');

exports.handler = async (event, context, callback) => {
    const request = event.Records[0].cf.request;

    // Primary ve secondary origin'ler
    const origins = {
        primary: {
            domainName: 'api-primary.example.com',
            port: 443,
            protocol: 'https',
            path: '/v1'
        },
        secondary: {
            domainName: 'api-secondary.example.com',
            port: 443,
            protocol: 'https',
            path: '/v1'
        }
    };

    let selectedOrigin = origins.primary;

    // Custom header'a göre route et
    const routingHeader = request.headers['x-origin-override'];
    if (routingHeader && routingHeader[0].value === 'secondary') {
        selectedOrigin = origins.secondary;
    }

    // Path'e göre route et
    if (request.uri.startsWith('/legacy/')) {
        selectedOrigin = origins.secondary;
    }

    // Primary origin'i health check yap
    try {
        const isHealthy = await checkOriginHealth(selectedOrigin.domainName);
        if (!isHealthy) {
            console.log(`Primary origin ${selectedOrigin.domainName} unhealthy, failing over`);
            selectedOrigin = origins.secondary;
        }
    } catch (error) {
        console.error('Health check failed:', error);
        selectedOrigin = origins.secondary;
    }

    // Request'i seçilen origin ile güncelle
    request.origin = {
        custom: {
            domainName: selectedOrigin.domainName,
            port: selectedOrigin.port,
            protocol: selectedOrigin.protocol,
            path: selectedOrigin.path,
            sslProtocols: ['TLSv1.2'],
            readTimeout: 30,
            keepaliveTimeout: 5,
            customHeaders: {}
        }
    };

    callback(null, request);
};

function checkOriginHealth(domainName) {
    return new Promise((resolve) => {
        const options = {
            hostname: domainName,
            port: 443,
            path: '/health',
            method: 'GET',
            timeout: 2000
        };

        const req = https.request(options, (res) => {
            resolve(res.statusCode === 200);
        });

        req.on('error', () => resolve(false));
        req.on('timeout', () => {
            req.destroy();
            resolve(false);
        });

        req.end();
    });
}

Performans Optimizasyonu#

Lambda@Edge Cold Start’larını Azaltmak#

Cold start’lar, function’lar viewer request fazında çalıştığında kullanıcı deneyimini doğrudan etkiliyor. En doğrudan etkili yöntem package size: dependency’leri handler dışında initialize ederek warm invocation’ların setup maliyetini tamamen atlamasını sağla.

// KÖTÜ: Handler içinde initialize et
exports.handler = async (event) => {
    const AWS = require('aws-sdk'); // Yavaş!
    const dynamodb = new AWS.DynamoDB.DocumentClient();
    // ...
};

// İYİ: Handler dışında initialize et
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
    // Warm invocation'larda initialization'ı yeniden kullanır
};

Memory allocation da önemli. Daha fazla memory, daha fazla CPU demek ve bu da cold start süresini azaltıyor. Orta düzeyde dependency’leri olan Lambda@Edge function’lar için 512 MB genellikle ideal nokta.

Dependency’ler etkiyi katlıyor. Ağır kütüphaneleri daha hafif alternatiflerle değiştir:

  • moment.jsdate-fns veya native Date
  • Tam AWS SDK → bireysel service client’ları
  • Büyük image processing kütüphaneleri → minimal implementasyon’lar

Cold start, function’ın cevap verebilmek için yüklemesi gereken her şeyle birlikte büyüyor. Bağımlılığı olmayan bir function neredeyse fark edilmiyor, AWS SDK’yı bundle etmek ölçülebilir bir gecikme ekliyor, Sharp gibi native modüller ise en kötü durum. Bir aralık varsaymak yerine kendi function’larını CloudWatch’ta ölç.

CloudFront Functions Optimizasyonu#

Kodu Minimal Tut: 10 KB limiti tüm kodu içeriyor. Hardcode değerler yerine konfigürasyon verisi için KeyValueStore kullan.

KeyValueStore’dan Yararlan: Function yeniden deploy’u önlemek için konfigürasyon verisini offload et. KeyValueStore sub-milisaniye okuma ile 5 MB storage sağlıyor.

Maliyet Analizi ve Optimizasyon#

Detaylı Maliyet Hesaplama#

Senaryo: Aylık 5 milyar request, 50ms ortalama süre, 128 MB memory

CloudFront Functions:

5,000M invocation × $0.10 = $500
Toplam: $500/ay

Lambda@Edge:

Request charge'ları: 5,000M × $0.60 = $3,000
Compute: 5,000M × 0.05s × 0.125GB × $0.00005001 = $1,563
Toplam: $4,563/ay

Tasarruf: CloudFront Functions kullanarak $4,063/ay (%89 azalma)

Maliyet Optimizasyon Stratejileri#

Lambda@Edge Memory’yi Optimize Et: CloudWatch metrikleri kullanarak memory allocation’ı doğru boyutlandır. Daha fazla memory, execution süresini azaltarak daha yüksek memory maliyetlerini dengeleyebilir.

Invocation Frekansını Azalt: Edge function invocation’larını minimize etmek için CloudFront cache policy’lerini etkili kullan. Her cache’lenen response bir function invocation’dan kaçınır.

Gerçek Kullanımı İzle: Maliyet threshold’ları için CloudWatch alarm’ları kur:

import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';

const lambdaEdgeCostAlarm = new cloudwatch.Alarm(this, 'LambdaEdgeCostAlarm', {
    metric: new cloudwatch.Metric({
        namespace: 'AWS/Lambda',
        metricName: 'Invocations',
        dimensionsMap: {
            FunctionName: edgeFunction.functionName,
        },
        statistic: 'Sum',
        period: cdk.Duration.days(1),
    }),
    threshold: 1_000_000_000, // günde 1 milyar invocation
    evaluationPeriods: 1,
    alarmDescription: 'Lambda@Edge invocation budget threshold aşıldı',
});

Debugging ve Logging Zorlukları#

Lambda@Edge logları function’ın çalıştığı AWS bölgesinde oluşuyor, bu da debugging’i karmaşık hale getiriyor.

Logları Bölgeler Arasında Bulmak#

CloudFront Response Header’larını Kontrol Et:

curl -I https://your-distribution.cloudfront.net/path
# Ara: x-amz-cf-pop: IAD89-P1
# IAD = us-east-1 region

Havaalanı Kodu-Region Eşlemesi:

  • IAD (Dulles) → us-east-1
  • SFO (San Francisco) → us-west-1
  • DUB (Dublin) → eu-west-1
  • NRT (Tokyo) → ap-northeast-1
  • SYD (Sydney) → ap-southeast-2

Structured Logging Yaklaşımı#

// Structured logging ile Lambda@Edge function
'use strict';

exports.handler = async (event, context) => {
    const request = event.Records[0].cf.request;
    const requestId = context.requestId;

    // Kolay parse için structured log
    const logContext = {
        requestId,
        uri: request.uri,
        method: request.method,
        country: request.headers['cloudfront-viewer-country']?.[0]?.value,
        timestamp: new Date().toISOString(),
    };

    console.log('REQUEST_START', JSON.stringify(logContext));

    try {
        // Logic buraya
        console.log('PROCESSING', JSON.stringify({ ...logContext, step: 'validation' }));

        return request;
    } catch (error) {
        console.error('ERROR', JSON.stringify({
            ...logContext,
            error: error.message,
            stack: error.stack,
        }));
        throw error;
    } finally {
        console.log('REQUEST_END', JSON.stringify(logContext));
    }
};

Structured log’ları sorgulamak için CloudWatch Logs Insights kullan:

fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 100

Edge Function Hatalarını Çözmek#

Response Size Aşımı (502 Hataları)#

1 MB’ı aşan bir response, CloudFront’un 502 dönmesine neden olur. Dönmeden önce boyutunu kontrol et:

const responseBody = JSON.stringify(data);
const sizeInBytes = Buffer.byteLength(responseBody, 'utf8');

if (sizeInBytes > 1048576) { // 1 MB = 1048576 byte
    console.error(`Response size ${sizeInBytes} 1MB limitini aşıyor`);
    // Bunun yerine S3 object referansı dön
    return {
        status: '200',
        body: JSON.stringify({
            url: `https://s3.amazonaws.com/bucket/response-${requestId}.json`
        })
    };
}

Viewer Request Timeout’ları#

Problem: Viewer request Lambda@Edge function timeout veriyor.

Promise.race timeout’u uygular:

const timeoutPromise = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 4000)
);

const result = await Promise.race([
    fetchExternalData(),
    timeoutPromise
]);

Cache Key Verimsizliği#

Cache hit ratio düşük ve origin request’leri yüksek. CloudFront cache key’i hesaplamadan önce query parameter’ları normalize et:

function normalizeQueryString(querystring) {
    // Tracking parameter'larını kaldır
    const trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'fbclid', 'gclid'];
    trackingParams.forEach(param => delete querystring[param]);

    // Kalan parameter'ları sırala
    const sorted = {};
    Object.keys(querystring).sort().forEach(key => {
        sorted[key] = querystring[key];
    });

    return sorted;
}

AWS CDK Deployment Pattern#

CloudFront’u edge function’larla deploy etmek için eksiksiz CDK stack:

// Edge function'larla CloudFront için AWS CDK stack
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 lambda from 'aws-cdk-lib/aws-lambda';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import * as path from 'path';

export class EdgeComputingStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    // ÖNEMLİ: Lambda@Edge us-east-1'de deploy edilmeli
    super(scope, id, { ...props, env: { region: 'us-east-1' } });

    // Origin için S3 bucket
    const bucket = new s3.Bucket(this, 'OriginBucket', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      encryption: s3.BucketEncryption.S3_MANAGED,
    });

    // Cache key normalizasyonu için CloudFront Function
    const cacheKeyFunction = new cloudfront.Function(this, 'CacheKeyNormalization', {
      code: cloudfront.FunctionCode.fromFile({
        filePath: path.join(__dirname, '../functions/cache-key-normalization.js'),
      }),
      runtime: cloudfront.FunctionRuntime.JS_2_0,
      comment: 'Daha iyi hit ratio için cache key normalize et',
    });

    // Security header'ları için CloudFront Function
    const securityHeadersFunction = new cloudfront.Function(this, 'SecurityHeaders', {
      code: cloudfront.FunctionCode.fromFile({
        filePath: path.join(__dirname, '../functions/security-headers.js'),
      }),
      runtime: cloudfront.FunctionRuntime.JS_2_0,
      comment: 'Her response için security header ekle',
    });

    // JWT authentication için Lambda@Edge function
    const jwtAuthFunction = new cloudfront.experimental.EdgeFunction(
      this,
      'JwtAuthFunction',
      {
        runtime: lambda.Runtime.NODEJS_22_X,
        handler: 'index.handler',
        code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/jwt-auth')),
        timeout: cdk.Duration.seconds(5),
        memorySize: 128,
      }
    );

    // Optimize caching için cache policy
    const cachePolicy = new cloudfront.CachePolicy(this, 'OptimizedCachePolicy', {
      cachePolicyName: 'EdgeComputingOptimized',
      comment: 'Normalize edilmiş cache key ile optimize policy',
      defaultTtl: cdk.Duration.hours(24),
      maxTtl: cdk.Duration.days(365),
      minTtl: cdk.Duration.seconds(1),
      enableAcceptEncodingGzip: true,
      enableAcceptEncodingBrotli: true,
      headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
        'CloudFront-Viewer-Country',
        'CloudFront-Viewer-Country-Region'
      ),
      queryStringBehavior: cloudfront.CacheQueryStringBehavior.allowList(
        'w', 'h', 'q', 'format'
      ),
    });

    // CloudFront distribution
    const distribution = new cloudfront.Distribution(this, 'Distribution', {
      defaultBehavior: {
        origin: new origins.S3Origin(bucket),
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        cachePolicy,
        functionAssociations: [
          {
            function: cacheKeyFunction,
            eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
          },
          {
            function: securityHeadersFunction,
            eventType: cloudfront.FunctionEventType.VIEWER_RESPONSE,
          },
        ],
        edgeLambdas: [
          {
            functionVersion: jwtAuthFunction.currentVersion,
            eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,
          },
        ],
      },
      enableLogging: true,
      logIncludesCookies: true,
    });

    // Output'lar
    new cdk.CfnOutput(this, 'DistributionDomainName', {
      value: distribution.distributionDomainName,
    });

    new cdk.CfnOutput(this, 'DistributionId', {
      value: distribution.distributionId,
    });
  }
}

Warning

Lambda@Edge function’ları CloudFront distribution’ınızın nerede deploy edildiğine bakılmaksızın her zaman us-east-1 bölgesinde oluşturulmalı.

Lambda@Edge’e Ne Zaman Geçmeli#

İki kesin sınır her iki yöndeki geçişlerin çoğunu belirliyor: CloudFront Functions’taki 10 KB kod sınırı ve Lambda@Edge’deki 1 MB response sınırı.

İşleyen bir sıra şu: CloudFront managed cache policy ile başla, cache key’i şekillendirmen gerektiğinde bir CloudFront Function ekle, Lambda@Edge’i ancak bu sınırlardan biri zorladığında devreye sok. Hangi servis path’e girerse girsin her request’in üzerinde duruyor; bu yüzden hata durumunda orijinal request’i olduğu gibi dön ve error rate’i takipte tut.

Kaynaklar#

İlgili yazılar