Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please provide an example with a simple cookie manipulation #15

Open
johannges opened this issue Aug 15, 2021 · 8 comments
Open

Please provide an example with a simple cookie manipulation #15

johannges opened this issue Aug 15, 2021 · 8 comments

Comments

@johannges
Copy link

I was not able to find any example for modifing or adding a cookie.

We need this feature to set the Client Language via Cookie.

@dbrown-git
Copy link
Contributor

This could be a good example. Can you provide more details about this example? Would you like to see an example of adding/modifying a cookie on in an incoming request, which would eventually be passed to the origin? This would be a viewer request CloudFront Function setting a Cookie header. Or would you like to see an example of adding/modifying a cookie on the way out to a client? This would be a viewer response CloudFront Function setting a Set-Cookie header.

@jeff-crosby
Copy link

jeff-crosby commented Oct 8, 2021

@dbrown-git I have a use case where we're still using the legacy cache settings in CloudFront and adding CloudFront-Viewer-Country to the allowlist so that I can cache a separate copy of each page by the viewer's country. Unfortunately, the only difference between each page is injecting the country code into some Javascript on the page for privacy-related logic (GDPR, etc). This reduces the efficiency of CloudFront just to inject a 2-letter code for the viewer's country.

It would be a huge boost to our cache hit ratio if we could remove CloudFront-Viewer-Country from the cache key and, instead, inject it into the cookie response to the client. I could then modify the Javascript to pull it from there, and we could return to caching the same version of each page for every country.

I just noticed CloudFront Functions yesterday in the AWS console, so I've been starting to experiment with it more to accomplish the use case above. It sounds like my use case would require a viewer response function. Will CloudFront-Viewer-Country still be populated if I remove it from the cache key? Thanks!

@joshbean
Copy link
Contributor

joshbean commented Oct 8, 2021

Will CloudFront-Viewer-Country still be populated if I remove it from the cache key?

Not with the legacy cache settings, no. This is one of the key benefits of using cache policies and origin request policies instead of the legacy cache settings. With the legacy cache settings, the only way to add a CloudFront- header is to include it in the cache key. But with an origin request policy, you can add the CloudFront-Viewer-Country header (or any other CloudFront- header) without including it in the cache key.

@jeff-crosby
Copy link

Thanks for the reply, @joshbean !

I have actually been experimenting today with changing over to an Origin Request and Cache Policy on my test CloudFront distribution. I have a Cache Policy with only Accept, Host, and Accept-Encoding headers in the cache key (we compress at origin in gzip or brotli, and we need Host since multiple subdomains render from same distro). I then have an Origin Request Policy with Accept, CloudFront-Viewer-Country, and Host headers in the allowlist.

I have also created a CloudFront function that just attempts to copy CloudFront-Viewer-Country code to cookies on the response. This part works. However, when I switch between US and Ireland (using our eu-west-1 VPN endpoint), I am never able to observe the same page being cached as one country and returned as a Hit when I'm the other country. This is not the behavior I want if I'm trying to optimize for a scenario where users from different countries can share the same cache entry yet the rendered page still has access to the country code CloudFront detected for the user. Any suggestions?

In case it helps, here is the CloudFront function I am testing, and I'm attaching it as a Viewer Response event type.

/**
 * Take the CloudFront-Viewer-Country 2-letter code and inject as a cookie in the response
 * so we no longer need to cache a separate copy of each page for each country.
 */
function handler(event) {
    
    var request = event.request;
    var response = event.response;
    var headers = request.headers;
    
    var e = new Date();
    // Add just 1 day; short-lived cookie
    e.setDate(e.getDate() + 1);
    
    if (headers['cloudfront-viewer-country']) {
        var countryCode = headers['cloudfront-viewer-country'].value;
        
        response['cookies'] = response['cookies'] || {};
      
        response['cookies']['country'] = {
            "value" : countryCode,
            "attributes": "Secure; Path=/; Domain=mydomain.com; Expires=" + e.toUTCString()
        }
    }
    else {
        response['cookies']['country'] = {
            "value" : 'unknown',
            "attributes": "Secure; Path=/; Domain=mydomain.com; Expires=" + e.toUTCString()
        }
    }

    return response;
}

@joshbean
Copy link
Contributor

@jeff-crosby Thanks for all of those details! I have a couple of thoughts to offer.

First, I notice that you have a couple of headers (Accept and Host) that are specified in both the cache policy and the origin request policy. There's no harm in doing that, but for simplicity's sake it's not technically necessary. Anything that's specified in a cache policy is automatically included in the origin request, so there's no need to specify these headers again in the origin request policy. The origin request policy is only for values that you want to get at the origin (or in your case, in a CloudFront function) but that you don't want to include in the cache key.

Second, it's unlikely (virtually impossible) that you'll ever get a cache entry that's shared across countries, particularly geographically distant countries like the US and Ireland. CloudFront's caching infrastructure includes hundreds of edge locations and more than a dozen regional edge caches around the world. When a user in the US requests an object through CloudFront, they're routed to the edge location that provides the lowest latency, typically one that's geographically close to the user. If the object isn't cached at the edge location, CloudFront checks a regional edge cache, before finally going to the origin. A subsequent user in a similar geographic location in the US is very likely to get a cache hit. However, a user in Ireland will get an entirely different routing path through an edge location that's geographically close to Ireland, then a regional edge cache somewhere in Europe, before finally going to the origin. The user in Ireland will almost never route through the same cache hosts as the user in the US.

If you want a single cache layer that all users globally are routed through, take a look at CloudFront Origin Shield. With Origin Shield, sending an initial request from the US and a subsequent request from Ireland should result in a cache hit for the user in Ireland. Origin Shield incurs an additional cost per 10,000 requests on top of the standard CloudFront per-request charges, so be aware of that if you decide to look into this feature.

@christopheryang90
Copy link

I'm trying to use Cloudfront functions to create a A/B testing setup with cookies that allow users to be served one variant consistently. Right now I'm stuck on how to set a cookie for a first-time user, and have them redirect to the function again after a cookie is set.
There are examples of this for Lambda@edge(example of what I'm trying to achieve: link) but I haven't seen one for Cloudfront functions yet.

Currently I'm working from the Viewer-request events:
So far I've tried to returning a response with a set-cookie header, but within the testing feature on cloudfront, it tells me that cloudfront responses do not support set-cookie.
image

If I try to set the cookie attribute in the request or response object, I get this error when visiting the distribution:
image

image

@jsakas
Copy link

jsakas commented Jul 25, 2024

For anyone else who ends up here, this worked for me:

async function handler (event) {
    const response = event.response
        
    response.cookies = {
        'X-My-Cookie': {
            value: 'Test',
            attributes: 'Secure; HttpOnly; Path=/'
        }
    };
    
    return response;
}

@ro-ka
Copy link

ro-ka commented Aug 2, 2024

For anyone else who ends up here, this worked for me:

async function handler (event) {
    const response = event.response
        
    response.cookies = {
        'X-My-Cookie': {
            value: 'Test',
            attributes: 'Secure; HttpOnly; Path=/'
        }
    };
    
    return response;
}

@jsakas Trying this did not set a Cookie for me – are you using this in a viewer request Lambda@Edge function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants