I have a cloudfront that is supposed to get images from 2 different S3 buckets. Fetching logic is decided by my lambda function based on the URL.
I have created a CloudFront
- added both the buckets as origin
- In default(*) behaviour, selected one of my buckets and added my lambda function's latest version in Origin Request as I want cache to handle incoming request and only call my lambda when cache missed.
In my lambda function
- Added necessary permissions for CloudWatch, CloudFront and S3.
When I hit the cloudfront URL for a specific object, it is not calling my lambda at all. I am able to access data in the bucket through cloudfront that I mentioned in my default behaviour. Which means there is no issue in Cloudfront able to connect to S3.
For all other URLs(which should have been handled by the Lambda) it is returning 403.
I have tried to verify requried permissions and connected Lambda edge with cloudfront as per docs, and expected Lambda to be called.
Also I am new to AWS and handling everything from console.
Edit 1 -
Adding my code
import { S3Client, HeadObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "us-east-1" });
const ORIGINAL_BUCKET = "abc";
const RESIZED_BUCKET = "def";
export const handler = async (event) => {
console.log("cloudfront-image-redirect lambda called");
try {
const { request } = event.Records[0].cf;
let uri = request.uri; // Example: /products/12345/red/winter/thumbnails/image1.jpg
console.log(`Received request for: ${uri}`);
// Extract productId, size, filename, and any nested paths (variation ID)
const match = uri.match(/^\/products\/([^/]+)\/((?:[^/]+\/)*)([^/]+)\/([^/]+)$/) || [];
const [, productId, nestedPath = "", size, fileName] = match;
if (!productId || !size || !fileName) {
console.log("Invalid URI structure:", uri);
return request; // If invalid, let CloudFront handle it
}
// Remove trailing slash from nestedPath
let variationPath = nestedPath.replace(/\/$/, "");
// Determine the correct bucket to check
let bucketToCheck, s3Key;
if (size === "original") {
bucketToCheck = ORIGINAL_BUCKET;
s3Key = variationPath
? `products/${productId}/${variationPath}/${fileName}`
: `products/${productId}/${fileName}`;
} else {
bucketToCheck = RESIZED_BUCKET;
s3Key = variationPath
? `products/${productId}/${variationPath}/${size}/${fileName}`
: `products/${productId}/${size}/${fileName}`;
}
try {
// ✅ Try fetching the exact image
await s3.send(new HeadObjectCommand({ Bucket: bucketToCheck, Key: s3Key }));
console.log(`Serving from ${bucketToCheck}: ${s3Key}`);
return request;
} catch (error) {
console.log(`Image not found: ${s3Key} in ${bucketToCheck}`);
}
// ✅ Fall back to the closest available image by reducing nested path depth
let pathSegments = variationPath.split("/").filter(Boolean);
while (pathSegments.length > 0) {
let reducedPath = pathSegments.join("/");
let fallbackKey = size === "original"
? `products/${productId}/${reducedPath}/${fileName}`
: `products/${productId}/${reducedPath}/${size}/${fileName}`;
try {
await s3.send(new HeadObjectCommand({ Bucket: bucketToCheck, Key: fallbackKey }));
console.log(`Falling back to: ${fallbackKey}`);
request.uri = `/products/${productId}/${reducedPath}/${size}/${fileName}`;
return request;
} catch (error) {
console.log(`Fallback not found: ${fallbackKey}`);
}
pathSegments.pop(); // Remove the last segment and try again
}
// ✅ Final fallback to completely original image (no variations)
if (size !== "original") {
let fallbackOriginalKey = `products/${productId}/${fileName}`;
try {
await s3.send(new HeadObjectCommand({ Bucket: ORIGINAL_BUCKET, Key: fallbackOriginalKey }));
console.log(`Final fallback to original: ${fallbackOriginalKey}`);
request.uri = `/products/${productId}/original/${fileName}`;
return request;
} catch (error) {
console.log("Original image not found either.");
}
}
console.log("No suitable image found, returning default response");
return request; // Let CloudFront handle 404
} catch (error) {
console.error("Lambda Execution Error:", error);
return {
status: "500",
statusDescription: "Internal Server Error",
body: JSON.stringify({ error: "Lambda function execution failed." }),
};
}
};