HOW TO SECURE APPWRITE

Appwrite Security Context

Appwrite is an open-source Firebase alternative offering auth, database, storage, and functions. The two distinct flavors — Appwrite Cloud (managed) and self-hosted — have different security envelopes: Cloud bounds the platform layer; self-hosted means you manage TLS, firewall, OS patching, and Docker security. The data model is document-level permissions (not RLS), with default labels that include users (any authenticated user) and any (the world). The default collection often ships with permissions wider than the founder realizes.

What You’ll Learn

  • How to lock down collection permissions (default config is dangerously open)
  • Server-side vs client-side API key separation
  • Self-hosted server hardening (TLS, firewall, SSH)
  • Cloud Functions audit for exposed secrets

Critical Steps

1. Configure collection permissions

In Appwrite Console → Database → Collection → Settings → Permissions: review the default. New collections often default to users for read/write, which means any logged-in user — not “the user who owns the row.” Use document-level permissions: when a document is created, assign it to user:<id> only:

await databases.createDocument(dbId, collId, ID.unique(), data, [
  Permission.read(Role.user(currentUserId)),
  Permission.update(Role.user(currentUserId)),
  Permission.delete(Role.user(currentUserId)),
]);

2. Separate server-side and client-side API keys

Appwrite has two key types: Web (anon, restricted to client) and Server (privileged, uses an API key). Server keys bypass permissions when given the right scopes — never embed a server key in frontend code, never commit one to git. Use Web SDK in browsers, Server SDK in Functions / your own backend.

3. Review authentication providers

Auth → Settings: disable providers you don’t use (Anonymous unless required, OAuth providers you haven’t set up). For each enabled OAuth provider: lock down the Authorized redirect URIs to your production domain only. Auth → Security: set password requirements, session length, and brute-force protection.

4. Enable HTTPS for self-hosted

If self-hosting: Appwrite ships with HTTP by default. Front it with a reverse proxy (Caddy / Nginx / Traefik) that handles TLS via Let’s Encrypt. Confirm the cert covers all hostnames your clients connect to. Without TLS, every API key transits in plaintext on the first hop.

5. Lock down the self-hosted server

For the Docker host running Appwrite: firewall to 80/443 only (block the database, Redis, and internal ports from public access), SSH key auth only (PasswordAuthentication no in sshd_config), automatic security updates (unattended-upgrades), and fail2ban on SSH. Default Appwrite Docker compose exposes some internal services on localhost; verify nothing is bound to 0.0.0.0 that shouldn’t be.

6. Configure file storage permissions

Storage → Bucket → Settings: set per-bucket permissions and per-file permissions on upload (use Permission.read(Role.user(userId))). Set max file size, allowed extensions, and antivirus scanning if your plan supports it. Default buckets often allow users read, which is “any logged-in user” again.

7. Audit Cloud Functions

Each Function: confirm it doesn’t hardcode secrets (use Function → Variables), validates inputs, checks the calling user’s identity (req.headers['x-appwrite-user-id'] is populated by Appwrite for authenticated calls), and returns generic errors in production.

8. Set rate limiting

Project Settings → Security: configure global rate limits. For specific endpoints, implement per-user limits in Cloud Functions (track in a dedicated collection, reject when over). Defaults are generous — tighten for /account/sessions and /account/email to mitigate credential stuffing.

9. Review team and membership settings

Project → Auth → Teams: audit team members and their roles. Team admins can invite anyone; ex-members retain access until manually removed. Configure invitation flows so links expire.

10. Configure database indexes

Indexes don’t directly affect security but unindexed queries scan the collection — an attacker can DoS by triggering one. Add indexes for every query path your app uses.

11. Enable audit logs

Project Settings → Audits: review weekly. Look for: bulk reads from a single user, repeated 4xx auth errors, unusual document mutations, new API keys, permission changes.

12. Secure webhooks

For incoming webhooks (Stripe, GitHub, etc.) handled by a Function: verify the webhook signature before trusting the payload. For outgoing webhooks (Appwrite → your service): use HTTPS endpoints only, validate the signature on the receiver. See Stripe webhook and paid-trust.

13. Review email templates

Auth → Templates: audit each template. Confirm: links use your production domain, no template includes user-controlled fields without escaping (email injection), URLs don’t include the user’s session token in query strings.

14. Configure session settings

Auth → Security → Session length: set to your tolerance (≤ 7 days for sensitive apps). Sessions limit per user: set a cap so a compromised account can’t accumulate sessions across devices.

15. Keep self-hosted Appwrite updated

If self-hosting: subscribe to GitHub releases and apply security patches within the same week. Run the upgrade in a maintenance window with a backup taken first. Self-hosted = you patch.

16. Run a security scan

The Vibe Code Scanner covers the deploy-side patterns; the full VibeEval scan probes Appwrite endpoints for missing auth, BOLA, and over-broad collection permissions.

Free Self-Audit Suite

Five free scanners.

Vibe Coding Security Risk Guide

Full risk catalogue.

Supabase Guide

Comparison reference for the closest managed alternative.

Automate Your Security Checks

VibeEval scans your Appwrite project’s endpoints for missing auth, over-broad permissions, and BOLA across roles.

SCAN YOUR APP

14-day trial. No card. Results in under 60 seconds.

START FREE SCAN