> For the complete documentation index, see [llms.txt](https://docs.carto.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.carto.com/carto-for-developers/reference/hosted-apps/bundle-requirements-and-limits.md).

# Bundle requirements and limits

What a valid hosted app bundle looks like, the limits CARTO enforces, and the headers and CSP it serves

{% hint style="info" %}
**Private Preview:** Hosted Apps is available to selected accounts during its Private Preview. To request access, contact your CARTO representative.
{% endhint %}

A hosted app is a directory of built static files. CARTO serves them as they were uploaded, under the rules on this page.

## Bundle rules

| Rule                     | Detail                                                                                                                                                                                |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A directory              | You pass a directory to `carto app deploy`, not an archive.                                                                                                                           |
| `index.html` at the root | Required. Otherwise the deploy fails with `Bundle directory must contain index.html at the root`. Point the command at your build output, not at the project root.                    |
| Subdirectories           | Preserved. Each file is served at its path in the bundle.                                                                                                                             |
| `carto.json`             | At the root, it is the [manifest](/carto-for-developers/reference/hosted-apps/app-manifest.md): read when you deploy and never uploaded. In a subdirectory, it is an ordinary file.   |
| Symbolic links           | Skipped. Anything a link points to must be a real file inside the directory to be deployed.                                                                                           |
| Relative asset paths     | The app is served under `/app/<slug>/`, not at the root of a domain, so asset URLs must be relative. In Vite, set `base: './'`. An absolute path like `/assets/app.js` returns `404`. |
| Whole files              | Files are always served whole. HTTP range requests are not supported.                                                                                                                 |

## Client-side routing

Client-side routing works, with one rule:

* A page request to a path **without a file extension** and without a trailing `/` that does not match any file returns `index.html`, so your router can handle it. `/app/my-app/dashboard/2026` loads the app, but `/app/my-app/dashboard/` returns `404`.
* A path **with a file extension** that does not match any file returns `404`. `/app/my-app/assets/missing.js` is an error, never HTML.

## Limits

| Limit                        | Value                                                                                                                                               |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Total bundle size            | 50 MB                                                                                                                                               |
| File size                    | 25 MB                                                                                                                                               |
| Files per bundle             | 1000                                                                                                                                                |
| Slug                         | 1 to 40 characters matching `/^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/`: lowercase letters, digits and hyphens, not starting or ending with a hyphen |
| Version label                | 1 to 64 characters matching `/^(?!.*\.\.)[a-zA-Z0-9.:_-]{1,64}$/`: letters, digits, `.`, `:`, `_` and `-`, with no `..`                             |
| App name                     | 1 to 200 characters                                                                                                                                 |
| App description              | Up to 2000 characters                                                                                                                               |
| Connections per manifest     | 20                                                                                                                                                  |
| Named Sources per connection | 200                                                                                                                                                 |
| SQL per Named Source         | 100,000 characters                                                                                                                                  |

These limits are checked before any file is uploaded, so a bundle over a limit fails without uploading anything. If a bundle is too large, the usual causes are source maps and unoptimized images: disable source maps for the deployed build, and compress images.

## Response headers

CARTO sets these headers on every file it serves from a bundle. A bundle cannot change them.

| Header                    | Value                           | Consequence                                                                                                                                                             |
| ------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Cache-Control`           | `no-store`                      | Nothing is cached by the browser or by intermediate caches. A redeploy takes effect on the next page load, and every load downloads the bundle again, so keep it small. |
| `Vary`                    | `Origin, Cookie, Authorization` | Responses are tied to the user's session.                                                                                                                               |
| `X-Frame-Options`         | `SAMEORIGIN`                    | The app cannot be embedded in an iframe on another site.                                                                                                                |
| `Content-Security-Policy` | See below                       | The app can only load resources from, and connect to, the allowed origins.                                                                                              |
| `Content-Type`            | Based on the file extension     | Files with an unknown extension are served as `application/octet-stream`.                                                                                               |

## The Content Security Policy

CARTO serves this policy with every file of a bundle:

```
default-src 'self' data: blob:;
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://unpkg.com https://cdn.jsdelivr.net https://*.cartocdn.com https://maps.googleapis.com;
style-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net https://*.cartocdn.com https://fonts.googleapis.com;
img-src 'self' data: blob: https://*.cartocdn.com https://*.googleapis.com https://*.gstatic.com https://lh3.googleusercontent.com https://s.gravatar.com https://i0.wp.com https://i1.wp.com https://i2.wp.com https://cdn.auth0.com;
font-src 'self' data: https://*.cartocdn.com https://fonts.gstatic.com;
worker-src 'self' blob:;
base-uri 'self';
object-src 'none';
form-action 'self';
connect-src 'self' data: blob: https://*.carto.com https://*.cartodb.com https://*.cartocdn.com https://*.googleapis.com https://unpkg.com https://cdn.jsdelivr.net
```

| Directive     | Allowed sources                                                                                                                                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `script-src`  | The bundle, inline scripts and `eval`, `unpkg.com`, `cdn.jsdelivr.net`, `*.cartocdn.com` and `maps.googleapis.com` (the Google Maps JavaScript API).                                                                                               |
| `style-src`   | The bundle, inline styles, `unpkg.com`, `cdn.jsdelivr.net`, `*.cartocdn.com` and `fonts.googleapis.com`.                                                                                                                                           |
| `connect-src` | The bundle, `data:` and `blob:` URIs, `*.carto.com`, `*.cartodb.com`, `*.cartocdn.com`, `*.googleapis.com`, `unpkg.com` and `cdn.jsdelivr.net`.                                                                                                    |
| `img-src`     | The bundle, `data:` and `blob:` URIs, `*.cartocdn.com`, `*.googleapis.com`, `*.gstatic.com`, and the hosts user avatars are served from: `lh3.googleusercontent.com`, `s.gravatar.com`, `i0.wp.com`, `i1.wp.com`, `i2.wp.com` and `cdn.auth0.com`. |
| `font-src`    | The bundle, `data:` URIs, `*.cartocdn.com` and `fonts.gstatic.com`.                                                                                                                                                                                |
| `worker-src`  | The bundle and `blob:` URIs.                                                                                                                                                                                                                       |

In practice:

* **Libraries** load from the bundle, or from `unpkg.com` and `cdn.jsdelivr.net`. Including them in the bundle is the most reliable option.
* **Basemaps** must come from an allowed origin: the CARTO basemaps (`BASEMAP` in `@deck.gl/carto`), served from `*.cartocdn.com`, or [Google Maps](/carto-for-developers/key-concepts/carto-for-deck.gl/basemaps/carto-google-maps.md), whose JavaScript API loads from `maps.googleapis.com`. Google basemaps, `GoogleMapsOverlay` and Street View work. Other basemap providers are blocked.
* **`data:` and `blob:` URLs** can be fetched as well as displayed, so assets that your build tool inlines, such as the icons of a deck.gl `IconLayer`, load normally.
* **Images, fonts and other files** from other hosts are blocked. Include them in the bundle, or use `data:` URIs. The one exception is the user's avatar: the `picture` URL in [`carto-info.json`](https://docs.carto.com/carto-for-developers/reference/hosted-apps/pages/Db3rNW8Y3wz2tckdFhPX#the-carto-info.json-contract) always points to an allowed host.
* **APIs** other than CARTO and the allowed origins cannot be called from the app.
* **Embedding** the app in another site is not possible, because of `X-Frame-Options: SAMEORIGIN`.

## No environment variables or secrets

A hosted app has no environment variables and no secrets. Its configuration comes from two places:

* **`carto-info.json`**, for the user's access token, the API base URL and the user's identity. See [Runtime and authentication](/carto-for-developers/reference/hosted-apps/runtime-and-authentication.md).
* **The bundle**, for anything else, set at build time.

Anyone who can open the app can read its files. Keep SQL in [Named Sources](/carto-for-developers/reference/hosted-apps/app-manifest.md#sources), and never include a credential in the bundle.

## Related

* [Runtime and authentication](/carto-for-developers/reference/hosted-apps/runtime-and-authentication.md): sessions, `carto-info.json`, sharing and versions.
* [App manifest (`carto.json`)](/carto-for-developers/reference/hosted-apps/app-manifest.md): the manifest at the root of the bundle.
* [`app` command reference](/carto-for-agents/cli/command-reference/app.md): every command and flag.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.carto.com/carto-for-developers/reference/hosted-apps/bundle-requirements-and-limits.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
