---
title: "Production — Preventing API Abuse"
aliases: [payload-abuse-prevention, payload-security-hardening]
tags: [payloadcms, security, production, auth, graphql]
sources: [raw/production__preventing-abuse.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Payload ships with several built-in security mechanisms configurable per-application. Always review these before going to production.

## Login Rate Limiting

Prevent brute-force attacks by locking accounts after repeated failures.

```ts
const Users: CollectionConfig = {
  slug: 'users',
  auth: {
    maxLoginAttempts: 5,   // lock after 5 failures
    lockTime: 600_000,     // lock for 10 minutes (ms)
  },
}
```

- Set on any Collection with `auth: true`
- User must wait `lockTime` ms before retrying
- Keep `maxLoginAttempts` low — 5 is a reasonable default

## Max Depth

Deep relationship population (`depth`) incurs a DB cost and can trigger infinite loops in circular references.

```ts
export default buildConfig({
  maxDepth: 3,   // default is 10 — lower is safer
})
```

- Set globally in `buildConfig`
- Choose the lowest value that doesn't break the dev experience
- Circular collection relationships → depth limit prevents infinite recursion → server crash prevention

## CSRF Protection

All requests to the Payload API are verified for authenticity. No extra config required by default.

- Configured via [[wiki/payloadcms/authentication-cookies|Cookie Strategy]] (`csrf` whitelist)
- Add trusted origins to `csrf` array when operating cross-domain

## CORS Configuration

Restrict which origins can call your API:

```ts
export default buildConfig({
  cors: ['https://myapp.com', 'https://admin.myapp.com'],
})
```

- Full docs: [[wiki/payloadcms/configuration|Payload Config — Overview]]
- Never use `'*'` in production

## GraphQL Complexity Limits

GraphQL lets clients write arbitrary queries — protect against expensive ones:

```ts
export default buildConfig({
  graphQL: {
    maxComplexity: 1000,   // default field = 1, relationship/upload = 10
    disable: true,          // turn off GraphQL entirely if unused
  },
})
```

- Every field adds 1 to complexity score; `relationship` and `upload` fields add 10
- Requests exceeding `maxComplexity` are rejected before execution
- **Best practice:** disable GraphQL (`graphQL.disable: true`) if not needed — zero attack surface

See also: [[wiki/payloadcms/graphql-overview|GraphQL API — Overview]]

## Malicious File Uploads

Upload collections can be a vector for distributing harmful files.

**Mitigation checklist:**
- Enable [[wiki/payloadcms/authentication-email|email verification]] before users can register
- Add bot prevention (captcha) at registration
- Restrict `create`/`update` access on upload collections to minimum required roles
- Limit `read` access — prevent user-uploaded files from being publicly served without auth
- Integrate ClamAV (or similar antivirus) via an [[wiki/payloadcms/hooks-collections|afterChange hook]] to scan uploads

## Key Takeaways

- **Login brute-force:** set `maxLoginAttempts` + `lockTime` on every auth-enabled collection
- **Depth safety:** lower `maxDepth` from the default 10 to the minimum your app needs
- **GraphQL:** disable entirely if unused (`graphQL.disable: true`); otherwise set `maxComplexity`
- **CORS/CSRF:** restrict origins explicitly — never wildcard in production
- **File uploads:** combine access control + email verification + antivirus hooks

## Sources

- [Payload Docs — Preventing Abuse](https://payloadcms.com/docs/production/preventing-abuse)
- `raw/production__preventing-abuse.md`
