Security Best Practices
This page is a security checklist written for project administrators wishing to harden their directus project.
It does not cover infrastructure security (TLS, reverse proxy, database hardening) or environment variable tuning. For environment variables that govern tokens, cookies, CSP, CORS, and rate limiting, see Security & Limits.
Restrict Access to System Collections
System collections control how your project behaves. Granting non-administrators create, update, or delete access on these collections is rarely intended, and often bypasses the intent of your data model permissions.
Flows
Flows run arbitrary operations. They can execute custom scripts, make HTTP requests, mutate data across any collection, and run under a service account with elevated permissions. Update access on directus_flows or directus_operations lets a user rewrite an existing automation, including replacing the body of a running flow with a malicious payload.
- Grant create, update, and delete on
directus_flowsanddirectus_operationsto administrators only. - For flows with a Webhook trigger, treat the trigger URL as a public endpoint. Require an access token or a shared secret in the request, and validate the payload inside the flow before taking action.
- Audit any flow that contains a Run Script operation before enabling it. Script operations execute Node.js code with the privileges of the flow's accountability.
Settings
Updating directus_settings changes behavior for every user in the project. Fields on this collection include the project name, theming, authentication policy, and URLs used in outbound email templates. A non-administrator with update access can inject CSS into the Data Studio through the theming group, change links in password-reset emails, or disable password requirements.
- Grant update on
directus_settingsto administrators only. - If non-administrators need to edit a specific field, scope field permissions tightly, and pair the policy with a Content Security Policy that restricts inline styles and untrusted sources.
Users, Roles & Policies
Write access on directus_users, directus_roles, or directus_policies can be used to escalate privileges. A user with unscoped create on directus_users can create a new administrator account. A user with update on directus_policies can grant themselves any permission.
- Grant create, update, and delete on
directus_users,directus_roles, anddirectus_policiesto administrators only. - If users need to manage their own profile, scope update permissions with
id = $CURRENT_USERand remove theroleandpoliciesfields from field permissions. - If a role needs to invite users, use field presets to lock the
rolefield to a specific non-administrator value, and remove the ability to editroleon existing users.
directus_users allows a user to create accounts and assign any role, including the administrator role. Always set a field preset for the role field when granting create access.Permissions
Permissions changes alter access control for the entire project. Update access on directus_permissions lets a user grant themselves access to any collection, field, or action.
Grant create, update, and delete on directus_permissions to administrators only.
Extensions
Extensions run code inside the Directus server. Enabling, disabling, or installing an extension changes how the project evaluates permissions, hooks, and custom endpoints. A user who can toggle extensions can disable a security-related extension, such as a custom permission validator or an audit hook, or install a new extension from the Marketplace.
- Grant update on
directus_extensionsto administrators only. - For self-hosted projects in production, keep
MARKETPLACE_TRUSTset to its defaultsandboxvalue. Set it toallonly if you vet every non-sandboxed extension before installation. - Manage extensions through deployment (shipping them in your
extensions/directory or a mountedEXTENSIONS_LOCATION) rather than through the Data Studio, so extension changes go through code review.
Data Model (Fields, Collections & Relations)
Editing directus_fields, directus_collections, or directus_relations changes the database schema. A user with update access can remove validation rules, change field types, or drop relationship constraints, weakening the guarantees the rest of your permissions depend on.
Grant create, update, and delete on directus_fields, directus_collections, and directus_relations to administrators only.
Audit Read Access on Sensitive System Collections
Several system collections contain data that bypasses the permissions set on other collections, or exposes sensitive account and network information. Scope read access on these collections deliberately.
Activity
The activity log records who performed each action, from which IP address, with which user agent, and against which items. Reading directus_activity exposes the network location and behavior of every user in the project.
Grant read on directus_activity to administrators only, or scope it with an item rule such as user = $CURRENT_USER so users only see their own activity.
Revisions & Versions
Revisions and versions store snapshots of item data at the time of each change. Reading directus_revisions or directus_versions returns the content of those snapshots, which bypasses the current read permissions on the source collection.
- Grant read on
directus_revisionsanddirectus_versionsto administrators only. - If you need to expose revisions for a specific collection, scope with an item rule that matches the collection and mirrors the user's read permissions on the source collection.
Sessions
directus_sessions stores active session tokens and metadata. Reading this collection exposes session identifiers that could be used to impersonate logged-in users. Update or delete access lets a user invalidate the sessions of other users.
Grant all access on directus_sessions to administrators only.
Shares
Shares have their own authentication flow, separate from user login. Reading directus_shares exposes share tokens, which grant access to the shared resource without a Directus account and bypass the project's regular permissions.
Grant all access on directus_shares to administrators only.
Access Control
Minimize the Public Role
The public role applies to every unauthenticated request. Granting read access on a collection to the public role exposes all items in that collection to anyone on the internet, including items you might assume are filtered by your frontend.
- Only grant public access to collections that are genuinely intended for unauthenticated consumption.
- If only a subset of items should be public (for example, published posts but not drafts), use custom permissions to restrict which items and fields are accessible.
- Before deploying, test the public endpoints with an unauthenticated request. This is the simplest way to catch unintended exposure.
Scope Junction Table Permissions
Junction tables connect two collections in a many-to-many relationship. Unrestricted create access on a junction collection lets a user associate any item on one side with any item on the other, regardless of the permissions on the related collections. A user with create on posts_tags can attach any tag to any post, even without read or update access to posts.
- Apply custom permissions on junction collections that mirror the intent of the related collections.
- Use item rules scoped to
$CURRENT_USERwhere appropriate to restrict which relationships can be created. - Use field rules to limit which foreign-key values can be set.
Require Two-Factor Authentication for Administrators
Administrator accounts can read and write any data in the project and change permissions. Compromising a single administrator account is equivalent to compromising the entire project.
Require Two-Factor Authentication for every user with an administrator policy, and review the administrator user list on a regular cadence.
Files & Assets
Restrict Public File Uploads
The public role has no file upload permission by default, and that is the safe choice. Granting the public role create access on directus_files lets any unauthenticated request upload arbitrary files to your storage backend, leading to storage exhaustion, delivery of hostile content from your domain, and abuse of your project as an open file host.
- Do not grant create on
directus_filesto the public role. - If you need to accept uploads from unauthenticated users (for example, a contact form with attachments), receive the file through a flow that validates MIME type, size, and content, then writes the file under a service account.
- Configure
FILES_MAX_UPLOAD_SIZEandFILES_MIME_TYPE_ALLOW_LISTto limit the shape of acceptable uploads.
Prefer Authorization Headers for Asset Requests
Assets can be requested with an access token in the query string (/assets/:id?access_token=...) or with an Authorization header. Tokens in the query string are stored in server logs, browser history, and intermediary proxy logs, widening the exposure of every request.
- Use the
Authorization: Bearer <token>header for asset requests from server-side and authenticated client code. - Reserve query-string tokens for cases where a header cannot be set (for example, direct
<img src="/assets/...">usage), and prefer short-lived tokens for those cases.
Server Hardening
Block Internal IPs for File Imports
The /files/import endpoint fetches a remote URL and stores the result as a file. The default IMPORT_IP_DENY_LIST only blocks 0.0.0.0 and the AWS instance metadata IP 169.254.169.254. Private network ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), IPv4 localhost (127.0.0.1), and IPv6 localhost (::1) are not blocked by default. A user with permission to import files can use this endpoint to reach internal services behind your network perimeter, which is a server-side request forgery (SSRF) attack.
Extend IMPORT_IP_DENY_LIST to cover private ranges and both IPv4 and IPv6 localhost. A conservative baseline:
IMPORT_IP_DENY_LIST=0.0.0.0,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,fc00::/7,fe80::/10
Review this list before granting file-import permissions to non-administrators, and extend it with any internal cloud metadata endpoints specific to your hosting provider.
Harden Content Security Policy When Using Custom CSS
If your project applies custom CSS through the theming group in settings, an attacker with write access to directus_settings can pivot a CSS injection into data exfiltration. CSS can load background images from attacker-controlled URLs, encoding extracted values in the request path, so a permissive Content Security Policy turns a theming-field vulnerability into an outbound data leak.
- Set explicit
CONTENT_SECURITY_POLICY_DIRECTIVES__*values that restrictimg-src,connect-src,font-src, andstyle-srcto'self'plus the exact domains your project needs. - Restrict
ASSETS_CONTENT_SECURITY_POLICY_DIRECTIVES__*separately for the assets endpoint, which serves user-uploaded content. - Revisit the policy whenever you add a new CDN, embed, or outbound integration to the Data Studio.
Next Steps
Get once-a-month release notes & real‑world code tips...no fluff. 🐰