我是一个很酷的URL和不破坏互联网的大粉丝,所以多年来我积累了一些域名,我为这些域名实施了很多重定向规则。近年来,我使用mod_rewrite实现了这些规则,并在Apache和我的LinodeVPS上运行它们,例如 blog.floehopper.org 的重定向规则。
然而,在2019年9月,我开始在GitHub Pages上发布这个网站,在这期间我几乎删除了我在该VPS上运行的所有其他东西。因此,我开始考虑关闭VPS并停止支付它的费用,这将是一件很好的事情!上面提到的遗留重定向是唯一阻止我这样做的事情。
在过去的18个月里,我在工作中和个人项目中都大量使用了AWS CDK。不幸的是,我只发表了一篇关于它的文章(我希望在不远的将来能弥补这一点)。总之,我读了一些关于Lambda@Edge的文章,并注意到CloudFormation和AWS CDK确实支持它,所以我想我要试一试。
我的基本想法是创建一个CloudFront分布,将一个自定义域名与之关联,配置一个Edge Lambda函数来处理对该分布的所有请求,并在JavaScript中实现适当的重定向,然后将相关的DNS记录指向CloudFront分布。 这被证明是非常简单的,尽管我必须使用AWS证书管理器来为该域名创建一个SSL证书,以使一切正常工作。
你可以在GitHub上找到完整的项目源代码。恐怕我还没有来得及更新AWS CDK生成的默认版本的README。如果你愿意,这里有几个关键项目文件的略微删减版本,说明我是如何为blog.floehopper.org 。
# file: lib/edge-redirector-stack.ts
import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as cloudfront from '@aws-cdk/aws-cloudfront';
import * as origins from '@aws-cdk/aws-cloudfront-origins';
import * as acm from '@aws-cdk/aws-certificatemanager';
export class EdgeRedirectorStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps)
{
super(scope, id, props);
this.createDistribution('blog.floehopper.org', 'blogFloehopperOrg', 'arn:aws:acm:us-east-1:687105911108:certificate/aa11ee5a-54db-4a04-8307-f77330f86cb5');
}
createDistribution(domain: string, handler: string, certificateArn: string)
{
const certificate = acm.Certificate.fromCertificateArn(this,`${handler}Certificate`, certificateArn);
new cloudfront.Distribution(this, `${handler}Distribution`,
{
defaultBehavior: {origin: new origins.HttpOrigin('example.com'), edgeLambdas: [{ eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, functionVersion: this.redirectVersion(domain, handler) }]}, domainNames: [domain], certificate: certificate, enableLogging: true});
}
redirectVersion(domain: string, handler: string) :lambda.IVersion {
const redirectFunction = new cloudfront.experimental.EdgeFunction(this, `${handler}Redirect`, { runtime: lambda.Runtime.NODEJS_12_X,handler:`${handler}.handler`, code: lambda.Code.fromAsset('./lambdaFunctions/redirect')});
return redirectFunction.currentVersion;
}
}
# file: lambdaFunctions/redirect/blogFloehopperOrg.js
'use strict';
exports.handler = function(event, context, callback) {
const request = event.Records[0].cf.request;
const mapping = [//Legacy Typo-style articles ['^/articles/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.+)$', (m) => `http://jamesmead.org/blog/${m[1]}-${m[2]}-${m[3]}-${m[4]}`], // Redirect blog.floehopper.org -> jamesmead.org ['^/(.*)$', (m) => `http://jamesmead.org/${m[1]}`], ['^$', (m) => `http://jamesmead.org`], ];
let redirectUrl;
for (const [pattern, url] of mapping) {
const match = request.uri.match(new RegExp(pattern));
if (match) {
if (typeof(url) == 'function') {
redirectUrl = url(match);
} else {
redirectUrl = url;
};
break;
};
};
let response;
if (redirectUrl) {
response = {status: '301',statusDescription: 'Moved Permanently',headers: { location: [{ key: 'Location', value: redirectUrl}],} };
} else {
response = {status: '404',statusDescription: 'Not Found' };
};
callback(null, response);
};