# Firewall, rate limits and error pages

This page brings together what protects a project at the front door and what a visitor sees when a request is refused: the request budget the edge applies to each project, the limits on a single request, the API rate limits for your tokens, and the error pages you can customise. It also says what is not yet available.

## The application firewall

A per-project application firewall that you configure (blocking rules, rate limiting per path, address lists) is planned through the `firewall.configure` operation. **It is not yet available**: the operation is declared in the registry, but no implementation answers it today, and the route returns 404. It is declared at the Owner rank and closed to agents, whatever their scope.

Meanwhile, a project is protected by the mechanisms below, which need no configuration. To restrict access to a preview (password, project members, list of IP addresses), see [Previews](previews.md).

## The project's request budget

The edge keeps, for each project, a request budget derived from its tier. All the project's routes share it: its production, its previews and its deployments' addresses.

| | Discovery | Standard | Pro | Sovereign |
| --- | --- | --- | --- | --- |
| Sustained rate | 100 req/s | 250 req/s | 1,000 req/s | 2,500 req/s |
| Burst admitted at once (10 s of budget) | 1,000 requests | 2,500 requests | 10,000 requests | 25,000 requests |

A burst goes through at full speed; beyond it, the edge answers **429** without your application seeing the request. The refusal carries `Retry-After: 1` and the `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` and `RateLimit-Policy` headers. A refused request consumes nothing: a flood of refused requests does not delay the return of service.

This budget is applied before everything else: preview protection, [routing rules](routing.md), the [cache](cache.md) and the application. A project caught in a spike therefore costs its neighbours nothing. It is not a commercial quota: requests are not billed on any tier.

Outbound traffic follows a related rule: beyond the tier's sustained outbound rate (20, 50, 200 and 500 Mb/s), the edge paces sending without refusing anything. See [Limits](limits.md).

## Limits on a request

These limits apply to every name served by the platform. Each refusal is an HTTP response that names the limit, in French and in English.

| Parameter | Value | Beyond |
| --- | --- | --- |
| URL length | 16 KB | 414 |
| Number of request headers | 100 | 431 |
| Combined header size | 32 KB | 431 |
| Request body | 100 MB, adjustable up to 5 GB with `requests.maxBodyMegabytes` in `glowsoft.json` | 413 |
| Connection to the application | 10 s | 502 |
| Receiving the response headers | 15 s | 504 |
| Complete response | 300 s, adjustable from 30 to 3,600 s with `requests.timeoutSeconds` | 504 |
| Long-lived connection (WebSocket, event stream) | 60 min | end of the connection |
| Concurrent connections per source address | 200 | connection refused |

## API rate limits

The API counts calls per token, over a 60-second window:

| Policy | Limit | Applies to |
| --- | --- | --- |
| `general` | 600 calls | every operation |
| `sensitive` | 60 calls | in addition, every write and every operation that returns a secret |

Each response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (in seconds) and `RateLimit-Policy`, for example `600;w=60, 60;w=60`. Beyond that, the call is refused with 429, the `rate_limited` code and a `Retry-After`. Each operation's reference page states its policies.

> **Tip.** An agent polling the state of a long-running task should space out its reads: `task_read` keeps no connection open, and every read counts against the `general` policy.

## Custom error pages

When the platform serves an error itself (application unreachable, timeout, rate limit, service suspended), the visitor sees a platform page, in French and in English. You can replace its title and message for seven codes.

| Code | Typical situation |
| --- | --- |
| 402 | Service suspended for an unpaid invoice. |
| 403 | Access refused. |
| 404 | Page not found. |
| 429 | Request budget exceeded. |
| 502 | Application unreachable. |
| 503 | Service unavailable, for example while waking up. |
| 504 | Response timeout. |

A page is a title (100 characters at most) and a message (1,000 characters at most), in plain text, per code and per language (`fr` or `en`). A line break separates two paragraphs of the message. The `<` and `>` characters and control characters are refused. The edge writes this text into its own page, with its own style sheet: an error page loads no script and no external resource.

Pages are served by the edge without redeploying, even when your application is stopped. A code or language you do not set keeps the platform's page. An error your application returns itself, with its own body, remains its own.

These cannot be customised: the 403 of a project frozen for abuse, which carries the case reference, and the states that come before any project, such as an unknown host name or a pending certificate.

### From the console

Open **“Domaines”** (Domains), then expand **Pages d'erreur** (Error pages) at the bottom of the screen. Each code has a title and a message per language; a field left empty keeps the platform's page. **Enregistrer les pages** (Save the pages) puts them online on the edge's next pass; **Tout retirer** (Remove all) restores the platform's pages.

### From the command line or the API

`errorpage.configure` replaces the whole list: a page missing from the list is removed, and an empty list removes them all. It holds 14 pages at most (seven codes, two languages), without duplicates.

```json
{
  "projectId": "prj_01JQ8ZK4M7NP2RSTVWXYZ34567",
  "pages": [
    {
      "status": 503,
      "language": "fr",
      "title": "Retour dans un instant",
      "message": "La boutique est en mise à jour.\nRevenez dans quelques minutes."
    },
    {
      "status": 503,
      "language": "en",
      "title": "Back in a moment",
      "message": "The shop is being updated.\nPlease come back in a few minutes."
    }
  ],
  "idempotencyKey": "maintenance-pages-2026-10-23"
}
```

```sh
bastide errorpage read --project-id prj_01JQ8ZK4M7NP2RSTVWXYZ34567
bastide errorpage configure --project-id prj_01JQ8ZK4M7NP2RSTVWXYZ34567 --pages "$(cat pages.json)"
```

Through the API: `GET` and `PUT /v1/projects/{projectId}/error-pages` on `https://api.bastide.app`. Setting pages requires the Maintainer rank; reading them, the Reader rank. These operations are reserved for a person: they have no MCP tool and are not open to service accounts.

## Common errors

| Symptom or code | Cause | Remedy |
| --- | --- | --- |
| Visitors receive 429 | The tier's request budget is exceeded. | [Cache](cache.md) public responses, or change tier. |
| `rate_limited` from the API | More than 600 calls, or more than 60 writes, in 60 seconds for this token. | Wait for `Retry-After`; space out repeated reads. |
| 413 on a file upload | Body over 100 MB. | Raise `requests.maxBodyMegabytes`, or upload to [storage](storage.md) directly. |
| 504 on a long process | The response exceeds the timeout. | Raise `requests.timeoutSeconds`, or make the process asynchronous. |
| Error page refused | Empty or too long text, with `<`, `>` or a control character, or a duplicate (code, language). | Fix the text; the message names the rule. |
| 404 on `firewall.configure` | The application firewall is not yet available. | Use preview protection and the request budget. |

## See also

- [Previews](previews.md)
- [Routing](routing.md)
- [Cache and images](cache.md)
- [Limits](limits.md)
- [Security](security.md)
- [errorpage.configure](../reference/operations/errorpage.configure.md), [firewall.configure](../reference/operations/firewall.configure.md) (operation pages, in French)
