Skill 137 · AWS Lambda Web Functions
Subchapter 137.3
references/domains-and-routing.mdMarkdown10 KBView on GitHub
Every endpoint gets a managed HTTPS domain (TLS automatic, no configuration):
{endpointId}.{generated}.lambda-web.{region}.on.aws{endpointId}.lambda-web.global.on.awsHomeRegion — single region, lower cost, good for dev/test:
{"endpointType": "HomeRegion"}MultiRegion — Route53 latency-based routing across the configured regions:
{"endpointType": "MultiRegion", "autoDeploymentMode": "Disabled", "revisionWeights": [{"revisionId": "{rev}", "weight": 100}], "regions": ["us-west-2", "eu-west-1"]}PerRegion — a separate endpoint per region: like MultiRegion, but each region also gets its own domain name, so a region can be addressed directly as well as through the shared domain:
{"endpointType": "PerRegion", "autoDeploymentMode": "Disabled", "revisionWeights": [{"revisionId": "{rev}", "weight": 100}], "regions": ["us-west-2", "eu-west-1"]}Important:
MultiRegionandPerRegionendpoints requireautoDeploymentMode: Disabled—LatestRevisionis only supported forHomeRegion. When Disabled, you must explicitly specifyrevisionWeights.
Multi-region is a property of an endpoint, not of the function: you add one to an existing function, and the function’s home region is fixed when it is created.
Active before they can appear in revisionWeights.create-web-function-revision, then update-web-function-endpoint./tmp.aws lambda-web create-web-function-endpoint \
--function-name {name} \
--endpoint-name global \
--endpoint-type MultiRegion \
--auth-type ApplicationManaged \
--auto-deployment-mode Disabled \
--revision-weights '[{"revisionId":"{rev}","weight":100}]' \
--regions us-west-2 eu-west-1 \
--region us-east-1 # home region — endpoint spans us-east-1 + us-west-2 + eu-west-1| Type | Exposure | Use case |
|---|---|---|
ApplicationManaged | Public — anyone on the internet with the URL; no AWS-layer auth. | Public websites/APIs where the app handles its own auth (JWT, sessions, API keys). Authorization header passed through to your code. |
IamAuth | Private — only IAM-signed (SigV4) callers. | Internal / service-to-service endpoints. |
For public websites, use ApplicationManaged — but the app is then responsible for its own authentication, authorization, input validation, and rate limiting. See iam-and-security.md (opens in a new tab).
L7 protection. There is no built-in WAF on the managed domain, and throttleConfig.rateLimit is endpoint-wide rather than per-client. For production ApplicationManaged workloads that need L7 DDoS protection, bot mitigation or IP filtering, put your own CloudFront distribution with AWS WAF in front of the endpoint and treat the endpoint domain as the origin.
autoDeploymentMode: LatestRevision — new revisions get 100% traffic immediatelyautoDeploymentMode: Disabled — manual control, enables canary/blue-greenFor canary or blue-green, revisionWeights splits traffic across at most 2 revisions and the weights must sum to exactly 100 (e.g. 90/10). To finish a rollout, replace the pair with a single entry at 100 — a weight of 0 is invalid, so drop the retired revision rather than zeroing it.
Configure in your app code (Lambda Web Functions doesn’t manage CORS):
// Express
import cors from 'cors';
app.use(cors({ origin: 'https://myapp.example.com' }));// Hono
import { cors } from 'hono/cors';
app.use('/api/*', cors({ origin: 'https://myapp.example.com' }));All endpoints enforce HTTPS. Your app receives plain HTTP on the internal port — no TLS handling needed in your code.
The managed domain serves HTTP/1.1. A client offering HTTP/2 in ALPN is answered with
http/1.1; there is no h2 negotiation, and no HTTP/3. Confirm for yourself with
curl -sv --http2 https://{domainName}/ and read the ALPN lines.
This matters before you design, not after: HTTP/2 multiplexing, header compression and server push are all unavailable, so a browser client pays a connection per parallel request. If you need HTTP/2 at the edge, terminate it on CloudFront — see Putting CloudFront in front.
Response bodies are returned uncompressed regardless of the request’s Accept-Encoding. No
content-encoding header comes back for gzip, br, or gzip, deflate, br, and the transferred
byte count is identical in every case, including identity. On text-heavy JSON that is a large
multiple of what a compressed response would cost in egress, and it usually shows up on the bill
before it shows up anywhere else.
Two fixes, and they compose:
// In the app — compress before the response leaves the environment.
import compression from 'compression';
app.use(compression());Or compress at the edge, by enabling automatic compression on a CloudFront distribution in front of the endpoint. Compressing in the app is the one that also reduces bytes out of the environment.
There is no native custom-domain or certificate configuration on a Web Function endpoint. The
endpoint is reachable only on its generated {endpointId}.{generated}.lambda-web.{region}.on.aws
name, and
nothing in endpoint-config accepts a domain name or an ACM certificate ARN. Confirm against
aws lambda-web create-web-function-endpoint help rather than assuming one can be attached.
No production site ships public traffic on the generated name, so the answer for a custom domain is CloudFront in front.
One distribution solves four separate problems: a custom domain, HTTP/2 at the edge, compression, and L7 protection. If you need any one of them, this is the shape.
domainName as a custom origin, HTTPS only. To make CloudFront
the only caller, use origin access control (OAC) as below.us-east-1, which is where
CloudFront reads certificates from regardless of where the function runs, and attach it to the
distribution as an alternate domain name.CNAME for a subdomain, or the equivalent at an external DNS provider.throttleConfig.rateLimit is endpoint-wide rather than
per-client.The endpoint stays reachable on its generated name after you put CloudFront in front, so the distribution is not by itself an access control. To let only your distribution invoke it:
IamAuth. Lambda does not authenticate ApplicationManaged requests
or evaluate a resource policy for them, so an ApplicationManaged endpoint stays public even
behind CloudFront with OAC.lambda and signing behavior
always-amz-auth (CloudFront rejects other signing behaviors for Web Function origins), and
attach it to the origin. CloudFront then signs every origin request, so viewers never sign.aws:SourceArn and aws:SourceAccount:{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowCloudFrontOAC",
"Effect": "Allow",
"Principal": {"Service": "cloudfront.amazonaws.com"},
"Action": "lambda:InvokeWebFunctionEndpoint",
"Resource": "arn:aws:lambda:{region}:{account}:web-function/{name}/endpoint/{endpoint}",
"Condition": {"StringEquals": {
"aws:SourceAccount": "{account}",
"aws:SourceArn": "arn:aws:cloudfront::{account}:distribution/{distribution-id}"
}}
}]
}aws lambda-web put-resource-policy \
--resource-arn arn:aws:lambda:{region}:{account}:web-function/{name} \
--policy file://policy.json --region {home-region}The policy attaches to the function ARN; each statement scopes Resource to an endpoint ARN.
A function has one policy and put-resource-policy replaces it, so to add a statement read the
current one with get-resource-policy, append to Statement, and pass its revisionId with
--revision-id.
App-level auth still works behind OAC. Lambda strips only an Authorization value that is itself an
AWS signature (AWS4-...), so forward the viewer’s Authorization header in the origin request
policy and a JWT or bearer token reaches the app. For the stack form, see
infrastructure-as-code.md (opens in a new tab).