> ## Documentation Index
> Fetch the complete documentation index at: https://docs.servers.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Web apps

> The platform builds the browser target of a Kotlin Multiplatform module and serves the result as a static site on its own host.

## Set up the module

Declare a browser target and mark it executable. Wasm and JS both work:

```kotlin theme={null}
kotlin {
    wasmJs {
        browser()
        binaries.executable()
    }
}
```

The platform runs `wasmJsBrowserDistribution`. If the module has no wasmJs browser target, it runs `jsBrowserDistribution` instead. Whatever the task writes to `build/dist/<target>/productionExecutable` is served as is, so `index.html` must be in that folder. The Compose Multiplatform wizard sets this up for you.

## Hosts

* The primary web module is served at `my-app-web.atoll.app`.
* Other web modules are served at `my-app-web--<module>.atoll.app`.
* [Previews](https://docs.servers.dev/deploy/previews.md#hosts) get their own hosts, such as `my-app--pr-42--web.atoll.app`, so a pull request never changes production.

See [Project structure](https://docs.servers.dev/project-structure.md#modules-and-hosts) for how module names become hosts.

## Talking to your Ktor server

A web app usually needs the URL of its API. Set it as an env var for builds and read it in your Gradle build, for example with BuildKonfig or a generated source file:

```sh theme={null}
atoll env set API_URL=https://my-app.atoll.app --no-runtime
```

See [Environment variables](https://docs.servers.dev/configure/environment-variables.md). Changes take effect on the next build.

The web app and the server are on different hosts, also behind custom domains. Allow the web app's origin in the server with Ktor's CORS plugin.

## Custom domains

Add a domain with `--web` to serve the primary web app on it:

```sh theme={null}
atoll domains add www.example.com --web
```

See [Custom domains](https://docs.servers.dev/configure/custom-domains.md).

## Caching

`index.html` and other HTML files are always revalidated, so a new deployment shows up at once. Files with a content hash in their name, such as `app.3f2a9c1b.js`, are cached for a year. Everything else is cached and revalidated with its ETag.

## Billing

Web apps are billed for the data they send, per GB. Builds count as build minutes. See [Billing](https://docs.servers.dev/teams/billing.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.