Something similar I did to be able to forward cookies to an ALB configured for sticky sessions without cloudfront using the cookies to cache. The reason for this is because cloudfront will use the cookies and their values when matching requests and cached responses but because the ALB creates a new session cookie on each response, the request never matches the cache since the viewer always has a new cookie value set.
I too moved the cookie to a custom header in the viewer request and then pulled it out of the header and placed in back in the cookie in the origin request. I used the same lambda function for the viewer request and origin request and just checked the config property to determine which trigger it was. I prefer this pattern so that I can follow the logic from viewer request to origin request when reading/writing the code since the callback response from the viewer requests becomes the event request for the origin request and I could run my tests on a single function. The logic is based on three flows:
- where there is no AWSALB cookie or customer header at all - in that case do nothing
- where there is the AWSALB cookie but no other cookies in the request
- where this is the AWSALB cookie along with other cookies
Using these three use cases the function was able to work properly.
Here's the function:
exports.handler = (event, context, callback) => {
// TODO implement
const util = require('util');
const COOKIE_TO_FORWARD = 'AWSALB';
let hasTheHeader = (request, headerKey) => {
if (request.headers[headerKey]) {
return true;
}
else return false;
}
//Returns the cookie key name from the value of the cookie header in the request
//let getCookieKey = cookieString => cookieString.slice(0,cookieString.indexOf("="));
const request = event.Records[0].cf.request
if(event.Records[0].cf.config.eventType == 'viewer-request') {
console.log('Viewer Request');
console.log(`viewer request – ${util.inspect(event, {showHidden: false, depth: null})}`);
hasTheHeader(event.Records[0].cf.request, 'cookie') ? console.log(`This request has cookies`) : console.log(`This request does NOT have cookies`);
// First check – If no cookies in Viewer Request, do nothing
if (!hasTheHeader(request, 'cookie')) {
console.log('viewer request first check evaluated - no cookies');
//pass request onto cloudfront cacheing layer or origin request
callback(null, request);
return;
}
// else there is a cookie header so get the list of cookies and put them in an array
let cookieList = request.headers.cookie[0].value.split('; ');
console.log(cookieList);
// Second check - If only the COOKIE_TO_FORWARD cookie exists and no other cookies, move it to a custom header and delete the cookie header
if ( (cookieList.length == 1) && (cookieList[0].startsWith(COOKIE_TO_FORWARD)) ) {
console.log('viewer request second check evaluated - only the COOKIE_TO_FORWARD cookie exists, no other cookies')
//move awsalb to custom header - format is important
request.headers.awsalbkey = [{'key': 'awsAlbKey', 'value': cookieList[0]}];
//remove cookie header
delete request.headers.cookie;
console.log(util.inspect(request, {showHidden: false, depth: null}));
//pass request onto cloudfront cacheing layer or origin request
callback(null, request);
return;
}
// Third check - If there are multiple cookies including the COOKIE_TO_FORWARD cookie, move only the COOKIE_TO_FORWARD cookie to a custom header and delete the cookie COOKIE_TO_FORWARD cookie
// get awsAlb cookie
const indexOfAwsALbCookie = cookieList.findIndex(element => element.startsWith('AWSALB='));
if ( (cookieList.length > 1) && (indexOfAwsALbCookie > -1) ) {
console.log('viewer request third check evaluated - the COOKIE_TO_FORWARD cookie exists along with other cookies')
//put awsAlb cookie value to custom header - format is important
request.headers.awsalbkey = [{'key': 'awsAlbKey', 'value': cookieList[indexOfAwsALbCookie]}];
//remove awsAlb cookie from list off cookies in request
cookieList.splice(indexOfAwsALbCookie,1);
let cookieListString = cookieList.join('; ');
request.headers.cookie[0].value = cookieListString;
console.log(util.inspect(request, {showHidden: false, depth: null}));
//pass request onto cloudfront cacheing layer or origin request
callback(null, request);
return;
}
}
else if(event.Records[0].cf.config.eventType == 'origin-request') {
console.log('Origin Request');
console.log(`origin request – ${util.inspect(event, {showHidden: false, depth: null})}`);
hasTheHeader(request, 'cookie') ? console.log(`This request has cookies`) : console.log(`This request does NOT have cookies`);
// First check – If no cookies in Viewer Request AND no awsalbkey header, do nothing as this is the first request to the origin
if (!hasTheHeader(request, 'cookie') && !hasTheHeader(request, 'awsalbkey')) {
console.log('origin request first check evaluated - no cookies and no awsalbkey header');
//send request to origin
callback(null, request);
return;
}
//Second check, if no cookie header AND COOKIE_TO_FORWARD customer header exists, then add the cookie header and cookie and remove the COOKIE_TO_FORWARD custom header
if (!hasTheHeader(request, 'cookie') && hasTheHeader(request, 'awsalbkey')) {
console.log('origin request second check evaluated - no cookies and has the awsalbkey header')
//add the cookie header and the cookie obtained from the custom header
request.headers.cookie = [];
var length = request.headers.cookie.push({'key': 'Cookie', 'value': request.headers.awsalbkey[0].value});
//remove the custom header
delete request.headers.awsalbkey;
console.log(util.inspect(request, {showHidden: false, depth: null}));
//send request to origin
callback(null, request);
return;
}
//else cookie list exists
let cookieListOrigin = request.headers.cookie[0].value.split('; ');
console.log(cookieListOrigin);
// Third check - If there are multiple cookies excluding the COOKIE_TO_FORWARD cookie and there's an COOKIE_TO_FORWARD custom header, move the COOKIE_TO_FORWARD custom header to the list of cookies and remove the COOKIE_TO_FORWARD custom header
let originIndexAwsAlbCookie = cookieListOrigin.findIndex(element => element.startsWith(COOKIE_TO_FORWARD));
if ( (originIndexAwsAlbCookie < 0) && (cookieListOrigin.length > 0) && (request.headers.awsalbkey) ) {
console.log('origin request third check evaluated - cookies exist without the awsalb cookie and has the awsalbkey header')
//add the awsalb customer header value to a new cookie in the cookie array
var length = cookieListOrigin.push(request.headers.awsalbkey[0].value);
let cookieListOriginString = cookieListOrigin.join('; ');
request.headers.cookie[0].value = cookieListOriginString;
//remove the custom header
delete request.headers.awsalbkey;
console.log(util.inspect(request, {showHidden: false, depth: null}));
//send request to origin
callback(null, request);
return;
}
}
callback(null, request);
};