I am trying to create a Lambda@Edge function to return Open Graph HTML for my Angular SPA application. I've installed it into the CloudFrond "Viewer Request" lifecycle. This lambda checks the user agent, and if it's the Facebook or Twitter crawler, it returns HTML (currently hard coded in the lambda for testing). If the request is from any other user-agent, the request is passed through to the origin. The pass-through logic is working properly, but if I try to intercept and return the Open Graph HTML for the crawlers, I get an error.
In CloudWatch, the error reported by CloudFront is:
ERROR Validation error: The Lambda function returned an invalid body, body should be of object type.
In Postman (by faking the user-agent), I get a 502 with:
The Lambda function result failed validation: The body is not a string, is not an object, or exceeds the maximum size.
I'm pulling my hair out with this one. Any ideas? Here's my lambda.
'use strict';
function buildReleaseResponse( request ) {
const content = `<\!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Hello, World" />
<meta property="og:description" content="This is an Open Graph test" />
<meta property="og:image" content="https://cdn.website.com/photos/SGmzAlNmwOGpnMeijMoW9.jpg" />
<meta property="og:url" content="https://www.website.com/hello-world" />
<title>Hello, World</title>
</head>
<body>
<h1>Open Graph Test</h1>
</body>
</html>`;
return {
statusCode: 200,
statusDescription: 'OK',
headers: {
"content-type": [
{
"key": "Content-Type",
"value": "text/html; charset=utf-8"
}
]
},
body: content.toString()
};
}
exports.handler = ( event, context, callback ) => {
const { request, response } = event.Records[0].cf;
let userAgentStr = "";
if (request.headers['user-agent']) {
if (request.headers['user-agent'].length > 0) {
userAgentStr = request.headers['user-agent'][0].value;
}
}
let newResponse = null;
if ( userAgentStr.match(/facebookexternalhit|twitterbot/i) ) {
if ( request.uri.startsWith("/radio/release/") ) {
newResponse = buildReleaseResponse(request);
}
}
if ( newResponse === null ) {
console.log("Passthrough.");
callback(null, request);
}
else {
console.log("Overriding response with: " + JSON.stringify(newResponse));
callback(null, newResponse);
}
};
Here is the response shown in cloudwatch (conole.log)
{
"statusCode": 200,
"statusDescription": "OK",
"headers": {
"content-type": [{
"key": "Content-Type",
"value": "text/html; charset=utf-8"
}],
"cache-control": [{
"key": "Cache-Control",
"value": "max-age=100"
}]
},
"body": "<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n \n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:title\" content=\"Yo Dog\" />\n <meta property=\"og:description\" content=\"Song by Raf Fiol and Mike Patterson\" />\n <meta property=\"og:image\" content=\"https://cdn.kompoz.com/photos/SGmzAlNmwOGpnMeijMoW9.jpeg\" />\n <meta property=\"og:url\" content=\"https://3-www.kompoz.com/radio/release/yo_dog\" />\n \n <title>Kompoz.com</title>\n </head>\n <body>\n <h1>Yo Dog</h1>\n </body>\n </html>"
}
Here is the CloudFront Lambda configuration
