Actions_
Learn Firewall actions in Appwrite: deny, bypass, challenge, rate limit, and redirect, including status codes and rate-limit behavior.
7 min read
When a request matches a rule's conditions, Appwrite applies the rule action. Only one action runs per request, because evaluation stops at the first matching enabled rule. See Priority.
There is no separate Allow action. Use Bypass to allowlist traffic that should skip later deny or rate limit rules.
Available actions
| Action | Client outcome |
|---|---|
| Deny | 403 with an access-denied error |
| Bypass | Request continues, and later Firewall rules are skipped |
| Challenge | Client must pass a challenge before the request continues |
| Rate limit | Under quota: continue and stop further rules. Over quota: 429 with Retry-After |
| Redirect | HTTP redirect (300 to 399) to the configured location |
Response headers
When Firewall stops a request, the response carries two headers that name the rule and the action it applied:
X-Appwrite-WAF-Rule: 6f2c1b9aX-Appwrite-WAF-Action: denyA challenge adds X-Appwrite-WAF-Challenge-Type. The headers arrive with the response. The traffic overview updates later.
Only API rules add the headers to allowed requests.
Deny
Deny rejects matching requests before they reach your application logic. Use it to block abusive IPs, lock down sensitive paths, or reject traffic from specific countries.
Bypass
Bypass allows the request and stops Firewall evaluation. Later rules (including deny and rate limit) do not run for that request.
Typical uses:
- Allowlist a trusted office IP before a broader deny rule
- Exempt health-check user agents or monitoring paths from rate limits
Give bypass rules a lower priority number than the deny or rate limit rules they override, so they run first.
Challenge
Challenge verifies matching requests before allowing them through. Clients that pass the challenge reach your application. Clients that fail do not.
Use challenge instead of deny on paths that attract abuse, such as sign-in and sign-up, where a deny rule would also block legitimate users.
| Setting | Purpose | Limits |
|---|---|---|
| Difficulty | Client-side proof-of-work cost. Higher values slow automated clients more, and also slow the challenge for legitimate users | 1 (easiest) to 5 (hardest), Console default 3 |
| TTL (seconds) | How long a visitor stays cleared after they pass the challenge | 900 (15 minutes) to 86400 (24 hours), Console default 1800 (30 minutes) |
Only browser navigation gets a challenge page
Appwrite serves the challenge page only for GET requests that accept HTML. Form posts, fetch calls, requests for a site's CSS and images, and every request matched by an API rule get 403 with a challenge-required error instead.
See Challenge automated traffic for a worked example.
Known bots are denied, not challenged
Appwrite denies obvious automated clients before it issues a challenge, including requests with no User-Agent and those containing a marker such as curl/, python-requests, go-http-client, or headlesschrome.
They count as Denied and never as Challenged, so a challenge rule aimed at them does nothing. Use Deny for clients you already know are scripts, and Challenge for traffic that might be a browser.
Clearance lasts for the whole site
Once a visitor solves a challenge, they stay cleared for the length of the TTL across every challenge rule on that hostname, not just the rule that challenged them.
To challenge every visitor to a site without building a rule by hand, use Attack mode.
Rate limit
Rate limit throttles matching requests that go over a quota. Each rule counts its own quota, separately for every client.
| Setting | Purpose | Limits |
|---|---|---|
| Request limit | Requests allowed per interval | 1 to 1000000 (Console default 100) |
| Interval (seconds) | Length of the window the request limit applies over | 1 to 86400 (Console default 60) |
| Limit by | What counts as one client | IP address (ip) or User ID (userId), Console default IP address |
| Strategy | How the quota is enforced over time (see Rate limit strategies) | fixedWindow, slidingWindow, or tokenBucket (Console default fixedWindow) |
| Max bucket size | Token bucket only: the largest burst allowed. The other strategies ignore it | 1 to 1000000 (Console default 50) |
Appwrite allows a matching request under the quota and skips later rules, the same as a bypass. A request over the quota gets 429 with a Retry-After header.
Treat that header as a minimum wait. With sliding window or token bucket, a client that retries the moment it expires may still be throttled.
If the rate limiter itself is unavailable, Appwrite allows the request. A rule can therefore look inactive during an outage.
You choose the strategy when you create the rule and cannot change it afterward. Through the API, an update that sends a different strategy is ignored without an error.
Per-user quotas
With Limit by set to User ID, each signed-in user gets their own quota, regardless of IP address.
An API rule uses the user already signed in on the request. A Functions or Sites rule reads a user JWT from the x-appwrite-user-jwt header, so your client has to send one.
A request with no signed-in user skips the rule and continues to the rules below it. Every other action stops evaluation on a match. This skip is the only exception.
If signed-out traffic also needs a quota, add a second rule limited by IP address.
Rate limit strategies
All three strategies hold a client to the same rate over time. They differ in how they treat bursts.
Fixed window
Requests are counted in back-to-back windows of the interval length, and the count resets at each boundary. Windows follow the clock, not a client's first request, so every client resets at the same moment.
A client can send a full quota at the end of one window and another at the start of the next, so brief bursts of up to twice the limit get through. Choose fixed window when that is acceptable. It is the default.
Sliding window
The previous window's count fades out instead of resetting, so no boundary burst gets through. Choose sliding window when you want an even rate.
Token bucket
A bucket refills at Request limit per Interval, up to Max bucket size. Each request spends one token, and requests are throttled once the bucket is empty.
A client that has been quiet spends its saved tokens in a burst, while its sustained rate stays at the limit. Choose token bucket for clients that legitimately burst, such as batch writes or retries after a reconnect. Set Max bucket size to the largest burst you are willing to serve, since a misbehaving client can spend it too.
Redirect
Redirect sends matching clients to another URL without running later Firewall rules.
| Setting | Purpose |
|---|---|
| Redirect location | Absolute or path location (for example https://example.com or /maintenance). Default /. |
| Status code | HTTP redirect status from 300 to 399 (default 302) |
Use redirects for maintenance pages and deprecated paths, or to move traffic off a path without denying it.
Was this page helpful?
Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.