AWS CloudFront Lambda@Edge 如何在四种触发器阶段运行代码?

来源:JS脚本作者:长沙SEO公司头衔:草根站长
导读:本期聚焦于长沙SEO公司创作的《AWS CloudFront Lambda@Edge 如何在四种触发器阶段运行代码?》,敬请观看详情。把一段代码放到 CloudFront 边缘节点上运行并不复杂,关键是放对阶段。Lambda@Edge 在请求和响应的四个不同位置提供执行机会:查看器请求、源站请求、源站响应、查看器响应。每个阶段能看到的数据不同,缓存命中后的行为也完全不同。比如只想给所有响应统一加安全头,放在源站响应阶段比查看器响应阶段更合理;而要做轻量的 A/B 测试或设备检测,查看器请求阶段才是首选。本文将逐一拆解这四个触发器的触发时机、可操作对象、典型代码示例,以及部署时的地域限制和包大小约束。读完可以明确判断自己的业务逻辑应该挂载到哪个阶段,避免因为阶段选错导致缓存失效或响应头丢失。

CloudFront 作为内容分发网络,在边缘站点上提供了大量缓存和转发能力。但很多定制逻辑无法靠配置实现,比如按设备类型改写 URL、统一注入源站认证头、给所有响应添加安全头。Lambda@Edge 就是解决这类问题的计算层,它允许在 CloudFront 处理请求和响应的过程中执行自定义代码。需要特别注意的是,Lambda@Edge 并不是一个笼统的入口,而是区分了四个触发器阶段:查看器请求、源站请求、源站响应、查看器响应。每个阶段能在不同时间点接触不同的数据,选错阶段可能带来缓存命中率下降、响应头丢失或函数执行成本增加。

AWS CloudFront Lambda@Edge 如何在四种触发器阶段运行代码?

一、四个触发阶段分别发生在请求链路的哪个位置

要理解 Lambda@Edge 的触发器阶段,先要搞清楚一个请求从用户浏览器到源站再返回的完整链路。用户发起请求后,CloudFront 边缘站点首先收到该请求,这时会触发查看器请求阶段。随后 CloudFront 会根据缓存键查找是否已有可用缓存。如果缓存命中,就直接进入查看器响应阶段;如果缓存未命中,CloudFront 会向源站发起回源请求,在发出回源请求前触发源站请求阶段。

源站接收到请求并返回响应后,CloudFront 边缘站点在把响应写进缓存之前触发源站响应阶段。接着 CloudFront 将响应缓存下来,最后在把响应返回给用户浏览器前触发查看器响应阶段。这四个阶段的执行顺序是固定的,但触发条件不同:只要用户请求到达边缘站点,查看器请求和查看器响应就一定会执行;而源站请求和源站响应只有在缓存未命中、需要回源时才会执行。

可以用一个简化的列表来梳理每个阶段的触发时机与可操作对象:

  • Viewer Request:用户请求到达 CloudFront 后、缓存查找之前触发。可操作用户请求头、URI、查询字符串等。
  • Origin Request:缓存未命中、CloudFront 准备向源站转发请求前触发。可操作回源请求头、URI、请求体策略等。
  • Origin Response:源站返回响应后、CloudFront 缓存响应之前触发。可操作源站响应头、状态码、响应体替换等。
  • Viewer Response:CloudFront 准备将响应返回给用户前触发。可操作最终返回给用户的响应头、状态码等,但无法修改已被缓存的响应体内容。

理解这个链路后,就能更准确地判断自己的业务逻辑应该放在哪个阶段。一个常见误区是把所有逻辑都塞进查看器请求阶段,因为它在每个请求上都会执行,即使缓存命中也不例外,这往往会增加延迟和成本。

二、各阶段典型场景与代码示例

Viewer Request:入口处的轻量判断与重写

查看器请求阶段适合做与用户直接相关的轻量级处理,例如移动端重定向、A/B 测试流量分配、URL 规范化、根据 Cookie 做访问控制。由于这个阶段发生在缓存查找之前,执行的任何逻辑都会影响所有请求,包括缓存命中的请求,因此函数必须保持非常轻量,避免引入明显的延迟。

下面这段示例代码演示了如何把根路径和 /index 请求重写为 /index.html,避免源站返回 403 或不完整的目录响应。函数接收 CloudFront 事件,取出 request 对象,修改 URI 后直接返回。

exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    const uri = request.uri;
    if (uri === '/' || uri === '/index') {
        request.uri = '/index.html';
    }
    return request;
};

这个阶段还能根据 User-Agent 判断设备类型,把移动端用户重定向到 /mobile 子目录。需要注意的是,重定向通常需要返回一个响应对象而不是修改请求对象,否则请求会继续向源站转发。在查看器请求阶段返回重定向响应是允许的,且不会占用回源资源。

Origin Request:把源站相关逻辑收敛到回源前

源站请求阶段只在缓存未命中时执行,因此它是处理源站相关逻辑的理想位置。比如给回源请求添加固定的认证头、修改回源路径以匹配源站目录结构、或者从边缘端剥离用户不需要的 Cookie,降低回源请求体积。

以下示例展示了如何只在 /api/ 路径下添加一个内部认证头。这样源站可以只信任带有该认证头的请求,避免用户直接绕过 CloudFront 访问源站时伪造请求头。

exports.handler = async (event) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const uri = request.uri;

    if (uri.startsWith('/api/')) {
        headers['x-origin-auth'] = [{ key: 'X-Origin-Auth', value: 'token-from-edge' }];
    }

    return request;
};

由于源站请求阶段只在回源时触发,比查看器请求阶段更适合执行涉及源站配置的逻辑。把认证、路径重写等操作收敛到这里,可以避免对缓存命中请求产生不必要的开销。同时要注意,这个阶段的执行超时时间比查看器阶段更长,可以承担稍重一些的处理,但仍不建议在函数中同步调用外部服务。

Origin Response:缓存写入前统一处理响应头

源站响应阶段发生在源站返回响应之后、CloudFront 写缓存之前。这里非常适合添加统一的响应安全头、设置 CORS 头、改写状态码或处理源站返回的错误页面。由于响应来自源站且尚未进入缓存,此时修改的效果会被 CloudFront 缓存下来,后续命中缓存的响应也会带上这些修改。

下面这段代码给所有源站响应添加了 HSTS 和 X-Frame-Options 头。这些头一旦被缓存,就不需要每个请求都执行逻辑,从而降低查看器响应阶段的压力。

exports.handler = async (event) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    headers['strict-transport-security'] = [
        { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubdomains' }
    ];
    headers['x-frame-options'] = [
        { key: 'X-Frame-Options', value: 'DENY' }
    ];

    return response;
};

如果把安全头添加逻辑放在查看器响应阶段,那么每次响应返回给用户时都要执行函数,即使响应已经被缓存过。而源站响应阶段则只在回源时执行一次,之后直接从缓存读取带着安全头的响应,成本和延迟都更低。这是阶段选择对性能影响的典型例子。

Viewer Response:最终交付前的个性化清理

查看器响应阶段发生在 CloudFront 把响应返回给用户之前的最后一刻。它适合处理那些不适合被缓存的、与具体用户相关的响应头或清理敏感信息。例如移除源站暴露的 Server、X-Powered-By 等内部技术栈信息,或者根据用户 Cookie 动态设置额外的个性化响应头。

下面的示例演示了如何删除源站返回的一些不必要响应头。这个操作必须在最终响应离开边缘节点前完成,而且这些删除动作不会被缓存,确保每次响应都能执行清理。

exports.handler = async (event) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    delete headers['server'];
    delete headers['x-powered-by'];
    delete headers['x-aspnet-version'];

    return response;
};

需要注意的是,查看器响应阶段无法修改已经被 CloudFront 缓存的响应体,只能操作响应头和状态码。如果需要修改响应体,可以考虑使用源站响应阶段或配合 Lambda 函数生成动态响应。此外,查看器响应阶段的超时时间也较短,只适合做轻量清理和定制。

三、部署与测试 Lambda@Edge 函数的注意事项

Lambda@Edge 函数有一个特殊的地域限制:函数必须创建在美国东部(弗吉尼亚北部)的 us-east-1 区域。之后 CloudFront 会自动把函数复制到全球边缘站点执行。如果你在其它区域创建 Lambda 函数,将无法直接关联到 CloudFront 分配,需要先在 us-east-1 区域部署并发布版本。

部署时通常先编写普通 Lambda 函数,然后通过 AWS CLI 或控制台在 us-east-1 创建函数。下面是一个使用 AWS CLI 创建函数的示例,注意使用 nodejs20.x 运行时,并指定正确的 IAM 角色。

aws lambda create-function \
--function-name edge-security-headers \
--runtime nodejs20.x \
--role arn:aws:iam::123456789012:role/service-role/edge-lambda-role \
--handler index.handler \
--zip-file fileb://function.zip \
--region us-east-1

创建完成后,Lambda@Edge 要求必须先发布一个版本,而不能直接使用 $LATEST。版本发布后才能在 CloudFront 的缓存行为中配置关联。下面是发布版本的命令:

aws lambda publish-version \
--function-name edge-security-headers \
--description "add security headers" \
--region us-east-1

随后在 CloudFront 分配的行为设置中,选择对应的 Lambda 函数 ARN 并指定触发器阶段即可。关联完成后,函数会被复制到边缘站点,这可能需要几分钟时间。测试时可以在 Lambda 控制台选择 CloudFront 测试事件模板,模拟不同阶段的输入结构。日志会分散在 CloudFront 边缘节点对应的区域,不一定集中在 us-east-1,需要在 CloudWatch 中查看对应区域的日志组。

四、性能影响、硬性限制与最佳实践

Lambda@Edge 在执行时会增加请求的延迟,尤其是查看器阶段,因为每次请求都会触发函数。因此要尽量避免在查看器请求和查看器响应阶段做复杂计算。查看器阶段的超时时间是 5 秒,源站阶段的超时时间是 30 秒,但实际业务中如果函数运行超过几百毫秒,用户就能明显感受到延迟。

Lambda@Edge 还有一些硬性限制需要提前了解。查看器阶段函数的部署包大小限制为 1MB,源站阶段为 50MB。函数内存范围为 128MB 到 3008MB。环境变量、VPC 访问、X-Ray 跟踪等功能在 Lambda@Edge 中不可用,函数也不能使用弹性文件系统。此外,每个函数只能与一个 CloudFront 发行版关联特定数量的触发器,具体配额可以在 AWS 文档中查看。

从性能角度看,合理的阶段选择比函数本身的优化更重要。能放进源站阶段的逻辑就不要放在查看器阶段,因为源站阶段仅在缓存未命中时执行。这样既能减少函数调用次数,也能降低缓存命中请求的延迟。对于响应头的添加,优先选择源站响应阶段,让修改后的响应直接被缓存。

在函数实现上,建议保持逻辑简单、避免同步调用外部 API,因为外部调用会显著增加延迟并可能超时。如果确实需要访问外部数据,可以考虑在部署前预取并打包进函数,或者使用 CloudFront 的缓存策略减少回源。同时要为函数编写错误处理逻辑,避免因为抛异常导致请求失败。Lambda@Edge 默认对函数异常有保护行为,但主动处理错误能让响应更加可控。

总结来说,四种触发器阶段为 CloudFront 提供了灵活的边缘计算能力,但每种阶段对应不同的数据可见性和性能成本。理解请求链路和缓存行为之后,把逻辑放到正确的阶段,是使用 Lambda@Edge 最重要的决策。选对阶段,很多看似复杂的需求只需要十几行代码就能在边缘稳定运行。

AWS CloudFrontLambda@Edge触发器阶段修改时间:2026-09-20 11:10:59

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0920/59622.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。