Search code examples
url-rewritingamazon-cloudfrontaws-lambda-edge

rewrite cloudfront origin host with lambda@edge, how?


I see people talking about how to rewrite a URI based on various information. But I'd like to normalize the domain name that is being requested. Here's what I've tried:

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if (request.method.toUpperCase() != 'GET') {
        callback(null, request);
        return;
    }

    request.origin = {
        custom: {
            domainName: 'slashdot.org',
            port: 443,
            protocol: 'https',
            path: request.uri
        }
    };
    request.headers.host = {
        "key": "Host",
        "value": request.origin.custom.domainName
    };

    console.log("returning req:", request);

    callback(null, request);
}

I was hoping that would pull up the request and that cloudfront would then make a request against my canonicalized domain. (for the example and testing, I'm using slashdot, since it's obvious it isn't my content).

Ultimately, I'm trying to canonicalize a request without doing redirects, but instead rewriting the request before hit hits the origin.


Solution

  • Oh, neat. I finally figured out how to do this. First, some restrictions:

    • With https this only works for domains under your certificate. So I can't use mydomain.com and slashdot.org unless I'm CmdrTaco. That's fine, my AWS ACM cert includes three domains, one of which is my actual domain. I'll call that actualdomain.com below.
    • This cannot be done on the viewer request, only the origin request, as host is read-only for the viewer request.

    Given that, I used "Example: Using an Origin-Request Trigger to Change From an Amazon S3 Origin to a Custom Origin" with some minor modifications. Here's the full Lambda code.

    'use strict';
    
    exports.handler = (event, context, callback) => {
        const request = event.Records[0].cf.request;
        const destDomain = 'actualdomain.com';
    
    
        /* Set custom origin fields*/
        request.origin = {
            custom: {
                domainName: destDomain,
                port: 443,
                protocol: 'https',
                path: '',
                sslProtocols: ['TLSv1', 'TLSv1.1', 'TLSv1.2'],
                readTimeout: 5,
                keepaliveTimeout: 5,
                customHeaders: {}
            }
        };
        request.headers['host'] = [{ key: 'host', value: destDomain}];
    
        callback(null, request);
    };
    

    So, similar to the example given in the docs, destDomain is the actual domain, and any legitimate domain in my certificate is proxied to that dest, without the end user actually seeing actualdomain.com.