Serverless Framework 自定义域名(Custom Domains)完整指南:基于 AWS API Gateway、ACM 与 Route 53 的自动化域名接入
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
自定义域名接入是任何对外提供服务的 Serverless API 上线前必经的一步。本指南以本仓库(Serverless Framework v4 仓库)为背景,系统讲解其内置的 Custom Domains 功能:只需在serverless.yml的provider层级声明domain或domains,框架即可自动完成 ACM SSL 证书申请与 DNS 校验、Route 53 记录写入以及 API Gateway 域名映射。读完本文,你将掌握单域名、多 Stage、多域名、REST/HTTP/WebSocket 三类 API 的域名配置方法,理解全部配置项的默认值与约束,并能处理第三方注册商域名、多区域部署等边缘场景。
功能背景:从社区插件到内置能力
文档 Custom Domains 明确指出:Serverless Framework v4 的自定义域名能力借鉴并致敬了 Amplify Education,并由 packages/serverless/lib/plugins/index.js 随内置插件一起加载。
这个内置插件暴露了两个独立命令与四个生命周期钩子(见 domains/index.js):
- 命令:
serverless create_domain(创建域名)、serverless delete_domain(删除域名); - 钩子:
before:deploy:deploy(部署前创建/确认域名并把域名信息写入 CloudFormation 输出)、after:deploy:deploy(部署后建立 base path 映射)、after:info:info(展示域名摘要)、before:remove:remove(移除 base path 映射)。
插件是否介入由配置决定:只有当service.provider.domain或service.provider.domains出现时,钩子才会被触发(index.js)。也就是说,不配置域名时这些流程零开销。
快速开始
最简用法是在provider下声明domain:
# serverless.yml service: my-service provider: name: aws runtime: nodejs20.x domain: api.example.com functions: hello: handler: src/hello.handler events: - httpApi: path: / method: get然后执行:
serverless deploy两点须知:
- 若域名已在 Route 53 中注册并托管,框架会自动完成域名关联、SSL 证书申请与 DNS 配置;若域名在第三方注册商处,可继续使用,只需按下文 第三方注册商域名接入 完成少量手动 DNS 步骤。
- 部署完成后,由于 DNS 传播与 SSL 证书激活需要时间,自定义域名通常需要 2–5 分钟才能完全生效。
从源码看,这套"自动"背后是 createDomain 的分支逻辑:先查询 API Gateway 中是否已存在该自定义域名;若不存在且未显式给出certificateArn,则先尝试在 ACM 中查找可用证书,找不到时若判定域名由 Route 53 托管,就自动请求新证书、写入 DNS 校验记录并等待校验通过,最后创建自定义域名并 UPSERT Route 53 记录。
基本用法
单一域名
字符串形式的domain是最直接的写法(内部会被转换为{ name: <domain> },见 index.js):
# serverless.yml service: my-service provider: name: aws runtime: nodejs20.x domain: api.example.com functions: hello: handler: src/hello.handler events: - httpApi: path: / method: get多 Stage 差异化域名
利用 Serverless Framework 的params按 Stage 注入域名,避免每个环境重复维护配置:
# serverless.yml service: my-service params: dev: domain: api.example.dev prod: domain: api.example.com provider: name: aws runtime: nodejs20.x domain: ${param:domain} functions: hello: handler: src/hello.handler events: - httpApi: path: / method: get多域名
同一服务需要暴露多个域名时使用domains数组:
# serverless.yml service: my-service provider: name: aws runtime: nodejs20.x domains: - api.example.com - api-v2.example.com functions: hello: handler: src/hello.handler events: - httpApi: path: / method: getdomain与domains甚至可以同时使用——初始化阶段插件会把二者拼接后统一处理(index.js),每个配置项都可以是字符串或对象,字符串一律视为{ name }。
高级用法:对象式配置
当你需要控制 base path、API 类型或端点类型时,把domain写成对象:
service: your-service provider: name: aws runtime: nodejs20.x domain: name: api.example.com basePath: v1 apiType: http endpointType: regional functions: hello: handler: src/hello.handler events: - httpApi: path: /users method: get websocket: handler: src/websocket.handler events: - websocket: route: $connect注意:一个服务中 HTTP、REST、WebSocket 三种 API 各只能存在一个实例,而domain只能映射到其中一个 API,所以当你混合部署多种 API 并需要分别绑定域名时,应改用domains列表(见下文"多域名配置")。
域名配置写法:字符串与对象
字符串格式(简单场景)
provider: domain: api.example.com对象格式(进阶控制)
provider: domain: name: api.example.com # 必填:自定义域名 basePath: v1 # 可选:API 映射的 base path apiType: http # 可选:http、rest、websocket endpointType: regional # 可选:regional、edge accessMode: strict # 可选:仅 REST(单层 basePath)时生效多域名对象写法
provider: domains: - name: api.example.com apiType: http basePath: v1 - name: api-staging.example.com apiType: http basePath: v1 - name: websocket.example.com apiType: websocket配置项参考表
以下是domain/domains条目支持的全部配置选项:
| 选项 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 自定义域名(如api.example.com)。兼容旧的domainName键名(domain-config.js) |
basePath | string | 否 | API 映射的 base path(如v1、api)。默认值为(none)(不映射) |
apiType | string | 否 | API 类型:http、rest或websocket,默认随服务自动检测为http(无 API 时回退 http)。每种 API 类型每服务仅一个,因此指定后会映射到对应 API |
endpointType | string | 否 | 端点类型:regional或edge,默认regional |
certificateArn | string | 否 | 复用已有的 ACM 证书 ARN;不提供时尝试查找或新建证书 |
certificateName | string | 否 | 指定名称的已有 ACM 证书;提供后不再自动新建 |
createRoute53Record | boolean | 否 | 是否自动创建 Route 53 记录,第三方注册商设false,默认true |
createRoute53IPv6Record | boolean | 否 | 是否额外创建 IPv6(AAAA)Route 53 记录,默认true |
hostedZoneId | string | 否 | Route 53 Hosted Zone ID;不提供时自动探测 |
hostedZonePrivate | boolean | 否 | 是否使用私有 Hosted Zone,默认false |
route53Profile | string | 否 | 执行 Route 53 操作所使用的 AWS Profile |
route53Region | string | 否 | 执行 Route 53 操作所使用的 AWS 区域 |
route53Params | object | 否 | 透传给 Route 53 API 的附加参数(含路由策略等,见下文扩展说明) |
splitHorizonDns | boolean | 否 | 为私有 Hosted Zone 启用 split-horizon DNS(同时写公共与私有 Zone) |
securityPolicy | string | 否 | 域名安全策略;经 API Gateway V2 的域名请使用TLS_1_2 |
accessMode | string | 否 | API Gateway 端点访问模式:basic或strict(仅限 API Gateway V1 管理的 REST 域名) |
tlsTruststoreUri | string | 否 | 双向 TLS(mTLS)信任库的 S3 URI |
tlsTruststoreVersion | string | 否 | TLS 信任库版本 |
enabled | boolean/string | 否 | 是否启用该域名,可为布尔值或条件表达式字符串,默认true |
allowPathMatching | boolean | 否 | 是否允许基于 path 匹配路由 |
preserveExternalPathMappings | boolean | 否 | 保留未被 Serverless Framework 托管的外部 path mappings,默认false |
源码中的默认值与归一化规则
理解这些默认值与解析逻辑能帮你避开不少坑(参见 models/domain-config.js 与 globals.js):
- apiType / endpointType 大小写与别名:
http/rest/websocket与regional/edge均不区分大小写,内部统一转为大写(如HTTP、REGIONAL);非法值会直接抛出DOMAIN_CONFIG_INVALID_*类错误。 - basePath 归一化:空值取默认
(none);自动去除首尾/;ping、sping是 AWS API Gateway 保留的健康检查路径,会打印警告(globals.js)。 - securityPolicy 默认 TLS_1_2:
tls_1_0、tls_1_2是旧式简写(等价TLS_1_0/TLS_1_2);TLS 1.3 只能通过形如SecurityPolicy_TLS13_2025_EDGE的增强策略原样透传;tls_1_3这类简写会被拒绝(单测见 domain-config.test.js)。 - enabled / createRoute53Record / createRoute53IPv6Record / autoDomain / preserveExternalPathMappings:默认分别为
true / true / true / true / false;enabled支持条件表达式字符串,可在多区域场景按需开关某个域名。 - route53Params 内部扩展:除透传 API 参数外,插件识别
routingPolicy(simple/latency/weighted,默认simple)、setIdentifier、weight(默认 200)、healthCheckId;latency/weighted路由策略不允许与edge端点组合。 - splitHorizonDns:仅在未指定
hostedZoneId且非hostedZonePrivate时生效,此时 Route 53 记录会同时写入对应的公有 Zone 与私有 Zone。
完整高级配置示例
provider: name: aws runtime: nodejs20.x domain: name: api.example.com basePath: v1 apiType: rest endpointType: regional certificateArn: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012 createRoute53Record: true createRoute53IPv6Record: true hostedZoneId: Z1PA6795UKMFR9 securityPolicy: TLS_1_2 accessMode: strict enabled: true allowPathMatching: false preserveExternalPathMappings: false route53Params: TTL: 300 Comment: 'Custom domain for API' functions: hello: handler: src/hello.handler events: - http: path: /users method: get支持的 API 类型
HTTP API(默认)
HTTP API 是默认且推荐优先使用的 API 类型:
service: my-service provider: domain: name: api.example.com apiType: http functions: hello: handler: src/hello.handler events: - httpApi: path: / method: getREST API
service: my-service provider: domain: name: api.example.com apiType: rest functions: hello: handler: src/hello.handler events: - http: path: / method: getWebSocket API
service: my-service provider: domain: name: websocket.example.com apiType: websocket functions: connect: handler: src/websocket.connect events: - websocket: route: $connect disconnect: handler: src/websocket.disconnect events: - websocket: route: $disconnect default: handler: src/websocket.default events: - websocket: route: $defaultapiType 的自动检测机制
大多数情况下你可以省略apiType。插件会扫描编译后的 CloudFormation 模板中的资源HttpApi、ApiGatewayRestApi、WebsocketsApi来推断服务实际包含的 API 类型(index.js):
- 未检测到任何 API 资源时回退到
http; - 恰好检测到一种类型则自动使用该类型;
- 同时检测到多种类型(例如既有 HTTP API 又有 WebSocket API)而你又未显式声明
apiType,框架会抛错并提示你为每个域名显式指定,避免域名"不知道该映射给谁"的歧义。
走 API Gateway V1 还是 V2?
理解了映射网关的分流规则,就能解释文末许多"注意事项"的由来。源码中的判定逻辑是(index.js):HTTP 与 WebSocket 一律走 API Gateway V2;REST 单层 basePath 走 V1,REST 多层 basePath(basePath 含/,如v1/test)也走 V2。
Base Path 映射
通过basePath可以把 API 挂载到域名的特定路径前缀下:
service: my-service provider: domain: name: api.example.com basePath: v1 functions: users: handler: src/users.handler events: - httpApi: path: /users method: get配置生效后,API 将可从https://api.example.com/v1/users访问。base path 映射的实际创建/更新发生在部署之后由 setupBasePathMappings 执行:先通过 CloudFormation 找到当前 API 的 ID,再比对已有映射(allowPathMatching: true时按 basePath 匹配,否则按 apiId 匹配),不存在则创建、存在则更新。
端点类型:Regional 与 Edge
Regional(推荐)
Regional 端点对来自同一 AWS 区域的请求做了优化:
provider: domain: name: api.example.com endpointType: regionalEdge
Edge 端点通过 CloudFront 进行全球分发:
provider: domain: name: api.example.com endpointType: edge两点与 Edge 相关的关键限制(源码级证据):
- 证书区域:Edge 端点的 ACM 证书必须在
us-east-1创建。源码中 ACM 客户端在edge时会强制使用us-east-1(默认区域),而regional时使用当前部署区域(acm-wrapper.js);查找证书失败时的报错信息也会专门提示这一点。 - HTTP / WebSocket 不支持 Edge:校验逻辑会直接抛出
DOMAIN_VALIDATION_INCOMPATIBLE_ENDPOINT_TYPE(index.js)。原因是 AWS API Gateway 的 HTTP/WebSocket API 本身不支持 edge 分发,因此这类域名必须使用regional,或改用 REST。
多域名配置(组合不同类型 API)
复杂应用常需要让 HTTP、WebSocket、REST 各自绑定不同域名,配置如下:
service: my-service provider: name: aws runtime: nodejs20.x domains: - name: api.example.com apiType: http basePath: v1 - name: websocket.example.com apiType: websocket - name: admin.example.com apiType: rest basePath: admin functions: # HTTP API functions getUsers: handler: src/users.get events: - httpApi: path: /users method: get # WebSocket functions connect: handler: src/websocket.connect events: - websocket: route: $connect # REST API functions adminPanel: handler: src/admin.panel events: - http: path: /dashboard method: get创建与删除时,插件会对domains内所有条目并行执行操作(Promise.all,见 index.js)。
安全策略与对账(Reconciliation)要点
配置域名安全策略时请牢记以下规则:
accessMode仅适用于由 API Gateway V1 管理的 REST 域名(即单层 basePath 的 REST);accessMode(basic或strict)要求配套使用形如SecurityPolicy_*的增强securityPolicy;校验不通过会抛出DOMAIN_VALIDATION_INCOMPATIBLE_ACCESS_MODE;accessMode不支持http与websocketAPI;- 走 API Gateway V2 的域名(HTTP、WebSocket,以及多层 basePath 的 REST),
securityPolicy请使用TLS_1_2;显式配置其他值时会被校验逻辑拒绝(DOMAIN_VALIDATION_INCOMPATIBLE_SECURITY_POLICY); - 当自定义域名已存在时,框架仅对显式配置的
securityPolicy与accessMode执行对账(reconciliation),未配置的字段保持不变; - 对 API Gateway V2 的
securityPolicy对账,框架会从配置的certificateArn或已存在的域名对象中解析证书上下文后再发送更新请求。
前置条件
Route 53 Hosted Zone
域名必须在你 AWS 账户中存在对应的 Route 53 Hosted Zone。满足条件后,框架将自动完成:
- 为域名创建 ACM SSL 证书;
- 通过 DNS 记录完成证书校验;
- 创建所需的 Route 53 DNS 记录;
- 把域名映射到你的 API Gateway。
Hosted Zone 的自动探测逻辑位于 route53-wrapper.js:列出账户全部 Hosted Zone 后,用"域名后缀匹配 + 最长 Zone 名优先"的方式选择目标 Zone;指定了hostedZoneId则直接使用,指定hostedZonePrivate则只匹配对应公有/私有类型的 Zone,找不到匹配 Zone 时抛出ROUTE53_HOSTED_ZONE_NOT_FOUND。
域名所有权
你必须拥有该域名,并将其 DNS 托管在 Route 53(或按下文第三方注册商流程手动托管)。
第三方注册商域名接入(手动证书与 DNS)
当域名注册在第三方注册商(而非 Route 53)时,需要手动创建并配置 SSL 证书与 DNS 记录。
第 1 步:手动申请 SSL 证书
- 在 AWS 控制台打开 AWS Certificate Manager(ACM);
- 为你的域名申请新证书;
- 选择 DNS 校验方式;
- 把校验用的 CNAME 记录添加到注册商处的 DNS 设置;
- 等待证书校验通过并签发;
- 复制证书 ARN。
第 2 步:配置 Serverless Framework
填入证书 ARN,并关闭自动 Route 53 记录创建:
service: my-service provider: name: aws runtime: nodejs20.x domain: name: api.example.com certificateArn: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012 createRoute53Record: false apiType: http endpointType: regional functions: hello: handler: src/hello.handler events: - httpApi: path: / method: get配置了certificateArn后,源码中的 createDomain 会跳过证书查找与自动创建;createRoute53Record: false则让 Route 53 记录写入整体跳过(route53-wrapper.js)。
第 3 步:手动创建 DNS 记录
服务部署完成后,需要在注册商处创建解析记录:
- 执行
serverless deploy; - 从部署输出中记下 API Gateway 域名;
- 在注册商的 DNS 设置中创建:
- 类型:CNAME(若用别名记录则 A/AAAA)
- 名称:你的子域(如
api.example.com取api) - 值:API Gateway 域名(如
d-1234567890.execute-api.us-east-1.amazonaws.com)
提示:部署完成后可用
serverless info查看域名摘要(该命令同样在after:info:info时打印每个域名的状态),或从 CloudFormation 输出的DistributionDomainName取值。源码会把DomainName、DistributionDomainName、HostedZoneId(HTTP/WebSocket 场景带Http/Websocket后缀)注入 CloudFormation Outputs,便于下游引用(index.js)。
第三方注册商配置项速查
| 选项 | 必填 | 说明 |
|---|---|---|
certificateArn | 是 | 手动创建的 ACM 证书 ARN |
createRoute53Record | 是 | 设为false,阻止自动创建 Route 53 记录 |
name | 是 | 自定义域名 |
apiType | 否 | API 类型(http、rest、websocket),默认 http |
endpointType | 否 | 端点类型(regional、edge),默认 regional |
basePath | 否 | API 映射的 base path |
accessMode | 否 | basic或strict(仅 REST + 单层 basePath) |
第三方注册商下的多域名示例
provider: name: aws runtime: nodejs20.x domains: - name: api.example.com certificateArn: arn:aws:acm:us-east-1:123456789012:certificate/api-cert-12345 createRoute53Record: false apiType: http - name: websocket.example.com certificateArn: arn:aws:acm:us-east-1:123456789012:certificate/ws-cert-12345 createRoute53Record: false apiType: websocketSSL 证书:自动创建、复用与续期
框架对证书的处理策略可以总结为"先复用、再自动创建"(index.js 与 acm-wrapper.js):
- 证书复用:未显式提供
certificateArn时,先在 ACM 中按certificateName(若指定)或按域名精确匹配查找有效期内证书;找到即复用。Edge 端点下,会额外提示证书必须位于us-east-1。 - 自动创建:找不到有效证书且未指定
certificateName时,先检查域名是否托管在 Route 53;若是,则自动申请 ACM 证书(DNS 校验方式)、把校验 CNAME 记录写入对应 Hosted Zone(TTL 300),并轮询等待证书校验通过后再创建自定义域名。域名不在 Route 53 时则无法自动创建,需要走第三方注册商流程。 - 自动续期:证书签发后由 AWS ACM 服务本身负责自动续期,框架无需干预。
- 双向 TLS(mTLS):如需 mTLS,配置
tlsTruststoreUri(形如s3://bucket-name/key-name的 S3 URI)与可选tlsTruststoreVersion。注意 edge 端点不支持 mTLS,配置会被校验拒绝;创建前插件还会通过 S3 客户端确认信任库对象真实存在(index.js)。
部署过程与底层调用链
一次携带自定义域名配置的serverless deploy实际触发两条插件流水线:
- 部署前(
before:deploy:deploy):并行处理每个域名——默认(autoDomain: true)先走 createDomain(必要时自动申请证书、等待校验、创建 API Gateway 自定义域名、UPSERT Route 53 A/AAAA 别名记录),随后轮询等待域名在 API Gateway 侧就绪(默认最长 120 秒、每 3 秒一次,可用autoDomainWaitFor调整),最后把域名信息写入 CloudFormation Outputs; - 部署后(
after:deploy:deploy):执行 base path 映射的创建/更新(setupBasePathMappings),并打印域名摘要(含各域名的分发域名与 Hosted Zone)。
与之相对,serverless remove会先移除 base path 映射,若autoDomain为真且无外部映射残留,还会顺带删除自定义域名与 Route 53 记录;preserveExternalPathMappings: true则用于保留非本服务管理的映射不误删(removeBasePathMappings)。
日常维护则可以直接使用serverless create_domain/serverless delete_domain两个独立命令做前置创建或单独清理。
限制
- 自定义域名仅支持部署到 AWS 的服务;
- 全自动流程要求域名由 Route 53 托管(第三方注册商需手动证书与 DNS);
- 证书校验可能需要几分钟;
- Edge 端点的自定义域名要求证书位于
us-east-1区域; - 一个服务中 HTTP、REST、WebSocket 各只能有一个 API 实例,多种 API 共存时请使用
domains并显式指定apiType。
故障排查
证书校验失败
若证书校验失败:
- 确认域名存在对应的 Route 53 Hosted Zone(找不到时会报
ROUTE53_HOSTED_ZONE_NOT_FOUND); - 用
dig或nslookup验证 DNS 传播; - 检查校验记录是否被正确创建(自动流程中校验 CNAME 的 TTL 为 300 秒)。
域名部署后无法访问
若部署后域名仍不可访问:
- 等待 DNS 传播(最长可达 48 小时);
- 确认 Route 53 记录已创建(A 与 AAAA 别名记录指向 API Gateway 分发域名);
- 检查 API Gateway 中的域名配置与 base path 映射状态;
- 确认函数本身已正确部署并可经由默认 API 端点访问。
多区域部署同一域名
使用同一域名做多区域部署时:
- 使用 regional 端点避免冲突;
- 考虑每个区域使用不同子域;
- 用 Route 53 路由策略(如延迟
latency或加权weighted,经route53Params.routingPolicy配置)做流量分发。
延伸阅读
想深入理解本文涉及的实现细节,可以在本仓库内继续阅读:
- 功能文档:自定义域名官方指南 domains.md;
- 插件主入口与生命周期:
packages/serverless/lib/plugins/aws/domains/index.js; - 配置模型与默认值归一化:
packages/serverless/lib/plugins/aws/domains/models/domain-config.js、.../models/domain-info.js; - 常量定义(API 类型、端点类型、TLS 版本、保留 basePath 等):
packages/serverless/lib/plugins/aws/domains/globals.js; - AWS 服务封装:
packages/serverless/lib/plugins/aws/domains/aws/下的acm-wrapper.js、route53-wrapper.js、api-gateway-v1-wrapper.js、api-gateway-v2-wrapper.js、cloud-formation-wrapper.js、s3-wrapper.js; - 单元测试(校验默认值与错误分支):
packages/serverless/test/unit/lib/plugins/aws/domains/models/domain-config.test.js与.../models/domain-info.test.js。
【免费下载链接】serverless⚡ Serverless Framework – Effortlessly build apps that auto-scale, incur zero costs when idle, and require minimal maintenance using AWS Lambda and other managed cloud services.项目地址: https://gitcode.com/GitHub_Trending/se/serverless
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考