Cloudfront Lambda@edge set cookie on Viewer Request

旧时模样 提交于 2019-12-02 07:40:05

I would argue that the really correct way to set a nonexistent cookie would be to return a 302 redirect to the same URI with Set-Cookie, and let the browser redo the request. This probably would not have much of an impact since the browser can reuse the same connection to "follow" the redirect.

But if you insist on not doing it that way, then you can inject the cookie into the request with your Viewer Request trigger and then emit a Set-Cookie with the same value in your Viewer Response trigger.

The request object, in a viewer response event, can be found at the same place where it's found in the original request event, event.Records[0].cf.request.

In a viewer-response trigger, this part of the structure contains the "request that CloudFront received from the viewer and that might have been modified by the Lambda function that was triggered by a viewer request event."

Use caution to ensure that you handle the cookie header correctly. The Cookie request header requires careful and accurate manipulation because the browser can use multiple formats when multiple cookies exist.

Once upon a time, cookies were required to be sent as a single request header.

Cookie: foo=bar; buzz=fizz

Parse these by splitting the values on ; followed by <space>.

But the browser may also split them with multiple headers, like this:

Cookie: foo=bar
Cookie: buzz=fizz

In the latter case, the array event.Records[0].cf.request.headers.cookie will contain multiple members. You need to examine the value attribute of each object in that array, check for multiple values within each, as well as accommodating the fact that the array will be completely undefined (not empty) if no cookies exist.


Bonus: Here's a function I wrote, that I believe correctly handles all cases including the case where there are no cookies. It will extract the cookie with the name you are looking for. Cookie names are case-sensitive.

// extract a cookie value from request headers, by cookie name
// const my_cookie_value = extract_cookie(event.Records[0].cf.request.headers,'MYCOOKIENAME');
// returns null if the cookie can't be found
// https://stackoverflow.com/a/55436033/1695906

function extract_cookie(headers, cname) {

    const cookies = headers['cookie'];
    if(!cookies)
    {
        console.log("extract_cookie(): no 'Cookie:' headers in request");
        return null;
    }

    // iterate through each Cookie header in the request, last to first

    for (var n = cookies.length; n--;)
    {
        // examine all values within each header value, last to first

        const cval = cookies[n].value.split(/;\ /);
        const vlen = cval.length;

        for (var m = vlen; m--;)
        {
            const cookie_kv = cval[m].split('=');
            if(cookie_kv[0] === cname)
            {
                return cookie_kv[1];
            }            
        } // for m (each value)    
    } // for n (each header)

    // we have no match if we reach this point
    console.log('extract_cookie(): cookies were found, but the specified cookie is absent');
    return null;

}

Are you able to add another directory: with the first cookie setter request, return (from the lambda) a redirect which includes the cookie-set header, that redirects to your actual content?

OK, long way round but:

  • Take cookie instruction from the incoming request
  • Set this somewhere (cache, etc)
  • Let the request get your object
  • on the Response, also call a function that reads the (cache) and sets the set-cookie header on the response if needed?
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!