diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/SKILL.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/SKILL.md index f170cc419..3c3b23805 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/SKILL.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/SKILL.md @@ -1,8 +1,8 @@ --- name: aws-lambda-web-functions description: > - Creates and deploys Node.js HTTP web applications on AWS Lambda Web Functions. Guides users through building web apps and APIs with Express, Fastify, or Hono, packaging, deploying via the aws lambda-web CLI, and iterating. Triggers when user wants to deploy a website or web app to AWS as serverless, deploy a Node.js HTTP server to AWS, build a serverless website or API with Node.js, deploy Express/Hono/Fastify apps, set up multi-region HTTP endpoints, connect a Node.js web app to private VPC resources, or explicitly mentions Lambda Web Functions. Does NOT trigger for non-Node.js runtimes, event-driven patterns (SQS, Kinesis, S3 events), or container deployments. -version: 1 + Creates and deploys Node.js HTTP web applications on AWS Lambda Web Functions. Guides users through building web apps and APIs with Express, Fastify, or Hono, packaging, deploying via the aws lambda-web CLI, and iterating. Triggers when user wants to deploy a website or web app to AWS as serverless, deploy a Node.js HTTP server to AWS, build a serverless website or API with Node.js, deploy Express/Hono/Fastify apps, set up multi-region HTTP endpoints, connect a Node.js web app to private VPC resources, or explicitly mentions Lambda Web Functions. Does NOT trigger for non-Node.js runtimes or frameworks such as Flask or Django, event-driven patterns (SQS, Kinesis, S3 events), or container deployments. +version: 2 --- # AWS Lambda Web Functions @@ -234,7 +234,7 @@ Two consequences worth planning around: - **I/O-bound workloads are the economical case.** A handler that spends most of its life waiting on the network, sharing an environment with other requests, costs less here than the same handler billed per-invoke on wall-clock duration. - **CPU-bound workloads give that advantage back.** Heavy rendering, image processing or crypto burns billable CPU wherever it runs, and an environment is allocated 2 vCPU — of which a single-threaded Node process uses only one (see Anti-Patterns). For sustained high-volume CPU work, especially where committed-use discounts apply, compare **`aws-lambda-managed-instances`**. -An endpoint's scaling controls `scalingConfig.maxEnvironments` and `throttleConfig.rateLimit` can be used to limit potential cost. See [scaling-and-concurrency.md](references/scaling-and-concurrency.md) for more details. +An endpoint's scaling controls `scalingConfig.maxEnvironments` and `throttleConfig.rateLimit` can be used to limit potential cost. Set them only when the user asks for a cap. See [scaling-and-concurrency.md](references/scaling-and-concurrency.md) for more details. ## IAM Setup @@ -267,9 +267,10 @@ What it looks like → why it fails → what to do instead. | Hardcoding `app.listen(3000)`, or binding `localhost` / `127.0.0.1` | The runtime probes the exact address it advertises. A mismatched port or a loopback-only bind never answers that probe | Parse `AWS_LAMBDA_HTTP_ENDPOINT` and bind that host and port | | Module-level mutable state holding per-request data — a `currentUser`, a request-scoped cache, a counter | One execution environment serves many concurrent requests in a single process, so requests read each other's values. This is a correctness and data-leak bug, not a performance one | Keep per-request data in the request scope; share only immutable config and SDK clients at module level | | Writing session or upload state to `/tmp` and expecting it on the next request | `/tmp` is per environment and shared between concurrent requests; the next request may land on a different environment, or a different Region on a multi-region endpoint | Keep state in DynamoDB, S3 or ElastiCache; use unique filenames for genuinely temporary files | -| Treating the endpoint URL as access control — "nobody knows this domain" | An `ApplicationManaged` endpoint is reachable by anyone on the internet with the URL. No AWS-layer auth, no built-in WAF, and the URL is not a secret | Implement auth in the app, or use `IamAuth`; add CloudFront + WAF for L7 protection → [iam-and-security.md](references/iam-and-security.md) | +| Treating the endpoint URL as access control — "nobody knows this domain" | An `ApplicationManaged` endpoint is reachable by anyone on the internet with the URL. No AWS-layer auth, no built-in WAF, and the URL is not a secret | Implement auth in the app, or use `IamAuth`; to let only CloudFront in, use `IamAuth` with CloudFront OAC and a resource-based policy → [domains-and-routing.md](references/domains-and-routing.md); add WAF for L7 protection → [iam-and-security.md](references/iam-and-security.md) | | Secrets in `serviceConfig.environmentVariables` | `get-web-function-revision` returns environment variables in plaintext to anyone with read access to the revision | Keep the secret in Secrets Manager or SSM, pass only its name or path, fetch at runtime | | Running `npm ci --omit=dev` before the build step | Pruning devDependencies deletes the bundler, so the build then fails with `esbuild: command not found` | Build first, prune afterwards — or zip the bundled output → [deployment.md](references/deployment.md) | +| Relying on the runtime for `@aws-sdk/*`, or marking it external (`externalModules`, `--external:@aws-sdk/*`), as on Event Functions | The Web Functions runtime does not include the AWS SDK, so the import fails at startup and every request returns 502 | Package the `@aws-sdk/*` clients you import like any other dependency → [deployment.md](references/deployment.md) | | Expecting an `update-*` call to change code, runtime, timeout, env vars or concurrency in place | All of those live on the immutable revision | Create a new revision. Only endpoint-level settings (auth, weights, scaling, throttle) update in place | | Re-uploading to the same S3 key and expecting a redeploy | A revision is pinned to an object version; CloudFormation reports "No changes to deploy" | Use a new key, or pass the new `versionId` | | Concluding the feature does not exist because `aws lambda-web` is missing | Absence means an outdated CLI, not a missing capability | Update the AWS CLI → [deployment.md](references/deployment.md) | @@ -308,10 +309,10 @@ Match on meaning, not exact wording — any trigger word in a row should pull th | [getting-started.md](references/getting-started.md) | first deploy, get started, set up, prerequisites, hello world, `--hello-world`, starter app, new project, from scratch, S3 code bucket, execution role, bootstrap, my first web function | | [frameworks.md](references/frameworks.md) | Express, Fastify, Hono, TypeScript, `.ts`, type stripping, ESM, `type: module`, scaffold, project structure, entry point, `index.js`, router, middleware | | [deployment.md](references/deployment.md) | deploy, redeploy, ship, release, CLI, `aws lambda-web`, `aws lambda-web deploy`, one command, revision, endpoint, packaging, ZIP, `npm ci`, timeout, environment variables, env vars, canary, blue/green, traffic weights, rollback, `versionId` | -| [scaling-and-concurrency.md](references/scaling-and-concurrency.md) | scaling controls, concurrency, `maxConcurrencyPerEnvironment`, `scalingConfig`, `maxEnvironments`, `throttleConfig`, `rateLimit`, throttling, RPS limit, vCPU quota | -| [infrastructure-as-code.md](references/infrastructure-as-code.md) | CloudFormation, CFN, CDK, `CfnResource`, escape hatch, SAM, Terraform, HashiCorp, HCL, infrastructure as code, IaC, template, stack, `AWS::Lambda::WebFunction`, `AWS::Lambda::NetworkConnector`, VPC egress in a stack, repeatable deployment, pipeline | +| [scaling-and-concurrency.md](references/scaling-and-concurrency.md) | scaling controls, concurrency, `maxConcurrencyPerEnvironment`, `scalingConfig`, `maxEnvironments`, `throttleConfig`, `rateLimit`, throttling, RPS limit, vCPU quota, load test, `Platform.EndpointMaxEnvThrottle` | +| [infrastructure-as-code.md](references/infrastructure-as-code.md) | CloudFormation, CFN, CDK, `CfnResource`, escape hatch, SAM, Terraform, HashiCorp, HCL, infrastructure as code, IaC, template, stack, `AWS::Lambda::WebFunction`, `AWS::Lambda::NetworkConnector`, VPC egress in a stack, `AWS::Lambda::ResourcePolicy`, repeatable deployment, pipeline | | [static-assets.md](references/static-assets.md) | static files, static assets, CSS, JS bundle, images, favicon, `public/`, `express.static`, SPA, single-page app, client-side routing, fallback route, bundling, esbuild, Vite, cache headers | -| [domains-and-routing.md](references/domains-and-routing.md) | multi-region, multiregion, global, latency routing, Route53, domain, custom domain, hostname, HTTPS, TLS, endpoint type, `HomeRegion`, `MultiRegion`, `PerRegion`, CORS, auth type, `ApplicationManaged`, `IamAuth` | +| [domains-and-routing.md](references/domains-and-routing.md) | multi-region, multiregion, global, latency routing, Route53, domain, custom domain, hostname, HTTPS, TLS, endpoint type, `HomeRegion`, `MultiRegion`, `PerRegion`, CORS, auth type, `ApplicationManaged`, `IamAuth`, CloudFront, OAC, origin access control, resource-based policy | | [iam-and-security.md](references/iam-and-security.md) | IAM, policy, permissions, role, trust policy, `PassRole`, least privilege, AccessDenied, security, auth, authentication, authorization, public endpoint, WAF, DDoS, rate limiting, secrets, KMS, encryption, security headers, `helmet` | | [observability.md](references/observability.md) | logs, logging, log group, log level, CloudWatch, metrics, monitoring, alarms, tracing, CloudTrail, audit, structured logging, health check, which region served, debug in production | | [architecture-patterns.md](references/architecture-patterns.md) | SSR, server-side rendering, React, JSX, streaming, SSE, server-sent events, LLM, AI gateway, token streaming, long-lived connection, webhook, background task, fire and forget, API backend, DynamoDB, connection pooling, thread safety, concurrency model | diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/architecture-patterns.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/architecture-patterns.md index 12ef539f9..4c3279540 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/architecture-patterns.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/architecture-patterns.md @@ -25,6 +25,10 @@ const [HOST, PORT] = (process.env.AWS_LAMBDA_HTTP_ENDPOINT || '0.0.0.0:3000').sp app.listen(PORT, HOST); ``` +The runtime does not include the AWS SDK, so install the clients as dependencies +(`npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb`) and package them → +[deployment.md](references/deployment.md). + ## SSR (Server-Side Rendering) Render HTML on the server per request — a plain `index.js` HTTP server, no build step and diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/deployment.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/deployment.md index db732e311..1c10b77b6 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/deployment.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/deployment.md @@ -161,6 +161,12 @@ zip -r function.zip index.js package.json node_modules/ public/ \ Include: `index.js`, package.json, node_modules/, public/. See Service Limits in [SKILL.md](SKILL.md) for the package size cap. +**Package the AWS SDK.** The Web Functions runtime does not include the AWS SDK, so package the +`@aws-sdk/*` clients your code imports like any other dependency: list them in `dependencies` (the +`npm ci --omit=dev` step removes `devDependencies`), or bundle them. Do not mark them external +(`externalModules: ['@aws-sdk/*']`, `--external:@aws-sdk/*`), which is common for Event Functions. +A missing client fails the import at startup and every request returns 502. + **If you have a build step** (esbuild for JSX or TypeScript), build *before* pruning. `npm ci --omit=dev` deletes the bundler along with the other devDependencies, so a later `npm run build` fails with `esbuild: command not found`: ```bash diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/domains-and-routing.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/domains-and-routing.md index cd3c76d56..7b9566c8d 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/domains-and-routing.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/domains-and-routing.md @@ -135,8 +135,8 @@ 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. -1. **Origin.** Use the endpoint's `domainName` as a custom origin, HTTPS only. It is a public - hostname, so there is no origin access identity to configure. +1. **Origin.** Use the endpoint's `domainName` as a custom origin, HTTPS only. To make CloudFront + the only caller, use origin access control (OAC) as below. 2. **Certificate.** Request or import an ACM certificate **in `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. @@ -149,8 +149,53 @@ and L7 protection. If you need any one of them, this is the shape. 6. **L7 protection.** Attach AWS WAF for rate limiting, bot control and IP reputation. The managed domain has none of its own, and `throttleConfig.rateLimit` is endpoint-wide rather than per-client. +7. **Logging.** Enable CloudFront standard logging and AWS WAF logging to an encrypted + destination, so edge traffic is available for audit and incident response. The function's own + logs record only requests that reach it. -The endpoint stays publicly reachable on its generated name after you put CloudFront in front, so -the distribution is not by itself an access control. -If only CloudFront should reach the app, enforce that in the app, for example by requiring a shared -secret header that the distribution adds and the app verifies. +### Restricting the endpoint to CloudFront with OAC + +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: + +1. **Set the endpoint to `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. +2. **Create an origin access control** with origin type `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. +3. **Grant CloudFront invoke in the function's resource-based policy**, scoped to the endpoint and + to your distribution with `aws:SourceArn` and `aws:SourceAccount`: + +```json +{ + "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}" + }} + }] +} +``` + +```bash +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](references/infrastructure-as-code.md). diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/iam-and-security.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/iam-and-security.md index c3005819b..b8bbe2969 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/iam-and-security.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/iam-and-security.md @@ -91,7 +91,7 @@ Web Functions endpoints are **public by default** (`ApplicationManaged`). Treat - **App-owned auth.** For `ApplicationManaged`, implement authentication and authorization in the app (validate a JWT / session / API key on every protected route). - **Input validation.** Validate and sanitize all request input (path, query, headers, body) — you're taking raw internet traffic. Guard against injection, oversized payloads, and malformed input. - **Rate limiting / abuse protection.** Set `throttleConfig.rateLimit` on the endpoint to cap requests per second, and cap `scalingConfig.maxEnvironments` to bound blast radius. That throttle is endpoint-wide, not per-client, so per-client quotas still belong in the app. -- **L7 protection (DDoS / bots / WAF).** No built-in WAF. For public-facing apps that need L7 DDoS/bot protection, place your own CloudFront distribution (with AWS WAF) in front of the endpoint. +- **L7 protection (DDoS / bots / WAF).** No built-in WAF. For public-facing apps that need L7 DDoS/bot protection, place your own CloudFront distribution (with AWS WAF) in front of the endpoint. To make the distribution the only caller, use an `IamAuth` endpoint with CloudFront OAC and a resource-based policy → [domains-and-routing.md](references/domains-and-routing.md). - **Security headers.** For browser-facing responses, set standard headers — `Content-Security-Policy`, `Strict-Transport-Security` (HSTS), `X-Frame-Options`, `X-Content-Type-Options`. Use a middleware like `helmet` for Express/Fastify rather than setting them by hand. - **Enforce TLS on the code bucket.** For production, add a `Deny` statement on `s3:*` for both the bucket and its objects conditioned on `aws:SecureTransport` being `false`, so artifacts cannot be fetched over plain HTTP. diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/infrastructure-as-code.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/infrastructure-as-code.md index ba955d1ec..ca41c1386 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/infrastructure-as-code.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/infrastructure-as-code.md @@ -107,6 +107,54 @@ to it. The CLI path does not do this: `aws lambda-web deploy` carries the previo connector ARN forward verbatim, and that ARN is version-qualified, so a redeploy keeps the old version. +## CloudFront-Only Access in a Stack + +To let only a CloudFront distribution invoke an `IamAuth` endpoint, attach the function's +resource-based policy with `AWS::Lambda::ResourcePolicy`. The property is **`ResourceArn`**, set to +the web-function ARN; `FunctionResourceArn` is not a property and CloudFormation rejects it. The +distribution's origin uses an `AWS::CloudFront::OriginAccessControl` with +`OriginAccessControlOriginType: lambda` and `SigningBehavior: always-amz-auth`. Why each piece is +needed: [domains-and-routing.md](references/domains-and-routing.md). + +```yaml + OriginAccessControl: + Type: AWS::CloudFront::OriginAccessControl + Properties: + OriginAccessControlConfig: + Name: {name}-oac + OriginAccessControlOriginType: lambda + SigningBehavior: always-amz-auth + SigningProtocol: sigv4 + # On the distribution's origin: OriginAccessControlId: !GetAtt OriginAccessControl.Id + + CloudFrontInvokePolicy: + Type: AWS::Lambda::ResourcePolicy + Properties: + ResourceArn: !GetAtt WebFunction.FunctionArn # the web-function/... ARN + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: { Service: cloudfront.amazonaws.com } + Action: lambda:InvokeWebFunctionEndpoint + Resource: !GetAtt WebFunctionEndpoint.EndpointArn + Condition: + StringEquals: + aws:SourceAccount: !Ref AWS::AccountId + aws:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${Distribution}" +``` + +`aws:SourceArn` must be the distribution's full ARN, so build it with `!Sub` as above. +`!Ref Distribution` returns only the distribution ID and `AWS::CloudFront::Distribution` has no +`Arn` attribute, so either form gives a condition that never matches and CloudFront gets 403. + +In CDK the generated L1 is `CfnResourcePolicy` (`aws-cdk-lib/aws-lambda`), whose property is +`resourceArn`; pass `fn.attrFunctionArn` from `CfnWebFunction`. A literal web-function ARN string +makes `cdk synth` warn that `ResourceArn` does not match the pattern +(`CloudFormation-Validate::F3017` and `F3031`), because CDK's built-in validation carries an older +schema; cfn-lint can report the same. The template is valid and the stack deploys; do not switch +to a custom resource to set the policy. + ## CDK There is no L2 construct for Web Functions. If your `aws-cdk-lib` has no generated L1 diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/migration.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/migration.md index 44d7f28ba..c904968b7 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/migration.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/migration.md @@ -27,6 +27,7 @@ see the decision gates in [SKILL.md](SKILL.md). - [ ] The app is safe to run with several concurrent requests in one process — no module-level per-request state. This is the single most common source of post-migration bugs, because Event Functions gave every request its own environment and hid the problem. - [ ] Nothing depends on state surviving in the environment or `/tmp` between requests. - [ ] The package, including `node_modules`, fits in 512 MB. +- [ ] Every `@aws-sdk/*` client the code imports is packaged, and none is marked external in the bundler config. Event Functions include the SDK in the runtime; Web Functions do not. - [ ] 2 vCPU / 2 GB per environment is enough (see Service Limits in [SKILL.md](SKILL.md)). - [ ] If the current function is VPC-attached, plan an egress network connector for the new revision; subnets and security groups move to the connector (see [networking.md](references/networking.md), and confirm the connector commands with `aws lambda-core help` or the AWS docs). diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/scaling-and-concurrency.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/scaling-and-concurrency.md index d42290374..1ba0074e6 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/scaling-and-concurrency.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/scaling-and-concurrency.md @@ -28,6 +28,22 @@ balance environment utilization and performance. See Multi-Concurrency Considerations in [architecture-patterns.md](references/architecture-patterns.md). +## New Endpoints Start With a Low Concurrency Limit + +The dynamic limit starts conservatively on a **new endpoint**, including a new endpoint added to an +existing function, and rises as Lambda learns the function's resource usage. It is kept when the +endpoint scales to zero, so later traffic starts from the learned level. A new revision whose +performance differs a lot from the previous one also goes through a short learning period. + +While the limit is low, each environment takes only a few concurrent requests, so a burst needs +more environments than it will later. If `scalingConfig.maxEnvironments` is set low, that burst +hits the cap and gets `429` with `lambda-web-error-type: Platform.EndpointMaxEnvThrottle` until +the limit rises. Left unset, the endpoint scales out instead and the burst is served. + +Do not set `maxEnvironments` unless the user asks for a cap. When they do, tell them a low cap on a +new endpoint can throttle bursts and load tests for the first few minutes, and that results from +a first load test are not representative of steady state; repeat the test and use the later runs. + ## Endpoint Scaling Controls There are two controls to limit an endpoint's scale within a region. Both are @@ -37,7 +53,10 @@ endpoint properties and can be updated without creating a new revision: endpoint can scale to. Optional, default is null. - If a request arrives when all `maxEnvironments` environments are fully - utilized, the request will be immediately throttled rather than queued. + utilized, the request will be immediately throttled rather than queued + (`429`, `Platform.EndpointMaxEnvThrottle`). On a new endpoint this happens at + lower load than later, see + [New Endpoints Start With a Low Concurrency Limit](#new-endpoints-start-with-a-low-concurrency-limit). - During revision updates, internal fleet cycling, and internally managed operational events, Lambda Web may temporarily exceed the `maxEnvironments` as Lambda Web generally prioritizes availability over strictly honoring the diff --git a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/troubleshooting.md b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/troubleshooting.md index 64fe1b5d6..abae30e05 100644 --- a/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/troubleshooting.md +++ b/skills/specialized-skills/serverless-skills/aws-lambda-web-functions/references/troubleshooting.md @@ -33,8 +33,13 @@ | HTTP 417 on a large upload | Client sent `Expect: 100-continue`, which curl adds automatically for bodies over ~1 MB | Suppress it (`curl -H 'Expect:' ...`), or stop the client sending 100-continue | | Large upload hangs until timeout, logs show 0 bytes received | Handler writes the response while the request body is still arriving; the request path is half-duplex | Read the request fully, then respond. Bodies under ~1 MB fit socket buffers and mask this | | HTTP 403 | Auth type mismatch | Use `ApplicationManaged` for public | +| HTTP 429 `Platform.EndpointMaxEnvThrottle` on a new endpoint under a burst or load test, gone a few minutes later | Per-environment concurrency starts low on a new endpoint and learns; a low `maxEnvironments` caps scale-out meanwhile | Raise or remove `maxEnvironments`; repeat the test and use later runs → [scaling-and-concurrency.md](references/scaling-and-concurrency.md) | +| CloudFront `IllegalOriginAccessConfiguration`: "Lambda-Web origins require always-amz-auth signing behavior" | OAC signing behavior is `always`, `no-override` or `never` | Use an OAC with signing behavior `always-amz-auth` → [domains-and-routing.md](references/domains-and-routing.md) | +| HTTP 403 through CloudFront with OAC | Endpoint is not `IamAuth`, OAC is not origin type `lambda` with `always-amz-auth`, or the resource policy lacks a `cloudfront.amazonaws.com` statement matching the distribution's `aws:SourceArn` | Fix each → [domains-and-routing.md](references/domains-and-routing.md) | +| `Unsupported property [FunctionResourceArn]` on `AWS::Lambda::ResourcePolicy` | Wrong property name | Use `ResourceArn` with the web-function ARN → [infrastructure-as-code.md](references/infrastructure-as-code.md) | | Static files 404 | Wrong path or not in ZIP | Use `__dirname` relative paths; check `unzip -l` | | Module not found | node_modules missing from ZIP | `npm ci --omit=dev` then include in zip | +| Crash on start + `Cannot find package '@aws-sdk/...'`, every request 502 | The runtime does not include the AWS SDK; the client is not packaged, is marked external, or sits in `devDependencies` | Package the imported `@aws-sdk/*` clients like any other dependency → [deployment.md](references/deployment.md) | | HTTP 507, or `EMFILE` / `too many open files` in logs | File-descriptor ceiling reached — often unclosed sockets, file handles or DB connections leaking across concurrent requests | Close handles per request and pool connections; a leak surfaces faster here because one execution environment serves many concurrent requests | | Connection reset with no HTTP status and nothing in the logs | Request header block too large in bytes — rejected before the app, so no status code and no log line | Measure total header size; shrink cookies, tokens and proxy headers → [Request header limits](#request-header-limits) | | HTTP 431 | Too many request header lines, including the ones the platform adds | Leave headroom below the documented ceiling → [Request header limits](#request-header-limits) |