• Documentation
  • Pricing
© 2026 Serverless, Inc. All rights reserved.

Framework

  • Overview
  • Documentation
  • Plugins360
  • Pricing

Learn

  • Blog
  • GuidesUpdated
  • Examples240
  • Courses

Resources

  • Support
  • Security
  • Trust Center
  • Status

Community

  • Slack
  • GitHub47k
  • Forum
  • Meetups

Company

  • About
  • Careers
  • Contact
  • Partners

Legal

  • Terms of Service
  • Privacy Policy
  • Trademark
  • DMCA

Web Apps on AWS Lambda

Serverless TeamServerless Team
October 28, 2026
Web Apps on AWS Lambda

AWS Lambda has been around for eleven years, and it changed how software gets built. But one thing has always been difficult: hosting a traditional web application on it. Lambda was designed for functions that respond to events, not servers that listen for HTTP requests, so putting Express, Fastify, Hono or Next.js on Lambda meant adapters, wrappers and glue code.

That changed when AWS announced Lambda Web Functions, a new form factor that runs a standard HTTP server on Lambda as-is. Your code listens on a port, and Lambda gives it an HTTPS endpoint, scales it with traffic, and bills for the CPU time it actively uses.

Now Serverless Framework makes developing and deploying AWS Lambda web apps easier than ever. Recent minor versions of the Framework add support for deploying web apps, and bring web apps to Dev Mode, so serverless dev runs them on your machine behind their real URLs. We covered what that unlocks in Dev Mode V2 for AWS Lambda.

In practice it looks like this. Write a standard Node.js HTTP server with Express, Fastify, Hono, Next.js or plain node:http, define it under the webApps block in serverless.yml, and serverless deploy puts it on AWS Lambda Web Functions with its own HTTPS endpoint. There is no handler to export and no adapter to install - the server you run on your laptop is the server that runs on Lambda.

The convention is a few lines of YAML. A web app is a name and the file that starts your server:

webApps:
  api:
    entry: src/server.js

As of v4.46.0, out now, serverless dev runs those web apps on your machine in the same session as your functions. Requests to the real URL reach your local server, which runs with the deployed environment and the web app's own IAM role, and the deployed code answers whenever your machine does not.

It also makes the Framework an ideal foundation for AI agents developing AWS Lambda web apps. One config and one command to deploy keep the surface an agent has to learn small, serverless dev hands it a real URL to test against with the web app's own credentials, and serverless agent inspect --web-apps lets it inspect what is actually deployed.

Lambda sends many concurrent requests to each execution environment, scales environments with traffic, and bills for the CPU time your code actively uses. For web traffic that spends most of its time waiting on databases and other APIs, that is a much better fit than one request per environment. And because web apps sit next to functions in the same service, your frontend, your API and your event-driven backend deploy together, with one config and one command.

  • Web Apps guide - how it works, configuration, custom domains, permissions and costs
  • Dev Mode guide - running functions and web apps on your machine with serverless dev
  • Dev Mode V2 announcement - what Dev Mode V2 brings to functions and web apps
  • Express example - a plain Express server on a public HTTPS endpoint
  • Next.js example - a Next.js app deployed from its standalone output
  • Custom domain example - a Next.js frontend and an Express API sharing one domain
  • Release notes - everything else in v4.46.0

Framework key features for Lambda web apps

  • Your server, unchanged. Your code starts a server and listens on process.env.PORT, as it does locally. The Framework adds a small start file that sets PORT and HOSTNAME to the address Lambda expects, so Express, Fastify, Hono and Next.js standalone output run on Node.js 24 with no Lambda-specific code.

  • Many requests per environment. Each execution environment serves up to 128 concurrent requests (64 by default, adjusted by Lambda), with 2 GB of memory and 2 vCPUs on arm64.

  • Pay for the CPU you use. Web apps are billed per request, for the CPU time your code actively uses, and for the memory each environment consumes while it runs. Requests that mostly wait on databases and APIs share the cost of an environment.

  • An HTTPS endpoint out of the box. Every web app gets its own endpoint, public by default, or limited to IAM-signed requests with url.authorizer: aws_iam. Cap scaling with url.maxEnvironments and url.rateLimit.

  • Custom domains through CloudFront. Add a webApp entry to provider.domains and the Framework creates the CloudFront distribution, uses an ACM certificate that covers the hostname or requests one when the zone is in Route 53, and points the Route 53 records at it. Several web apps can share one hostname by path.

  • The same provider settings. Web apps inherit provider environment variables, IAM, VPC and tags, just like functions. TypeScript entries are bundled with esbuild automatically.

  • Local development on the real URL. serverless dev runs the web app on your machine behind its real URL, so webhooks, OAuth callbacks and mobile clients reach your working copy, with no redeploy on each change.

  • Full CLI coverage. serverless invoke --web-app, serverless logs --web-app, serverless dev, serverless info, serverless diff, serverless rollback and serverless agent inspect --web-apps all work with web apps.

How it works

Install the Framework once. From then on, v4 updates itself:

npm i serverless -g

Define the web app in serverless.yml. entry is the file that starts your server:

service: my-web-app
frameworkVersion: '4'

provider:
  name: aws
  region: us-east-1

webApps:
  api:
    entry: src/server.js

Write the server. Listen on process.env.PORT - the Framework sets it on Lambda, and the fallback port works on your machine:

import express from 'express'

const app = express()

app.get('/', (req, res) => res.json({ message: 'Hello from a web app' }))
app.get('/health', (req, res) => res.json({ ok: true }))

app.listen(process.env.PORT ?? 3000)

Install Express with npm install express and set "type": "module" in package.json. Then deploy:

serverless deploy

The deploy packages the server, creates the web function and its endpoint, and prints the URL:

Deploying "my-web-app" to stage "dev" (us-east-1)
endpoint: https://abc123example.lambda-web.us-east-1.on.aws

Send it a request with curl, or with the Framework. serverless invoke --web-app prints the status, headers and body, and exits with an error on any 4xx or 5xx, so it doubles as a CI health check:

serverless invoke --web-app api --path /health
serverless logs --web-app api --tail

Deploy again with no changes and you get No changes to deploy. A code change creates a new revision behind the same URL, and serverless rollback takes you back to an earlier deployment.

Develop locally with serverless dev

serverless dev runs your web apps on your machine against a deployed development stage, in the same session as your functions:

serverless dev --stage alice

Each web app's server starts on your machine with the deployed environment variables and the web app's execution-role credentials, never your own AWS keys, so it sees exactly the permissions it has on Lambda. Requests to its real URL, and to a custom domain in front of it, reach that server. A local URL on localhost serves the same server directly, with your framework's hot reload. The session prints both:

web apps:
  api   https://def456example.lambda-web.us-east-1.on.aws
        http://localhost:52357

Behind the scenes, the session creates a dev revision of the web function that runs a small bridge in place of your app, and moves the main endpoint's traffic to it. The bridge hands each request to your machine over AWS IoT Core, and bodies over 64 KiB travel through the deployment bucket. If your machine does not take a request within about 3 seconds - it is asleep, offline or the CLI is stopped - a fallback endpoint pinned to a copy of the deployed revision answers, so callers always get a response.

A web app whose entry is a source file runs from source and restarts on every save. For a framework with its own dev server, or an entry that is a build output such as Next.js standalone output, set dev.command. It runs in your shell from the service directory, with PORT and HOSTNAME set:

webApps:
  site:
    entry: .next/standalone/server.js
    dev:
      command: next dev --hostname 127.0.0.1
  api:
    entry: src/server.ts

Do UI work on the local URL. A web function serves HTTP only, so hot reload's WebSocket connection cannot open on the real URL, where pages pick up your changes on refresh. Use the real URL for what needs it: webhooks, OAuth callbacks, custom domains and end-to-end tests.

--web-app and --skip-web-app choose which web apps run on your machine. --local-only <web app> keeps the real URL on the deployed code and serves the web app only on its local URL, which suits a stage your teammates also use.

Press Ctrl+C or run serverless dev stop, and the session moves main back to the deployed revision and deletes every revision and endpoint it created. A session started with --detach also stops after an idle timeout. Dev Mode never runs on the prod or production stage.

The first session with web apps in an AWS account and region updates Dev Mode's IAM role so web functions can use it, which needs iam:UpdateAssumeRolePolicy once. Earlier CLI versions refuse the updated role, so upgrade together with your teammates.

Adding functions and events

A web app answers HTTP requests. Everything else - queues, schedules, streams, S3 events, webhooks - stays with Lambda functions, and both live in the same service. Use functions for event processing, for runtimes other than Node.js, and for short high-volume or CPU-heavy requests, where per-request pricing favors functions:

service: shop
frameworkVersion: '4'

provider:
  name: aws
  region: us-east-1
  environment:
    TABLE_NAME: shop-${sls:stage}

webApps:
  api:
    entry: src/server.ts

functions:
  nightlyReport:
    handler: src/report.handler
    events:
      - schedule: rate(1 day)
  stripeWebhook:
    handler: src/webhook.handler
    events:
      - httpApi: 'POST /webhooks/stripe'

The web app and the functions share provider.environment and the service's execution role, so one grant in provider.iam.role.statements covers both. A web app and a function can't share a name, since both deploy to Lambda under that name.

Frontend and backend on one domain

This is where a full web application comes together. Deploy a Next.js frontend and an Express API as two web apps, and serve both from one hostname, routed by path:

provider:
  name: aws
  region: us-east-1
  domains:
    - name: shop.example.com
      webApp: site # every path the other entry doesn't match
    - name: shop.example.com
      basePath: api # /api and /api/*
      webApp: api

webApps:
  site:
    entry: .next/standalone/server.js
    build: false
    package:
      patterns: ['!**', '.next/standalone/**']
  api:
    entry: src/server.ts

Requests to /api and /api/* reach the API, and every other path reaches the Next.js site. The path arrives unchanged, so the API defines its routes under /api, and the frontend calls its backend on the same origin. CloudFront signs the requests it forwards, so nobody can call the web app endpoints anonymously around your domain.

For Next.js, build with output: 'standalone', copy public/ and .next/static/ into the standalone directory, and deploy it as it is with build: false. The Next.js example has the complete setup.

Getting started

Serverless Framework v4 updates itself, so unpinned projects already run v4.46.0. If you pin frameworkVersion to an exact version, raise it to 4.46.0.

Then add a webApps block to any service, or start from one of the examples. Deploy it, then run serverless dev to work on it locally behind its real URL.

Learn more:

  • Web Apps guide - how it works, configuration, custom domains, permissions and costs
  • Dev Mode guide - running functions and web apps on your machine with serverless dev
  • Dev Mode V2 announcement - what Dev Mode V2 brings to functions and web apps
  • Express example - a plain Express server on a public HTTPS endpoint
  • Next.js example - a Next.js app deployed from its standalone output
  • Custom domain example - a Next.js frontend and an Express API sharing one domain
  • Release notes - everything else in v4.46.0

Eleven years in, AWS Lambda is finally a natural home for a traditional web app. Add a webApps block, deploy, and your app is live behind its own HTTPS endpoint. Run serverless dev, and your next change is one save away. If you have wanted to run a real web server on AWS Lambda without rewriting it, now is the time to try it.

Also in v4.46.0

  • Functions subscribe to EventBridge Custom Event Buses. An eventBridge event whose eventBus is a Custom Event Bus now creates a subscriber, with batches of up to 10 events and the new ordering, batchSize, maximumBatchingWindow, async, resumePosition and role settings. Bus names, the default bus and Classic event bus ARNs work as before.

  • Invoke durable functions. serverless invoke reaches durable functions through the alias their deploy publishes, prints the durable execution ARN, and adds --json. durableConfig now also applies to provided.al2023 runtimes, such as Go, and to container images.

  • Prune sandbox images. serverless prune trims sandbox image versions, with --sandbox <name> and custom.prune.automatic, and each deploy deletes sandbox zips no remaining deployment points to.

  • Fixes. Custom domains pick an issued certificate that covers the hostname, including ECDSA certificates, and EventBridge events on the default bus invoke the function.

Contents

  • Framework key features for Lambda web apps
  • How it works
  • Develop locally with serverless dev
  • Adding functions and events
  • Frontend and backend on one domain
  • Getting started
  • Also in v4.46.0

Related

GuidesPluginsExamplesSlack CommunitySupport