Cloudflare Access Configuration
Cloudflare Access replaces VPNs with identity-aware application access. Every request is authenticated and authorized at the edge before reaching your application. This guide covers standard configuration patterns.
Core Concepts
Cloudflare Access evaluates identity on every request. Key differences from VPN:
- VPN grants network-level access. A compromised credential exposes the entire network.
- Access grants application-level access. Each application has its own policy. Blast radius is limited to one app.
Access works by injecting an authentication step at the Cloudflare edge. Users authenticate via your identity provider, and Cloudflare issues a signed JWT. Your application can optionally validate this JWT for defense in depth.
IdP Integration
Access supports multiple identity providers simultaneously. Common configurations:
- Azure AD / Entra ID - enterprise SSO, group-based policies
- Google Workspace - domain-based access
- Okta - enterprise SSO with MFA
- GitHub - useful for developer tools and staging environments
- One-Time PIN (OTP) - email-based access for external users without IdP accounts
Application Setup
Each protected application is defined by:
- Application domain - the hostname to protect (e.g.,
app.example.com) - Application type - Self-hosted, SaaS, or Bookmark
- Session duration - how long the JWT is valid before re-authentication
Create an application via API
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/access/apps" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"name": "Internal Dashboard",
"domain": "dashboard.example.com",
"type": "self_hosted",
"session_duration": "12h",
"auto_redirect_to_identity": true
}' Access Policies
Policies define who can access the application. Each application can have multiple policies evaluated in order.
Policy types
- Allow - grant access if conditions match
- Block - deny access if conditions match
- Bypass - skip authentication (use with extreme caution)
- Service Auth - for machine-to-machine access via service tokens
Common policy patterns
# Allow engineering team (Azure AD group)
Include: Azure AD Groups = "Engineering"
Require: Country = "AT", "DE", "CH"
# Allow specific email domain
Include: Emails ending in = "@example.com"
# Block external access except via OTP
Include: Login Methods = "OTP"
Exclude: Country not in = "AT", "DE" Service Tokens
Service tokens enable machine-to-machine authentication without user interaction. Use them for CI/CD pipelines, monitoring, and API integrations.
- Each token is a client ID + client secret pair
- Passed as HTTP headers:
CF-Access-Client-IdandCF-Access-Client-Secret - Tokens have configurable expiry (rotate regularly)
- Scope tokens to specific applications via Service Auth policies
# Authenticate with a service token
curl -H "CF-Access-Client-Id: ${CLIENT_ID}" \
-H "CF-Access-Client-Secret: ${CLIENT_SECRET}" \
https://api.example.com/health Session Management
Session duration controls how often users must re-authenticate:
- Short (1-4h) - high-security applications, admin panels
- Medium (8-12h) - standard business applications (typical workday)
- Long (24h-7d) - low-risk internal tools, documentation
Set the application-level session duration, not the IdP session. Access re-authenticates against the IdP when the Access session expires, regardless of whether the IdP session is still active.
Audit Logging
Access logs every authentication event. Logs include:
- User email and identity provider
- Application accessed
- Policy matched (allow/deny)
- Source IP and country
- Timestamp
# Retrieve Access audit logs
curl -s "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/access/logs/access_requests?limit=25" \
-H "Authorization: Bearer ${API_TOKEN}" \
| jq '.result[] | .user_email, .action, .created_at, .ip_address'