SASE (Secure Access Service Edge) is the idea, coined by Gartner in 2019, that networking and network security should stop being separate boxes in a data center and instead converge into a single cloud-delivered service. Instead of backhauling every remote worker through a VPN concentrator so a stack of appliances can inspect their traffic, users and offices connect to the nearest edge location of a global network, and every security policy is enforced right there, close to the user, for all traffic at once.
Cloudflare's implementation of this model is Cloudflare One. It runs on the same global anycast network as the rest of Cloudflare, which means every data center runs every service: a request is authenticated, filtered, inspected, and logged in a single pass at whichever location the user happens to hit.
Every SASE platform is really two products fused together. One half is network-as-a-service: the plumbing that gets packets from users, devices, offices, and servers onto the provider's network. In Cloudflare's case that is the WARP device client, Cloudflare Tunnel for connecting servers and private networks without opening inbound ports, and Magic WAN and Magic Transit for whole-site and whole-network connectivity.
The other half is the security stack, often called SSE (Security Service Edge), that sits on top of that plumbing and decides what traffic is allowed to do. This is where the actual protections live, and it is the half people mean when they ask what SASE protects against.
Cloudflare One ships seven distinct protection services. Each one answers a different question about your traffic, and they compose: the same request can pass through several of them in one trip through the edge.
| Protection | What it protects | Cloudflare product |
|---|---|---|
| ZTNA (Zero Trust Network Access) | Access to each internal app, checked per user, per device, per request | Cloudflare Access |
| SWG (Secure Web Gateway) | Outbound traffic: DNS, HTTP, and network-level filtering of what devices reach | Cloudflare Gateway |
| CASB (Cloud Access Security Broker) | SaaS tenants: misconfigurations, risky sharing, exposed files | Cloudflare CASB |
| DLP (Data Loss Prevention) | Sensitive data (PII, credentials, source code) leaving in transit | Cloudflare DLP |
| RBI (Remote Browser Isolation) | The endpoint browser, by rendering untrusted pages at the edge instead | Browser Isolation |
| Email Security | Inboxes: phishing, business email compromise, malicious attachments | Cloud Email Security |
| FWaaS (Firewall as a Service) | Network traffic, with firewall rules enforced at the edge instead of on hardware | Magic Firewall |
Alongside these seven, Cloudflare One also ships Digital Experience Monitoring (DEM), which observes connectivity and performance rather than blocking anything, so I count it as observability rather than a protection layer.
Diagram: One edge, seven protections.
- ZTNA (Access · per-app identity checks)
- Secure Web Gateway (Gateway · DNS, HTTP, network)
- CASB (SaaS posture scanning)
- DLP (data in transit)
- Browser Isolation (pages rendered at the edge)
- Email Security (phishing · BEC · attachments)
- FWaaS (Magic Firewall)
- Cloudflare One (single-pass SASE edge)
- Network services (WARP · Tunnel · Magic WAN)
- ZTNA to Cloudflare One
- Secure Web Gateway to Cloudflare One
- CASB to Cloudflare One
- DLP to Cloudflare One
- Browser Isolation to Cloudflare One
- Email Security to Cloudflare One
- FWaaS to Cloudflare One
- Network services carries the traffic to Cloudflare One
The guide that makes up the rest of this post publishes internally self-hosted applications through Cloudflare Tunnel and locks them behind identity, device posture, and access policies. It is worth being precise about the category: Cloudflare Tunnel itself is not a protection, it is plumbing from the network-as-a-service half. It creates an outbound-only connection from your server to Cloudflare's edge, so the app needs no open inbound ports and no public IP exposure.
The protection wrapped around that tunnel is ZTNA. Every request to the app's hostname is intercepted by Cloudflare Access, which verifies who the user is against the identity provider, checks that their device is enrolled and passing posture checks, and only then lets the request through to the tunnel. No user or device is trusted by default, access is evaluated per application and per request, and the private network is never exposed. That is the definition of Zero Trust Network Access.
Diagram: The ZTNA request path this guide builds.
- External caller (/webhook paths only)
- Team member (WARP client enrolled)
- Google IdP (OAuth 2.0 + PKCE)
- Cloudflare Access (email allow-list + posture)
- Cloudflare Tunnel (no inbound ports)
- cloudflared (Docker, shared network)
- Internal app (n8n on the Docker network)
- Team member request to Cloudflare Access
- Cloudflare Access verify identity to Google IdP
- Cloudflare Access allowed to Cloudflare Tunnel
- Cloudflare Tunnel proxied request to cloudflared
- cloudflared service DNS name to Internal app
- External caller bypass policy to Cloudflare Access
This guide covers the end-to-end setup for protecting internally self-hosted applications behind a Cloudflare tunnel, enforced by Zero Trust access policies. It covers identity provider integration, tunnel configuration, device posture, access policies, and client deployment via Docker.
Path: Integrations > Identity Providers
- 01Add the provider
Click Add identity provider and select Google.
- 02Retrieve credentials
Retrieve the Client ID and Client Secret from Google Cloud Console. The OAuth 2.0 app type must be Web Application.
- 03Enable PKCE
Enable Proof Key for Code Exchange (PKCE): ON.
- 04Save and verify
Click Save, then use the Test button to verify the connection.
Path: Networks > Connectors
- 01Choose the connector type
Select tunnel type: Cloudflared.
- 02Name it
Name the tunnel and click Next.
- 03Copy the token
Copy the generated cloudflared tunnel token. This is needed for deployment.
- 04Continue
Click Next to proceed to the application route setup.
This step maps a subdomain to the internal service that should be tunneled.
| Hostname field | Value |
|---|---|
| Subdomain | The subdomain for accessing the service |
| Domain | Selectable from the dropdown; these are domains connected to Cloudflare |
| Path | Leave empty to protect all paths, or specify a sub-path |
| Service field | Value |
|---|---|
| Type | HTTP, for services communicating over the local/docker network |
| URL | The service's DNS name and port (e.g. the Docker Compose service name and its exposed port) |
Click Save. This can be updated later if needed.
Path: Reusable Components > Posture Checks
Create two posture checks, one for WARP and one for Gateway:
- 1Click Add a check.
- 2Pick WARP from the list. The name is auto-filled. Save.
- 3Click Add a check again.
- 4Select Gateway. The name is auto-filled. Save.
Path: Access Controls > Policies
Three policies need to be created. Each is referenced by name in later steps.
| Field | Value |
|---|---|
| Policy name | Team Email Check |
| Action | Allow |
| Session duration | 12 hours |
| Rule type | Selector | Value |
|---|---|---|
| Include [OR] | Emails | Comma-separated list of authorised team emails |
Leave all other settings as default and save.
| Field | Value |
|---|---|
| Policy name | PostureCheck |
| Action | Allow |
| Session duration | Same as application session timeout |
| Rule type | Selector | Value |
|---|---|---|
| Include [OR] | WARP | - |
| Require [AND] | Gateway | - |
Click +Add require to add the Gateway rule. Leave everything else default and save.
| Field | Value |
|---|---|
| Policy name | Webhook Public Bypass |
| Action | Bypass |
| Session duration | Same as application session timeout |
| Rule type | Selector | Value |
|---|---|---|
| Include [OR] | Everyone | - |
Leave everything else default and save.
Path: Access Controls > Applications
Two applications need to be created: one for the protected app, and one for public webhook paths.
Destinations: under Public Hostnames, add the same subdomain and domain configured in the tunnel step. Leave the path empty to protect all routes.
- Allow access through browser-based RDP, SSH, or VNC sessions: OFF
Access Policies: add existing policy and select PostureCheck (Step 4, Policy 2).
| Authentication setting | Value |
|---|---|
| Accept all available identity providers | OFF |
| Identity provider | Pick Google from the dropdown |
| Apply instant authentication | OFF |
| Authenticate with Cloudflare One Client | OFF (identity provider is Google) |
Set the App name and Session duration to 6 hours. Save.
Before the how, the why: not every route in an app can sit behind interactive authentication. Webhook endpoints are called machine-to-machine by outside services (a payment provider, a Git host, an automation trigger) that cannot complete a browser login against your identity provider, so an auth challenge would silently break the integration. This second application exists to carve exactly those public routes out of Zero Trust enforcement while every other path on the same hostname stays protected. Keep the carve-out as narrow as possible and lean on the app's own safeguards for those routes, such as webhook signature verification or secret tokens in the URL.
Destinations: under Public Hostnames, use the same subdomain and domain. In the Path field, enter the webhook path that must remain publicly accessible without third-party authentication. For example, n8n exposes two paths: /webhook and /webhook-test.
Access Policies: add existing policy and select Webhook Public Bypass (Step 4, Policy 3).
- Accept all available identity providers: ON (likely inconsequential for a Bypass policy)
Set a name and Session duration to 1 month. Save.
Path: Access Controls > Access Settings > Manage your App Launcher
Verify the following configuration is in place:
| Setting | Expected value |
|---|---|
| Assigned policy | PostureCheck only |
| Login methods | Google only |
| Session duration | 15 minutes |
Path: Team & Resources > Devices > Device Profiles
| Field | Value |
|---|---|
| Name | Onboarding Profile |
| Description | Device profile for onboarding selected individuals |
| Expression selector | Operator | Value |
|---|---|---|
| User email | in | Comma-separated list of authorised emails |
| Setting | Value | Notes |
|---|---|---|
| Captive portal detection | ON, 3 min timeout | Allows users to authenticate on hotel, airport, or other public Wi-Fi |
| Mode switch | OFF | |
| Device tunnel protocol | WireGuard | |
| Lock device client switch | OFF | Turn ON when employees are fully subsidised with company devices |
| Allow device to leave organisation | ON | Turn OFF when employees are fully subsidised with company devices |
| Allow updates | ON | |
| Auto connect | OFF | Turn ON when employees are fully subsidised with company devices |
| Support URL | Your support URL | Use the small save button beside the field to save it separately |
| Service mode | Traffic and DNS Mode | |
| Local domain fallback | Default | Configure only if a local domain needs to be excluded from tunneling for local dev/testing |
Select Exclude IPs and domains, then click Manage. Find the address 100.64.0.0/10 and delete it.
| Setting | Value | Notes |
|---|---|---|
| Directly route Microsoft 365 traffic | OFF | |
| Allow users to enable local network exclusion | ON, 10 min timeout | Lets users access local printers and similar devices |
| Device client interface IP DNS registration | ON | |
| SCCM VPN boundary support | ON | |
| NetBT (NetBIOS over TCP/IP) | OFF |
Save the profile.
Path: Devices > Management
- 01Open the policies
Go to Device enrollment permissions > Manage > Policies.
- 02Attach the email policy
Select existing policies and choose Team Email Check (Step 4, Policy 1). Save.
- 03Set the login method
Switch to the Login methods tab, select Google, and save.
| Setting | Value | Notes |
|---|---|---|
| Allow admin override codes | OFF | Enable only when an admin override scenario is needed |
| Install CA to system certificate store | ON | Install available certificates to the certificate store of enrolled Windows, macOS, and Linux devices |
| Assign a unique IP address to each device | ON | Override the device's existing local interface IP with a unique IP address randomly assigned from the Carrier Grade NAT space (i.e. 100.64/10) |
| Setting | Value | Notes |
|---|---|---|
| Disconnect the Cloudflare One Client on all devices | OFF | Global disconnection of client for the entire organization |
| Setting | Value | Notes |
|---|---|---|
| Allow all Cloudflare One traffic to reach enrolled devices | OFF | Advanced use case. Turn ON when needed and update configuration accordingly for better privacy |
Path: Secure Web Gateway > Traffic Policies > Traffic Settings
| Setting | Value | Notes |
|---|---|---|
| Allow Secure Web Gateway to proxy traffic | ON, UDP & ICMP | |
| Inspect HTTPS requests with TLS decryption | OFF | Turn ON when needed |
| Allow protocol detection | OFF | Turn ON when needed and select required ports to scan |
All of the following can be enabled as needed, leave OFF by default:
| Setting | Default |
|---|---|
| Match extended email addresses | OFF |
| Allow egress policy host selectors | OFF |
| Scan files for malware | OFF, enable upload/download file scanning as needed |
| Allow enhanced file detection | OFF |
Set Log traffic activity to Capture All for all three categories.
The cloudflared client can be deployed in multiple ways. This guide covers the Docker method, specifically for self-managed systems like Coolify or AWS ECS (Elastic Container Service), where you control the Docker networks and containers yourself.
None of this is specific to Docker or Coolify either. Any internal service can be protected this way as long as a cloudflared client can be deployed in front of it, whether that is a bare metal VM, a Kubernetes cluster, or another container runtime. The Docker network example is what this guide walks through because it is the closest to a real production setup.
Diagram: One tunnel token, one client per server.
- Cloudflare Tunnel (one shared tunnel token)
- cloudflared A (AWS VM)
- cloudflared B (GCP VM)
- Docker network A (apps on server A)
- Docker network B (apps on server B)
- cloudflared A same token to Cloudflare Tunnel
- cloudflared B same token to Cloudflare Tunnel
- cloudflared A shared network to Docker network A
- cloudflared B shared network to Docker network B
- cloudflared A cannot reach to Docker network B
To keep these manageable, deploy all cloudflared instances in a single dedicated Coolify project and follow a consistent naming convention: [ServerName_ServerModel].
- 01Create the client
Create and assign the client to its server using the Coolify Cloudflared template (uses the latest image).
- 02Set the tunnel token
Go to the Environment Variables section and put the token we copied in Step 2 as the value of CLOUDFLARE_TUNNEL_TOKEN and save.
- 03Remove host networking
Edit the generated Docker Compose file: remove the network_mode: host entry from the cloudflared service.
Add the following inside the cloudflared service block:
networks:
- coolifyOutside of the services block, add the external network declaration:
networks:
coolify:
external: trueApply the same external network setup to the target app's Compose file (e.g. the n8n instance). This injects the cloudflared client into the shared Docker network, allowing it to resolve service names.
- The service is protected behind the Cloudflare tunnel. Only enrolled users who have authenticated via the identity provider can access the app's subdomain. Unauthenticated requests are dropped.
- Anyone without the Cloudflare One Client can still reach the team's App Launcher page, but attempting to use the identity provider will fail. The client being off triggers an error.
- Anyone with the client installed and running but not enrolled will fail the IdP check, as their email is not on the allow-list.
Of the seven protection layers Cloudflare SASE offers, this setup exercises one fully (ZTNA) and one partially (SWG, through the WARP client and Gateway proxy settings in Section 9). That is a deliberate starting point rather than a limitation: once devices are enrolled and traffic flows through the edge, turning on TLS inspection, DLP, or malware scanning is a dashboard toggle away, not a new architecture. Start with ZTNA for the apps that must never be public, and grow into the rest of the stack as the need appears.
