
Application Performance - Edge functions
Edge functions are powerful developer tools to add custom logic at the edge with CloudFront.
- Implementing advanced HTTP logic. CloudFront provides you with native rules such as redirection from HTTP to HTTPs, routing to different origins based the request path, etc... Edge functions allow you to go beyond the native in CloudFront to implement advanced HTTP logic such as normalizing cache keys, rewriting URLS, HTTP CRUD operations, etc...
- Reducing application latency. Some application logic can be offloaded from the origin to the edge to benefit from caching (e.g. A/B testing) or to execute closer to users (e.g. HTTP redirections, URL shortening, HTML rendering). In a micro-services or micro-front architectures, you can use edge functions to implement common logic (e.g Authorization & Authentication) once at the entry point of the application instead of implementing it in each component separately.
- Protecting the application perimeter. Edge functions can be used to enforce security controls such as access control, and advanced geoblocking at the edge. This allows you to reduce the attack surface of your origin and remove unnecessary scaling costs.
- Request routing. You can use edge functions to route each HTTP request to a specific origin based on application logic. This can be useful for scenarios such as advanced failover, origin load balancing, multi-region architectures, migrations, application routing, etc...
- Viewer events: Executed for every request, before checking CloudFront cache. It's ideal for cache normalization, authorization or placing unique cookies. CloudFront Functions are only allowed on viewer events.
- Origin events: Executed on cache misses, before going to the origin to fetch files. It's ideal to generate content or manipulate responses before caching it.
- Associate an Edge function to the most specific cache behavior, to avoid unnecessary functions execution cost.
- Use the most optimal edge function runtime for your case. For example, if your logic can be implemented either using CloudFront Functions or Lambda@Edge on viewer events, use CloudFront Functions. If your logic can be implemented with CloudFront Functions on viewer events, or with Lambda@Edge on origin events, use CloudFront Functions, unless the cache hit ratio is very high.
- Learn about restrictions on edge functions when you design your application. Note that, on viewer events, you can either use Lambda@Edge or CloudFront Functions, but not both (e.g. Lambda@Edge on viewer request event and CloudFront Functions on viewer response event).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function handler(event) {
var request = event.request;
var headers = request.headers;
var host = request.headers.host.value;
var country = 'DE';
var newurl = `https://${host}/de/index.html`;
if (headers['cloudfront-viewer-country']) {
var countryCode = headers['cloudfront-viewer-country'].value;
if (countryCode === country) {
var response = {
statusCode: 302,
statusDescription: 'Found',
headers: { location: { value: newurl } },
};
return response;
}
}
return request;
}
- Learn about best practices for using Lambda@Edge, especially around managing execution concurrency. Concurrency measures the number of concurrently running Lambda containers per Regional Edge Cache region. In each region, Lambd@Edge concurrency has quotas in terms of bursting speed and absolute limit.
- Learn about best practices for fetching external data from your Lambda@Edge function.
- Lambda@Edge logs are shipped to CloudWatch logs in the region where they were executed with a log group name prefixed by us-east-1. If you need to centralize Lambda@Edge logs in a single region, consider the following Lambda@Edge log aggregator solution. Note that every function execution generates logs to CloudWatch Logs (in contrast to CloudFront Functions, where logs are generated only when explicitly written in the function code). You can disable Lambda@Edge logs by removing permissions to send logs to CloudWatch from its associated IAM role, .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const aws = require('aws-sdk');
const s3 = new aws.S3({ region: 'us-east-1' });
const s3Params = {
Bucket: 'redirections-configuration',
Key: 'redirects.json',
};
const TTL = 5000; // TTL of 5 seconds
async function fetchRedirectionsFromS3() {
const response = await s3.getObject(s3Params).promise();
return JSON.parse(response.Body.toString('utf-8')).map(
({ source, destination }) => ({
source: new RegExp(source),
destination,
})
);
}
let redirections;
function fetchRedirections() {
if (!redirections) {
redirections = fetchRedirectionsFromS3();
setTimeout(() => {
redirections = undefined;
}, TTL);
}
return redirections;
}
exports.handler = async event => {
const request = event.Records[0].cf.request;
try {
const redirects = await fetchRedirections();
for (const { source, destination } of redirects) {
if (source.test(request.uri)) {
return {
status: '302',
statusDescription: 'Found',
headers: {
location: [{ value: destination }],
},
};
}
}
return request;
} catch (_error) {
return request;
}
};
Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.