3.1 KiB
3.1 KiB
Cloudflare Access (Zero Trust) Setup
Overview
Cloudflare Access sits in front of subdomains and checks user identity before traffic reaches the origin server. Provides SSO and 2FA for any self-hosted service without server-side auth code.
When to Use
- Replace Caddy basic_auth for internal pages
- Add 2FA layer over existing login systems (TwentyCRM magic link, DocuSeal)
- Unify auth across multiple services (CRM, internal dashboard, DocuSeal)
- Protect any self-hosted web app without modifying the app itself
Prerequisites
- Cloudflare API token with Access: Apps and Policies: Edit permission
- Existing DNS-only token can be updated (Edit → Add permission, then regenerate)
- Subdomains must be proxied through Cloudflare (orange cloud) for Access to work
Setup via API
Account
- Account ID: from
GET /client/v4/accounts - Token: with Access: Apps and Policies: Edit permission
Create an Access Application
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCT/access/apps" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"App Name","domain":"subdomain.yourdomain.com","type":"self_hosted","session_duration":"24h"}'
Add a Policy to an Application
APP_ID=$(curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCT/access/apps" ... | jq '.result[] | select(.name=="App Name").id')
# Policy with email domain include (no 2FA requirement yet)
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCT/access/apps/$APP_ID/policies" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Team","decision":"allow","include":[{"email_domain":{"domain":"germainebrown.com"}}]}'
Policy Types
include— who is ALLOWED (email, email_domain, everyone, etc.)require— what is REQUIRED (totp, mfa, etc.)exclude— who is BLOCKED
Known Issues
require: [{totp: {}}]returnedinvalid 'require' configurationin testing (Jul 7, 2026). May need to configure 2FA at the Zero Trust org level first, or use the dashboard UI.- 2FA/one-time passcode is a "require" rule but may need to be set via the Cloudflare dashboard rather than API for initial setup.
- Session duration of "24h" means users re-authenticate daily. Adjust based on security needs.
Dashboard Setup (Fallback)
If API fails:
- Login at https://one.dash.cloudflare.com/
- Access → Applications → Add application → Self-hosted
- Enter subdomain, session duration
- Add policy: name, include (email or email_domain), require (2FA if needed)
- Save
DNS Requirement
- Subdomains covered by Access must be proxied (orange cloud) in Cloudflare DNS
- This enables Cloudflare to intercept requests before they reach the origin
- Non-proxied (gray cloud) records bypass Access entirely
Removing Caddy Basic Auth After Access is Working
Once Access is confirmed working, remove the basic_auth block from the Caddyfile or the old auth section:
# In Caddyfile: remove @internal handle block and basic_auth
# Reload: caddy reload --config /etc/caddy/Caddyfile