# Cloudflare One

# Account limits

import { Render } from "~/components";

This page lists the default account limits for rules, applications, fields, and other features. These limits may be increased on Enterprise accounts. To request a limit increase, contact your account team.

## Access

| Feature                  | Limit |
| ------------------------ | ----- |
| Applications             | 500   |
| Audit Logpush jobs       | 5     |
| Email addresses per rule | 1,000 |
| Rule groups              | 300   |
| Rules per rule group     | 1,000 |
| IP addresses per rule    | 1,000 |
| mTLS root certificates   | 50    |
| Service tokens           | 50    |
| Identity providers       | 50    |
| Reusable policies        | 500   |
| Rules per application    | 1,000 |
| Domains per application  | 50    |
| Infrastructure targets   | 5,000 |
| MCP portals              | 20    |
| MCP servers per portal   | 80    |
| Custom domains per MCP portal | 5 |
| MCP portal session inactivity timeout | 24 hours |

## Application Library

| Feature                                                                                                | Zero Trust Free | Zero Trust Pay-as-you-go | Zero Trust Enterprise |
| ------------------------------------------------------------------------------------------------------ | --------------- | ------------------------ | --------------------- |
| [Custom applications per account](/cloudflare-one/team-and-resources/app-library/custom-applications/) | 50              | 50                       | 100                   |

## Gateway

| Feature                                   | Limit |
| ----------------------------------------- | ----- |
| DNS policies per account                  | 500   |
| Network policies per account              | 500   |
| HTTP policies per account                 | 500   |
| Egress policies per account               | 500   |
| Resolver policies per account             | 500   |
| DNS locations                             | 250   |
| Source IP CIDRs per DNS location          | 1,500 |
| Concurrent streams for HTTP/2 connections | 256   |
| PAC files (Standard users)                | 50    |
| PAC files (Enterprise users)              | 1,000 |
| Proxy endpoints (Standard users)          | 50    |
| Proxy endpoints (Enterprise users)        | 500   |
| Source IP CIDRs per proxy endpoint        | 2,000 |
| Lists                                     | 100   |
| Entries per list (Standard users)         | 1,000 |
| Entries per list (Enterprise users)       | 5,000 |
| List API requests per minute              | 600   |
| DNS Logpush jobs                          | 5     |
| HTTP Logpush jobs                         | 5     |

HTTP policies using [Forward](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) (beta) count toward the HTTP policy limit. Invoked Workers are subject to [Workers limits](/workers/platform/limits/), including memory, CPU, subrequest, and background execution limits.

## Data Loss Prevention (DLP)

<Render file="data-loss-prevention/dlp-limits-table" product="cloudflare-one" />

## Cloudflare Tunnel

| Feature                                            | Limit |
| -------------------------------------------------- | ----- |
| `cloudflared` tunnels per account                  | 1,000 |
| Routes (CIDR routes + Hostname routes) per account | 50,000 (shared with Cloudflare Mesh) |
| Published applications per tunnel                  | Limited by 1 MB configuration size |
| Active `cloudflared` replicas per tunnel           | 25    |
| Virtual networks per account                       | 1,000 |

To run more than 1,000 tunnels in a single account, refer to [Tunnels for Platforms](/tunnel/features/tunnels-for-platforms/).

Published applications (public hostnames) do not count toward the routes limit. For details, refer to [Published application limits](/tunnel/concepts/routing/#published-application-limits).

## Cloudflare Mesh

| Feature                                            | Limit |
| -------------------------------------------------- | ----- |
| Mesh nodes per account                             | 50    |
| Routes (CIDR routes + Hostname routes) per account | 50,000 (shared with Cloudflare Tunnel) |

## Digital Experience Monitoring (DEX)

| Feature                 | Limit                                                                                      |
| ----------------------- | ------------------------------------------------------------------------------------------ |
| DEX Tests per account   | Zero Trust Free: 10 <br/> Zero Trust Standard: 30 <br/> Zero Trust Enterprise: 50 <br/>    |
| Remote captures per day | Zero Trust Free: 100 <br/> Zero Trust Standard: 200 <br/> Zero Trust Enterprise: 800 <br/> |

## Certificates

| Feature                        | Limit |
| ------------------------------ | ----- |
| Active certificates            | 10    |
| Certificates generated per day | 3     |
| Custom certificates            | 5     |

## Maximum number of characters

| Feature                       | Character limit |
| ----------------------------- | --------------- |
| Application name              | 350             |
| Group name                    | 350             |
| mTLS certificates name        | 350             |
| Service token name            | 350             |
| IdP name                      | 350             |
| Target name                   | 255             |
| Application URL               | 63              |
| Team domain                   | 63              |
| MCP portal name                 | 350             |
| MCP server name                 | 350             |
| MCP server description          | 512             |
| MCP tool alias                  | 40              |
| MCP portal/server ID            | 32              |
| Gateway API policy expression | 140,000         |

## Cloudflare One Client
| Feature                                                                    | Limit  |
| -------------------------------------------------------------------------- | ------ |
| Characters per device profile expression                                   | 10,000 |
| Combined Split Tunnel and Local Domain Fallback entries per device profile | 1,000  |
| Device IP profiles per account                                             | 30     |

# API and Terraform

You can manage your Cloudflare Zero Trust configuration using the API or Terraform. For more information, refer to the following links:

- [API reference](/api/)
- [Terraform provider reference](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs)
- [Terraform how-to documentation](/terraform/)

Detailed API and Terraform examples for Cloudflare Zero Trust are available in our [implementation guides](/cloudflare-one/implementation-guides/) and throughout the Cloudflare Zero Trust documentation.

## Set dashboard to read-only

Super Administrators can lock all settings as read-only in the Cloudflare One dashboard. Read-only mode ensures that all updates for the account are made through the API or Terraform.

To enable read-only mode:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Settings** > **Admin controls**.
2. Enable **Set dashboard to read-only**.

All users, regardless of [user permissions](/cloudflare-one/roles-permissions/), will be prevented from making configuration changes through the UI.

## Scoped API tokens

The administrators managing policies and groups in Cloudflare Zero Trust might be different from those responsible for configuring WAF custom rules or other Cloudflare settings. You can configure scoped API tokens so that team members and automated systems can manage Cloudflare Zero Trust settings without having permission to modify other configurations in Cloudflare.

You can create a scoped API token [via the dashboard](/fundamentals/api/get-started/create-token/) or [via the API](/fundamentals/api/how-to/create-via-api/). For a list of available token permissions, refer to [API token permissions](/fundamentals/api/reference/permissions/).

# Glossary

import { Glossary, Render } from "~/components";

Review definitions for Cloudflare One terms.

<Glossary product="cloudflare-one" />

# Cloudflare One

import {
	CardGrid,
	Description,
	Feature,
	GlossaryTooltip,
	LinkButton,
	LinkTitleCard,
	Plan,
	RelatedProduct,
	Render,
	Stream,
} from "~/components";

<Description>

Secure your organization with Cloudflare One — a cloud security platform that replaces legacy perimeters with Cloudflare's global network.

</Description>

<Plan type="all" />

Cloudflare One is Cloudflare's [Secure Access Service Edge (SASE)](https://www.cloudflare.com/learning/access-management/what-is-sase/) platform. SASE is an architectural model that unifies enterprise networking services with Zero Trust security.

[Zero Trust](https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/) is a security model designed around the principle of least privilege. In the past, once you logged into a corporate network, you were "trusted" to move around freely. Zero Trust changes that. It assumes that threats can exist both outside and inside the network. Therefore, every request is authenticated and authorized based on identity and context before granting access.

The Cloudflare One platform allows organizations to move away from a patchwork of hardware appliances and point solutions. Instead, it consolidates security and networking through a unified control plane that includes products like [Cloudflare Access](/cloudflare-one/access-controls/policies/), [Secure Web Gateway (SWG)](/cloudflare-one/traffic-controls/), [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), [Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/), [Remote Browser Isolation (RBI)](/cloudflare-one/remote-browser-isolation/), [Cloud Access Security Broker (CASB)](/cloudflare-one/integrations/cloud-and-saas/), and [Email security](/cloudflare-one/email-security/).

Refer to our [SASE reference architecture](/reference-architecture/architectures/sase/) to learn how to plan, deploy, and manage SASE architecture with Cloudflare.

<Stream
	id="a4ede34e937c81781575af6494828b5b"
	title="SASE - The evolution of corporate networks"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/51e1e33c-58ac-4eec-216e-d5b71a2dad00/public"
/>

<LinkButton variant="primary" href="/cloudflare-one/setup/">
	Get started
</LinkButton>
<LinkButton variant="secondary" href="https://dash.cloudflare.com/">
	Cloudflare dashboard
</LinkButton>
<LinkButton variant="primary" href="/cloudflare-one/implementation-guides/">
	Implementation guides
</LinkButton>

## Products

<RelatedProduct header="Access" href="/cloudflare-one/access-controls/policies/" product="access">

Authenticate users accessing your applications, seamlessly onboard third-party users, and log every event and request.

</RelatedProduct>

<RelatedProduct header="Cloudflare Tunnel" href="/cloudflare-one/networks/connectors/cloudflare-tunnel/" product="zero-trust-networks">

Securely connect your resources to Cloudflare without exposing a public IP by using Cloudflare Tunnel, which establishes outbound-only connections from your infrastructure to Cloudflare's global network via the lightweight `cloudflared` daemon.

</RelatedProduct>

<RelatedProduct header="Cloudflare Mesh" href="/mesh/" product="cloudflare-mesh">

Connect devices and private networks to each other with post-quantum encrypted mesh networking, using Cloudflare Mesh nodes instead of a central concentrator.

</RelatedProduct>

<RelatedProduct header="Secure Web Gateway (SWG)" href="/cloudflare-one/traffic-controls/" product="gateway">

Inspect and filter DNS, network, HTTP, and egress traffic to enforce your company's Acceptable Use Policy (UAP), block risky sites with custom blocklists and threat intelligence, and enhance visibility and protection across SaaS applications.

</RelatedProduct>

<RelatedProduct header="Cloudflare One Client" href="/cloudflare-one/team-and-resources/devices/cloudflare-one-client/" product="warp-client">

Protect corporate devices by privately sending traffic from those devices to Cloudflare's global network, build device posture rules, and enforce security policies anywhere.

</RelatedProduct>

<RelatedProduct header="Browser Isolation (RBI)" href="/cloudflare-one/remote-browser-isolation/" product="browser-isolation">

Mitigate the impact of attacks by executing all browser code in the cloud and securely browse high-risk or sensitive websites in a remote browser.

</RelatedProduct>

<RelatedProduct header="Cloud Access Security Broker (CASB)" href="/cloudflare-one/integrations/cloud-and-saas/" product="casb">

Protect users and sensitive data at rest in SaaS applications and cloud environments, scan for misconfigurations, and detect insider threats as well as unsanctioned application usage to prevent data leaks and compliance violations.

</RelatedProduct>

<RelatedProduct header="Data Loss Prevention (DLP)" href="/cloudflare-one/data-loss-prevention/" product="data-loss-prevention">

Scan your web traffic and SaaS applications for the presence of sensitive data such as social security numbers, financial information, secret keys, and source code.

</RelatedProduct>

<RelatedProduct header="Email security" href="/cloudflare-one/email-security/" product="email-security">

Configure policies to manage your inbox, automatically move emails based on disposition, and use screen criteria to investigate messages.

</RelatedProduct>

<RelatedProduct header="Digital Experience Monitoring (DEX)" href="/cloudflare-one/insights/dex/" product="dex">

Monitor device, network, and application performance across your Zero Trust organization.

</RelatedProduct>

<RelatedProduct header="Cloudflare WAN" href="/cloudflare-wan/" product="cloudflare-wan">

Replace legacy MPLS and SD-WAN circuits by connecting branch sites, data centers, and cloud networks to Cloudflare over IPsec, GRE, or direct interconnect.

</RelatedProduct>

<RelatedProduct header="Cloudflare One Appliance" href="/cloudflare-wan/configuration/appliance/" product="cloudflare-wan">

Connect branch sites to Cloudflare One using a Cloudflare-managed hardware or virtual appliance.

</RelatedProduct>

<RelatedProduct header="Cloudflare Network Firewall" href="/cloudflare-network-firewall/" product="cloudflare-network-firewall">

Filter traffic at the network layer with firewall-as-a-service, applying packet filtering rules before traffic reaches your infrastructure.

</RelatedProduct>

<RelatedProduct header="Multi-Cloud Networking" href="/multi-cloud-networking/" product="multi-cloud-networking">

Discover resources across your public cloud accounts and manage connectivity between them from a single control plane.

</RelatedProduct>

<RelatedProduct header="Data Localization Suite" href="/data-localization/" product="data-localization">

Control the regions where Cloudflare inspects and stores your traffic data, to meet data residency requirements.

</RelatedProduct>

---

## More resources

<CardGrid>

<LinkTitleCard
	title="SASE video series"
	href="/learning-paths/sase-overview-course/series/evolution-corporate-networks-1/"
	icon="seti:video"
>
	New to Zero Trust and SASE? Get started with our introductory SASE video
	series.
</LinkTitleCard>

<LinkTitleCard
	title="Reference architecture"
	href="/reference-architecture/architectures/sase/"
	icon="open-book"
>
	Explore our reference architecture to learn how to evolve your network and
	security architecture to Cloudflare One, our SASE platform.
</LinkTitleCard>

<LinkTitleCard
	title="Plans"
	href="https://www.cloudflare.com/plans/zero-trust-services/"
	icon="seti:shell"
>
	Cloudflare Zero Trust offers both Free and Paid plans. Access to certain
	features depends on a customer's plan type.
</LinkTitleCard>

<LinkTitleCard title="Limits" href="/cloudflare-one/account-limits/" icon="document">

    Learn about account limits. These limits may be increased on Enterprise accounts.

</LinkTitleCard>

<LinkTitleCard
	title="Support"
	href="/cloudflare-one/troubleshooting/"
	icon="laptop"
>
	Find troubleshooting guides for Cloudflare One products and learn how to collect information for Support.
</LinkTitleCard>

<LinkTitleCard
	title="Community"
	href="https://community.cloudflare.com/"
	icon="comment-alt"
>
	Ask questions, get answers, and share tips.
</LinkTitleCard>

</CardGrid>

<Render file="non-contract-enablement" product="fundamentals" />

# Roles and permissions

import { Render } from "~/components";

When creating a Cloudflare Zero Trust account, you will be given the Super Administrator role. As a Super Administrator, you can invite members to join your Zero Trust account and assign them different roles. There is no limit to the number of members which can be added to a given account. Any members with the proper permissions will be able to make configuration changes while actively logged into Zero Trust (unless [read-only mode](/cloudflare-one/api-terraform/#set-dashboard-to-read-only) is enabled).

To check the list of members in your account, or to manage roles and permissions, refer to our [Account setup](/fundamentals/manage-members/) documentation.

## Zero Trust roles

Only Super Administrators will be able to assign or remove the following roles from users in their account. Scroll to the right to see a full list of permissions for each role.

|                                               | Access Read | Access Edit | Gateway Read | Gateway Edit | Gateway Report | DNS Location Read | DNS Location Edit | Billing Read | Billing Edit | DEX Read | DEX Edit | CASB Read | CASB Edit |
| --------------------------------------------- | ----------- | ----------- | ------------ | ------------ | -------------- | ----------------- | ----------------- | ------------ | ------------ | -------- | -------- | --------- | --------- |
| Super Administrator                           | ✅          | ✅          | ✅           | ✅           | ✅             | ✅                | ✅                | ✅           | ✅           | ✅       | ✅       | ✅        | ✅        |
| Cloudflare Zero Trust[^1]                     | ✅          | ✅          | ✅           | ✅           | ✅             | ✅                | ✅                | ✅           | ❌           | ✅       | ✅       | ✅        | ✅        |
| Cloudflare Access                             | ✅          | ✅          | ✅           | ❌           | ✅             | ❌                | ❌                | ✅           | ❌           | ❌       | ❌       | ❌        | ❌        |
| Cloudflare Gateway                            | ✅          | ❌          | ✅           | ✅           | ✅             | ✅                | ✅                | ✅           | ❌           | ❌       | ❌       | ❌        | ❌        |
| Cloudflare Zero Trust Read Only               | ✅          | ❌          | ✅           | ❌           | ✅             | ✅                | ❌                | ✅           | ❌           | ✅       | ❌       | ✅        | ❌        |
| Cloudflare Zero Trust Reporting               | ❌          | ❌          | ❌           | ❌           | ✅             | ❌                | ❌                | ✅           | ❌           | ✅       | ❌       | ❌        | ❌        |
| Cloudflare Zero Trust DNS Locations Write[^2] | ❌          | ❌          | ❌           | ❌           | ❌             | ✅                | ✅                | ❌           | ❌           | ❌       | ❌       | ❌        | ❌        |
| Cloudflare DEX                                | ❌          | ❌          | ❌           | ❌           | ❌             | ❌                | ❌                | ❌           | ❌           | ✅       | ✅       | ❌        | ❌        |
| Cloudflare CASB Read                          | ❌          | ❌          | ✅           | ❌           | ❌             | ❌                | ❌                | ❌           | ❌           | ❌       | ❌       | ✅        | ❌        |
| Cloudflare CASB                               | ❌          | ❌          | ✅           | ❌           | ❌             | ❌                | ❌                | ❌           | ❌           | ❌       | ❌       | ✅        | ✅        |

[^1]: The **Cloudflare Zero Trust** role grants administrator access to all Zero Trust products including Access, Gateway, the Cloudflare One Client, Tunnel, Browser Isolation, CASB, DLP, DEX, and Email security.

[^2]: Users with the **Cloudflare Zero Trust DNS Locations Write** role can view all DNS locations for an organization but can only create and edit [secure DNS locations](/cloudflare-one/traffic-controls/resolver-endpoints/#secure-dns-locations).

### Cloudflare Zero Trust PII

By default, only Super Administrators can view end users' PII in the Gateway activity logs, such as Device IDs, Source IPs, or user emails. No other roles will have the ability to read PII unless Super Administrators explicitly assign the **Cloudflare Zero Trust PII** role to them.

The Cloudflare Zero Trust PII role should be considered an add-on role, to be combined with any role from the table above. For example, Super Administrators may decide to assign the Cloudflare Gateway role to a user, and add the Cloudflare Zero Trust PII role to allow that user to access PII in the Gateway logs.

:::note
The Cloudflare Zero Trust PII role does not apply to Access authentication logs. PII is always visible in Access logs.
:::

## Email security roles

For more information on Email security roles, refer to [Account-scoped roles](/fundamentals/manage-members/roles/#account-scoped-roles).

- **Cloudflare Zero Trust**: Can edit Cloudflare [Zero Trust](/cloudflare-one/). Grants administrator access to all Zero Trust products including Access, Gateway, the Cloudflare One Client, Tunnel, Browser Isolation, CASB, DLP, DEX, and Email security.
- **Cloudflare Zero Trust PII**: Can read PII in Zero Trust. This includes Email security.
- **Email security Analyst** and **Email security Configuration Admin**: Has full access to all admin features in Email security.
- **Email security Integration Admin**: Can read and set up integrations only.
- **Email security Configuration Admin**: Has administrator access. Cannot take actions on emails, or read emails.
- **Email security Analyst**: Has analyst access. Can take action on emails and read emails.
- **Email security Reporting**: Can read metrics.
- **Email security Read Only**: Can read all information, but cannot take action on anything.
- **Email security Policy Admin**: Can read all settings, but only write [allow policies](/cloudflare-one/email-security/policies/inbound/allow-policies/), [trusted domains](/cloudflare-one/email-security/policies/inbound/trusted-domains/), and [blocked senders](/cloudflare-one/email-security/policies/inbound/blocked-senders/).

# Videos

import { CardGrid, LinkCard } from "~/components";

<CardGrid>
	<LinkCard
		title="Build and secure your SASE corporate network"
		description="Dive into Cloudflare's Secure Access Service Edge (SASE) platform and learn how it's been designed to revolutionize the idea of the corporate network."
		href="/learning-paths/sase-overview-course/series/evolution-corporate-networks-1/"
	/>
	<LinkCard
		title="Understand and troubleshoot Cloudflare WARP"
		description="In this series, we cover the basics of Cloudflare WARP, share useful troubleshooting tips, and explain the warp-diag logs in detail."
		href="/learning-paths/warp-overview-course/series/warp-basics-1/"
	/>
	<LinkCard
		title="What's a Cloudflare Tunnel?"
		description="Cloundflare Tunnel is like a private, secure pathway from your computer to the Internet, so you don't have to leave the front door (your network) wide open."
		href="/videos/what-is-cf-tunnel/"
	/>
	<LinkCard
		title="Add your domain to Cloudflare"
		description="To begin using a Cloudflare Tunnel, you need a domain name. Learn how DNS works and how Cloudlare manages your domain through the metaphor of an online address book."
		href="/videos/the-online-address-book/"
	/>
	<LinkCard
		title="Set up Access policies for your tunnel"
		description="Set up access policies using Cloudflare Access to verify the identity of every user."
		href="/videos/set-up-access-policies/"
	/>
	<LinkCard
		title="Set up Cloudflare Tunnel"
		description="Set up Cloudflare Tunnel to create a secure link between your private environment and the Cloudflare edge."
		href="/videos/set-up-cf-tunnel/"
	/>
</CardGrid>

# Authenticate coding agents

Coding agents such as Claude Code, OpenCode, and Windsurf often need to reach resources protected by [Cloudflare Access](/cloudflare-one/access-controls/). When a resource is behind Access, unauthenticated requests receive a redirect or `403` error instead of the expected response. Your agent needs a way to authenticate before it can reach the resource.

This page covers two authentication methods:

- [**cloudflared**](#use-cloudflared) — authenticates under your user identity. Use for interactive development where you can complete a browser login.
- [**Service tokens**](#use-service-tokens) — authenticates with a static credential pair. Use for headless or automated workflows where no browser is available.

:::note
Cloudflare Access also supports Managed OAuth for protected resources, which you can use to grant authorization to coding agents.
:::

## Use cloudflared

With `cloudflared`, your agent authenticates under your user identity. On first use, `cloudflared` opens a browser window for an interactive login. After that, the session persists for the [session duration](/cloudflare-one/access-controls/access-settings/session-management/) configured for the application. After the session expires, the next request requires a new browser login.

### Prerequisites

[Download and install cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/).

### Make requests with cloudflared access curl

For direct requests to a protected resource, use `cloudflared access curl`. This handles authentication automatically and does not require token management.

```sh
cloudflared access curl https://example.com/api/endpoint
```

If this is the first request in a session, `cloudflared` opens a browser for the user to authenticate. Prompt the user to complete the login if needed.

### Use a reusable token

Some agents make HTTP requests using their own client libraries instead of calling `cloudflared` directly. In this case, log in to get a token and pass it as a header:

```sh
CF_TOKEN=$(cloudflared access login https://example.com)
curl --header "cf-access-token: $CF_TOKEN" https://example.com/api/endpoint
```

The token is valid for the session duration configured for the application.

For more information, refer to [Connect through Access using a CLI](/cloudflare-one/tutorials/cli/) and [Client-side cloudflared](/cloudflare-one/access-controls/applications/non-http/cloudflared-authentication/).

## Use service tokens

Service tokens are static credential pairs that authenticate requests without a browser login. Use them for automated workflows where no user is present.

1. [Create a service token](/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token) and save the **Client ID** and **Client Secret**.

2. In the Access application's policy configuration, add a [Service Auth policy](/cloudflare-one/access-controls/policies/#service-auth). This policy type accepts service token credentials instead of requiring an identity provider login. Use the **Service Token** selector and select the token you created.

   | Action       | Rule type | Selector      | Value            |
   | ------------ | --------- | ------------- | ---------------- |
   | Service Auth | Include   | Service Token | Your agent token |

3. Store the Client ID and Client Secret in a secure location on your machine that your agent can read.

4. Include both values as headers in requests to the protected resource:

   ```sh
   curl --header "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
        --header "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
        https://example.com/api/endpoint
   ```

For more information, refer to [Service tokens](/cloudflare-one/access-controls/service-credentials/service-tokens/).

## Configure your agent

Add an `AGENTS.md` file to your project root with the following skill definition. This instructs coding agents to automatically detect Cloudflare Access-protected resources and authenticate using the standard OAuth 2.0 flow with PKCE (RFC 9728).

````markdown
---
name: access-oauth
description: "Detect Cloudflare Access-protected websites and authenticate via the standard OAuth 2.0 flow (RFC 9728 resource metadata, dynamic client registration, authorization code + PKCE)"
license: MIT
compatibility: opencode
metadata:
  category: authentication
  audience: developers
---

# Access OAuth Authentication

Authenticate to Cloudflare Access-protected resources using standard OAuth 2.0
(resource metadata discovery, dynamic client registration, authorization code with PKCE).

## When to Use

Use this skill when:

- You need to access a URL that returns HTTP 401
- The response contains a `www-authenticate: Bearer` header with a `resource_metadata` URL
- The resource metadata indicates it is a Cloudflare Access-protected resource
- You want to authenticate interactively through the user's IdP

## Step 1: Detect a Protected Resource

Make a request and inspect the response headers:

```bash
curl -sI -L <URL> 2>&1
```

Look for a **401** response with a `www-authenticate` header like:

```
www-authenticate: Bearer realm="OAuth", error="invalid_token",
  error_description="Missing or invalid access token",
  resource_metadata="https://<hostname>/.well-known/cloudflare-access-protected-resource/"
```

If you see this header, the site supports the OAuth flow. Proceed to Step 2.

The JSON body of the 401 will also contain:

```json
{
	"error": "invalid_token",
	"error_description": "Missing or invalid access token",
	"resource_metadata": "https://<hostname>/.well-known/cloudflare-access-protected-resource/"
}
```

### If No `www-authenticate` Header

If the 401 does not include `www-authenticate` with `resource_metadata`, the site may
not support this OAuth flow. Fall back to `cloudflared access curl` or browser-based
authentication.

## Step 2: Fetch Resource Metadata

Fetch the resource metadata URL from the `www-authenticate` header:

```bash
curl -s https://<hostname>/.well-known/cloudflare-access-protected-resource/
```

Expected response:

```json
{
	"resource": "https://<hostname>",
	"protected": true,
	"team_domain": "<team>.cloudflareaccess.com",
	"authorization_servers": ["https://<team>.cloudflareaccess.com"],
	"authentication_method": "cloudflared",
	"authentication_method_description": "Use `cloudflared access curl`...",
	"authentication_method_documentation": "https://developers.cloudflare.com/cloudflare-one/tutorials/cli/"
}
```

Extract the **authorization server** URL from `authorization_servers[0]` (e.g. `https://<team>.cloudflareaccess.com`).

## Step 3: Fetch OAuth Authorization Server Metadata

```bash
curl -s https://<team>.cloudflareaccess.com/.well-known/oauth-authorization-server
```

Expected response:

```json
{
	"issuer": "<team>.cloudflareaccess.com",
	"authorization_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/authorization",
	"token_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/token",
	"response_types_supported": ["code"],
	"response_modes_supported": ["query"],
	"grant_types_supported": ["authorization_code", "refresh_token"],
	"token_endpoint_auth_methods_supported": [
		"client_secret_basic",
		"client_secret_post",
		"none"
	],
	"revocation_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/revoke",
	"registration_endpoint": "https://<team>.cloudflareaccess.com/cdn-cgi/access/oauth/registration",
	"code_challenge_methods_supported": ["S256"]
}
```

Verify that:

- `"none"` is in `token_endpoint_auth_methods_supported` (allows public clients)
- `"authorization_code"` is in `grant_types_supported`
- `"S256"` is in `code_challenge_methods_supported`
- A `registration_endpoint` is present

Extract the **registration_endpoint**, **authorization_endpoint**, and **token_endpoint**.

## Step 4: Dynamic Client Registration

Register a public OAuth client:

```bash
curl -s -X POST <registration_endpoint> \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": ["http://localhost:8400/callback"],
    "token_endpoint_auth_method": "none",
    "grant_types": ["authorization_code"],
    "response_types": ["code"],
    "resource": "https://<hostname>"
  }'
```

Expected response:

```json
{
	"client_id": "<uuid>",
	"redirect_uris": ["http://localhost:8400/callback"],
	"grant_types": ["authorization_code"],
	"response_types": ["code"],
	"token_endpoint_auth_method": "none",
	"registration_client_uri": "...",
	"client_id_issued_at": 1234567890
}
```

Save the **client_id**.

## Step 5: Generate PKCE Challenge

Generate a code verifier and S256 challenge. Ensure the challenge starts with an
alphanumeric character to avoid URL parsing issues:

```bash
while true; do
  CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=' | tr '/+' '_-')
  CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr -d '=' | tr '/+' '_-')
  if [[ "$CODE_CHALLENGE" =~ ^[a-zA-Z0-9] ]]; then
    break
  fi
done
```

**Important**: The code challenge MUST start with `[a-zA-Z0-9]`. A leading `-` or `_`
can cause URL parameter parsing failures on the authorization server.

## Step 6: Authorization Code Flow with Local Callback

Start a local HTTP server to catch the callback, then direct the user to the
authorization URL.

### Build the Authorization URL

```
<authorization_endpoint>?
  client_id=<client_id>&
  redirect_uri=http%3A%2F%2Flocalhost%3A8400%2Fcallback&
  response_type=code&
  code_challenge=<CODE_CHALLENGE>&
  code_challenge_method=S256&
  resource=<URL-encoded target resource>
```

### Start the Callback Listener and Prompt the User

Run a Python HTTP server on port 8400 that captures the authorization code:

```python
python3 -c '
import http.server, urllib.parse

class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        params = urllib.parse.parse_qs(parsed.query)
        if "code" in params:
            code = params["code"][0]
            with open("/tmp/oauth_code.txt", "w") as f:
                f.write(code)
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(b"<h1>Got it!</h1><p>Authorization code received. You can close this tab.</p>")
            print(f"CODE={code}", flush=True)
        elif "error" in params:
            err = params.get("error", [""])[0]
            desc = params.get("error_description", [""])[0]
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(f"<h1>Error</h1><p>{err}: {desc}</p>".encode())
            print(f"ERROR: {err} - {desc}", flush=True)
        else:
            self.send_response(400)
            self.end_headers()
            self.wfile.write(b"Unexpected request")
            print(f"Unexpected: {self.path}", flush=True)
        import threading
        threading.Thread(target=self.server.shutdown).start()
    def log_message(self, format, *args):
        pass

print("Listening on http://localhost:8400 ...", flush=True)
print("Open the authorization URL in your browser.", flush=True)
http.server.HTTPServer(("", 8400), Handler).serve_forever()
'
```

**Important**: Use a timeout of at least 120000ms for this bash command since the user
needs time to authenticate in the browser.

Tell the user to open the authorization URL in their browser. After they authenticate
with their IdP, the browser will redirect to `http://localhost:8400/callback?code=<code>`,
the server will capture it and shut down.

## Step 7: Exchange Code for Token

```bash
curl -s -X POST <token_endpoint> \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=<AUTH_CODE>" \
  -d "client_id=<CLIENT_ID>" \
  -d "redirect_uri=http://localhost:8400/callback" \
  -d "code_verifier=<CODE_VERIFIER>"
```

Expected response:

```json
{
	"access_token": "oauth:<token>",
	"token_type": "bearer",
	"expires_in": 900,
	"scope": "",
	"resource": "https://<hostname>/",
	"refresh_token": "oauth:<refresh_token>"
}
```

Save the **access_token** and **refresh_token**.

## Step 8: Access the Protected Resource

```bash
curl -s https://<hostname>/ \
  -H "Authorization: Bearer <access_token>"
```

This should now return the actual content behind Cloudflare Access.

## Step 9: Refresh the Token (if needed)

If the access token expires (default 900 seconds), use the refresh token:

```bash
curl -s -X POST <token_endpoint> \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=<REFRESH_TOKEN>" \
  -d "client_id=<CLIENT_ID>"
```

## Quick Reference: Full Flow Summary

```
1. curl -sI <URL>                          # Detect 401 + www-authenticate header
2. curl -s <resource_metadata_url>         # Get authorization server
3. curl -s <as>/.well-known/oauth-authorization-server  # Get endpoints
4. POST <registration_endpoint>            # Register public client
5. Generate PKCE code_verifier + challenge # S256, alphanumeric start
6. Start localhost:8400 listener           # Catch callback
7. User opens authorization URL            # Browser-based IdP auth
8. POST <token_endpoint>                   # Exchange code for token
9. curl -H "Authorization: Bearer <token>" # Access resource
```

## Troubleshooting

| Problem                                                 | Cause                                                               | Fix                                                                        |
| ------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `code_challenge_method must be S256 for public clients` | Code challenge starts with `-` or `_`, corrupting the URL parameter | Regenerate until challenge starts with `[a-zA-Z0-9]`                       |
| `invalid_grant` on token exchange                       | Code expired or verifier mismatch                                   | Redo the auth flow; codes are single-use and short-lived                   |
| 401 after using token                                   | Token expired (default 15 min)                                      | Use refresh token to get a new access token                                |
| No `www-authenticate` header                            | Site doesn't support OAuth resource metadata                        | Fall back to `cloudflared access curl` or browser auth                     |
| No `registration_endpoint` in AS metadata               | Dynamic registration not enabled                                    | Must use a pre-registered client or different auth method                  |
| Port 8400 already in use                                | Previous listener didn't shut down                                  | Kill the process or use a different port (update redirect_uri accordingly) |
````

# Event subscriptions

import { Render } from "~/components";

<Render file="event-subscriptions/intro" product="queues" />

## Available Access events

<Render file="event-subscriptions/access-events" product="queues" />

# Access controls

import { DirectoryListing } from "~/components";

Learn how to secure your self-hosted and SaaS applications with Zero Trust policies.

<DirectoryListing />

## Troubleshooting

For help resolving common issues with Cloudflare Access, refer to [Troubleshoot Access](/cloudflare-one/access-controls/troubleshooting/).

Refer to our [reference architecture](/reference-architecture/architectures/sase/) for an understanding on how to architect a Zero Trust and SASE solution.

# Troubleshoot Access

import { Render } from "~/components";

<Render file="troubleshooting/access" product="cloudflare-one" />

---

## How to contact Support

If you cannot resolve the issue, [open a support case](/support/contacting-cloudflare-support/). Please provide a [HAR file](/support/troubleshooting/general-troubleshooting/gathering-information-for-troubleshooting-sites/#generate-a-har-file) captured while reproducing the error and the **Ray ID** if an error page is displayed.

# App Launcher

import { Render } from "~/components";

<Render file="access/app-launcher" product="cloudflare-one" />

# Independent MFA

import { Tabs, TabItem, APIRequest, Details, Render } from "~/components";

Independent multi-factor authentication (MFA) allows you to enforce MFA requirements directly in Access without relying on your identity provider (IdP). Users authenticate with their IdP as usual, and Access prompts for an additional authentication method before granting access to the application.

Because you can [configure MFA at the application and policy level](/cloudflare-one/access-controls/policies/mfa-requirements/#independent-mfa), you can enforce stricter authentication methods like hardware security keys on sensitive applications without requiring them across your entire organization. This allows you to add additional security where it matters most while avoiding MFA fatigue for your broader user population.

## Supported MFA methods

| MFA method                         | Description                                                                                                                                                                                                                                                                                                 |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authenticator application          | Time-based one-time passwords (TOTP) generated by apps such as Google Authenticator, Microsoft Authenticator, or Authy. Access supports one TOTP authenticator per user at a time.                                                                                                                          |
| Security key                       | Hardware security keys that support the [WebAuthn](https://www.w3.org/TR/webauthn-2/) standard. Users can enroll multiple security keys.                                                                                                                                                                    |
| Biometrics                         | Built-in device authenticators that use [WebAuthn](https://www.w3.org/TR/webauthn-2/), including Apple Touch ID, Apple Face ID, and Windows Hello. Users can enroll multiple biometrics.                                                                                                                    |
| Personal Identity Verification (PIV) key (infrastructure apps only) | YubiKey PIV keys used for public key authentication during SSH connections. Requires YubiKey firmware 4.3 or later. This method is only available for [infrastructure applications](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/). Users can enroll multiple PIV keys. |
| FIDO2 key (infrastructure apps only) | YubiKey FIDO2 keys used for public key authentication during SSH connections. This method is only available for [infrastructure applications](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/) and is separate from the browser-based WebAuthn security key method. |

## Turn on independent MFA

Before you can [enforce independent MFA on applications and policies](/cloudflare-one/access-controls/policies/mfa-requirements/#independent-mfa), you must turn on independent MFA at the organization level.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Allow multi-factor authentication (MFA)**, select the [MFA methods](#supported-mfa-methods) you want to allow in your organization.
3. Set an **Authentication duration**. This determines how long a user can log in to Access without being prompted for MFA again. If the user does not have an active MFA session for the required authenticator method, they must complete MFA in addition to IdP authentication.
4. (Optional) To avoid double prompting a user for MFA, you can enable [**Use identity provider MFA**](#use-identity-provider-mfa). This will check the AMR value passed from the identity provider at the time of authentication, if that AMR value passes an allowed MFA method, the user will not be prompted for MFA for the duration configured.
5. (Optional) To apply your MFA methods and authentication duration to all Access applications, select **Apply global MFA settings by default**. You can [override the global MFA settings](/cloudflare-one/access-controls/policies/mfa-requirements/#configure-independent-mfa-for-an-application) for individual applications and policies.
   :::note
   The [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) is exempt from the global MFA requirement. Users must be able to access the App Launcher without MFA to enroll their authenticators.
   :::
6. Select **Save**.

</TabItem>
<TabItem label="API">

1. Get your existing Zero Trust organization configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="GET"
   />

2. Send a `PUT` request to update your organization's MFA settings. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="PUT"
   	json={{
   		auth_domain: "your-team-name.cloudflareaccess.com",
   		name: "Your Team Name",
   		mfa_config: {
   			allowed_authenticators: ["totp", "biometrics", "security_key"],
   			session_duration: "24h",
   		},
   		mfa_required_for_all_apps: false,
   	}}
   />

   Set `allowed_authenticators` to an array containing one or more of:
   - `totp` — Authenticator application (time-based one-time passwords).
   - `biometrics` — Biometrics (Touch ID, Face ID, Windows Hello).
   - `security_key` — Security keys (hardware keys that support WebAuthn).
   - `piv_key` — PIV keys (YubiKeys).
   - `ssh_fido2_key` — FIDO2 keys for SSH connections to infrastructure applications.

   `piv_key` and `ssh_fido2_key` apply only to SSH connections to infrastructure applications. If allowed at the organization level, these MFA methods will only apply to infrastructure applications. They will not be available for other application types.

   Set `session_duration` to a duration string (for example, `30m`, `1h`, `24h`). To require MFA on every access, use `0m`.

</TabItem>
</Tabs>

After you turn on independent MFA, users can [enroll authenticators](#enroll-authenticators) through the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/).

### Configure PIV key requirements

If you plan to use PIV keys for [MFA for infrastructure applications](/cloudflare-one/access-controls/policies/mfa-requirements/#infrastructure-applications), configure the PIV key requirements in your organization's Access settings. These requirements determine which PIV keys users can enroll.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Allow multi-factor authentication (MFA)**, turn on the "Personal Identity Verification (PIV) key" authenticator.
3. Configure the following settings:

   | Setting           | Description                                    | Options                                                         |
   | ----------------- | ---------------------------------------------- | --------------------------------------------------------------- |
   | **Key type**      | The SSH key algorithm                          | ECDSA, Ed25519, RSA                                             |
   | **Key size**      | The key length in bits                         | ECDSA: 256, 384, 521. RSA: 2048, 3072, 4096                     |
   | **PIN policy**    | When the user must enter their PIV PIN         | `never`, `once` (once per session), `always` (every use)        |
   | **Touch policy**  | When the user must touch the hardware key      | `never`, `always` (every use), `cached` (cached for 15 seconds) |
   | **Require FIPS**  | Require the PIV key to be on a FIPS-validated device | `true`, `false`                                            |

4. Select **Save**.

:::note

<Render
	file="access/infra-mfa-global-settings-warning"
	product="cloudflare-one"
/>
:::

## Restrict authenticators by AAGUID

An [AAGUID](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#authenticator-attestation-guid) (Authenticator Attestation GUID) is a 128-bit identifier that indicates the make and model of a [WebAuthn](https://www.w3.org/TR/webauthn-2/) authenticator. By restricting enrollment to a specific set of AAGUIDs, you can require that users only enroll approved hardware, such as FIPS-validated security keys or company-issued devices.

Access checks the AAGUID when a user registers an authenticator. Because enrollment applies to the entire organization, you configure AAGUID restrictions at the organization level.

For FIDO2 keys, Access also checks the AAGUID each time the user connects to an infrastructure application. If you remove a FIDO2 key from the allowed list, that key stops working even if the user enrolled it earlier.

PIV keys do not present an AAGUID, so these restrictions do not apply to them.

When `required_aaguids` is unset, Access does not enforce AAGUID restrictions.

:::caution
Some authenticators do not send an AAGUID during WebAuthn registration, such as YubiKey 4 and earlier models using U2F (CTAP1). Users cannot enroll these authenticators when AAGUID restrictions are turned on. Before turning on AAGUID restrictions, confirm that your required authenticators are in the [FIDO Alliance Metadata Service](https://fidoalliance.org/metadata/).
:::

### 1. Create an AAGUID list

AAGUIDs are managed using [Lists](/cloudflare-one/reusable-components/lists/). Create a list of type **AAGUID**, then reference the list in your organization's MFA configuration.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Resources** > **Lists**.
2. Select **Create new list**.
3. Enter a **List name** (for example, `Approved security keys`) and an optional description.
4. Set **List type** to **MFA AAGUIDs**.
5. Add one or more AAGUID entries:
   - To add predefined AAGUIDs, select authenticators from the **Known authenticators** list.
   - To add a custom AAGUID, fill out the following fields:
     - **MFA AAGUIDs** — The AAGUID of the authenticator, in 32-character hexadecimal format without dashes (for example, `8c39ee867f9a4a959ba3f6b097e5c2ee`).
     - **Description** — An optional label such as the authenticator's name and model.
6. Select **Save**.

</TabItem>
<TabItem label="API">

Send a `POST` request to create the list:

<APIRequest
	path="/accounts/{account_id}/gateway/lists"
	method="POST"
	json={{
		name: "Approved security keys",
		description: "AAGUIDs for MFA enrollment",
		type: "AAGUID",
		items: [
			{
				value: "8c39ee867f9a4a959ba3f6b097e5c2ee",
				description: "YubiKey Bio Series - FIDO Edition (Enterprise Profile)",
			},
		],
	}}
/>

The response contains an `id` (UUID) for the list. Use this ID when you assign the list to your organization's MFA configuration.

</TabItem>
</Tabs>

:::tip
You can look up AAGUIDs for common authenticators in the [FIDO Alliance Metadata Service](https://fidoalliance.org/metadata/). Most vendors also publish AAGUIDs for their hardware on their support sites.
:::

### 2. Assign an AAGUID list to your organization

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Allow multi-factor authentication (MFA)**, go to **Limit MFA to specific authentication methods**.
3. Select an existing [AAGUID list](#1-create-an-aaguid-list).
4. Select **Save**.

After you save, only authenticators whose AAGUIDs appear in the list can be enrolled. Users with previously enrolled security keys or biometrics outside the list can continue to use them until they are [deleted by an administrator](#delete-a-user-authenticator). A previously enrolled FIDO2 key outside the list stops working on the next connection. To remove the restriction, unset the AAGUID list.

</TabItem>
<TabItem label="API">

1. Get your existing Zero Trust organization configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="GET"
   />

2. Send a `PUT` request to assign the list. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request. Set `mfa_config.required_aaguids` to the ID of your AAGUID list.

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="PUT"
   	json={{
   		auth_domain: "your-team-name.cloudflareaccess.com",
   		name: "Your Team Name",
   		mfa_config: {
   			allowed_authenticators: ["security_key", "totp", "biometrics"],
   			session_duration: "24h",
   			required_aaguids: "05ddacda-5131-41ab-9eeb-6763f8dce3be",
   		},
   	}}
   	code={{ mark: ["required_aaguids"] }}
   />

   To remove the restriction, set `required_aaguids` to `null`.

</TabItem>
</Tabs>

:::note
AAGUID restrictions and [AMR matching](#use-identity-provider-mfa) can both be turned on simultaneously, but AMR matching is more permissive than independent MFA when AAGUID restrictions are configured. Most identity providers do not include AAGUID information in their AMR claims, so Access cannot verify that the authenticator used for IdP MFA is in the list of approved AAGUIDs. If the IdP returns a matching AMR value, Access will skip the independent MFA prompt regardless of which authenticator the user used at the IdP.
:::

## Use identity provider MFA

If your identity provider already prompts users for MFA, you can configure Access to accept that MFA instead of prompting again. Access checks the Authentication Method Reference (AMR) claim returned by the IdP, as defined in [RFC 8176](https://datatracker.ietf.org/doc/html/rfc8176). If the AMR value matches an [allowed authenticator type](#supported-mfa-methods) for the application or policy, Access skips the independent MFA prompt.

### Supported AMR values

| AMR value | Matches Access authenticator type | Description                            |
| --------- | --------------------------------- | -------------------------------------- |
| `hwk`     | Security key                      | Proof-of-possession of a hardware key |
| `swk`     | Security key                      | Proof-of-possession of a software key |
| `otp`     | Authenticator application         | One-time password                     |
| `face`    | Biometrics                        | Facial recognition                    |
| `fpt`     | Biometrics                        | Fingerprint                           |
| `iris`    | Biometrics                        | Iris scan                             |
| `retina`  | Biometrics                        | Retina scan                           |
| `vbm`     | Biometrics                        | Voice biometric                       |

Access ignores AMR values that do not map to a supported authenticator type (for example, `pwd`, `sms`, `tel`, `geo`, `kba`, `sc`, `pin`, `user`, `mca`, `rba`, `wia`).

### Turn on AMR matching

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Allow multi-factor authentication (MFA)**, turn on **Use identity provider MFA**.
3. Under **Authentication Method Reference (AMR) matching duration**, set how long a successful IdP MFA remains valid. During this period, users can log in to Access without an additional MFA prompt. You can set a custom duration (default 24 hours) or check for a [valid AMR value](#supported-amr-values) on every login.
4. Select **Save**.

</TabItem>
<TabItem label="API">

1. Get your existing Zero Trust organization configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="GET"
   />

2. Send a `PUT` request to update your organization's AMR matching settings. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="PUT"
   	json={{
   		auth_domain: "your-team-name.cloudflareaccess.com",
   		name: "Your Team Name",
   		mfa_config: {
   			allowed_authenticators: ["totp", "biometrics", "security_key"],
   			session_duration: "24h",
   			amr_matching_enabled: true,
   			amr_session_duration: "1h",
   		},
   	}}
   	code={{ mark: ["amr_matching_enabled", "amr_session_duration"] }}
   />

</TabItem>
</Tabs>

### When AMR matching is skipped

Access does not apply AMR matching in the following cases:

- The IdP does not return an `amr` claim.
- The IdP returns only AMR values that do not map to an [allowed authenticator type](#supported-mfa-methods) for the application or policy.
- The user's AMR matching session has expired because they last performed MFA via their IdP longer ago than the configured AMR matching duration.

In these cases, Access falls back to checking for existing MFA sessions. If there are no valid MFA sessions, Access prompts the user to complete independent MFA.

:::note
Identity providers differ in how they populate the `amr` claim. Some providers, including Okta, may return provider-specific values such as `pop` that are not part of RFC 8176. Test the behavior with your IdP before relying on AMR matching for production applications.
:::

## Turn off independent MFA

:::caution
Turning off independent MFA removes MFA protection on all Access applications. Before turning off independent MFA, verify that your Access policies provide adequate coverage. Remove [custom MFA settings](/cloudflare-one/access-controls/policies/mfa-requirements/) from any applications and policies that use it, then turn off independent MFA at the organization level.
:::

To turn off independent MFA for the organization:

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Allow multi-factor authentication (MFA)**, turn off **Apply global MFA settings by default**.
3. Turn off all MFA methods (**Biometrics**, **Security key**, and **Authenticator application**).

If you get an error updating MFA settings, ensure that you have removed custom MFA settings from all applications and policies.

</TabItem>
<TabItem label="API">

1. Get your existing Zero Trust organization configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="GET"
   />

2. Send a `PUT` request with an empty `allowed_authenticators` array. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/organizations"
   	method="PUT"
   	json={{
   		auth_domain: "your-team-name.cloudflareaccess.com",
   		name: "Your Team Name",
   		mfa_config: {
   			allowed_authenticators: [],
   		},
   	}}
   />

</TabItem>
</Tabs>

## Enroll authenticators

Users enroll authenticators through the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/).

If a user already has at least one authenticator enrolled, Access requires them to [verify with an existing MFA method](#mfa-verification-for-authenticator-changes) before they can add a new authenticator.

To enroll an authenticator:

1.  Go to your organization's App Launcher at `<your-team-name>.cloudflareaccess.com`.
2.  Log in with your identity provider or with a one-time PIN (OTP).
3.  Go to **Account** > **MFA devices** > **Add an MFA device**.
    :::note
    Administrators can also share a direct enrollment link to help onboard users: `<your-team-name>.cloudflareaccess.com/AddMfaDevice`
    :::
4.  If you already have an MFA device enrolled, complete the MFA verification prompt.
5.  Select the authenticator type you want to enroll and follow the on-screen instructions.

    <Details header="Authenticator application">

        1. Select **Authenticator application**.
        2. Scan the QR code with your authenticator app (for example, Google Authenticator, Microsoft Authenticator, or Authy). Alternatively, you can manually enter the setup key into your authenticator app. Use SHA1 as the hash function and set the time-step size to 30 seconds.
        3. Enter the 6-digit time-based one-time password (TOTP) generated by your authenticator app to verify enrollment.

        :::note
        You can only have one TOTP authenticator enrolled at a time. If you use multiple devices, scan the same QR code on each device during enrollment. To replace an existing TOTP authenticator, delete it first and then enroll a new one.
        :::

        </Details>

        <Details header="Security key">
        1. Select **Security key**.
        2. When your browser prompts you, insert your security key and follow the on-screen instructions.
        3. After your browser confirms the registration, the security key is enrolled.

        You can enroll multiple security keys for backup purposes.

        </Details>

        <Details header="Biometrics">
        1. Select **Biometrics** > **Register biometrics**.
        2. You will be prompted to enroll with an authenticator type that is available on your device (for example, **Add macOS Touch ID** or **Add Windows Hello**).
        3. After your browser confirms the registration, the platform authenticator is enrolled.

        </Details>

        <Details header="PIV key (infrastructure applications only)">
        PIV key enrollment requires additional client-side setup and is only used for [MFA with infrastructure applications](/cloudflare-one/access-controls/policies/mfa-requirements/#infrastructure-applications). For full instructions, refer to [Enroll a PIV key for infrastructure apps](#enroll-a-piv-key-for-infrastructure-apps).
        </Details>

        <Details header="FIDO2 key (infrastructure applications only)">
        FIDO2 key enrollment requires additional client-side setup and is only used for [MFA with infrastructure applications](/cloudflare-one/access-controls/policies/mfa-requirements/#infrastructure-applications). This method is separate from browser-based security key enrollment. For full instructions, refer to [Enroll a FIDO2 key for infrastructure apps](#enroll-a-fido2-key-for-infrastructure-apps).
        </Details>

You can now use these authenticators to log in to your organization's applications.

### Enroll a FIDO2 key for infrastructure apps

Before users can enroll, turn on the **FIDO2 key** authenticator in your organization's [global MFA settings](#turn-on-independent-mfa).

The App Launcher generates the enrollment command for your account. Do not construct the enrollment command manually.

:::note
On macOS, install OpenSSH with Homebrew before you run the command. The `ssh-keygen` included with macOS does not support FIDO2 keys. On Linux, use an OpenSSH build that includes `libfido2` support.
:::

1. Go to your organization's App Launcher at `<your-team-name>.cloudflareaccess.com`.
2. Log in with your identity provider or a one-time PIN (OTP).
3. Go to **Account** > **MFA devices** > **Add an MFA device** > **FIDO2 key**.
4. Select the tab for your operating system.
5. Copy the enrollment command shown in the App Launcher.
6. Run the command in a local terminal on the device you use for SSH.
7. Follow the prompts, then copy the JSON output.
8. Return to the App Launcher and paste the JSON into the enrollment field.
9. Complete the enrollment.

#### Configure your SSH client for a FIDO2 key

The command creates the identity at `~/.ssh/id_ed25519_sk_cf`.

Add the following configuration to your `~/.ssh/config` file:

```txt
Host *
  IdentityFile ~/.ssh/id_ed25519_sk_cf
```

Alternatively, supply the identity when you connect:

```sh
ssh -i ~/.ssh/id_ed25519_sk_cf <username>@<target IP>
```

For more information on how OpenSSH uses FIDO2 hardware keys, refer to Yubico's [Securing SSH with FIDO2](https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html).

### Enroll a PIV key for infrastructure apps

PIV key enrollment is separate from the general authenticator enrollment above and requires additional client-side setup.

Before enrolling, you must have a YubiKey with firmware 4.3 or later and a key generated in PIV slot `9a`. If you have not generated a PIV key yet, refer to [Generate a PIV key](#generate-a-piv-key).

#### Generate a PIV key

If you do not already have a PIV key on your YubiKey, generate one in slot `9a`:

```bash
ykman piv keys generate \
  --algorithm ECCP256 \
  --pin-policy once \
  --touch-policy always \
  9a pubkey.pem
```

Touch your YubiKey when it blinks to confirm key generation. Then create a self-signed certificate to make the key visible to SSH agents:

```bash
ykman piv certificates generate --subject "CN=SSH-Identity" 9a pubkey.pem
```

After generating the key, [generate attestation certificates](#generate-attestation-certificates) and continue with enrollment.

#### Generate attestation certificates

Attestation certificates prove that the key was generated on genuine hardware. Run the following commands to export them from your YubiKey:

```bash
ykman piv keys attest 9a leaf.pem
ykman piv certificates export f9 intermediate.pem
```

- `leaf.pem` contains the public key and metadata for the key in slot `9a`.
- `intermediate.pem` is the YubiKey attestation CA certificate.

#### Upload certificates to Cloudflare

1. Go to your organization's App Launcher at `<your-team-name>.cloudflareaccess.com`.
2. Log in with an identity provider (IdP), a one-time pin, or with Cloudflare as your IdP.
3. Go to **Account** > **MFA devices** > **Add an MFA device**.
4. Select **PIV key**.
5. Paste the contents of `leaf.pem` into the **Leaf certificate** field.
6. Paste the contents of `intermediate.pem` into the **Intermediate certificate** field.
7. Select **Enroll**.

Access extracts and stores the SSH public key from your certificate for future authentication to infrastructure apps. You can enroll multiple PIV keys for backup purposes.

#### Configure your SSH client

After enrollment, configure your SSH client to use the PIV key. The following example uses `yubikey-agent` on macOS. For Linux, refer to the [yubikey-agent documentation](https://github.com/nicholasgasior/yubikey-agent).

1. Install and start `yubikey-agent`:

   ```bash
   brew install yubikey-agent
   brew services start yubikey-agent
   ```

2. Extract the SSH public key from your leaf certificate:

   ```bash
   openssl x509 -in leaf.pem -pubkey -noout | ssh-keygen -i -m PKCS8 -f /dev/stdin > ~/.ssh/id_yubikey.pub
   ```

3. Add the following to your `~/.ssh/config`:

   ```txt
   Host *
     IdentityAgent /opt/homebrew/var/run/yubikey-agent.sock
     IdentitiesOnly yes
     AddKeysToAgent yes
     IdentityFile ~/.ssh/id_yubikey.pub
   ```

4. Verify that the key is loaded:

   ```sh
   ssh-add -L
   ```

   The output should show an `ecdsa-sha2-nistp256` key.

### Delete an authenticator

Users can delete their own authenticators from the App Launcher. If the user has at least one authenticator enrolled, Access requires them to [verify with an existing MFA method](#mfa-verification-for-authenticator-changes) before they can remove a device.

1. Go to your organization's App Launcher at `<your-team-name>.cloudflareaccess.com`.
2. Go to **Account** > **MFA devices**.
3. Select the 3-dot menu next to the MFA device, then select **Remove MFA device**.
4. If you have other MFA devices enrolled, complete the MFA verification prompt.

Administrators can also [delete authenticators on behalf of users](/cloudflare-one/access-controls/access-settings/independent-mfa/#delete-a-user-authenticator).

### MFA verification for authenticator changes

When a user has at least one authenticator enrolled, Access requires MFA verification before the user can add or remove an authenticator from the App Launcher. This verification step is separate from the IdP login and uses the user's existing independent MFA device.

After the user completes MFA verification, they have 10 minutes to add or remove authenticators without additional prompts. This window is tied to the current device. After 10 minutes, or if the user switches to a different device, Access requires MFA verification again.

This prevents an attacker with compromised IdP credentials from modifying a user's enrolled authenticators. Even if an attacker gains access to the user's IdP session, they cannot bypass the independent MFA verification step without also possessing the user's enrolled authenticator. If a user loses their only authenticator and cannot verify, an administrator can delete it to allow re-enrollment. Refer to [Manage user authenticators](#manage-user-authenticators).

:::note
MFA verification is not required when a user enrolls their first authenticator, since they do not yet have an MFA device to verify with.
:::

## Manage user authenticators

Administrators can view and delete authenticators enrolled by users. This is useful for resolving lockouts or responding to security events.

### View user authenticators

To view a user's enrolled authenticators:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Users**.
2. Select a user.
3. Go to **MFA devices**. Each entry shows the authenticator's ID, its user-configured name, and the MFA method.

### Delete a user authenticator

If a user is locked out or you need to revoke an authenticator for security reasons, you can delete it from the dashboard or API.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Users**.
2. Select the user whose authenticator you want to delete.
3. Under **MFA devices**, find the authenticator and select **Delete**.

The user will need to enroll a new authenticator the next time they access an application that requires MFA.

</TabItem>
<TabItem label="API">

Send a `DELETE` request to remove a specific authenticator:

<APIRequest
	path="/accounts/{account_id}/access/users/{user_id}/mfa_authenticators/{authenticator_id}"
	method="DELETE"
/>

Parameters:

- `user_id` — The UUID of the user. You can find this in the user details under **Team & Resources** > **Users**.
- `authenticator_id` — The unique identifier for the authenticator.

</TabItem>
</Tabs>

### Lockout recovery

If a user loses access to all of their enrolled authenticators:

1. [Delete](#delete-a-user-authenticator) the user's authenticators.
2. The user can then access a protected application and will be provided a link to enroll a new authenticator.
3. Alternatively, share the direct enrollment link with the user: `<your-team-name>.cloudflareaccess.com/AddMfaDevice`.

:::tip
To prevent lockouts, users should enroll multiple authenticators (for example, a security key and an authenticator application) when available.
:::

## Related links

- [Enforce MFA on applications and policies](/cloudflare-one/access-controls/policies/mfa-requirements/)

# Access settings

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Require Access protection

Cloudflare Access allows you to require Access protection for all hostnames in your account. When this setting is turned on, traffic to any hostname without a matching [Access application](/cloudflare-one/access-controls/applications/) is automatically blocked.

This deny-by-default approach prevents accidental exposure of internal resources to the public Internet. Without this setting, a developer could deploy a new application or create a DNS record and inadvertently expose the resource before configuring an Access application.

## Turn on Access protection

:::caution
Turning on Access protection blocks traffic to any hostname that does not have an Access application. Before turning on this setting, verify that all publicly accessible hostnames have an [Access application with an Allow or Bypass policy](#allow-traffic-to-a-hostname).
:::

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com) and go to **Zero Trust** > **Access controls** > **Access settings**.
2. Turn on **Block traffic to all domains in this account**. You will see a dialog confirming you understand the scope of this change. Select **Confirm**.

	Traffic to all hostnames in the account is now blocked unless an Access application exists for the hostname.

3. (Optional) Under **Hostnames to Exempt**, select specific domains to exempt from the **Block traffic to all domains in this account** setting. Traffic to exempted hostnames is allowed even if no Access application exists.

	:::note
	Cloudflare recommends limiting exemptions to hostnames that host only public-facing content. Internal applications should have an Access application configured.
	:::

## Allow traffic to a hostname

To allow traffic to a hostname when **Block traffic to all domains in this account** is turned on:

1. [Create an Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for the hostname.
2. Add an [Allow policy](/cloudflare-one/access-controls/policies/#allow) to grant access to authorized users.
3. (Optional) Add a [Bypass policy](/cloudflare-one/access-controls/policies/#bypass) if the hostname should be publicly accessible without authentication.

## Blocked request behavior

When a user attempts to access a hostname without an Access application, Cloudflare displays a block page with `Error 1050: This resource is blocked by this account's Default-Deny policy.` The user cannot proceed until an administrator creates an Access application for that hostname.

# Session management

import { GlossaryTooltip, Render } from "~/components";

A user session determines how long a user can access an Access application without re-authenticating.

## Session durations

When a user logs in to an application protected by Access, Access validates their identity against your Access policies and generates two signed JSON Web Tokens (JWTs):

| Token                                                                                                               | Description                                                                                                          | Expiration                                                                                                                               | Storage                                                                           |
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Global session token                                                                                                | Stores the user's identity from the IdP and provides single sign-on (SSO) functionality for all Access applications. | [Global session duration](#global-session-duration)                                                                                      | Your Cloudflare <GlossaryTooltip term="team domain">team domain</GlossaryTooltip> |
| [Application token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/) | Allows the user to access a specific Access application.                                                             | [Policy session duration](#policy-session-duration), which defaults to the [application session duration](#application-session-duration) | The hostname protected by the Access application                                  |

The user can access the application for the entire duration of the application token's lifecycle. When the application token expires, Cloudflare will automatically issue a new application token if the global token is still valid (and the user's identity still passes your Access policies). If the global token has also expired, the user will be prompted to re-authenticate with the IdP.

The global token expiration is usually set to equal or exceed the application token expiration. Setting a longer global token provides a more secure way to allow for longer user sessions, since the global token cannot be used to directly access an application.

In summary, Access checks sessions from most specific to least specific:

1. **[Client session](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/)** (if enabled) — Overrides all other durations. The user re-authenticates when this expires.
2. **[Policy session](#policy-session-duration)** — Controls access to a specific application for users matching a specific policy.
3. **[Application session](#application-session-duration)** — The default policy session duration for all policies in the application.
4. **[Global session](#global-session-duration)** — Controls how often the user must log in to the IdP across all applications.

Refer to the [Order of enforcement](#order-of-enforcement) flowchart for a visual representation.

<Render file="access/one-time-pin-warning" product="cloudflare-one" />

### Global session duration

The global session duration determines how often Cloudflare Access prompts the user to log in to their identity provider. You can set a global session duration between 15 minutes and one month. The default value is 24 hours.

To set the global session duration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Set your global session duration**, select **Edit**,
3. Select the desired timeout duration from the dropdown menu.
4. Select **Save**.

The user will be required to re-authenticate with the IdP after this period of time.

### Policy session duration

The policy session duration determines how long the user can access a self-hosted Access application. When the user's session expires, Access rechecks their stored user identity against the application's Access policies.

By default, the policy session duration is equal to the [application session duration](#application-session-duration). To configure more granular permissions for specific users, you can change the policy session duration to a value ranging from immediate timeout to one month. For example, you may wish to set the application session duration to seven days for engineers, but set a policy session duration to 24 hours for contractors.

To set the policy session duration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.
2. Choose a policy and select **Configure**.
3. Select a **Session Duration** from the dropdown menu.
4. Save the policy.

Users who match this policy will be issued an application token with this expiration time.

### Application session duration

The application session duration is the default [policy session duration](#policy-session-duration) for all policies in an Access application. Available session durations range from immediate timeout to one month. The default value is 24 hours.

To set the application session duration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Choose an application and select **Configure**.
3. Select a **Session Duration** from the dropdown menu.
4. Save the application.

Users who match a policy configured with a _Same as application session timeout_ duration will be issued an application token with this expiration time.

#### SaaS applications

<Render
	file="access/saas-apps/saas-sessions"
	product="cloudflare-one"
	params={{
		session: "Application session durations",
		token: "Access application token",
	}}
/>

#### SSH, RDP, and VNC

<Render file="access/self-hosted-app/ssh-sessions" product="cloudflare-one" />

For [infrastructure applications](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/), connections are limited by a configurable [session duration](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#session-duration), which defaults to 24 hours. When the session duration elapses, any active SSH connections through that application are closed and users must reconnect. When a target matches multiple infrastructure applications, the shortest session duration applies.

### Cloudflare One Client session duration

When [Authenticate with Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/#configure-client-sessions-in-access) is enabled for an Access application, the Cloudflare One Client session duration takes precedence over all other session durations (application, policy, and global). As long as the Cloudflare One Client session is valid and the user is running the Cloudflare One Client, the user will not be prompted to re-authenticate with the IdP — even if the global session has expired.

#### Return 401 responses for non-browser traffic

By default, failed Cloudflare One Client authentication requests return a `302` redirect to the Access login page. API clients, command-line tools, and automation often cannot complete this browser login flow. You can return a `401 Unauthorized` response for non-browser traffic instead so that these clients can detect the authentication failure directly.

To enable this behavior:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Under **Cloudflare One Client authentication**, ensure that **Enable authentication using the Cloudflare One Client session** is turned on.
3. Turn on **Return 401 response for non-browser traffic**.

You can also set `warp_auth_non_browser_401` to `true` using the [Update your Zero Trust organization](/api/resources/zero_trust/subresources/organizations/methods/update/) API.

This account setting only applies to failed Cloudflare One Client authentication. It is separate from `service_auth_401_redirect`, which controls Service Auth behavior for an individual Access application.

### MFA session duration

If you use [independent multi-factor authentication (MFA)](/cloudflare-one/access-controls/access-settings/independent-mfa/), the MFA session duration determines how long a user can log in to Cloudflare Access without being prompted for MFA. The MFA session is independent of the global, policy, and application session durations. When logging in to an Access app with [MFA enabled](/cloudflare-one/access-controls/policies/mfa-requirements/#configure-independent-mfa-for-an-application), users must complete an MFA challenge if their last MFA authentication falls outside the configured session duration. After authenticating with their identity provider, users are prompted for MFA. The [`CF_Device` cookie](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#cf_device) ensures both authentication steps occur on the same device. MFA session durations do not affect how long a user has access to the application (that is controlled by the [application token](#session-durations)).

### Order of enforcement

The following flowchart illustrates how Access enforces user sessions for a self-hosted application.

| Flowchart setting                       | Dashboard location                                                                                                                                |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authenticate with Cloudflare One Client | **Access controls** > **Applications** > select an application > **Configure** > **Authentication** > **Authenticate with Cloudflare One Client** |
| Cloudflare One Client session duration  | **Access controls** > **Access settings** > **Cloudflare One Client authentication** > **Session duration**                                       |
| Policy session duration                 | **Access controls** > **Policies** > select a policy > **Configure** > **Session duration**                                                       |
| Application session duration            | **Access controls** > **Applications** > select an application > **Configure** > **Overview** > **Session Duration**                              |
| Global session duration                 | **Access controls** > **Access settings** > **Set your global session duration**                                                                  |

```mermaid
flowchart TB
    %% Accessibility
    accTitle: Access session durations
    accDescr: Flowchart describing the order of enforcement for Access sessions

    %% In with user traffic
    start["User goes to Access application"]
    start--"Enabled for this application" -->warpsession[Cloudflare One Client session expired?]
    start-- "Disabled for this application" --> policysession[Policy session expired?]

		warpsession--"Yes"-->idp[Prompt to log in to IdP]
		warpsession--"No"-->accessgranted[Access granted]

		policysession--"Yes"-->globalsession[Global session expired?]
		policysession--"No"-->accessgranted

		globalsession--"Yes"-->idp
		globalsession--"No"-->refreshtoken[Check identity against Access policies]
		refreshtoken-->accessgranted
		idp-->refreshtoken


```

## Revoke user sessions

Access provides two options for revoking user sessions: per-application and per-user.

### Per-Application

To immediately terminate all active sessions for a specific application:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate the application for which you would like to revoke active sessions and select **Configure**.
3. Select **Revoke existing tokens**.

Unless there are changes to rules in the policy, users can start a new session if their profile in your identity provider is still active.

### Per-User

Access can immediately revoke a single user session across all applications in your account. However, if the user's identity profile is still active, they can generate a new session.

If you want to permanently revoke a user's access:

1. Disable their account in your identity provider so that they cannot authenticate.
2. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Users**.
3. Select the checkbox next to the user you want to revoke.
4. Select **Action** > **Revoke**.

The user will no longer be able to log in to any application protected by Access. The user will still count towards your seat subscription until you [remove the user](/cloudflare-one/team-and-resources/users/seat-management) from your account.

### Subsequent Logins

When administrators revoke a user's Cloudflare Access token, that user will not be able to log in again for up to 1 minute. If they attempt to do so, Cloudflare Access will display an error.

## Log out as a user

To log out of Access, the end user can visit either of the following URLs:

- `<your-application-domain>/cdn-cgi/access/logout`
- `<your-team-name>.cloudflareaccess.com/cdn-cgi/access/logout`

This action [revokes the user's session](#per-user) across all applications. Access will immediately clear the authorization cookie from the user's browser, and all previously issued tokens will stop being accepted in 20-30 seconds. The only difference between these two URLs is which domain the authorization cookie is deleted from. For example, going to `<your-application-domain>/cdn-cgi/access/logout` will remove the application cookie and make the logout action feel more instantaneous.

You can use these URLs to create custom logout buttons or links directly within your application.

:::note

At this time, end users cannot log themselves out on a per-application basis.
:::

## AJAX

Pages that rely heavily on AJAX or single-page applications can block sub-requests due to an expired Access token without prompting the user to re-authenticate.

You can configure Access to provide a `401` response on sub-requests with an expired session token. We recommend using this response code to either force a page refresh or to display a message to the user that their session has expired.

In order to receive a `401` for an expired session, add the following header to all AJAX requests:

`X-Requested-With: XMLHttpRequest`

# AI controls

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Allow MCP servers to access self-hosted applications

import { Render } from "~/components";

MCP servers often need to call internal applications on behalf of authenticated users. For example, an MCP server that helps employees interact with internal tools needs to forward the user's identity to those downstream services (the internal applications the MCP server connects to) so that each request is authorized with the correct permissions.

The [Linked App Token](/cloudflare-one/access-controls/applications/linked-app-token/) policy selector enables this by allowing an Access policy on one application to accept tokens issued for another. There are two ways to set this up depending on how your MCP server is deployed.

## Self-hosted MCP server (recommended)

If your MCP server is a [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/), Cloudflare Access handles authentication automatically. The MCP server receives the user's JWT from Access in the `Cf-Access-Jwt-Assertion` header and should forward it to downstream applications in the `Cf-Access-Token` header. No OAuth implementation is needed in your MCP server code.

```mermaid
flowchart LR
accTitle: Self-hosted MCP server accessing internal applications
    User --> client["MCP client"]
    client --> mcp["MCP server <br> (self-hosted app)"]
    mcp -- "Cf-Access-Token: &lt;JWT&gt;" --> app1["Internal API <br> (self-hosted app)"]
    mcp -- "Cf-Access-Token: &lt;JWT&gt;" --> app2["Company wiki <br> (self-hosted app)"]
    idp[Identity provider] <--> mcp
```

### Prerequisites

- Add your downstream applications (for example, your `Internal API` and `Company wiki`) as [self-hosted Access applications](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).
- Add your MCP server as a [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).

### 1. Configure downstream applications

On each self-hosted application that the MCP server needs to access (for example, the `Internal API` and `Company wiki` apps), create a Linked App Token policy:

<Render
	file="access/create-linked-app-token-policy"
	product="cloudflare-one"
	params={{
		sourceAppLabel: "the MCP server application",
		downstreamAppLabel: "the downstream application",
		exampleValue: "mcp-server-app",
		policyName: "Allow requests from MCP server",
		exampleName: "mcp-server-app",
		appType: "self_hosted",
	}}
/>

### 2. Configure your MCP server

In your MCP server code, forward the `Cf-Access-Jwt-Assertion` header from incoming requests as the `Cf-Access-Token` header on outgoing requests to the downstream application:

```txt
Cf-Access-Token: <JWT from Cf-Access-Jwt-Assertion>
```

Access will now validate the JWT token against the Linked App Token rule and propagate the user's identity to the downstream application.

## SaaS MCP server (Access for SaaS with OAuth)

If your MCP server is registered as an [Access for SaaS OIDC application](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/) and implements [MCP OAuth](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization), it receives an OAuth `access_token` from Cloudflare Access. The MCP server forwards this token to downstream self-hosted applications in the `Authorization: Bearer` header.

This approach requires your MCP server to implement the OAuth authorization code flow. Use the [self-hosted MCP server approach](#self-hosted-mcp-server-recommended) if you want Cloudflare to handle authentication for you.

```mermaid
flowchart LR
accTitle: SaaS MCP server accessing internal applications
    User --> client["MCP client"]
    client --> mcp["MCP server <br> (Access for SaaS app)"]
    mcp -- "Authorization: Bearer &lt;token&gt;" --> app1["Internal API <br> (self-hosted app)"]
    mcp -- "Authorization: Bearer &lt;token&gt;" --> app2["Company wiki <br> (self-hosted app)"]
    idp[Identity provider] <--> mcp
```

### Prerequisites

- Add your downstream applications (for example, your `Internal API` and `Company wiki`) as [self-hosted Access applications](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).
- Add your MCP server as an [Access for SaaS OIDC application](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/#saas-managed-third-party-mcp-server).

### 1. Configure downstream applications

On each self-hosted application that the MCP server needs to access (for example, the `Internal API` and `Company wiki` apps), create a Linked App Token policy:

<Render
	file="access/create-linked-app-token-policy"
	product="cloudflare-one"
	params={{
		sourceAppLabel: "the MCP server application",
		downstreamAppLabel: "the downstream application",
		exampleValue: "mcp-server-app",
		policyName: "Allow requests from MCP server",
		exampleName: "mcp-server-app",
		appType: "saas",
	}}
/>

### 2. Configure your MCP server

Configure the MCP server to forward the `access_token` in outgoing requests:

```txt
Authorization: Bearer ACCESS_TOKEN
```

## Known limitations

<Render
	file="access/linked-app-token-known-limitations"
	product="cloudflare-one"
/>

# MCP server portals

import { Render, Tabs, TabItem, CURL, Steps } from "~/components";

An MCP server portal centralizes multiple [Model Context Protocol (MCP) servers](https://www.cloudflare.com/learning/ai/what-is-model-context-protocol-mcp/) onto a single HTTP endpoint.

![MCP clients connect through an MCP portal to access internal MCP servers and SaaS MCP servers.](~/assets/images/cloudflare-one/applications/mcp-portal.png)

This guide explains how to add MCP servers to Cloudflare Access, create an MCP portal with customized tools and policies, and connect users to the portal using an MCP client.

## Key features

MCP server portals provide the following capabilities:

- **Streamlined access to multiple MCP servers**: MCP server portals support both unauthenticated MCP servers and MCP servers secured using OAuth (for example, via [Access for SaaS](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/) or a [third-party OAuth provider](/agents/model-context-protocol/protocol/authorization/)). Users log in to the portal URL through Cloudflare Access and are prompted to authenticate separately to each server that requires OAuth.
- **MCP protocol compatibility**: The portal supports stateless MCP `2026-07-28` and earlier 2025 Streamable HTTP clients and servers. The portal selects the supported protocol for each connection without requiring protocol settings.
- **Customized tools per portal**: Admins can tailor an MCP portal to a particular use case by choosing the specific tools and prompt templates that they want to make available to users through the portal. This allows users to access a curated set of tools and prompts — the less external context exposed to the AI model, the better the AI responses tend to be.
- **Tool and prompt aliases**: Admins can [rename tools and prompts](#rename-tools-and-prompts-with-aliases) and edit their descriptions at the portal or server level without modifying the upstream MCP server. Aliases help end users find the right tool and help AI agents select the correct one.
- **Context optimization**: Portals support query parameter options that reduce context window usage by minimizing or hiding tool definitions. Refer to [Optimize context](#optimize-context) for details.
- **Non-browser client support**: MCP clients authenticate to the portal using a standard OAuth 2.0 authorization code flow via [managed OAuth](/cloudflare-one/access-controls/applications/http-apps/managed-oauth/). This managed OAuth configuration applies to the portal's Access application. It is separate from upstream OAuth used by individual MCP servers in the portal. Non-browser clients receive a `401` response with a `WWW-Authenticate` header pointing to Access's OAuth discovery endpoints, rather than a browser redirect. You can also connect using [Access service tokens](#connect-with-a-service-token) for machine-to-machine access.
- **Code Mode**: Code Mode collapses all upstream tools into two tools for search and code execution. The AI agent writes JavaScript that calls typed methods for each tool. The code runs in an isolated [Dynamic Worker](/dynamic-workers/) environment. Admins can control whether Code Mode is unavailable, optional, on by default, or required. Refer to [Code Mode](#code-mode) for configuration and connection instructions.
- **Observability**: Once the user's AI agent is connected to the portal, Cloudflare Access logs the individual requests made using the tools in the portal. You can optionally route portal traffic through [Cloudflare Gateway](#route-portal-traffic-through-gateway) for richer HTTP logging and data loss prevention (DLP) scanning.

## How it works

The following diagram shows how requests flow through an MCP server portal.

![Request flow diagram showing how an MCP client connects through Cloudflare Access and the MCP server portal to reach upstream MCP servers, with an optional Gateway path for DLP inspection.](~/assets/images/cloudflare-one/applications/mcp-portal-request-flow.svg)

1. An MCP client connects to the portal URL and receives a `401` response with OAuth discovery metadata.
2. The user authenticates through Cloudflare Access via their identity provider or uses [service token](#connect-with-a-service-token) headers.
3. Access validates the user's identity, and the portal returns the tools available from enabled upstream servers.
4. When the user calls a tool, the portal identifies the target server from the [tool namespace](#tool-namespacing), attaches the appropriate credentials, and proxies the request. If [Gateway routing](#route-portal-traffic-through-gateway) is turned on, the request passes through Cloudflare Gateway for HTTP logging and DLP inspection.
5. The upstream server processes the request and returns a response through the same path.

For servers that use Dynamic Client Registration (DCR), background synchronization of tools and prompts runs approximately every two hours using admin credentials. When server-level Gateway routing is turned on, DCR and background synchronization route through Gateway.

### Transport

The portal accepts stateless [MCP `2026-07-28`](https://modelcontextprotocol.io/specification/2026-07-28) and earlier 2025 Streamable HTTP clients at its `/mcp` endpoint. The portal selects the protocol from each request. You do not need to configure a protocol version.

The portal connects to upstream MCP servers using [Streamable HTTP](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http) or [SSE](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#server-sent-events-sse-deprecated) transport. For Streamable HTTP servers, the portal checks for MCP `2026-07-28` support and uses the stateless protocol when available. If the upstream server does not support it, the portal falls back to the 2025 handshake on the same connection. SSE connections always use the legacy protocol.

Client and upstream protocol selection are independent. For example, a 2025 client can connect through a portal to a stateless upstream server. A stateless client can also connect to a legacy upstream server.

You do not need to specify which transport your upstream server uses. The portal automatically detects the correct transport by trying multiple connection strategies in order:

| Upstream URL pattern | Connection strategies (in order)                                                                                        |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Ends in `/mcp`       | Streamable HTTP only                                                                                                    |
| Ends in `/sse`       | SSE (or Streamable HTTP if Gateway routing is turned on)                                                                |
| All other URLs       | Streamable HTTP on original URL, then SSE on original URL, then Streamable HTTP on `{url}/mcp`, then SSE on `{url}/sse` |

If a connection attempt returns a `404`, `405`, or `406` error, the portal falls back to the next strategy. All other errors stop the connection attempt.

### Client identity header

When a portal sends a request to an upstream MCP server, it adds an `X-Forwarded-User-Agent` header that identifies the downstream MCP client.

The portal selects the header value in the following order:

| Source             | Behavior                                                                                                                                   |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `clientInfo.name`  | Preferred when the MCP client sends a valid value. The portal removes the ` (via mcp-remote <VERSION>)` suffix before forwarding the name. |
| HTTP `User-Agent`  | Used when valid MCP client information is unavailable.                                                                                     |
| Neither is present | The portal omits `X-Forwarded-User-Agent` and continues the request.                                                                       |

An upstream server can record this header in logs or use it for diagnostics. Do not use it for authentication or authorization because the MCP client controls the value.

### Built-in portal tools

Every portal exposes the following built-in tools to MCP clients, in addition to the upstream server tools:

| Tool                          | Description                                                                                         |
| ----------------------------- | --------------------------------------------------------------------------------------------------- |
| `portal_list_servers`         | Lists all available upstream servers with their ID, name, and whether they are currently turned on. |
| `portal_toggle_servers`       | Opens a URL-based server selection page where you can turn servers on or off.                       |
| `portal_toggle_single_server` | Turns a single server on or off by server ID, without leaving the MCP client.                       |

When [context optimization](#optimize-context) is turned on, additional tools are exposed depending on the mode:

| Mode                 | Additional tools                                                                     |
| -------------------- | ------------------------------------------------------------------------------------ |
| `minimize_tools`     | `portal_query_tools` — Search tools by regex pattern and return full definitions.    |
| `search_and_execute` | `portal_query_tools` and `portal_execute` — Search tools and execute them via proxy. |

### Session lifecycle

MCP `2026-07-28` requests are stateless and do not create an MCP protocol session. The portal retains authentication, server selection, and upstream OAuth state for the user's portal authorization grant.

Earlier 2025 clients create a session that persists until the user disconnects or the session expires after 24 hours of inactivity.

Users can turn individual servers on or off without disconnecting. For stateless requests, server toggles apply to every request that uses the same portal authorization grant. For legacy clients, toggles are scoped to the MCP session. Toggles do not affect other users.

### Naming

MCP server portals were previously referred to as **Agents Gateway** in some contexts. The API paths, Terraform resources, and internal codebases may still use `agents_gateway` or `agw` prefixes. The product name is **MCP server portals** and the dashboard navigation is **MCP Portals**.

## Prerequisites

- An [active domain on Cloudflare](/fundamentals/manage-domains/add-site/) for the portal URL that uses either a [full setup](/dns/zone-setups/full-setup/) or a [partial (`CNAME`) setup](/dns/zone-setups/partial-setup/)
- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured on Cloudflare Zero Trust

## Add an MCP server

Add individual MCP servers to Cloudflare Access to bring them under centralized management.

To add an MCP server:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Go to the **MCP servers** tab.
3. Select **Add MCP server**.
4. Enter any name for the server.
5. (Optional) Enter a custom string for the **Server ID**.
6. In **HTTP URL**, enter the full URL of your MCP server. For example, if you want to add the [Cloudflare Documentation MCP server](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/docs-ai-search), enter `https://docs.mcp.cloudflare.com/mcp`.
7. Add [Access policies](/cloudflare-one/access-controls/policies/) to show or hide the server in an [MCP server portal](#create-a-portal). The MCP server link will only appear in the portal for users who match an Allow policy. Users who do not pass an Allow policy will not see this server through any portals.

   :::caution
   Blocked users can still connect to the server (and bypass your Access policies) by using its direct URL. If you want to enforce authentication through Cloudflare Access, [configure Access as the server's OAuth provider](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/).
   :::

8. Under **OAuth credentials**, select **Automatic (recommended)** to use DCR. Add the [dashboard callback URL](#dashboard-callback-url) to the OAuth provider's redirect URI allowlist.
9. Select **Save and connect server**.
10. If the MCP server supports OAuth, you will be redirected to log in to your OAuth provider. You can log in to any account on the MCP server. The account used to authenticate will serve as the admin credential for that MCP server. You can [configure an MCP portal](#create-a-portal) to use this admin credential to make requests.

Cloudflare Access will validate the server connection and retrieve a list of prompts and tools. Once the server is successfully connected, the [server status](#server-status) will change to **Ready**. You can now add the MCP server to an [MCP server portal](#create-a-portal).

### Connect a private MCP server

MCP server portals can connect to an MCP server available only on your private network. The MCP server URL, [OAuth protected resource metadata](https://www.rfc-editor.org/rfc/rfc9728.html), and DCR endpoint can use a private hostname when server-level Gateway routing is turned on. OAuth authorization and token endpoints must be accessible on the public Internet.

Before you add the server, connect its network to Cloudflare. Use [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) or another [Cloudflare One connector](/cloudflare-one/networks/connectors/). Configure a [private hostname route](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/) or [CIDR route](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/) for the server.

To add the private MCP server:

<Steps>

1. Follow the steps in [Add an MCP server](#add-an-mcp-server), and enter the private URL in **HTTP URL**.
2. Turn on **Route traffic through Cloudflare Gateway**. Private server registration and capability synchronization require this setting.
3. Select **Save and connect server**.
4. Complete the upstream OAuth flow, if required.
5. Wait for the server status to change to **Ready**. You can then add the server to a portal.

</Steps>

### Configure manual OAuth credentials

Use manual OAuth credentials when the upstream provider does not support [OAuth Dynamic Client Registration](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#dynamic-client-registration). This flow uses an OAuth application that you register with the upstream provider.

<Steps>

1. Add the MCP server with **OAuth** as its authentication method.
2. In **Zero Trust** > **Access controls** > **MCP Portals**, go to the **MCP servers** tab.
3. Find the server, select the three dots > **Edit**, and go to **Authentication**.
4. Under **OAuth credentials**, select **Manual credentials**.
5. Copy the displayed **Redirect URI to register at the upstream provider**. Add it to the OAuth application's allowed redirect URIs. For more information, refer to [Shared Cloudflare callback URL](#shared-cloudflare-callback-url).
6. Select **Discover OAuth endpoints**. If discovery fails, expand **Show OAuth endpoints (advanced)** and enter the **Authorization endpoint** and **Token endpoint**. You can also enter an optional **Revocation endpoint** and **Issuer**.
7. Enter the OAuth application's **Client ID** and **Client secret**.
8. (Optional) Enter the space-separated **Scope** values requested from users.
9. (Optional) Enter the **Token endpoint auth method** expected by the provider. Supported values are `client_secret_post` and `client_secret_basic`.
10. Select **Save server**.

</Steps>

Cloudflare stores the client secret encrypted and does not return it through the dashboard or API. When editing the server, leave **Client secret** blank to keep the existing value. To rotate the secret, create or activate the replacement at the upstream provider, enter the new value, and save the server.

Manual credentials require per-user authentication. Leave **Require user auth** enabled when you add the server to a portal. The server remains in **Waiting** status until the first user completes upstream OAuth. Cloudflare then retrieves the server's tools and prompts and changes its status to **Ready**.

### MCP Apps

[MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview) — tools that declare a UI resource in their description — will also be available after successfully connecting to an MCP server. A list of MCP clients that support MCP Apps is available in the [Extension Support Matrix](https://modelcontextprotocol.io/extensions/client-matrix).

### Server status

The MCP server status indicates the server's connection and tool and prompt synchronization status in Cloudflare Access.

| Status        | Description                                                                                                                                                                                         |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Error         | The server could not be reached or returned an error. Refer to [error details](#error-details) to identify and fix the cause.                                              |
| Sync Required | The server's OAuth credentials can no longer be refreshed and the server needs to be reauthenticated. To fix the issue, [reauthenticate the server](#reauthenticate-the-mcp-server).                |
| Waiting       | The server's tools and prompts are being synchronized. A server with manual OAuth credentials remains in this state until its first user completes upstream OAuth.                      |
| Ready         | The server connected successfully and its tools and prompts were synchronized. This status does not guarantee that the server will connect or return resources when queried.              |

#### Error details

When an MCP server is in the **Error** or **Sync Required** state, hover over its status in the dashboard to view available diagnostic information. The API returns these details in the `error_details` object:

| Field         | Description                                                                                                            |
| ------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `status_code` | The HTTP status code returned by the MCP server, if available.                                                         |
| `mcp_code`    | The MCP protocol error code, if the server returned an MCP error.                                                      |
| `retryable`   | Whether the error is likely to be temporary and the connection is worth retrying.                                     |
| `is_upstream` | `true` if the MCP server returned the error; `false` if the connection to the MCP server failed.                       |
| `cause`       | The underlying error message.                                                                                          |

If `is_upstream` is `true`, use `status_code`, `mcp_code`, and `cause` to troubleshoot the MCP server. If it is `false`, verify that the server URL is correct and reachable. Retry the sync when `retryable` is `true`; otherwise, correct the reported cause before trying again. Reauthenticate the server only when the error points to expired or invalid credentials.

### Reauthenticate the MCP server

To reauthenticate an MCP server in Cloudflare Access:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Go to the **MCP servers** tab.
3. Select the server that you want to reauthenticate, then select **Edit**.
4. Select **Authenticate server**.

You will be redirected to log in to your OAuth provider. The account used to authenticate will serve as the new admin credential for this MCP server.

### Synchronize the MCP server

For servers that use DCR, Cloudflare Access synchronizes tools and prompts approximately every two hours. During synchronization, Cloudflare connects to your MCP server using the [admin credential](#reauthenticate-the-mcp-server) and fetches the current list of tools and prompts. If the admin credential's OAuth access token has expired, Cloudflare refreshes it automatically using the stored refresh token before connecting.

Resources are not synchronized or stored. When an MCP client sends a `resources/list` request, the portal fetches resources live from the upstream servers. Servers that cannot connect or respond are omitted from the result.

:::note
Synchronization uses the admin credential, not individual user credentials. If the admin credential's refresh token has expired or been revoked, the [server status](#server-status) will change to **Sync Required** and you will need to [reauthenticate the server](#reauthenticate-the-mcp-server). This will not impact end users' ability to connect to the MCP server. It will impact the ability to fetch tool and prompt information or additions and removals.
:::

To manually refresh the MCP server in Zero Trust:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Go to the **MCP servers** tab and find the server that you want to refresh.
3. Select the three dots > **Sync capabilities**.

The MCP server page will show the updated list of tools and prompts. By default, new tools and prompts are automatically enabled. For mappings with `default_disabled` set to `true`, they remain hidden until explicitly enabled.

You can also trigger a sync via the API. The sync endpoint returns the current server state after synchronization, including the updated [server status](#server-status), tool count, and [error details](#error-details) if the sync failed.

### Upstream OAuth callback URLs

MCP server portals use upstream OAuth callback URLs to authorize administrators and end users so they can access MCP servers.

OAuth providers typically exact-match the full URI, including the path.

If your upstream OAuth provider restricts redirect URIs, add the URLs that match your server's configuration:

| OAuth configuration | **Use the Cloudflare-hosted OAuth callback** on? | Redirect URIs to allowlist at the upstream provider |
| --- | --- | --- |
| Automatic (DCR) | No | The [dashboard callback URL](#dashboard-callback-url) for administrator authorization. If users authorize with their own credentials, also add `https://<your-portal-hostname>/servers-callback` for each portal using this server. |
| Automatic (DCR) | Yes | The [dashboard callback URL](#dashboard-callback-url) for administrator authorization. If users authorize with their own credentials, also add `https://oauth-callbacks.cloudflareaccess.com/cdn-cgi/access/outbound-oauth-callback`. |
| Manual OAuth credentials | Not available | `https://oauth-callbacks.cloudflareaccess.com/cdn-cgi/access/outbound-oauth-callback`. The dashboard DCR callback URL is not used for this configuration. |

For automatic (DCR) servers, expand **Advanced settings** to access the **Use the Cloudflare-hosted OAuth callback** setting.

For manual OAuth configured through the API, register the [shared Cloudflare callback URL](#shared-cloudflare-callback-url) with the upstream provider. Set `is_shared_oauth_callback_enabled` to `true`. Cloudflare uses the shared callback URL regardless of the value of `registration_info.redirect_uris`, so you can omit this field. You cannot override the shared callback URL.

#### Dashboard callback URL

When Cloudflare registers the OAuth client using DCR, the dashboard uses the following callback URL:

```txt
https://dash.cloudflare.com/<account-tag>/one/access-controls/ai-controls/mcp-server/oauth-callback/<server-id>
```

The dashboard callback URL is scoped to the account and server.

Add the dashboard callback URL to the OAuth provider's redirect URI allowlist before authorizing the upstream MCP server. Otherwise, authorization may fail.

DCR uses the dashboard callback URL even when the **Use the Cloudflare-hosted OAuth callback** setting is on.

End users never use the dashboard callback URL to authorize the upstream MCP server.

#### Portal callback URL

When a user authorizes an upstream MCP server that requires per-user OAuth, the portal performs an OAuth authorization code flow with the upstream server on the user's behalf. As part of this flow, the portal registers a callback URL (`redirect_uri`) with the upstream server. The upstream server redirects to this URL after the user authorizes access.

When the **Use the Cloudflare-hosted OAuth callback** setting is off, the portal uses a callback URL on your portal domain:

```txt
https://<your-portal-hostname>/servers-callback
```

Add the callback URL to the OAuth provider's redirect URI allowlist.

Use `/servers-callback`, not `/server-callback`, for automatic (DCR) servers when the shared callback setting is off. Manual OAuth servers use the [shared Cloudflare callback URL](#shared-cloudflare-callback-url) instead.

#### Shared Cloudflare callback URL

When you configure an MCP server with manual OAuth credentials in the dashboard, Cloudflare uses the following shared callback URL for dashboard and per-user authorization:

```txt
https://oauth-callbacks.cloudflareaccess.com/cdn-cgi/access/outbound-oauth-callback
```

The **Use the Cloudflare-hosted OAuth callback** setting is not available when you configure manual OAuth credentials in the dashboard.

When Cloudflare uses DCR to register the OAuth client, turn on **Use the Cloudflare-hosted OAuth callback** to use the shared callback URL for per-user authorization. If the setting is off, per-user authorization uses the [portal-scoped callback URL](#portal-callback-url).

Use the shared callback URL if the upstream OAuth provider limits the number of allowed redirect URIs or if you want one callback URL for a server across multiple portals. DCR still uses the dashboard callback URL for administrator authorization, regardless of whether the **Use the Cloudflare-hosted OAuth callback** setting is on. The setting is off by default. Configure it separately for each MCP server that uses DCR.

To turn on the setting, go to **Zero Trust** > **Access controls** > **MCP Portals** > **MCP servers**, add or edit an MCP server that uses DCR, and turn on **Use the Cloudflare-hosted OAuth callback** under **Basic information** > **Advanced settings**.

## Create a portal

To create an MCP server portal:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Select **Add MCP server portal**.
3. Enter any name for the portal.
4. Under **Custom domain**, select a domain from an active zone in your account. You can optionally specify a subdomain.
5. [Add MCP servers](#add-an-mcp-server) to the portal.
6. (Optional) Under **MCP servers**, [configure the tools and prompts](#manage-tools-and-prompts) available through the portal.
7. (Optional) Configure **Require user auth** for servers that support OAuth: - `Enabled`: (default) User will be prompted to utilize their own login credentials to establish a connection with the MCP server. - `Disabled`: Users who are connected to the portal will automatically have access to the MCP server via its [admin credential](#reauthenticate-the-mcp-server).
8. Add [Access policies](/cloudflare-one/access-controls/policies/) to define the users who can connect to the portal URL.

   :::caution
   [Independent MFA](/cloudflare-one/access-controls/policies/mfa-requirements/#independent-mfa), [purpose justification](/cloudflare-one/access-controls/policies/require-purpose-justification/), and [temporary authentication](/cloudflare-one/access-controls/policies/temporary-auth/) will not be enforced for MCP servers that are authorized through an MCP portal. For example, if independent MFA is enabled on a policy assigned to a server, users will not be prompted to perform MFA to authorize the server after authenticating to the portal. Refer to [Policy limitations](#policy-limitations) for details.
   :::

9. Select **Add an MCP server portal**.
10. (Optional) [Customize the login experience](#customize-login-settings) for the portal.

Users can now [connect to the portal](#connect-to-a-portal) at `https://<subdomain>.<domain>/mcp` using an MCP client.

### Customize login settings

Cloudflare Access automatically creates an Access application for each MCP server portal. You can customize the portal login experience by updating Access application settings:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Find the portal that you want to configure, then select the three dots > **Edit**.
3. To configure identity providers for the portal:
   1. Go to **Authentication**.
   2. Select the [identity providers](/cloudflare-one/integrations/identity-providers/) that you want to enable for your application.
   3. (Recommended) If you plan to only allow access via a single identity provider, turn on **Apply instant authentication**. End users will not be shown the [Cloudflare Access login page](/cloudflare-one/reusable-components/custom-pages/access-login-page/). Instead, Cloudflare will redirect users directly to your SSO login event.
4. To customize the block page:
   1. Go to **Additional settings**.
   2. <Render file="access/access-block-page" product="cloudflare-one" />
5. Select **Save**.

## Manage tools and prompts

When you add an MCP server to a portal, all of its tools and prompts are available to portal users by default. You can customize which tools and prompts are exposed, rename them with aliases, and override their descriptions.

### Turn off individual tools or prompts

To hide specific tools or prompts from portal users:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Under **Servers**, select the server name to open its tools panel.
4. Scroll down to the **Tools** section, then turn off the toggle next to any tool or prompt that you want to hide from users.

   ![The tools panel for an MCP server shows a list of tools with a toggle next to each one.](~/assets/images/cloudflare-one/applications/mcp-portal-manage-tools.png)

5. Select **Save**.

Turned-off tools will not appear in the portal's tool list. Users will not be able to call them.

### Use an allowlist pattern

By default, all tools and prompts from an MCP server are available in the portal. You can invert this behavior so that all capabilities are hidden by default and only explicitly turned-on tools and prompts are exposed. This is useful when an MCP server has many capabilities but you only want to expose a curated subset.

To configure an allowlist via the API, set `default_disabled` to `true` on the server-to-portal mapping, then explicitly list the tools you want to expose in `updated_tools`. To expose prompts, list them in `updated_prompts` in the same way.

When a portal update includes `servers`, the supplied array replaces the complete server-to-portal mapping. Include every attached server and preserve its existing mapping fields. Omitting a server removes it from the portal, and omitting a mapping field resets that field to its default value.

```json title="API request body (portal update)"
{
	"servers": [
		{
			"server_id": "example-server",
			"default_disabled": true,
			"updated_tools": [
				{
					"name": "search_documents",
					"enabled": true
				},
				{
					"name": "list_projects",
					"enabled": true
				}
			]
		}
	]
}
```

With `default_disabled` set to `true`, only `search_documents` and `list_projects` will be available to portal users. All other tools and all prompts from this server will be hidden.

### Rename tools and prompts with aliases

Aliases let you give tools and prompts clearer names in the portal. Use aliases to:

- Replace unclear tool names with names that match your organization's terminology.
- Add or improve descriptions so AI agents select the correct tool.
- Standardize naming across multiple MCP servers in a portal.

Alias names must be 1-40 characters and can only contain letters, numbers, hyphens, and underscores. Names must start and end with an alphanumeric character. The value must match `^[a-zA-Z0-9]+([_-][a-zA-Z0-9]+)*$`. For example, `search_customer_records` or `get-user-profile`. No two tools or prompts on the same server can share the same name, whether that name is an alias or the original upstream name.

#### Alias precedence

You can set aliases at two levels. Portal-level aliases take precedence over server-level aliases.

| Level            | Field          | Scope                                                         |
| ---------------- | -------------- | ------------------------------------------------------------- |
| **Server-level** | `alias`        | Applies across all portals that include this server           |
| **Portal-level** | `portal_alias` | Applies only within a specific portal; overrides server-level |

When multiple names exist, the portal resolves them in this order: `portal_alias` > `server_alias` > `alias` > original tool name.

If no alias is set, the portal uses the original name and description from the upstream server.

Custom descriptions follow the same precedence. Set a description by including the `description` field on an entry in `updated_tools` or `updated_prompts`. In API responses, server-level descriptions are returned as `server_description` and portal-level descriptions are returned as `portal_description`. Portal-level descriptions take precedence over server-level descriptions when both are set.

#### Set aliases in the dashboard

<Tabs syncKey="aliasLevel"> <TabItem label="Portal-level alias">

To set an alias that applies to a specific portal:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Go to the **Servers** tab.
4. Select the **Tools authorized** or **Prompts authorized** value for the server you want to configure (for example, `10/10`).
5. Find the tool or prompt you want to modify, then select the three dots > **Edit**.
6. In the modal, update the **Name** and **Description** as needed.
7. Select **Confirm**.

</TabItem> <TabItem label="Server-level alias">

To set an alias that applies across all portals using a server:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Go to the **MCP servers** tab.
3. Find the server you want to configure, then select the three dots > **Edit**.
4. Go to the **Tools** or **Prompts** tab.
5. Find the tool or prompt you want to modify, then select the three dots > **Edit**.
6. In the modal, update the **Name** and **Description** as needed.
7. Select **Confirm**.
8. Scroll to the bottom of the page and select **Save server**.

</TabItem> </Tabs>

Tools and prompts that have been modified display a **Modified** label in the dashboard.

#### Set aliases with the API

Send a `PUT` request to the [update a MCP portal](/api/resources/zero_trust/subresources/access/subresources/ai_controls/subresources/mcp/subresources/portals/methods/update/) endpoint. Include the `alias` field for each tool or prompt you want to rename.

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/portals/{id}"
	method="PUT"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		servers: [
			{
				server_id: "example-server",
				updated_tools: [
					{
						name: "original_tool_name",
						enabled: true,
						description: "A clearer description of what this tool does.",
						alias: "renamed_tool",
					},
				],
				updated_prompts: [
					{
						name: "original_prompt_name",
						enabled: true,
						description: "An updated description for this prompt.",
						alias: "renamed_prompt",
					},
				],
			},
		],
	}}
/>

To set server-level aliases that apply across all portals, send a `PUT` request to the [update a MCP server](/api/resources/zero_trust/subresources/access/subresources/ai_controls/subresources/mcp/subresources/servers/methods/update/) endpoint with the same `updated_tools` and `updated_prompts` fields.

#### Reset an alias

To reset a tool or prompt to its original upstream name, open the edit modal for the tool or prompt in the dashboard and select "Reset to server definition." When using the API, omit the `alias` field from the corresponding entry in `updated_tools` or `updated_prompts`.

#### How aliases affect end users

MCP clients receive the aliased name and description instead of the original. End users do not see the original name.

If you change an alias while a user has an active session, the user must reauthenticate to see the update. Refer to [Manage portal sessions](#manage-portal-sessions) for reauthentication options.

:::caution
If the upstream server renames a tool or prompt, your alias for it will be removed on the next sync. Verify that your aliases still apply after each sync.
:::

### Tool and prompt namespacing

All tools and prompts exposed through a portal are automatically namespaced with the server ID as a prefix. The format is `{server_id}_{original_name}`. For example, a tool named `list_issues` on a server with ID `github` appears as `github_list_issues` in the portal. This prevents name collisions when multiple MCP servers expose tools with the same name.

Prompts follow the same pattern. A prompt named `summarize` on a server with ID `github` appears as `github_summarize`.

#### How the server ID is determined

The server ID used for namespacing comes from the **Server ID** field you set when [adding an MCP server](#add-an-mcp-server). You can enter a custom server ID in step 5 of the setup process, or let Cloudflare generate one automatically.

Choose short, descriptive server IDs when you plan to expose the server through a portal. The server ID becomes part of every tool name that MCP clients and AI agents see.

#### Parsing namespaced names

The portal splits namespaced names on the **first** underscore only. Everything before the first underscore is the server ID, and everything after it is the tool or prompt name. This means tool names can contain underscores without ambiguity.

| Namespaced name              | Server ID | Tool name             |
| ---------------------------- | --------- | --------------------- |
| `github_list_issues`         | `github`  | `list_issues`         |
| `github_create_pull_request` | `github`  | `create_pull_request` |
| `sentry_get_issue_details`   | `sentry`  | `get_issue_details`   |

Because the split happens on the first underscore, server IDs themselves cannot contain underscores. Use hyphens instead when you need a multi-word server ID (for example, `my-server`).

#### Namespacing with aliases

If you [rename a tool with an alias](#rename-tools-and-prompts-with-aliases), the alias replaces the original tool name in the namespaced format. The server ID prefix still applies.

For example, if you alias the tool `list_issues` to `issues` on a server with ID `github`, the namespaced name becomes `github_issues`.

#### Namespacing in Code Mode

When [Code Mode](#code-mode) is active, the portal applies an additional transformation to make namespaced tool names safe for use as JavaScript identifiers. Hyphens and dots in the namespaced name are replaced with underscores, names that start with a digit get a `_` prefix, and JavaScript reserved words get a `_` suffix. For example, a server with ID `my-server` and a tool named `get-data` would appear as `my_server_get_data` in the Code Mode sandbox.

This sanitization happens automatically. You do not need to call any helper functions when using Code Mode as an end user.

#### Helper functions in the Agents SDK

If you are building an MCP client with the [Agents SDK](/agents/model-context-protocol/apis/client-api/), the SDK provides helper functions for working with server IDs and tool names:

- **`normalizeServerId`** (exported from `agents/mcp/client`) normalizes a caller-supplied server ID into a safe string. For example, `"GitHub MCP!"` becomes `"github-mcp"`. The SDK calls this automatically when you pass an `id` option to `addMcpServer()`.
- **`sanitizeToolName`** (exported from `@cloudflare/codemode`) converts a tool name into a valid JavaScript identifier by replacing hyphens and dots with underscores. This is called automatically in Code Mode contexts. Refer to the [Code Mode SDK reference](/agents/tools/codemode/api-reference/#code-and-output-utilities) for details.

:::note
The Agents SDK uses a `tool_{server_id}_{tool_name}` format (with a `tool_` prefix) when returning tools from `getAITools()`. This differs from the portal format, which uses `{server_id}_{tool_name}` without the prefix.
:::

### Portal-native tools

In addition to upstream MCP server tools, the portal exposes its own built-in tools that let AI agents manage server connections and discover tools during a session. These tools use the `portal_` prefix and are not associated with any upstream server.

#### Always available

The following tools are available in every portal session, regardless of the connection mode:

| Tool                          | Description                                                                                                                                                                                                                                                                        |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `portal_list_servers`         | Lists all upstream MCP servers with their IDs, names, and whether they are currently enabled in the session.                                                                                                                                                                       |
| `portal_toggle_servers`       | Opens a server selection flow. Returns a URL that the user visits in a browser to enable or disable servers and manage OAuth credentials.                                                                                                                                          |
| `portal_toggle_single_server` | Toggles a single server on or off without requiring a browser visit. Accepts a `server_id` and an `action` (`toggle` or `untoggle`). If the server requires OAuth and the user has not authenticated yet, the portal falls back to the browser-based `portal_toggle_servers` flow. |

These tools power the [session management](#manage-portal-sessions) features described later in this guide. AI agents call them automatically when you ask to enable a server, disable a server, or return to the server selection page.

#### Context optimization tools

When you connect with the [`optimize_context`](#optimize-context) query parameter, the portal exposes additional tools for discovering and calling upstream tools:

| Tool                 | Available in                           | Description                                                                                                                                                                                                                                   |
| -------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `portal_query_tools` | `minimize_tools`, `search_and_execute` | Searches upstream tools by name, description, or schema using a regex pattern. Returns full tool definitions so the agent can call them. Required in `minimize_tools` mode because upstream tool schemas are stripped to reduce context size. |
| `portal_execute`     | `search_and_execute`                   | Calls an upstream tool by name with the provided arguments. In `search_and_execute` mode, upstream tools are hidden from the tool list entirely, so agents must use `portal_query_tools` to discover them and `portal_execute` to call them.  |

#### Code Mode tools

When you connect with [Code Mode](#code-mode) enabled, the portal replaces all upstream tools with two code execution tools:

| Tool                      | Description                                                                                                                                                                                                      |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `portal_codemode_search`  | Searches available tools by running JavaScript in a sandboxed Worker. The sandbox provides a `codemode.tools()` function that returns all upstream tool definitions with sanitized names.                        |
| `portal_codemode_execute` | Calls upstream tools by running JavaScript in a sandboxed Worker. The sandbox provides a `codemode` proxy object where each property maps to an upstream tool. Supports `Promise.all()` for parallel tool calls. |

Refer to the [Code Mode SDK reference](/agents/tools/codemode/api-reference/) for details on writing code for these tools.

## Manage portals via API

In addition to the dashboard, you can manage MCP server portals programmatically using the Cloudflare API. The following examples show common operations.

:::caution
Unlike the dashboard, the API does not automatically create a DNS record for your portal hostname. After creating a portal via the API, you must create a proxied CNAME record that points your portal subdomain to `gateway.agents.cloudflare.com`. Without this record, the portal will return `522` errors.
:::

### List portals

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/portals"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

### Create a portal

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/portals"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		name: "Engineering Portal",
		hostname: "mcp.example.com",
		code_mode: "opt_in",
		secure_web_gateway: false,
	}}
/>

### List MCP servers

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/servers"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

### Create an MCP server

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/servers"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		name: "GitHub MCP Server",
		hostname: "https://github-mcp.example.workers.dev/mcp",
		auth_type: "oauth",
	}}
/>

The `auth_type` field accepts the following values:

| Value             | Description                                                                                                                                          |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `oauth`           | The server requires OAuth authentication. You can use DCR or provide manual OAuth credentials.                                                   |
| `bearer`          | The server uses a static bearer token or custom authentication headers. Provide the credentials in `auth_credentials` (refer to [Bearer authentication credentials](#bearer-authentication-credentials)). |
| `unauthenticated` | The server does not require authentication.                                                                                                          |

#### Manual OAuth credentials

To create an MCP server with a pre-registered OAuth client, set `auth_type` to `oauth` and provide both `auth_credentials` and `client_secret`. The `auth_credentials` value is required and must be a JSON-encoded string:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/servers"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		id: "github",
		name: "GitHub MCP Server",
		hostname: "https://github-mcp.example.com/mcp",
		auth_type: "oauth",
		auth_credentials:
			'{"auth_mode":"manual","config":{"authorization_endpoint":"https://github.com/login/oauth/authorize","token_endpoint":"https://github.com/login/oauth/access_token"},"registration_info":{"client_id":"<client-id>","token_endpoint_auth_method":"client_secret_basic","scope":"repo read:user"}}',
		client_secret: "<client-secret>",
		is_shared_oauth_callback_enabled: true,
	}}
/>

The decoded `auth_credentials` object must contain:

- `auth_mode`: Must be `manual`.
- `config.authorization_endpoint` and `config.token_endpoint`: The upstream provider's OAuth endpoints. `issuer` and `revocation_endpoint` are optional.
- `registration_info.client_id`: The client ID issued by the upstream provider.
- `registration_info.redirect_uris`: Cloudflare uses only the first URI in the array. When `is_shared_oauth_callback_enabled` is `true`, omit this field. Cloudflare adds the [shared callback URL](#shared-cloudflare-callback-url).
- `registration_info.token_endpoint_auth_method`: Optional. Accepted values are `none`, `client_secret_post`, and `client_secret_basic`.
- `registration_info.scope`: Optional space-delimited scope string.

Before you create the server, add the [shared callback URL](#shared-cloudflare-callback-url) to the upstream provider's allowed redirect URIs. Set `is_shared_oauth_callback_enabled` to `true`. Cloudflare then uses the shared callback for dashboard authorization and for users who connect through any portal that contains the server.

Do not include `client_secret` or OAuth tokens inside `auth_credentials`. Send `client_secret` as the separate sibling field shown above.

To change the OAuth metadata, send `auth_credentials` in a `PUT` request to the [update an MCP server](/api/resources/zero_trust/subresources/access/subresources/ai_controls/subresources/mcp/subresources/servers/methods/update/) endpoint. An existing manual OAuth server can be updated without `client_secret`; the stored secret remains unchanged. Send a new `client_secret` to rotate it. A new manual OAuth server, or a server being changed to manual OAuth, requires a non-empty `client_secret`.

The client secret is write-only. Cloudflare encrypts it before storage and never returns it from read, create, or update requests. Responses also omit the raw `auth_credentials` value. Use `auth_config_summary.has_client_secret` and `auth_config_summary.client_secret_version` to confirm that a secret is configured and identify its current version.

#### Bearer authentication credentials

The `auth_credentials` field accepts two forms:

- **A raw bearer token** — the portal sends the value as the `Authorization: Bearer <token>` header on requests to the upstream MCP server:

  ```json
  {
    "auth_type": "bearer",
    "auth_credentials": "your-bearer-token"
  }
  ```

- **A JSON-encoded object of custom headers** — for upstream MCP servers that require multiple headers or a non-standard header name:

  ```json
  {
    "auth_type": "bearer",
    "auth_credentials": "{\"headers\":{\"X-Api-Key\":\"<api-key>\",\"X-Client-Id\":\"<client-id>\"}}"
  }
  ```

  The value of `auth_credentials` must be a JSON string. The parsed object must have a `headers` field mapping header names to string values. The portal forwards all headers verbatim to the upstream MCP server.

### View tool-call analytics

Use the account endpoint to retrieve daily or monthly MCP tool-call counts across the account:

By default, these counts include only successful tool calls. To include errors and cached calls, set `response_status=all`. Use `error` or `cached` to count either status separately.

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/analytics/tool-calls/timeseries?granularity=daily&aggregate=false&tz=utc&days=30"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

The same query parameters apply to each endpoint:

| Parameter         | Values and behavior                                                                                                                                                                                                                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `granularity`     | `daily` or `monthly`. Defaults to `daily`.                                                                                                                                                                                                                                     |
| `aggregate`       | `true` or `false`. Defaults to `false`. Set to `true` to request aggregated counts.                                                                                                                                                                                            |
| `tz`              | `utc`, `Z`, or a fixed `+HH:MM` or `-HH:MM` offset. Defaults to `utc`. Offsets range from `-12:00` through `+14:00`; `+14:00` and `-12:00` are the limits. The offset is used for local-day bucketing and does not account for daylight saving time changes within the window. |
| `days`            | An integer from `1` to `179`. For daily results, this sets the trailing window and defaults to `7`. It is ignored when `granularity=monthly`.                                                                                                                                  |
| `response_status` | `success`, `error`, `cached`, or `all`. Defaults to `success`. Set to `all` to include every status. Invalid values, including an empty value, return `400`.                                                                                                                   |

The response `result` includes the selected `granularity`, `aggregate`, `tz`, and `response_status`, the `start` and `end` of the window as epoch milliseconds, a `series` of `day` and `count` values, and the `total` count. The window includes `start` and excludes `end`. Daily `day` values use `YYYY-MM-DD` in the requested timezone offset.

Use a scoped endpoint to limit the counts:

- Server: `GET /accounts/{account_id}/access/ai-controls/mcp/analytics/servers/{server_id}/tool-calls/timeseries`
- Portal: `GET /accounts/{account_id}/access/ai-controls/mcp/analytics/portals/{portal_id}/tool-calls/timeseries`

### Force sync an MCP server

To manually trigger a synchronization of tools and prompts from an upstream MCP server:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/servers/{server_id}/sync"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

### Delete a portal

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/portals/{id}"
	method="DELETE"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

## Configure via Terraform

You can manage MCP servers and portals using the Cloudflare Terraform provider. Use the [`cloudflare_zero_trust_access_ai_controls_mcp_server`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_ai_controls_mcp_server) and [`cloudflare_zero_trust_access_ai_controls_mcp_portal`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_ai_controls_mcp_portal) resources.

:::caution
Unlike the dashboard, the Terraform provider does not automatically create DNS records for your portal hostname. You must create a CNAME record that points your portal subdomain to `gateway.agents.cloudflare.com`. Without this record, the portal will return `522` errors.
:::

The following example creates an unauthenticated upstream MCP server, a portal, and the Access resources required for a service token to use the server through the portal:

```hcl title="MCP server portal with service token access"
resource "cloudflare_zero_trust_access_service_token" "mcp_bot" {
  account_id = var.cloudflare_account_id
  name       = "MCP bot"
  duration   = "8760h"
}

resource "cloudflare_zero_trust_access_policy" "mcp_bot" {
  account_id = var.cloudflare_account_id
  name       = "Allow MCP bot"
  decision   = "non_identity"

  include = [{
    service_token = {
      token_id = cloudflare_zero_trust_access_service_token.mcp_bot.id
    }
  }]
}

resource "cloudflare_zero_trust_access_ai_controls_mcp_server" "docs" {
  account_id = var.cloudflare_account_id
  id         = "cloudflare-docs"
  name       = "Cloudflare documentation"
  hostname   = "https://docs.mcp.cloudflare.com/mcp"
  auth_type  = "unauthenticated"
}

resource "cloudflare_zero_trust_access_application" "docs" {
  account_id = var.cloudflare_account_id
  name       = "Cloudflare documentation"
  type       = "mcp"

  destinations = [{
    type          = "via_mcp_server_portal"
    mcp_server_id = cloudflare_zero_trust_access_ai_controls_mcp_server.docs.id
  }]

  policies = [{
    id         = cloudflare_zero_trust_access_policy.mcp_bot.id
    precedence = 1
  }]
}

resource "cloudflare_zero_trust_access_ai_controls_mcp_portal" "engineering" {
  account_id = var.cloudflare_account_id
  id         = "engineering"
  name       = "Engineering Portal"
  hostname   = "mcp.example.com"

  servers = [{
    server_id = cloudflare_zero_trust_access_ai_controls_mcp_server.docs.id
    on_behalf = false
  }]
}

resource "cloudflare_zero_trust_access_application" "engineering" {
  account_id = var.cloudflare_account_id
  name       = "Engineering Portal"
  type       = "mcp_portal"
  domain     = cloudflare_zero_trust_access_ai_controls_mcp_portal.engineering.hostname

  destinations = [{
    type = "public"
    uri  = cloudflare_zero_trust_access_ai_controls_mcp_portal.engineering.hostname
  }]

  policies = [{
    id         = cloudflare_zero_trust_access_policy.mcp_bot.id
    precedence = 1
  }]
}

resource "cloudflare_dns_record" "mcp_portal" {
  zone_id = var.cloudflare_zone_id
  name    = "mcp"
  content = "gateway.agents.cloudflare.com"
  type    = "CNAME"
  proxied = true
  ttl     = 1
}
```

The provider marks the service token's `client_secret` as sensitive, but Terraform still stores it in state. Store Terraform state securely and pass the credentials to your MCP client through a secret manager or environment variables.

## Code Mode

[Code Mode](/agents/tools/codemode/) reduces context window usage by replacing upstream tool definitions with two tools for search and code execution. The connected AI agent writes JavaScript that calls typed `codemode.*` methods. The generated code runs in an isolated [Dynamic Worker](/dynamic-workers/) environment. Authentication credentials and environment variables remain outside the model context.

Code Mode is useful for portals with many MCP servers or tools. Context window usage stays fixed as the portal adds tools.

### Code Mode policies

Each portal has a Code Mode policy. The default policy is _Opt-in_.

| Policy        | API value    | Default behavior         | Client override                                  |
| ------------- | ------------ | ------------------------ | ------------------------------------------------ |
| Off           | `off`        | Code Mode is unavailable | Query parameters are ignored                     |
| Opt-in        | `opt_in`     | Code Mode is off         | Add `?codemode=search_and_execute` to turn it on |
| On by default | `default_on` | Code Mode is on          | Add `?codemode=off` to turn it off               |
| Enforced      | `enforced`   | Code Mode is on          | Query parameters are ignored                     |

Use _Opt-in_ or _On by default_ if some clients run their own Code Mode implementation. These policies let clients avoid nested code execution.

### Upstream servers with Code Mode turned on

MCP portals do not support upstream MCP servers that have their own Code Mode turned on. When adding a server to your portal, connect to a version of the server that returns its full tool list. If the upstream server runs its own Code Mode, use the server's opt-out mechanism if available. Alternatively, consider turning off Code Mode on your MCP portal.

### Configure a Code Mode policy

<Steps>
1. Get your existing MCP portal configuration:

   <CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/ai-controls/mcp/portals/{id}"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
   />

2. Add `code_mode` to the response body. Set the value to `off`, `opt_in`, `default_on`, or `enforced`.
3. Send the complete body in a `PUT` request to the [Update a MCP Portal](/api/resources/zero_trust/subresources/access/subresources/ai_controls/subresources/mcp/subresources/portals/methods/update/) endpoint. Including the complete body prevents other portal settings from being overwritten.
</Steps>

The `allow_code_mode` API field is deprecated. Use `code_mode` for new integrations.

### Connect with Code Mode

The portal policy determines whether the MCP client needs a query parameter. For _Opt-in_, append `?codemode=search_and_execute` to the portal URL. For _On by default_, clients can append `?codemode=off` instead.

For example, an _Opt-in_ portal at `https://<subdomain>.<domain>/mcp` uses this URL:

```txt
https://<subdomain>.<domain>/mcp?codemode=search_and_execute
```

For MCP clients with server configuration files, use the portal URL with the query string parameter:

```json title="MCP client configuration with Code Mode"
{
	"mcpServers": {
		"example-portal": {
			"url": "https://<subdomain>.<domain>/mcp?codemode=search_and_execute"
		}
	}
}
```

When Code Mode is active, the portal advertises `portal_codemode_search` and `portal_codemode_execute`. The AI agent can discover tools and compose multiple tool calls in one execution.

For more information on building with Code Mode, refer to the [Code Mode SDK reference](/agents/tools/codemode/api-reference/).

## Route portal traffic through Gateway

When Gateway routing is turned on, calls to MCP servers protected by your MCP server portal are routed through [Cloudflare Gateway](/cloudflare-one/traffic-controls/). This makes portal traffic appear in your [Gateway HTTP logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) alongside the rest of your organization's HTTP traffic. You can then create [Data Loss Prevention (DLP) policies](#example-gateway-policy) to detect and block sensitive data from being sent to your upstream MCP servers.

### How Gateway routing works

When a user calls a tool through the portal, the portal proxies the request to the upstream MCP server. With Gateway routing turned on, this outbound request passes through Cloudflare Gateway before reaching the upstream server. Gateway inspects the traffic and applies any matching HTTP policies, including DLP scanning.

Because portal traffic routes through Gateway, it also respects [Gateway egress policies](/cloudflare-one/traffic-controls/egress-policies/). This means outbound requests to upstream MCP servers will originate from your dedicated egress IPs or Gateway IP ranges rather than generic Cloudflare IPs. If your upstream MCP servers restrict inbound traffic by source IP (for example, to a VPN or corporate IP range), you can use egress policies to ensure portal traffic comes from a predictable set of IPs.

:::note
Server-level Gateway routing also applies to DCR and [admin credential synchronization](#synchronize-the-mcp-server). Portal-level Gateway routing applies only to real-time requests through that portal.
:::

### TLS decryption

DLP inspection requires Gateway to decrypt TLS traffic. For portal traffic, Gateway decrypts and inspects the payload automatically — you do not need to turn on the account-level [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) setting. Because the portal terminates the connection from the MCP client and re-originates the request through Gateway, Gateway decrypts portal traffic regardless of whether the global TLS decryption setting is on.

This automatic decryption applies only to traffic that flows through the portal. To inspect MCP traffic that does not pass through the portal — for example, an agent on a [device running the WARP client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) connecting directly to an upstream MCP server — you must turn on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) as you would for any other [HTTP policy](/cloudflare-one/traffic-controls/http-policies/).

:::note
Portal traffic ignores the global TLS decryption setting, but it still respects [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) HTTP policies. If a Do Not Inspect policy matches portal traffic, Gateway does not decrypt it and DLP cannot scan it. Check that your Do Not Inspect policies do not unintentionally exempt your upstream MCP servers from inspection.
:::

### Supported transports

Gateway routing supports [Streamable HTTP](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http) connections only. If an upstream MCP server is configured with a Server-Sent Events (SSE) endpoint (a URL ending in `/sse`), the portal will automatically attempt to connect using Streamable HTTP instead. If the upstream server does not support Streamable HTTP, the connection will fail when Gateway routing is turned on.

### Enable Gateway routing

You can route every server in a portal through Gateway or turn on routing for individual servers. If Gateway routing is enabled on the portal, it applies to every server in that portal regardless of the server-level setting.

#### Route every server in a portal

To route traffic from every MCP server in a portal through Gateway:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Under **Basic information**, turn on **Route traffic through Cloudflare Gateway**.
4. Select **Save**.

#### Route an individual server

To route traffic from one MCP server through Gateway without enabling Gateway routing for the entire portal:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **AI controls**.
2. Go to the **MCP servers** tab.
3. Find the server you want to configure, then select the three dots > **Edit**.
4. Under **Basic information**, turn on **Route traffic through Cloudflare Gateway**.
5. Select **Save**.

The API field for the server-level setting is `secure_web_gateway`. It defaults to `false`.

Portal traffic will now appear in your [Gateway HTTP logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/). To apply DLP scanning, [create a Gateway HTTP policy](#example-gateway-policy).

### Example Gateway policy

To scan traffic for sensitive data, [create a Gateway HTTP policy](/cloudflare-one/data-loss-prevention/dlp-policies/) that matches both the MCP server and a predefined or custom [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/).

Gateway HTTP policies for MCP portal traffic must explicitly target the upstream MCP server. Ensure that your policy matches the upstream MCP server hostname (for example, `example-mcp-server.example.workers.dev`) rather than the portal URL (`<subdomain>.<domain>`).

For example, the following policy blocks traffic that contains [credentials and secrets](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#credentials-and-secrets) or [financial information](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#financial-information):

| Selector    | Operator | Value                                              | Logic | Action |
| ----------- | -------- | -------------------------------------------------- | ----- | ------ |
| Host        | in       | `example-mcp-server.example.workers.dev`           | And   | Block  |
| DLP Profile | in       | _Credentials and Secrets_, _Financial Information_ |       |        |

### What happens when a request is blocked

When a tool call matches a Block DLP policy, Gateway blocks it and the portal surfaces the block to the MCP client as an error rather than completing the tool call. This applies in both directions:

- **Tool call requests**: If the data the agent sends to a tool matches a DLP profile, Gateway blocks the outbound request and the agent receives an error indicating the request was blocked.
- **Tool call responses**: If the data the upstream server returns matches a DLP profile, Gateway blocks the response and the portal returns an error instead of the matched content.

The agent can retry the request, but it will continue to be blocked until the content no longer matches the policy.

### Limitations

- DLP [AI prompt profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#ai-prompt) do not apply to MCP server portal traffic. AI prompt profiles are designed for specific web client API paths and do not match the MCP protocol format. Use standard DLP profiles instead.
- SSE transport is not supported through Gateway. If your upstream MCP server only supports SSE, Gateway routing will not work for that server.
- Portal-level Gateway routing does not apply to DCR or background synchronization. To route these operations through Gateway, turn on Gateway routing for the individual server.

## Connect to a portal

Users can connect to your MCP server running at `https://<subdomain>.<domain>/mcp` using [Workers AI Playground](https://playground.ai.cloudflare.com/), [MCP inspector](https://github.com/modelcontextprotocol/inspector), or [other MCP clients](/agents/model-context-protocol/guides/remote-mcp-server/#connect-from-an-mcp-client) that support remote MCP servers.

To test in Workers AI Playground:

1. Go to [Workers AI Playground](https://playground.ai.cloudflare.com/).
2. Under **MCP Servers**, enter `https://<subdomain>.<domain>/mcp` for the portal URL.
3. Select **Connect**.
4. In the popup window, log in to your Cloudflare Access identity provider.
5. The popup window will list the MCP servers in the portal that require authentication. For each of these MCP servers, select **Connect** and follow the login prompts.
6. Select **Done** to complete the portal authentication process.

Workers AI Playground will show a **Connected** status and list the available tools. You can now ask the AI model to complete a task using an available tool. Requests made to an MCP server will appear in your [portal logs](#view-portal-logs).

For MCP clients with server configuration files, add the portal as a remote HTTP server. The exact URL field depends on your client. For example:

```json title="MCP client configuration for MCP portals"
{
	"mcpServers": {
		"example-mcp-server": {
			"url": "https://<subdomain>.<domain>/mcp"
		}
	}
}
```

### Portal homepage

When users visit the portal domain (`https://<subdomain>.<domain>/`) in a browser, the portal displays a homepage with connection details and setup instructions.

:::note
Do not visit the MCP endpoint URL (`https://<subdomain>.<domain>/mcp`) directly in a browser. The `/mcp` path is intended for MCP clients only and will return an `invalid token` error if accessed in a browser.
:::

The homepage shows:

- The portal name and your organization branding (if configured in Cloudflare Access)
- The MCP endpoint URL with a copy button
- Per-client connection instructions for Claude Desktop, Workers AI Playground, OpenCode, Windsurf, and other MCP clients with OS-specific file paths

Authenticated users see their email address and a **Sign out** button in the session bar. Users who are not authenticated can still view the homepage and connection instructions.

### Sign out of a portal

To end a portal session, select **Sign out** from the [portal homepage](#portal-homepage) (`https://<subdomain>.<domain>/`). The sign-out flow:

1. Revokes all portal-level OAuth grants for your user.
2. Deletes all upstream MCP server OAuth states associated with your session.
3. Redirects through Cloudflare Access logout.

After sign-out, the portal displays a confirmation page with a summary of the revoked sessions. To reconnect, visit the portal homepage and authenticate again.

### Connect with a service token

You can connect to an MCP portal using an [Access service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) for machine-to-machine access. Service tokens bypass the browser-based OAuth flow and authenticate using the `CF-Access-Client-Id` and `CF-Access-Client-Secret` headers.

A service token session is authorized twice: once at the portal URL, and once for each upstream MCP server it tries to reach through the portal. Both checks need a matching [Service Auth policy](/cloudflare-one/access-controls/policies/#service-auth).

#### Required configuration

| Where                             | Policy action | Include rule       | Purpose                                                                        |
| --------------------------------- | ------------- | ------------------ | ------------------------------------------------------------------------------ |
| Portal Access application         | Service Auth  | Your service token | Lets the bot connect to the portal URL.                                        |
| Each linked MCP server Access app | Service Auth  | Your service token | Lets the bot see and call that server's tools through the portal.              |
| Server's portal mapping           | n/a           | n/a                | **Require user auth** must be **off** so the portal uses the admin credential. |

:::note
**Require user auth** in the dashboard maps to the `on_behalf` field on the portal-server mapping in the API and Terraform. For each linked server you want a service token to reach, set `on_behalf` to `false`. Servers with `on_behalf: true` are excluded from service token sessions because they require a per-user OAuth grant that a service token cannot provide.
:::

If a linked MCP server does not have a Service Auth policy matching the token, that server is hidden from the bot's tool list.

#### Set up a service token connection

1. [Create a service token](/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token) in your Zero Trust account.
2. Open the portal's Access application and add a Service Auth policy that includes the service token.
3. For each upstream MCP server you want the bot to reach:
   1. Open the server's Access application and add a Service Auth policy that includes the same service token.
   2. Open the portal and edit the server. Turn **Require user auth** off so the portal uses the [admin credential](#reauthenticate-the-mcp-server) for that server.
4. Connect from your MCP client with the service token headers.

To configure this flow with the API, set `servers[].on_behalf` to `false` in the [MCP portal request](/api/resources/zero_trust/subresources/access/subresources/ai_controls/subresources/mcp/subresources/portals/methods/create/). Create a `non_identity` Access policy with a `service_token` include rule. Attach the policy to the portal and each linked server Access application. For a complete Terraform example, refer to [Configure via Terraform](#configure-via-terraform).

To verify the connection, send an MCP `initialize` request with the service token headers:

```sh
curl https://<subdomain>.<domain>/mcp \
  -H "CF-Access-Client-Id: <CLIENT_ID>" \
  -H "CF-Access-Client-Secret: <CLIENT_SECRET>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"service-token-test","version":"1.0.0"}}}'
```

For `mcp-remote`, pass the credentials through environment variables instead of storing them in the MCP client configuration:

```json title="MCP client configuration for service token connections"
{
	"mcpServers": {
		"example-portal": {
			"command": "npx",
			"args": [
				"-y",
				"mcp-remote@latest",
				"https://<subdomain>.<domain>/mcp",
				"--header",
				"CF-Access-Client-Id:${ACCESS_CLIENT_ID}",
				"--header",
				"CF-Access-Client-Secret:${ACCESS_CLIENT_SECRET}"
			],
			"env": {
				"ACCESS_CLIENT_ID": "<CLIENT_ID>",
				"ACCESS_CLIENT_SECRET": "<CLIENT_SECRET>"
			}
		}
	}
}
```

:::note
Service tokens do not support per-user OAuth with upstream MCP servers. The portal uses the [admin credential](#reauthenticate-the-mcp-server) for every upstream request made by a service token session. Servers with **Require user auth** turned on are excluded from service token sessions because they require a per-user OAuth grant that a service token cannot provide.
:::

### Device authentication

MCP server portals require a browser-based authentication flow. [Device authentication](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) (picking up identity from the Cloudflare One Client without a browser redirect) is not currently supported for MCP portals. Users must complete the Access login flow in a browser when first connecting.

## Optimize context

MCP server portals support context optimization options that reduce how many tokens tool definitions consume in the model's context window. These options are useful when a portal aggregates many MCP servers or servers that expose a large number of tools.

To use context optimization, append the `optimize_context` query parameter to your portal URL when connecting from an MCP client.

### Minimize tools

The `minimize_tools` option strips tool descriptions and input schemas from all upstream tools, leaving only their names. The portal exposes a special `query` tool that agents use to search and retrieve full tool definitions on demand. Agents can discover tools without loading all definitions upfront.

This option provides up to 5x savings in token usage, though querying tool definitions before use adds a small amount of overhead.

To connect with `minimize_tools`, use the following portal URL:

```txt
https://<subdomain>.<domain>/mcp?optimize_context=minimize_tools
```

For MCP clients with server configuration files:

```json title="MCP client configuration with minimize_tools"
{
	"mcpServers": {
		"example-portal": {
			"url": "https://<subdomain>.<domain>/mcp?optimize_context=minimize_tools"
		}
	}
}
```

### Search and execute

The `search_and_execute` option hides all upstream tools and exposes only two tools to the agent: `query` and `execute`. The `query` tool searches and retrieves tool definitions. The `execute` tool runs the upstream tools. The generated code runs in an isolated [Dynamic Worker](/dynamic-workers/) environment, which keeps authentication credentials and environment variables out of the model context.

This option reduces the initial token cost of portal tools to a small constant, regardless of how many tools are available. However, the agent becomes fully reliant on `query` to discover tools before it can call them.

To connect with `search_and_execute`, use the following portal URL:

```txt
https://<subdomain>.<domain>/mcp?optimize_context=search_and_execute
```

For MCP clients with server configuration files:

```json title="MCP client configuration with search_and_execute"
{
	"mcpServers": {
		"example-portal": {
			"url": "https://<subdomain>.<domain>/mcp?optimize_context=search_and_execute"
		}
	}
}
```

For more information on the Code Mode pattern behind `search_and_execute`, refer to [Code Mode](/agents/tools/codemode/).

## Manage portal sessions

Once connected to a portal, users can manage their upstream MCP server sessions without leaving their MCP client. The portal uses [MCP elicitations](https://modelcontextprotocol.io/specification/2025-03-26/server/elicitation) to provide a server selection page where you can enable or disable servers, log out of individual servers, and reauthenticate.

### Return to the server selection page

To manage your server connections during an active session, ask your AI agent to take you back to the server selection page. For example, prompt your agent with:

> Take me back to the server selection page.

The portal returns an authorization URL. Open this URL in your web browser to access the server selection page:

```txt
https://<subdomain>.<domain>/authorize?elicitationId=<ELICITATION_ID>
```

From this page you can:

- **Enable or disable servers** — Toggle individual upstream MCP servers on or off. Disabling a server removes its tools from the active session, which reduces context window usage.
- **Log out and reauthenticate** — Log out of a server and log back in if you need to change which data the server has access to. For example, you may need to reauthenticate with different permissions.

### Enable or disable a server inline

You can also enable or disable a specific server directly from your MCP client without visiting the server selection page. For example:

> Enable the wiki server.

> Disable my Jira server.

The portal toggles the server and refreshes the active tool list immediately. The resulting list only includes tools and prompts allowed by the portal policy. A server can be enabled while exposing no tools or prompts if its allowlist is empty.

### Reauthenticate a server

When an upstream MCP server token expires, the portal prompts you to reauthenticate from within your MCP client. Open the provided URL in your browser and complete the login to restore the session.

If your MCP client does not display the reauthentication prompt, use its authentication controls to remove the saved portal credentials. Then reconnect to the portal.

### Authorize new servers

When an admin adds a new upstream MCP server to a portal, the portal automatically prompts connected users to authorize the new server. The portal batches admin changes and redirects you to the authorization flow once, rather than interrupting for each individual server update.

## View portal logs

Portal logs allow you to monitor user activity through an MCP server portal. You can view logs on a per-portal or per-server basis.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **MCP Portals**.
2. Find the portal or server that you want to view logs for, then select the three dots > **Edit**.
3. Select **Logs**.

### Log fields

| Field      | Description                                         |
| ---------- | --------------------------------------------------- |
| Time       | Date and time of the request                        |
| Status     | Whether the server successfully returned a response |
| Server     | Name of the MCP server that handled the request     |
| Capability | The tool used to process the request                |
| Duration   | Processing time for the request in milliseconds     |

### Export logs with Logpush

:::note[Availability]
Only available on Enterprise plans.
:::

You can automatically export MCP portal logs to third-party storage destinations or security information and event management (SIEM) tools using [Logpush](/logs/logpush/). This allows you to integrate with your existing security workflows and retain logs for as long as your business requires.

To set up a Logpush job for MCP portal logs, refer to [Logpush integration](/cloudflare-one/insights/logs/logpush/). For a list of available log fields, refer to [MCP portal logs](/logs/logpush/logpush-job/datasets/account/mcp_portal_logs/).

## Known limitations

MCP server portals have the following known limitations:

- **Only remote HTTP MCP servers are supported.** MCP servers that use [stdio transport only](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports) (for example, `github/github-mcp-server`) do not expose a remote HTTP endpoint and cannot be added to an MCP server portal. To use a stdio-only server, you must self-host it behind an HTTP endpoint and authenticate with a [bearer token or custom headers](#create-an-mcp-server).
- **Some MCP servers block proxy-based clients.** Certain MCP servers reject requests from proxy-based clients like MCP server portals, returning a `403` error on the registration endpoint. These servers are not compatible with MCP server portals until those providers add Cloudflare as a supported MCP client.
- **Manual OAuth capabilities are captured during the first user authorization.** Servers configured with [manual OAuth credentials](#configure-manual-oauth-credentials) remain in **Waiting** status until a user completes upstream OAuth. Cloudflare stores the tools and prompts returned during that connection. Background and manual capability synchronization do not refresh them.
- **Admin OAuth tokens can expire silently.** The admin credential used to [authenticate an MCP server](#reauthenticate-the-mcp-server) is subject to the upstream provider's token expiration policy. When the token expires, the server status changes to **Error** or **Sync Required** and the server will not appear in the portal for end users. Admins are not notified when this happens. Periodically check the [server status](#server-status) and [reauthenticate](#reauthenticate-the-mcp-server) servers that show an error.
- **Each portal supports up to 80 MCP servers.**

## Policy limitations

MCP servers use a dedicated Access application type (_mcp_) that does not support the following Access policy features when the server is authorized through a portal.

- **[Independent MFA](/cloudflare-one/access-controls/policies/mfa-requirements/#independent-mfa)** — Users will not be prompted to perform MFA through Cloudflare Access when authorizing a server, regardless of whether MFA global enforcement is enabled or whether an MFA policy is assigned to the server.
- **[Purpose justification](/cloudflare-one/access-controls/policies/require-purpose-justification/)** — Users will not be prompted to provide a purpose justification when authorizing a server.
- **[Temporary authentication](/cloudflare-one/access-controls/policies/temporary-auth/)** — Users will not be prompted to request access and approvers will not receive approval requests when a user authorizes a server.

These limitations only apply to servers that are being authorized through a portal. Access policy selectors such as Emails, Groups, Country, and Device Posture Checks will be enforced.

Independent MFA, purpose justification, and temporary authentication will be enforced for servers that are not authorized through a portal.

## Troubleshooting

### Portal creation fails because of a zone hold

Cloudflare creates a custom hostname for each portal custom domain. Portal creation fails when a zone hold that includes subdomains applies to this hostname.

[Release the zone hold](/fundamentals/account/account-security/zone-holds/#release-zone-holds) for the selected zone and each parent zone. Then create the portal again. A parent zone can belong to a different Cloudflare account.

### After authenticating to the portal, my user receives the error `No allowed servers available, check your Zero Trust Policies`.

1. An MCP portal and server must both have an attached Access policy. Ensure that all MCP servers assigned to the portal have their own associated policy.
2. The server's admin authentication may be expired. Check that the [server's status](#server-status) is **Ready**. If the status shows **Error** or **Sync Required**, [reauthenticate the server](#reauthenticate-the-mcp-server).

### The portal URL does not prompt for authentication when it is added to an MCP client.

1. Verify that the portal has an assigned Access policy.
2. Verify that the portal URL does not have any applied [Workers](/workers/configuration/routing/custom-domains/), [Page Rules](/rules/page-rules/manage/), [custom hostname](/cloudflare-for-platforms/cloudflare-for-saas/domain-support/) definitions, or any other configuration that may interfere with its ability to connect to the MCP client.

### The portal returns a `522` error.

A `522` error indicates that Cloudflare cannot reach the portal's origin. This typically means the DNS record for the portal hostname is missing or misconfigured.

1. Verify that a CNAME record exists for your portal subdomain pointing to `gateway.agents.cloudflare.com`.
2. Ensure the CNAME record has **Proxy status** turned on in Cloudflare DNS.
3. If you created the portal using the [API](#manage-portals-via-api) or the [Terraform provider](#configure-via-terraform), you must create the DNS record separately. Unlike the dashboard, the API and Terraform provider do not auto-create DNS records.

### An MCP server is stuck in `Waiting` status.

The `Waiting` status means Cloudflare is attempting to connect to the upstream MCP server and fetch its tools and prompts. If the server stays in this status:

1. Verify that the upstream MCP server URL is correct and the server is reachable.
2. Check that the upstream server supports [Streamable HTTP](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http) or SSE transport. The portal will attempt multiple connection strategies automatically.
3. If the server requires authentication, verify that the admin credentials are valid by [reauthenticating the server](#reauthenticate-the-mcp-server).
4. Select the three dots > **Sync capabilities** to manually retry the connection.

### An MCP server shows `Stale` status.

A `Stale` status means the admin credential for the server could not be refreshed during the last synchronization attempt. The server's tools may still work for users who have their own OAuth tokens (servers with **Require user auth** turned on), but the admin credential needs to be refreshed.

To resolve this, [reauthenticate the server](#reauthenticate-the-mcp-server) with valid admin credentials.

### Tool calls fail with an `unauthorized` error.

1. If the server uses per-user OAuth (**Require user auth** is turned on), the user's OAuth token may have expired. Ask the user to [reauthenticate the server](#reauthenticate-a-server) from their MCP client.
2. If the server uses admin credentials, check the [server status](#server-status). A status of **Error** or **Sync Required** indicates the admin credential needs to be refreshed.
3. If the user recently changed permissions on the upstream service (for example, revoked OAuth scopes), they will need to reauthenticate.

### OAuth authentication fails with a redirect URI error when connecting to an upstream MCP server.

Errors such as `invalid_redirect_uri`, `invalid_client_metadata`, or `Redirect URI not allowed` indicate that the upstream OAuth provider rejected a redirect URI that Cloudflare used to perform callback. Verify that you added the correct callback URL to the upstream OAuth provider's redirect URI allowlist. For more information, refer to [Upstream OAuth callback URLs](#upstream-oauth-callback-urls).

Contact the upstream MCP server vendor if you do not control the allowlist.

### Tool calls fail when Gateway routing is turned on.

1. Verify that the upstream MCP server supports Streamable HTTP transport. SSE transport is not supported through Gateway.
2. If the upstream server URL ends in `/sse`, the portal automatically attempts to connect using Streamable HTTP on a `/mcp` path instead. If the server does not support this, the connection will fail.
3. Check [Gateway HTTP logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) for DLP block events. If a DLP policy is blocking the traffic, the portal returns an error to the MCP client with the DLP rule ID.

### Users cannot connect from an MCP client

1. Verify that the client supports Streamable HTTP and OAuth discovery.
2. Configure the portal URL as a remote HTTP server.
3. If authentication fails repeatedly, use the client authentication controls to remove the saved portal credentials and reconnect.

### The portal homepage shows the wrong name or domain.

The portal homepage displays your Access organization name and branding. If the displayed name is incorrect:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Settings** > **General** > **Team name**.
2. Update your team name. The change will take effect the next time a user visits the portal homepage.

# Secure MCP servers

import {
	Render,
	GlossaryTooltip,
	Tabs,
	TabItem,
	APIRequest,
	DashButton,
} from "~/components";

You can secure [Model Context Protocol (MCP) servers](https://www.cloudflare.com/learning/ai/what-is-model-context-protocol-mcp/) with Cloudflare Access. Choose an approach based on who manages the MCP server code and hostname:

| Approach                                                                            | Best for                                                                                                      | Auth handled by                                          |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| [Customer-managed third-party MCP server](#customer-managed-third-party-mcp-server) | Third-party MCP server code running on a hostname you control in Cloudflare                                   | The third-party MCP server                               |
| [SaaS-managed third-party MCP server](#saas-managed-third-party-mcp-server)         | Third-party MCP servers hosted by the provider that support customer-provided OAuth or OIDC identity settings | Third-party MCP server, with Access as the OIDC provider |

## Customer-managed third-party MCP server

Use this setup when the MCP server runs on a hostname you control in Cloudflare, but the server code is managed by a third party and already handles its own OAuth flow. In this setup, do not enable Access Managed OAuth. You also do not need to add the MCP server hostname as a public hostname on the generated Access application.

1. Ensure the MCP server hostname has **Proxy status** turned on in Cloudflare DNS.
2. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **AI controls**.
3. Go to the **MCP servers** tab.
4. Select **Add an MCP server**.
5. Enter a name for the server.
6. In **HTTP URL**, enter the MCP server URL, including the MCP path. For example, `https://mcp.example.com/mcp`.
7. Configure [Access policies](/cloudflare-one/access-controls/policies/) to define the users who can use the MCP server.
8. <Render
   	file="access/access-choose-idps-default"
   	product="cloudflare-one"
   />
9. Select **Save and connect server**.
10. If the MCP server prompts you to authenticate, complete the third-party OAuth flow.

:::note

Only enable Managed OAuth for MCP servers that validate the Access JWT sent by Cloudflare. For third-party MCP server code that you cannot update, use the server's existing OAuth flow instead.

:::

## SaaS-managed third-party MCP server

Use this setup when a third-party provider hosts the MCP server and lets you configure a custom OAuth or OIDC identity provider. In this setup, the MCP server implements the OAuth authorization code flow against Cloudflare Access and receives an `access_token` that it can use to call downstream services.

The following guide uses a remote <GlossaryTooltip term="MCP server">MCP server</GlossaryTooltip> on [Cloudflare Workers](/workers/) to show the Access for SaaS setup. For a SaaS-managed server, follow your provider's setup instructions and use the Access for SaaS values created in [Step 2](#2-create-an-access-for-saas-app). When users connect to the MCP server using an <GlossaryTooltip term="MCP client">MCP client</GlossaryTooltip>, they will be prompted to log in to your [identity provider](/cloudflare-one/integrations/identity-providers/) and are only granted access if they pass your [Access policies](/cloudflare-one/access-controls/policies/#selectors).

### Prerequisites

- Create a [Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization).
- Configure [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) or connect a third-party [identity provider](/cloudflare-one/integrations/identity-providers/).

### 1. Deploy an example MCP server

To deploy our [example MCP server](https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-cf-access) to your Cloudflare account:

<Tabs>
<TabItem label="Dashboard">

1. Select the following button to launch the quickstart flow:

   [![Deploy to Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-cf-access)

2. Select the account that contains your Zero Trust organization.
3. On the **Create an application** page, configure the following fields:
   - **Git account**: Select an existing account or connect a new GitHub or GitLab account.
   - **Create private Git repository**: Choose whether the project repository should be public or private.
   - **Project name**: `mcp-server-cf-access`
   - **Select KV namespace**: _Create new_
   - **Name your KV namespace**: `OAUTH_KV`

   We will configure `ACCESS_CLIENT_ID` and the other secret values in a later step.

4. Select **Create and deploy**.

The MCP server will be deployed to your `*.workers.dev` subdomain at `mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev`. A new git repository will be set up on your GitHub or GitLab account for your MCP server, configured to automatically deploy to Cloudflare each time you push a change or merge a pull request to the main branch of the repository.

</TabItem>
<TabItem label = "CLI">

You can use the [Wrangler CLI](/workers/wrangler) to create the MCP server on your local machine and deploy it to Cloudflare.

:::note[Prerequisites]

- Install [npm](https://docs.npmjs.com/getting-started)
- Install [Node.js](https://nodejs.org/en/)
  :::

1.  Open a terminal and clone our example project:

        ```sh
        npm create cloudflare@latest -- mcp-server-cf-access --template=cloudflare/ai/demos/remote-mcp-cf-access
        ```

    During setup, select the following options: - For _Do you want to add an AGENTS.md file to help AI coding tools understand
    Cloudflare APIs?_, choose `No`. - For _Do you want to use git for version control?_, choose `No`. - For _Do you want to deploy your application?_, choose `No` (we will be making some changes before deploying).

2.  Go to the project directory:

    ```sh
    cd mcp-server-cf-access
    ```

3.  Create a [Workers KV namespace](/kv/concepts/kv-namespaces/) to store the key. The binding name should be `OAUTH_KV` if you want to run the example as written.

    ```sh
    npx wrangler kv namespace create "OAUTH_KV"
    ```

        The command will output the binding name and KV namespace ID:

        ```sh output
        {
        	"kv_namespaces": [
        		{
        			"binding": "OAUTH_KV",
        			"id": "<YOUR_KV_NAMESPACE_ID>"
        		}
        	]
        }
        ```

4.  Open `wrangler.jsonc` in an editor and insert your `OAUTH_KV` namespace ID:

    ```jsonc
    "kv_namespaces": [
    	{
    		"binding": "OAUTH_KV",
    		"id": "<YOUR_KV_NAMESPACE_ID>"
    	}
    ],
    ```

5.  You can now deploy the Worker to Cloudflare's global network:

    ```sh
    npx wrangler deploy
    ```

The Worker will be deployed to your `*.workers.dev` subdomain at `mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev`.

</TabItem>
</Tabs>

### 2. Create an Access for SaaS app

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application**.
3. Select **SaaS application**.
4. In **Application**, enter a custom name (for example, `MCP server`) and select the textbox that appears below.
5. Select **OIDC** as the authentication protocol.
6. Select **Add application**.
7. In **Redirect URLs**, enter the authorization callback URL for your MCP server. The callback URL for our [example MCP server](#1-deploy-an-example-mcp-server) is
   `https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/callback`.
8. Copy the following values to input into our example MCP server. Other MCP servers may require different sets of input values.
   - **Client secret**
   - **Client ID**
   - **Token endpoint**
   - **Authorization endpoint**
   - **Key endpoint**

9. (Optional) Under **Advanced settings**, turn on [**Refresh tokens**](/cloudflare-one/access-controls/applications/http-apps/saas-apps/generic-oidc-saas/#advanced-settings) if you want to reduce the number of times a user needs to log in to the identity provider.
10. Configure [Access policies](/cloudflare-one/access-controls/policies/) to define the users who can access the MCP server.
11. <Render
    	file="access/access-choose-idps-default"
    	product="cloudflare-one"
    />
12. Select **Create**.

</TabItem>
<TabItem label="API">

1. Make a `POST` request to the [Access applications](/api/resources/zero_trust/subresources/access/subresources/applications/methods/create/) endpoint:

   <APIRequest
   	path="/accounts/{account_id}/access/apps"
   	method="POST"
   	json={{
   		name: "MCP server",
   		type: "saas",
   		saas_app: {
   			auth_type: "oidc",
   			redirect_uris: [
   				"https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/callback",
   			],
   			grant_type: ["authorization_code", "refresh_tokens"],
   			refresh_token_options: {
   				lifetime: "90d",
   			},
   		},
   		policies: ["f174e90a-fafe-4643-bbbc-4a0ed4fc8415"],
   		allowed_idps: [],
   	}}
   />

2. Copy the `client_id` and `client_secret` returned in the response.
3. Build the OAuth endpoint URLs using your <GlossaryTooltip term="team name">team name</GlossaryTooltip> and the `client_id` returned in the response:

   | Endpoint               | URL                                                                                          |
   | ---------------------- | -------------------------------------------------------------------------------------------- |
   | Token endpoint         | `https://<TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<CLIENT_ID>/token`         |
   | Authorization endpoint | `https://<TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<CLIENT_ID>/authorization` |
   | Key endpoint           | `https://<TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<CLIENT_ID>/jwks`          |

</TabItem>
</Tabs>

### 3. Configure your MCP server

Your MCP server needs to perform an OAuth 2.0 authorization flow to get an `access_token` from the SaaS app created in [Step 2](#2-create-an-access-for-saas-app). When setting up the OAuth client on your MCP server, you will need to paste in the OAuth endpoints and credentials from the Access for SaaS app.

To add OAuth endpoints and credentials to our [example MCP server](#1-deploy-an-example-mcp-server):

<Tabs>
<TabItem label="Dashboard">

1.  In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to the **Workers & Pages** page.

    <DashButton url="/?to=/:account/workers-and-pages" />

2.  Select the `mcp-server-cf-access` Worker.
3.  Go to **Settings**.
4.  Under **Variables and Secrets**, update each secret with the corresponding value obtained from the [Access for SaaS app](#2-create-an-access-for-saas-app).

    | Workers secret             | SaaS app field         |
    | -------------------------- | ---------------------- |
    | `ACCESS_CLIENT_ID`         | Client ID              |
    | `ACCESS_CLIENT_SECRET`     | Client secret          |
    | `ACCESS_TOKEN_URL`         | Token endpoint         |
    | `ACCESS_AUTHORIZATION_URL` | Authorization endpoint |
    | `ACCESS_JWKS_URL`          | Key endpoint           |

        :::note
        Use the Client ID, Client secret, and OAuth endpoints copied from the Cloudflare One dashboard. Do not use the OAuth values from your [third-party identity provider](/cloudflare-one/integrations/identity-providers/generic-oidc/).
        :::

5.  For `COOKIE_ENCRYPTION_KEY`, you can use the following command to generate a random string:

        ```sh
        openssl rand -hex 32
        ```

    Enter the output of this command into `COOKIE_ENCRYPTION_KEY`.

</TabItem>
<TabItem label="CLI">

1.  Create the following [Workers secrets](/workers/configuration/secrets/):

    ```sh
    npx wrangler secret put ACCESS_CLIENT_ID
    npx wrangler secret put ACCESS_CLIENT_SECRET
    npx wrangler secret put ACCESS_TOKEN_URL
    npx wrangler secret put ACCESS_AUTHORIZATION_URL
    npx wrangler secret put ACCESS_JWKS_URL
    ```

2.  When prompted to enter a secret value, paste the corresponding values obtained from the [Access for SaaS app](#2-create-an-access-for-saas-app).

    | Workers secret             | SaaS app field         |
    | -------------------------- | ---------------------- |
    | `ACCESS_CLIENT_ID`         | Client ID              |
    | `ACCESS_CLIENT_SECRET`     | Client secret          |
    | `ACCESS_TOKEN_URL`         | Token endpoint         |
    | `ACCESS_AUTHORIZATION_URL` | Authorization endpoint |
    | `ACCESS_JWKS_URL`          | Key endpoint           |

        :::note
        Use the Client ID, Client secret, and OAuth endpoints copied from the Cloudflare One dashboard. Do not use the OAuth values from your [third-party identity provider](/cloudflare-one/integrations/identity-providers/generic-oidc/).
        :::

3.  Generate a random string for the cookie encryption key:

    ```sh
    openssl rand -hex 32
    ```

    Store the output of this command in a Workers secret:

    ```sh
    npx wrangler secret put COOKIE_ENCRYPTION_KEY
    ```

</TabItem>
</Tabs>

### 4. Test the connection

You can now connect to your MCP server at `https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/mcp` using [Workers AI Playground](https://playground.ai.cloudflare.com/), [MCP inspector](https://github.com/modelcontextprotocol/inspector), or [other MCP clients](/agents/model-context-protocol/guides/remote-mcp-server/#connect-from-an-mcp-client) that support remote MCP servers.

To test in Workers AI Playground:

1. Go to [Workers AI Playground](https://playground.ai.cloudflare.com/).

2. Under **MCP Servers**, enter `https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/mcp` for the MCP server URL.

3. Select **Connect**.

4. A popup window will appear requesting access to the MCP server. Select **Approve**.

5. Follow the prompts to log in to your identity provider.

Workers AI Playground will show a **Connected** status. The MCP server should successfully obtain an `access_token` from Cloudflare Access.

## Next steps

To allow the MCP server to make authenticated requests to other self-hosted applications on behalf of the user, create a [Linked App Token](/cloudflare-one/access-controls/ai-controls/linked-apps/) policy on the downstream application. The MCP server forwards the `Cf-Access-Jwt-Assertion` header it receives from Access as a `Cf-Access-Token` header to the downstream application.

# Add bookmarks

import { Render } from "~/components";

<Render file="access/bookmarks" product="cloudflare-one" />

# Choose an application type

Cloudflare Access sits in front of your applications and checks every request against your Access policies before letting users through. It supports several application types, each designed for a different use case. Your choice depends on where your application is hosted, how users connect to it, and what level of control you need over sessions and authorization.

Most teams start with self-hosted applications and expand to SaaS applications, infrastructure targets, or a combination over time.

## Compare application types

The following table summarizes the key differences between each application type. For detailed setup instructions, refer to the section for each type.

|                                      | Self-hosted application                                                                             | SaaS application                                                                   | Infrastructure application                                                              | Bookmark                                                                         |
| ------------------------------------ | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **What it protects**                 | Resources you own and manage: public web apps, private network destinations, and Cloudflare Workers | Third-party SaaS tools your team uses (Salesforce, Atlassian, Workday)             | Individual servers and infrastructure targets, reachable over public or private network | External URLs displayed in the App Launcher (not gated by Access authentication) |
| **Requires Cloudflare One Client**   | Depends on destination type and [policy requirements](/cloudflare-one/access-controls/policies/)    | No                                                                                 | Yes                                                                                     | No                                                                               |
| **Clientless access available**      | Yes (public hostnames, browser isolation, `cloudflared access` CLI)                                 | Not applicable — users access the SaaS app directly                                | No                                                                                      | Not applicable                                                                   |
| **Authentication and authorization** | Access policies with session management and application tokens signed to the application            | Access policies with SAML/OIDC assertion                                           | Infrastructure policies with protocol-aware authorization (ports, usernames)            | Visibility-only policies for the App Launcher                                    |
| **Private network routing required** | Only for private destinations                                                                       | No                                                                                 | Yes                                                                                     | No                                                                               |
| **Session and token management**     | Full (application tokens, session duration, forced re-authentication)                               | Full                                                                               | Full                                                                                    | None                                                                             |
| **Audit logging**                    | Authentication events and per-request Access logs                                                   | Authentication events                                                              | Authentication events, SSH command logs                                                 | App Launcher authentication only                                                 |
| **Use when**                         | Most use cases — web apps, private apps, Zero Trust networking, Workers                             | Enforcing compliance for SaaS apps, supporting multiple identity providers for SSO | Granular server access control with protocol-level authorization                        | Organizing links in a single portal                                              |

## Self-hosted applications

Self-hosted applications are the most versatile application type and account for the majority of Access deployments. A self-hosted application represents any resource where you control where traffic goes — whether that is a public website on Cloudflare DNS, a non-web service on your private network connected with a Cloudflare Tunnel, or a Worker running on Cloudflare.

Self-hosted applications use the full Access policy engine, including session management, application tokens, forced re-authentication, device posture checks, and identity provider groups.

### Public hostname applications

If your application is already on the public Internet with DNS managed through Cloudflare (or a partial CNAME setup, where your DNS is hosted elsewhere but Cloudflare proxies the traffic), you can place Access in front of it by matching the application's hostname. Cloudflare proxies the request, presents a login page, and only forwards traffic to your origin after the user passes your Access policies.

This is the most common starting point. You do not need to install anything on the user's device — authentication happens entirely in the browser.

For setup instructions, refer to [Add a self-hosted public application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).

### Private applications

You can also use self-hosted applications to protect resources on your private network by targeting specific private IPs, hostnames, or CIDR ranges (blocks of IP addresses, for example `10.0.0.0/8`) with an attached port or port range. This is the primary method for building Zero Trust network access on Cloudflare.

Private network applications require that users route traffic through Cloudflare — typically by running the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on their device. You must also connect your private network to Cloudflare using a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) or [Cloudflare Mesh](/mesh/).

With private network applications, you define the same types of Access policies as you do for public applications, but apply them to private destinations. This gives you granular, identity-aware control over who can reach what on your network — replacing broad VPN-level access with per-application or per-service policies. Access policies are reusable, so you can apply the same policy across multiple applications.

For setup instructions, refer to [Add a self-hosted private application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/).

### Protecting Workers

Self-hosted applications can also protect a Cloudflare Worker directly by name, rather than by hostname or IP. When you select a Worker as the destination, you can cover the Worker together with all of its preview deployments, or cover the preview deployments only.

This is the safest and most straightforward way to put authentication in front of a Worker. Instead of configuring individual routes on the Worker and managing authentication at the route level, you link the entire Worker (and optionally its preview deployments) to an Access application. Any request to the Worker on any route passes through Access first.

For Workers-specific setup instructions, refer to [Cloudflare Access for Workers](/workers/configuration/cloudflare-access/).

### CLI access with cloudflared

Self-hosted applications support client-side `cloudflared` authentication. Users can install `cloudflared` on their device and run `cloudflared access login <hostname>` from the command line to authenticate through your Access policies without the Cloudflare One Client installed. This is useful for SSH sessions, API calls, and other command-line workflows where a browser-based login flow is impractical.

For more information, refer to [cloudflared authentication](/cloudflare-one/access-controls/applications/non-http/cloudflared-authentication/).

## SaaS applications

SaaS applications are for third-party tools that your organization uses but does not host — services like Salesforce, Atlassian, Slack, or Workday. With a SaaS application, you configure Cloudflare Access as the single sign-on (SSO) provider for the third-party service using SAML or OIDC, the two most common identity federation protocols.

When users sign in to the SaaS application, they are redirected to Cloudflare. Cloudflare redirects to your configured identity provider for authentication, then evaluates your Access policies against the authenticated user. If the user passes both checks, Cloudflare issues a signed credential (a SAML assertion or OIDC token) back to the SaaS application confirming the user's identity.

### When to use SaaS applications

Use a SaaS application when you want to:

- **Enforce consistent Access policies across third-party tools.** Apply the same identity, device posture, and location requirements that you use for your internal applications to external SaaS tools.
- **Aggregate multiple identity providers.** Cloudflare can federate authentication across multiple identity providers (IdPs), which means you can swap or add identity providers without reconfiguring each SaaS application individually. This is not typically possible with direct SSO integrations.
- **Apply Cloudflare-specific controls.** Enforce requirements that your SaaS provider cannot check on its own — for example, requiring the Cloudflare One Client or passing a device posture check before granting access to the SaaS tool.

### Limitations

SaaS applications require that the third-party tool supports SAML or OIDC federation. Not all SaaS tools offer this, and some impose restrictions on the number of SSO integrations or the features available through federated authentication. Check your SaaS vendor's documentation for SSO compatibility.

For setup instructions, refer to [SaaS applications](/cloudflare-one/access-controls/applications/http-apps/saas-apps/).

## Infrastructure applications

Infrastructure applications provide protocol-aware access control for servers and infrastructure targets, whether reachable over a public hostname or a private network. Unlike self-hosted applications, which evaluate whether a user can reach a destination, infrastructure applications also control what a user can do after connecting — which usernames they can authenticate as, which ports they can access, and which commands they can run.

Infrastructure applications require the Cloudflare One Client. For targets on your private network, you must also connect the network to Cloudflare through [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) or [Cloudflare Mesh](/mesh/).

### When to use infrastructure applications

Use an infrastructure application when you need:

- **Protocol-level authorization.** Define policies that grant specific users access to specific ports and usernames on a target server.
- **Command logging.** All SSH sessions and commands are logged for compliance and auditing. You can export logs to a storage service or SIEM using [Logpush](/cloudflare-one/insights/logs/logpush/).
- **Short-lived certificates.** Eliminate long-lived SSH keys by authenticating users with certificates that expire quickly. This removes the risk of a stolen or forgotten key granting permanent access to your servers.

Infrastructure applications support SSH. You can still use [self-hosted applications](#self-hosted-applications) to secure access to servers over other protocols (including SSH), but infrastructure applications are the only way to supplementally control user authorization.

For setup instructions, refer to [Add an infrastructure application](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/).

## Bookmarks

Bookmarks are not secured by Access. A bookmark is a link to any URL that you want to display in the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) alongside your other applications. You can assign Access policies to bookmarks, but those policies only control whether the bookmark tile is visible in the App Launcher — they do not protect the destination URL.

Use bookmarks to give users a single portal where they can find all of the tools they use, including external applications that are not integrated with Cloudflare.

For setup instructions, refer to [Add bookmarks](/cloudflare-one/access-controls/applications/bookmarks/).

## Private network applications (legacy)

:::note
Not recommended for new deployments. Use a [self-hosted application](#private-applications) to secure private network destinations instead.
:::

The legacy private network application type creates Gateway Network policies to control access to a private IP address. When you add a legacy private network application, Cloudflare generates two Gateway rules — one Allow rule and one Block rule — because Gateway Network policies are not default-deny (unlike Access policies, which require an explicit Allow rule before any user can reach a protected application).

Legacy private network applications do not support per-session management, application tokens, or the full set of features available in Access policies. This application type is deprecated for new customers and remains available to existing customers.

If you are currently using legacy private network applications, we strongly recommend migrating to [self-hosted private network applications](#private-applications) for more comprehensive policy controls and session management.

For more information, refer to [Private network applications (legacy)](/cloudflare-one/access-controls/applications/non-http/legacy-private-network-app/).

# Applications

import { DirectoryListing } from "~/components";

Cloudflare Access protects applications through several application types. Not sure which to use? Refer to [Choose an application type](/cloudflare-one/access-controls/applications/choose-application-type/).

<DirectoryListing />

# Linked App Token

import { Render } from "~/components";

The **Linked App Token** policy selector allows an Access policy on one application to accept tokens issued for another application. This is useful when one application needs to make authenticated requests to another on behalf of a user — for example, an MCP server calling internal APIs, or a microservice forwarding user identity to a downstream service.

Linked App Token supports two flows:

- [**Self-hosted to self-hosted**](#self-hosted-to-self-hosted) — A self-hosted application forwards its Access JWT to another self-hosted application. This is the simplest setup and requires no additional OAuth configuration.
- [**SaaS to self-hosted**](#saas-to-self-hosted) — An Access for SaaS application (such as an [MCP server using OAuth](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/#saas-managed-third-party-mcp-server)) sends its OAuth access token to a self-hosted application.

## Self-hosted to self-hosted

In this flow, Application A is a [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) that needs to make requests to Application B, another self-hosted Access application. When a user authenticates to Application A, Cloudflare Access sends the user's JWT to Application A in the `Cf-Access-Jwt-Assertion` header. Application A can then forward that token to Application B in the `Cf-Access-Token` header. Access will validate the token against the Linked App Token rule on Application B's policy and allow the request if the token was issued for Application A.

```mermaid
flowchart LR
accTitle: Self-hosted to self-hosted linked app token flow
    User --> appA["Application A <br> (self-hosted)"]
    appA -- "Cf-Access-Token: &lt;JWT&gt;" --> appB["Application B <br> (self-hosted)"]
    idp[Identity provider] <--> appA
```

### Prerequisites

- Two [self-hosted Access applications](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/)

### 1. Create a Linked App Token policy

Create a policy on Application B (the downstream application that will receive forwarded requests):

<Render
	file="access/create-linked-app-token-policy"
	product="cloudflare-one"
	params={{
		sourceAppLabel: "Application A",
		downstreamAppLabel: "Application B",
		exampleValue: "application-a",
		policyName: "Allow requests from Application A",
		exampleName: "application-a",
		appType: "self_hosted",
	}}
/>

### 2. Forward the Access JWT

When Cloudflare Access authenticates a user to Application A, it sends a signed JWT in the `Cf-Access-Jwt-Assertion` request header. Application A must forward this token to Application B in the `Cf-Access-Token` header:

```txt
Cf-Access-Token: <JWT from Cf-Access-Jwt-Assertion>
```

When Access receives the request to Application B, it will:

1. Extract the token from the `Cf-Access-Token` header.
2. Validate that the token was issued for Application A (matching the `app_uid` in the Linked App Token rule).
3. If valid, Access issues a new `Cf-Access-Jwt-Assertion` scoped to Application B's AUD tag, forwards it to Application B's origin, and attributes the request to the original user in the audit log.

## SaaS to self-hosted

In this example an [Access for SaaS application](/cloudflare-one/access-controls/applications/http-apps/saas-apps/) (for example, an MCP server that implements [OAuth](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization)) needs to make requests to a self-hosted Access application. The SaaS app obtains an OAuth access token from Cloudflare Access and sends it to the self-hosted application in the `Authorization: Bearer` header.

```mermaid
flowchart LR
accTitle: SaaS to self-hosted linked app token flow
    User --> appA["Application A <br> (Access for SaaS)"]
    appA -- "Authorization: Bearer &lt;token&gt;" --> appB["Application B <br> (self-hosted)"]
    idp[Identity provider] <--> appA
```

### Prerequisites

- A [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/)
- An [Access for SaaS OIDC application](/cloudflare-one/access-controls/applications/http-apps/saas-apps/)

### 1. Create a Linked App Token policy

Create a policy on the self-hosted application (Application B):

<Render
	file="access/create-linked-app-token-policy"
	product="cloudflare-one"
	params={{
		sourceAppLabel: "the Access for SaaS app (Application A)",
		downstreamAppLabel: "the self-hosted app (Application B)",
		exampleValue: "application-a",
		policyName: "Allow requests from SaaS app",
		exampleName: "my-saas-app",
		appType: "saas",
	}}
/>

### 2. Configure token forwarding

The SaaS application must forward the OAuth `access_token` to the self-hosted application in an HTTP header:

```txt
Authorization: Bearer ACCESS_TOKEN
```

The end-to-end flow is:

1. The user authenticates against the Access for SaaS app via OAuth.
2. Upon success, the application receives an `access_token`.
3. The application makes a request to the self-hosted application with the token in the `Authorization: Bearer` header.
4. Cloudflare Access inspects the token and validates it against the `linked_app_token` rule. If valid, the request is allowed.

## Known limitations

<Render
	file="access/linked-app-token-known-limitations"
	product="cloudflare-one"
/>

# Add web applications

import { Render } from "~/components";

Cloudflare Access allows you to secure your web applications by acting as an identity-aware proxy. Access sits in front of your application and checks each request against your [Access policies](/cloudflare-one/access-controls/policies/) before allowing it through. You can use signals from your existing identity providers (IdPs), device posture providers, and [other selectors](/cloudflare-one/access-controls/policies/#selectors) to control who can reach the application.

![Cloudflare Access verifies a user's identity before granting access to your application.](~/assets/images/cloudflare-one/applications/diagram-saas.jpg)

You can protect the following types of web applications:

- [**SaaS applications**](/cloudflare-one/access-controls/applications/http-apps/saas-apps/) consist of applications your team relies on that are not hosted by your organization. Examples include Salesforce and Workday. To secure SaaS applications, you must integrate Cloudflare Access with the SaaS application's SSO configuration.

- **Self-hosted applications** consist of internal applications that you host in your own environment. These can be the data center versions of tools like the Atlassian suite or applications created by your own team. Setup requirements for a self-hosted application depend on whether the application is publicly accessible on the Internet or restricted to users on a private network.
  - [**Public hostname applications**](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) are web applications that have public DNS records. Anyone on the Internet can access the application by entering the URL in their browser and authenticating through Cloudflare Access. Securing access to a public website requires a Cloudflare DNS [full setup](/dns/zone-setups/full-setup/) or [partial CNAME setup](/dns/zone-setups/partial-setup/).
  - [**Private network applications**](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) do not have public DNS records, meaning they are not reachable from the public Internet. To connect using a private IP or private hostname, the user's traffic must route through Cloudflare Gateway. The preferred method is to install the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on the user's device. Alternative options include forwarding traffic from a [network location](/cloudflare-wan/), using [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/), or [Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/).

- [**Model Context Protocol (MCP) servers**](/cloudflare-one/access-controls/ai-controls/) are web applications that enable generative AI tools to read and write data within your business applications. For example, Salesforce provides an [MCP server](https://github.com/salesforcecli/mcp) for developers to interact with resources in their Salesforce tenant using GitHub Copilot or other AI code editors.

- [**Cloudflare Dashboard SSO**](/fundamentals/manage-members/dashboard-sso/) is a special type of SaaS application that manages SSO settings for the Cloudflare dashboard and has limited permissions for administrator edits.

# Managed OAuth

import { APIRequest, Tabs, TabItem } from "~/components";

When you protect an application with Cloudflare Access, by default non-browser clients — such as CLIs, AI agents, SDKs, and scripts — cannot complete the browser-based login redirect. They receive a `302` redirect with no usable token or authorization endpoint.

Managed OAuth solves this by turning Access into a standard OAuth 2.0 authorization server for your application. Access enforces the same policies as a browser login, and your origin sees no difference.

:::note
If you run your own OAuth server behind an Access application and rely on your own `WWW-Authenticate` headers, do not enable this feature. Enabling managed OAuth replaces the `401` response behavior on the protected application.
:::

## Prerequisites

- A [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/), [MCP server application](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/), or [MCP server portal](/cloudflare-one/access-controls/ai-controls/mcp-portals/)
- An OAuth client that supports [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)

## Enable managed OAuth on a self-hosted application

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Find the application you want to configure, then select the three dots on the right > **Edit**.
3. Go to the **Advanced settings** tab and turn on **Managed OAuth**.
4. (Optional) Configure [Managed OAuth settings](#managed-oauth-settings).
5. Select **Save**.

</TabItem>
<TabItem label="API">

1. Get your existing Access application configuration:

   <APIRequest
    path="/accounts/{account_id}/access/apps/{app_id}"
    method="GET"
   />

2. Make a `PUT` request and set `oauth_configuration.enabled` to `true`. To avoid overwriting your existing configuration, the request body should contain all fields returned by the previous `GET` request.

   <APIRequest
    path="/accounts/{account_id}/access/apps/{app_id}"
    method="PUT"
    json={{
     oauth_configuration: {
      enabled: true,
     },
    }}
   />

</TabItem>
</Tabs>

To test, open an RFC 8707-compliant OAuth client and make a request to your application. The client should open a browser window prompting you to log in to Access. Refer to the [Authorization flow](#authorization-flow) section for more details.

## Enable managed OAuth on an MCP server application

Managed OAuth is available on [MCP server applications](/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/) and allows MCP clients to authenticate users through Access using a standard OAuth 2.0 flow. Use this flow for MCP servers served through Cloudflare in the same account as your Zero Trust organization. The MCP server must validate the Access JWT sent in the `Cf-Access-Jwt-Assertion` header.

Do not enable Managed OAuth for third-party MCP server code that already handles its own OAuth flow and cannot validate Access JWTs.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Find the MCP server application you want to configure, then select the three dots on the right > **Edit**.
3. Go to the **Advanced settings** tab and turn on **Managed OAuth**.
4. (Optional) Configure [Managed OAuth settings](#managed-oauth-settings).
5. Select **Save**.

</TabItem>
<TabItem label="API">

1. Get your existing Access application configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="GET"
   />

2. Make a `PUT` request and set `oauth_configuration.enabled` to `true`. To avoid overwriting your existing configuration, the request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="PUT"
   	json={{
   		oauth_configuration: {
   			enabled: true,
   		},
   	}}
   />

</TabItem>
</Tabs>

To test, open an MCP client and connect to the protected MCP server. The client should open a browser window prompting you to log in to Access. Refer to the [Authorization flow](#authorization-flow) section for more details.

## Enable managed OAuth on an MCP server portal

Managed OAuth is available on [MCP server portals](/cloudflare-one/access-controls/ai-controls/mcp-portals/) and is the mechanism that allows MCP clients to authenticate users through the portal without a browser cookie flow.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **AI controls**.
2. Find the portal you want to configure, then select the three dots on the right > **Edit**.
3. Go to the **Advanced settings** tab, turn on **Managed OAuth**.
4. (Optional) Configure [Managed OAuth settings](#managed-oauth-settings).
5. Select **Save**.

</TabItem>
<TabItem label="API">

1. Get your existing configuration for the portal's underlying Access application:

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="GET"
   />

2. Make a `PUT` request and set `oauth_configuration.enabled` to `true`. To avoid overwriting your existing configuration, the request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="PUT"
   	json={{
   		oauth_configuration: {
   			enabled: true,
   		},
   	}}
   />

</TabItem>
</Tabs>

To test, open an MCP client and [connect to the MCP portal](/cloudflare-one/access-controls/ai-controls/mcp-portals/#connect-to-a-portal). The client should open a browser window prompting you to log in to Access. Refer to the [Authorization flow](#authorization-flow) section for more details.

## Managed OAuth settings

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

Configure these settings in the **Advanced settings** tab of your [self-hosted app](#enable-managed-oauth-on-a-self-hosted-application), [MCP server application](#enable-managed-oauth-on-an-mcp-server-application), or [MCP server portal](#enable-managed-oauth-on-an-mcp-server-portal).

- **Allow localhost clients**: Allow any client with redirect URIs on `localhost`.
- **Allow loopback clients**: Allow any client with redirect URIs on `127.0.0.1`.
- **Allowed redirect URIs**: Redirect URIs allowed for dynamically registered clients (for example, `https://playground.ai.cloudflare.com/*`). The URL must use `https`. Paths may end in `/*` to match all sub-paths.
- **Grant session duration**: How long the OAuth refresh token remains valid.
- **Access token lifetime**: How long an OIDC Access token can be used to authenticate with your application. Cloudflare recommends configuring a short **Access token lifetime** (default 15 minutes) in conjunction with a longer **Grant session duration**. When the access token expires, Cloudflare uses the refresh token to issue a new one after re-evaluating the user against your Access policies. When the refresh token expires, the user must re-authenticate with the identity provider.

:::note[Recommended configuration for CLI and agent use cases]
For CLI tools, AI agents, and other non-browser clients, set a short access token lifetime (5–15 minutes) with a longer grant session duration (1–2 weeks). With this configuration:

- The client refreshes tokens silently in the background using the refresh token — no user interaction required.
- Access policies are re-evaluated on each token refresh, maintaining continuous identity verification.
- The user only sees the authorization prompt when the grant session (refresh token) expires.

For example, with a 15-minute access token and a 2-week grant session, a user authenticates once via the browser and then works uninterrupted for up to two weeks before needing to re-authenticate.
:::

</TabItem>
<TabItem label="API">

Configure these settings via the `oauth_configuration` object on the [Access applications](/api/resources/zero_trust/subresources/access/subresources/applications/methods/update/) endpoint.

| Dashboard setting | API field |
| --- | --- |
| Allow localhost clients | `dynamic_client_registration.allow_any_on_localhost` |
| Allow loopback clients | `dynamic_client_registration.allow_any_on_loopback` |
| Allowed redirect URIs | `dynamic_client_registration.allowed_uris` |
| Grant session duration | `grant.session_duration` |
| Access token lifetime | `grant.access_token_lifetime` |

1. Get your existing Access application configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="GET"
   />

2. Make a `PUT` request with your Managed OAuth settings. To avoid overwriting your existing configuration, the request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="PUT"
   	json={{
   		oauth_configuration: {
   			enabled: true,
   			dynamic_client_registration: {
   				enabled: true,
   				allow_any_on_localhost: true,
   				allow_any_on_loopback: true,
   				allowed_uris: [
   					"https://playground.ai.cloudflare.com/*",
   				],
   			},
   			grant: {
   				access_token_lifetime: "5m",
   				session_duration: "24h",
   			},
   		},
   	}}
   />

</TabItem>
</Tabs>

## Authorization flow

When managed OAuth is enabled, Access returns a `401` response instead of a `302` redirect to non-browser clients. The `401` includes a `WWW-Authenticate` header that points the client to Access's OAuth discovery metadata.

The authorization flow proceeds as follows:

1. The client fetches the OAuth authorization server metadata from the `/.well-known/` endpoint:

   ```txt
   https://<your-app-domain>/.well-known/oauth-authorization-server
   ```

   This endpoint conforms to [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) and [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) and returns the authorization and token endpoint URLs for the application.

2. The client initiates an authorization code flow. It opens the user's browser to the Access authorization endpoint, where the user logs in to their IdP as usual.

3. Access issues an OAuth access token to the client. The client uses this token in subsequent requests to the protected application.

### Token format

Managed OAuth issues **opaque** access tokens (for example, `oauth:CvNoo...`), not JSON Web Tokens (JWTs). This is by design — the OAuth flow gives clients the ability to make requests on a user's behalf without exposing identity information to the client.

When a client presents an opaque token to your application, Cloudflare resolves the token into the user's identity on the backend and forwards a signed assertion to your origin. From your origin's perspective, the request looks the same as a browser-authenticated request.

Because the token is opaque, the client cannot decode it or forward it directly as a JWT to other applications. To make authenticated requests to downstream Access applications, use the [Linked App Token](/cloudflare-one/access-controls/applications/linked-app-token/) pattern — your origin reads the `Cf-Access-Jwt-Assertion` header and forwards it to downstream apps as `Cf-Access-Token`.

### Multi-domain applications

If your Access application is configured with [multiple domains](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#multi-domain-applications), an OAuth token obtained through any one domain is valid for all domains in the same application. The user authenticates once and can use the same token to access all domains without additional prompts.

This is useful when you have multiple internal services that share a common trust boundary. Instead of configuring separate Access applications with [Linked App Token](/cloudflare-one/access-controls/applications/linked-app-token/) policies, you can add all domains to a single application and authenticate once via Managed OAuth.

:::note
Consolidating domains into a single Access application means all domains share the same Access policies. If you need per-domain policy separation, use separate applications with the [Linked App Token](/cloudflare-one/access-controls/applications/linked-app-token/) pattern instead.
:::

## Managed OAuth vs service tokens

Both managed OAuth and [service tokens](/cloudflare-one/access-controls/service-credentials/service-tokens/) allow non-browser clients to authenticate with Access-protected applications, but they serve different use cases:

| | Managed OAuth | Service tokens |
| --- | --- | --- |
| **Authentication model** | User-based — the end user logs in through their identity provider | Machine-based — a shared secret authenticates the service itself |
| **Best for** | Interactive CLI tools, AI agents, SDKs where a human initiates the request | Fully automated systems, cron jobs, CI/CD pipelines, server-to-server communication |
| **User identity** | Access knows which user made the request | No user identity — requests are attributed to the service token |
| **Policy enforcement** | Can use identity-based policies (for example, require specific groups or emails) | Requires a [Service Auth](/cloudflare-one/access-controls/policies/#service-auth) policy action |
| **Credential management** | No shared secrets to distribute — users authenticate with their own credentials | Requires distributing and rotating Client ID and Client Secret |

Use managed OAuth when you want non-browser clients to authenticate users the same way a browser would — the user logs in once, and the client receives an OAuth token to make requests on their behalf.

Use service tokens when no human is involved and you need a machine identity to access your application programmatically.

# Publish a self-hosted application to the Internet

import { Render, Stream } from "~/components";

You can securely publish internal tools and applications by adding Cloudflare Access as an authentication layer between the end user and your origin server.

This page describes how to make a web application accessible to anyone on the Internet via a public hostname. To make the application available over a private IP or hostname, refer to [Add a self-hosted private application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/).

## Prerequisites

- An [active domain on Cloudflare](/fundamentals/manage-domains/add-site/)
- Domain uses either a [full setup](/dns/zone-setups/full-setup/) or a [partial (`CNAME`) setup](/dns/zone-setups/partial-setup/)

:::note
If your domain uses a [partial (`CNAME`) setup](/dns/zone-setups/partial-setup/), refer to [Partial (CNAME) setup](#partial-cname-setup) for additional DNS configuration steps.
:::

## 1. Add your application to Access

<Stream
	id="68749ece14a062ff81eeb1079e0325ed"
	title="How to set up Cloudflare Access"
	thumbnail="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/tunnel%203_%20set%20up%20access.png"
/>

<Render
	file="access/self-hosted-app/generic-public-app"
	product="cloudflare-one"
/>

## 2. Connect your origin to Cloudflare

[Set up a Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) to publish your internal application. Only users who match your Access policies will be granted access.

:::note
We recommend [creating an Access application](#1-add-your-application-to-access) before setting up the tunnel route. If you do not have an Access application in place, the published application will be available to anyone on the Internet.
:::

If your application is already publicly routable, a tunnel is not strictly required. However, you will then need to protect your origin IP using [other methods](/fundamentals/security/protect-your-origin-server/).

## 3. Validate the Access token

<Render file="access/secure-tunnel-with-access" product="cloudflare-one" />

Users can now connect to your self-hosted application after authenticating with Cloudflare Access.

## Partial (CNAME) setup

If your domain uses a [partial (`CNAME`) setup](/dns/zone-setups/partial-setup/), Cloudflare does not manage your DNS zone. You must manually create DNS records at your external provider after adding a published application route to your tunnel.

### Add a published application route

In the tunnel configuration, [add a published application route](/cloudflare-one/networks/routes/add-routes/#add-a-published-application-route) that maps a hostname to your internal service. For example, set the hostname to `app.example.com` and point it to `http://localhost:8080`.

### Create a CNAME record at your DNS provider

In a [full DNS setup](/dns/zone-setups/full-setup/), Cloudflare automatically creates DNS records when you add a published application route to a tunnel. In a partial (`CNAME`) setup, you must add a CNAME record at the DNS provider that hosts your domain (your authoritative DNS provider).

At your external DNS provider, create a CNAME record with the following values:

- **Name**: The hostname you configured in the tunnel (for example, `app.example.com`)
- **Target**: `<HOSTNAME>.cdn.cloudflare.net` (for example, `app.example.com.cdn.cloudflare.net`)

:::note
The zone apex (for example, `example.com`) cannot use a CNAME record due to [DNS specification restrictions](https://datatracker.ietf.org/doc/html/rfc1912#section-2.4). Some DNS providers work around this with [CNAME flattening](/dns/zone-setups/partial-setup/#cname-flattening), which resolves the CNAME at the provider level. If your provider does not support CNAME flattening, use a subdomain instead.
:::

## Product compatibility

<Render
	file="access/self-hosted-app/product-compatibility"
	product="cloudflare-one"
/>

# Application token

Cloudflare Access includes the application token with all authenticated requests to your origin. A typical JWT looks like this:

`eyJhbGciOiJSUzI1NiIsImtpZCI6IjkzMzhhYmUxYmFmMmZlNDkyZjY0.eyJhdWQiOlsiOTdlMmFhZ TEyMDEyMWY5MDJkZjhiYzk5ZmMzNDU5MTNh.zLYsHmLEginAQUXdygQo08gLTExWNXsN4jBc6PKdB`

As shown above, the JWT contains three Base64-URL values separated by dots:

- [Header](#header)
- [Payload](#payload)
- [Signature](#signature)

Unless your application is connected to Access through Cloudflare Tunnel, your application must [validate the token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/) to ensure the security of your origin. Validation of the header alone is not sufficient — the JWT and signature must be confirmed to avoid identity spoofing.

## Header

```json
{
	"alg": "RS256",
	"kid": "9338abe1baf2fe492f646a736f25afbf7b025e35c627be4f60c414d4c73069b8",
	"typ": "JWT"
}
```

- `alg` identifies the encoding algorithm.
- `kid` identifies the key used to sign the token.
- `typ` designates the token format.

## Payload

The payload contains the actual claim and user information to pass to the application. Payload contents vary depending on whether you authenticated to the application with an identity provider or with a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/).

### Identity-based authentication

```json
{
	"aud": ["32eafc7626e974616deaf0dc3ce63d7bcbed58a2731e84d06bc3cdf1b53c4228"],
	"email": "user@example.com",
	"exp": 1659474457,
	"iat": 1659474397,
	"nbf": 1659474397,
	"iss": "https://yourteam.cloudflareaccess.com",
	"type": "app",
	"identity_nonce": "6ei69kawdKzMIAPF",
	"sub": "7335d417-61da-459d-899c-0a01c76a2f94",
	"country": "US"
}
```

| Field          | Description                                                                                                                                                                                                                                                                                     |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| aud            | [Application audience (AUD) tag](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/#get-your-aud-tag) of the Access application.                                                                                                                                                    |
| email          | The email address of the authenticated user, verified by the identity provider.                                                                                                                                                                                                                 |
| exp            | The expiration timestamp for the token (Unix time).                                                                                                                                                                                                                                             |
| iat            | The issuance timestamp for the token (Unix time).                                                                                                                                                                                                                                               |
| nbf            | The not-before timestamp for the token (Unix time), used to check if the token was received before it should be used.                                                                                                                                                                           |
| iss            | The Cloudflare Access domain URL for the application.                                                                                                                                                                                                                                           |
| type           | The type of Access token (`app` for application token or `org` for global session token).                                                                                                                                                                                                       |
| identity_nonce | A cache key used to get the [user's identity](#user-identity).                                                                                                                                                                                                                                  |
| sub            | The ID of the user. This value is unique to an email address per account. The user would get a different `sub` if they are [removed](/cloudflare-one/team-and-resources/users/seat-management/#remove-a-user) and re-added to your Zero Trust organization, or if they log into a different organization. |
| country        | The country where the user authenticated from.                                                                                                                                                                                                                                                  |

#### Custom SAML attributes and OIDC claims

Access allows you to add custom SAML attributes and OIDC claims to your JWT for enhanced verification, if supported by your identity provider. This is configured when you setup your [SAML](/cloudflare-one/integrations/identity-providers/generic-saml/) or [OIDC](/cloudflare-one/integrations/identity-providers/generic-oidc/) provider. Identity provider groups are only included in the token when you explicitly configure `groups` as a custom SAML attribute or OIDC claim. Access does not add them automatically.

:::caution[Custom claims may be trimmed to fit cookie size limits]
Custom SAML attributes and OIDC claims are included in the token's `custom` claim on a best-effort basis. To keep the token within browser cookie size limits, Access trims custom attributes and claims when the serialized `custom` claim exceeds roughly 1 KB (1,000 bytes), dropping configured values from the end of the list first. Groups are usually the largest contributor to this size, so a user who belongs to many groups can receive a token without their `groups` claim while other users on the same application keep it.

Do not rely on custom claims in the JWT for authorization decisions when they may grow large. To retrieve the user's full identity, including all groups, call the `/cdn-cgi/access/get-identity` endpoint described below.
:::

#### User identity

User identity is useful for checking application permissions. For example, your application can validate that a given user is a member of an Okta or Microsoft Entra ID group such as `Finance-Team`.

Due to cookie size limits and bandwidth considerations, the application token only contains a subset of the user's identity. To get the user's full identity, send the `CF_Authorization` cookie to `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/get-identity`. Your request should be structured as follows:

```sh
curl -H 'cookie: CF_Authorization=<user-token>' https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/get-identity
```

Access will return a JSON structure containing the following data:

| Field                | Description                                                                 |
| -------------------- | --------------------------------------------------------------------------- |
| email                | The email address of the user.                                              |
| idp                  | Data from your identity provider.                                           |
| geo                  | The country where the user authenticated from.                              |
| user_uuid            | The ID of the user.                                                         |
| devicePosture        | The device posture attributes.                                              |
| account_id           | The account ID for your organization.                                       |
| iat                  | The timestamp indicating when the user logged in.                           |
| ip                   | The IP address of the user.                                                 |
| auth_status          | The status if authenticating with mTLS.                                     |
| common_name          | The common name on the mTLS client certificate.                             |
| service_token_id     | The Client ID of the service token used for authentication.                 |
| service_token_status | True if authentication was through a service token instead of an IdP.       |
| is_warp              | True if the user enabled WARP.                                              |
| is_gateway           | True if the user enabled the Cloudflare One Client and authenticated to a Zero Trust team.       |
| gateway_account_id   | An ID generated by the Cloudflare One Client when authenticated to a Zero Trust team. |
| device_id            | The ID of the device used for authentication.                               |
| version              | The version of the `get-identity` object.                                   |
| device_sessions      | A list of all sessions initiated by the user.                               |

### Service token authentication

```json
{
	"type": "app",
	"aud": ["32eafc7626e974616deaf0dc3ce63d7bcbed58a2731e84d06bc3cdf1b53c4228"],
	"exp": 1659474457,
	"iss": "https://yourteam.cloudflareaccess.com",
	"common_name": "e367826f93b8d71185e03fe518aff3b4.access",
	"iat": 1659474397,
	"sub": ""
}
```

| Field       | Description                                                                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| type        | The type of Access token (`app` for application token or `org` for global session token).                                                        |
| aud         | The [application audience (AUD) tag](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/#get-your-aud-tag) of the Access application. |
| exp         | The expiration timestamp of the JWT (Unix time).                                                                                                 |
| iss         | The Cloudflare Access domain URL for the application.                                                                                            |
| common_name | The Client ID of the service token (`CF-Access-Client-Id`).                                                                                      |
| iat         | The issuance timestamp of the JWT (Unix time).                                                                                                   |
| sub         | Contains an empty string when authentication was through a service token.                                                                        |

## Signature

Cloudflare generates the signature by signing the encoded header and payload using the SHA-256 algorithm (RS256). In RS256, a private key signs the JWTs and a separate [public key](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/#access-signing-keys) verifies the signature.

For more information on JWTs, refer to [jwt.io](https://jwt.io/).

# CORS

import { Render, PackageManagers, DashButton } from "~/components";

Cross-Origin Resource Sharing ([CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)) is a mechanism that uses HTTP headers to grant a web application running on one origin permission to reach selected resources in a different origin. The web application executes a cross-origin HTTP request when it requests a resource that has a different origin from its own, including domain, protocol, or port.

For a CORS request to reach a site protected by Access, the request must include a valid `CF-Authorization` cookie. This may require additional configuration depending on the type of request:

- [Simple requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) are sent directly to the origin, without triggering a preflight request. For configuration instructions, refer to [Allow simple requests](#allow-simple-requests).

- [Preflighted requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests) cause the browser to send an OPTIONS request before sending the actual request. The OPTIONS request checks which methods and headers are allowed by the origin. For configuration instructions, refer to [Allow preflighted requests](#allow-preflighted-requests).

:::caution[Important]

- Do not troubleshoot CORS in Incognito mode, as this will cause disruptions with Access due to `CF-Authorization` being blocked as a third-party cookie on cross origin requests.

- Safari, in particular Safari 13.1, handles cookies in a unique format. In some cases, this can cause CORS to fail. This will be dependent on Apple releasing a patch for handling cookies. This is known to impact macOS 10.15.4 when running Safari 13.1 (15609.1.20.111.8).

:::

## Allow simple requests

If you make a simple CORS request to an Access-protected domain and have not yet logged in, the request will return a `CORS error`. There are two ways you can resolve this error:

- **Option 1** — [Log in and refresh the page](#authenticate-manually).
- **Option 2** — [Create a Cloudflare Worker which automatically sends an authentication token](#send-authentication-token-with-cloudflare-worker). This method only works if both sites involved in the CORS exchange are behind Access.

### Authenticate manually

1. Visit the target domain in your browser. You will see the Access login page.
2. Log in to the target domain. This generates a `CF-Authorization` cookie.
3. Refresh the page that made the CORS request. The refresh resends the request with the newly generated cookie.

## Allow preflighted requests

If you make a preflighted cross-origin request to an Access-protected domain, the OPTIONS request will return a `403` error. This error occurs regardless of whether you have logged in to the domain. This is because the browser never includes cookies with OPTIONS requests, by design. Cloudflare will therefore block the preflight request, causing the CORS exchange to fail.

There are three ways you can resolve this error:

- **Option 1** — [Bypass OPTIONS requests to origin](#bypass-options-requests-to-origin).
- **Option 2** — [Configure Cloudflare to respond to the OPTIONS request](#configure-response-to-preflight-requests).
- **Option 3** — [Create a Cloudflare Worker which automatically sends an authentication token](#send-authentication-token-with-cloudflare-worker). This method only works if both sites involved in the CORS exchange are behind Access.

### Bypass OPTIONS requests to origin

You can configure Cloudflare to send OPTIONS requests directly to your origin server. To bypass Access for OPTIONS requests:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate the origin that will be receiving OPTIONS requests and select **Configure**.
3. Go to **Advanced settings** > **Cross-Origin Resource Sharing (CORS) settings**.
4. Turn on **Bypass options requests to origin**. This will remove all existing CORS settings for this application.

It is still important to enforce CORS for the Access JWT -- this option should only be used if you have CORS enforcement established in your origin server.

### Configure response to preflight requests

You can configure Cloudflare to respond to the OPTIONS request on your behalf. The OPTIONS request never reaches your origin. After the preflight exchange resolves, the browser will then send the main request which does include the authentication cookie (assuming you have logged into the Access-protected domain).

To configure how Cloudflare responds to preflight requests:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Locate the origin that will be receiving OPTIONS requests and select **Configure**.

3. Go to **Advanced settings** > **Cross-Origin Resource Sharing (CORS) settings**.

4. Configure these [CORS settings](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers) to match the response headers sent by your origin.

   For example, if you have configured `api.mysite.com`to return the following headers:

   ```txt
   headers: {
     'Access-Control-Allow-Origin': 'https://example.com',
     'Access-Control-Allow-Credentials' : true,
     'Access-Control-Allow-Methods': 'GET, OPTIONS',
     'Access-Control-Allow-Headers': 'office',
     'Content-Type': 'application/json',
   }
   ```

   then go to `api.mysite.com` in Access and configure **Access-Control-Allow-Origin**, **Access-Control-Allow-Credentials**, **Access-Control-Allow-Methods**, and **Access-Control-Allow-Headers**.
   ![Example CORS settings configuration in Cloudflare One](~/assets/images/cloudflare-one/policies/CORS-settings.png)

5. Select **Save**.

6. (Optional) You can check your configuration by sending an OPTIONS request to the origin with `curl`. For example,

   ```bash
   curl --head --request OPTIONS https://api.mysite.com \
   --header 'origin: https://example.com' \
   --header 'access-control-request-method: GET'
   ```

   should return a response similar to:

   ```txt
   HTTP/2 200
   date: Tue, 24 May 2022 21:51:21 GMT
   vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
   access-control-allow-origin: https://example.com
   access-control-allow-methods: GET
   access-control-allow-credentials: true
   expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
   report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=A%2FbOOWJio%2B%2FjuJv5NC%2FE3%2Bo1zBl2UdjzJssw8gJLC4lE1lzIUPQKqJoLRTaVtFd21JK1d4g%2BnlEGNpx0mGtsR6jerNfr2H5mlQdO6u2RdOaJ6n%2F%2BS%2BF9%2Fa12UromVLcHsSA5Y%2Fj72tM%3D"}],"group":"cf-nel","max_age":604800}
   nel: {"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}
   server: cloudflare
   cf-ray: 7109408e6b84efe4-EWR
   ```

## Send authentication token with Cloudflare Worker

If you have two sites protected by Cloudflare Access, `example.com` and `api.mysite.com`, requests made between the two will be subject to CORS checks. Users who log in to `example.com` will be issued a cookie for `example.com`. When the user's browser requests `api.mysite.com`, Cloudflare Access looks for a cookie specific to `api.mysite.com`. The request will fail if the user has not already logged in to `api.mysite.com`.

To avoid having to log in twice, you can create a Cloudflare Worker that automatically sends authentication credentials to `api.mysite.com`.

### Prerequisites

- [Workers account](/workers/get-started/guide/)
- `wrangler` installation
- `example.com` and `api.mysite.com` domains [protected by Access](/cloudflare-one/access-controls/applications/http-apps/)

### 1. Generate a service token

Follow [these instructions](/cloudflare-one/access-controls/service-credentials/service-tokens/) to generate a new Access service token. Copy the `Client ID` and `Client Secret` to a safe place, as you will use them in a later step.

### 2. Add a Service Auth policy

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Find your `api.mysite.com` application and select **Configure**.

3. Select the **Policies** tab.

4. Add the following policy:
   | Action | Rule type | Selector |
   | ------------ | --------- | ------------- |
   | Service Auth | Include | Service Token |

### 3. Create a new Worker

Open a terminal and run the following command:

<PackageManagers
	type="create"
	pkg="cloudflare@latest"
	args={"authentication-worker"}
/>

This will prompt you to install the [`create-cloudflare`](https://www.npmjs.com/package/create-cloudflare) package and lead you through setup.

<Render
	file="c3-post-run-steps-hello-world"
	product="workers"
	params={{
		type: "Worker only",
		lang: "JavaScript",
	}}
/>

Go to your project directory.

```sh
cd authentication-worker
```

Open `/src/index.js` and delete the existing code and paste in the following example:

```js
// The hostname where your API lives
const originalAPIHostname = "api.mysite.com";

export default {
	async fetch(request, env) {
		// Change just the host. If the request comes in on example.com/api/name, the new URL is api.mysite.com/api/name
		const url = new URL(request.url);
		url.hostname = originalAPIHostname;

		// If your API is located on api.mysite.com/anyname (without "api/" in the path),
		// remove the "api/" part of example.com/api/name

		// url.pathname = url.pathname.substring(4)

		// Best practice is to always use the original request to construct the new request
		// to clone all the attributes. Applying the URL also requires a constructor
		// since once a Request has been constructed, its URL is immutable.
		const newRequest = new Request(url.toString(), request);

		newRequest.headers.set("cf-access-client-id", env.CF_ACCESS_CLIENT_ID);
		newRequest.headers.set("cf-access-client-secret", env.CF_ACCESS_CLIENT_SECRET);
		try {
			const response = await fetch(newRequest);

			// Copy over the response
			const modifiedResponse = new Response(response.body, response);

			// Delete the set-cookie from the response so it doesn't override existing cookies
			modifiedResponse.headers.delete("set-cookie");

			return modifiedResponse;
		} catch (e) {
			return new Response(JSON.stringify({ error: e.message }), {
				status: 500,
			});
		}
	},
};
```

Then, deploy the Worker to your Cloudflare account:

```sh
npx wrangler deploy
```

### 4. Configure the Worker

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to the **Workers & Pages** page.

	<DashButton url="/?to=/:account/workers-and-pages" />

2. Select your newly created Worker.

3. In the **Triggers** tab, go to **Routes** and add `example.com/api/*`. The Worker is placed on a subpath of `example.com` to avoid making a cross-origin request.

4. In the **Settings** tab, select **Variables**.

5. Under **Environment Variables**, add the following [secret variables](/workers/configuration/environment-variables/#add-environment-variables-via-the-dashboard):
   - `CF_ACCESS_CLIENT_ID` = `<service token Client ID>`
   - `CF_ACCESS_CLIENT_SECRET` = `<service token Client Secret>`

The Client ID and Client Secret are copied from your [service token](#1-generate-a-service-token).

6. Enable the **Encrypt** option for each variable and select **Save**.

### 5. Update HTTP request URLs

Modify your `example.com` application to send all requests to `example.com/api/` instead of `api.mysite.com`.

HTTP requests should now work seamlessly between two different Access-protected domains. When a user logs in to `example.com`, the browser makes a request to the Worker instead of to `api.mysite.com`. The Worker adds the Access service token to the request headers and then forwards the request to `api.mysite.com`. Since the service token matches a Service Auth policy, the user no longer needs to log in to `api.mysite.com`.

## Troubleshooting

In general, we recommend the following steps when troubleshooting CORS issues:

1. Capture a HAR file with the issue described, as well as the JS console log output recorded simultaneously. This is because the HAR file alone will not give full visibility on the reason behind cross-origin issues.
2. Ensure that the application has set `credentials: 'same-origin'` in all fetch or XHR requests.
3. If you are using the [cross-origin setting](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) on script tags, these must be set to "use-credentials".

:::caution[CORS is failing on the same domain]

CORS checks do not occur on the same domain. If this error occurs, it is likely the request is being sent without the `CF-Authorization` cookie.
:::

# Authorization cookie

import { Details, GlossaryTooltip, Render } from "~/components";

When you protect a site with Cloudflare Access, Cloudflare checks every HTTP request bound for that site to ensure that the request has a valid `CF_Authorization` cookie. If a request does not include the cookie, Access will block the request.

## Access JWTs

The `CF_Authorization` cookie contains the user's identity in the form of a [JSON Web Token (JWT)](https://www.cloudflare.com/learning/access-management/token-based-authentication/). Cloudflare securely creates these tokens through the OAUTH or SAML integration between Cloudflare Access and the configured identity provider.

Access generates two separate `CF_Authorization` tokens depending on the domain:

- **Global session token**: Generated when a user logs in to Access. This token is stored as a cookie at your <GlossaryTooltip term="team domain">team domain</GlossaryTooltip> (for example, `https://<your-team-name>.cloudflareaccess.com`) and prevents a user from needing to log in to each application.
- [**Application token**](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/): Generated for each application that a user reaches. This token is stored as a cookie on the protected domain (for example, `https://jira.site.com`) and may be used to [validate requests](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json) on your origin.

### Multi-domain applications

Cloudflare Access allows you to protect and manage multiple domains in a single [self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/). After a user has successfully authenticated to one domain, Access will automatically issue a `CF_Authorization` cookie when they go to another domain in the same Access application. This means that users only need to authenticate once to a multi-domain application.

Access can preemptively set the cookie for every domain through a series of redirects when the user first authenticates. This allows single-page applications (SPAs) to retrieve data from other subdomains before the user visits each subdomain. Wildcarded subdomains (for example, `*.example.com`) cannot receive preemptive cookies because Access does not know which concrete subdomain to redirect to. Wildcarded paths are supported.

Use the [Eager redirect cookie](#eager-redirect-cookie) setting to control this behavior.

:::note
Access previously chose the cookie behavior based on the number of domains. It preemptively set cookies for applications with five or fewer domains and issued cookies as users visited each domain for applications with more than five domains. Applications without a saved setting retain this behavior. Administrators can override it by turning the Eager redirect cookie setting on or off.
:::

## Access cookies

The following Access cookies are essential to Access functionality. Cookies that are marked as required cannot be opted out of. The following cookies are not used for tracking or analytics.

### CF_Authorization (team domain)

| Details                                                                                                                                                                                                                                                                                                                        | Expiration                                                                                                                                                                                                                                                                                                                                                                                            | HttpOnly | SameSite | Required? |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- | --------- |
| [JSON web token (JWT)](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#access-jwts) set on the `cloudflareaccess.com` [team domain](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name) that contains the user's identity and enables Access to perform single sign-on (SSO) | <details><summary>View</summary>If set, adheres to [global session duration](/cloudflare-one/access-controls/access-settings/session-management/#global-session-duration).<br/><br/>If not, adheres to [application session duration](/cloudflare-one/access-controls/access-settings/session-management/#application-session-duration).<br/><br/>If neither are set, defaults to 24 hours.</details> | Yes      | None     | Required  |

### CF_Authorization (Access application domain)

| Details                                                                                                                                                                                                                                                         | Expiration                                                                                                                                                                                                                                                                                                                                                                                            | HttpOnly                     | SameSite                     | Required? |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | ---------------------------- | --------- |
| [JSON web token (JWT)](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#access-jwts) set on the domain protected by Access that allows Access to confirm that the user has been authenticated and is authorized to reach the origin | <details><summary>View</summary>If set, adheres to [policy session duration](/cloudflare-one/access-controls/access-settings/session-management/#policy-session-duration).<br/><br/>If not, adheres to [application session duration](/cloudflare-one/access-controls/access-settings/session-management/#application-session-duration).<br/><br/>If neither are set, defaults to 24 hours.</details> | Admin choice (Default: None) | Admin choice (Default: None) | Required  |

### CF_Binding

| Details                                                                                                                | Expiration                                                                                                                                                                                                                                                                                                                                                                                            | HttpOnly | SameSite | Required? |
| ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- | --------- |
| Refer to [Binding cookie](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#binding-cookie) | <details><summary>View</summary>If set, adheres to [policy session duration](/cloudflare-one/access-controls/access-settings/session-management/#policy-session-duration).<br/><br/>If not, adheres to [application session duration](/cloudflare-one/access-controls/access-settings/session-management/#application-session-duration).<br/><br/>If neither are set, defaults to 24 hours.</details> | Yes      | None     | Optional  |

### CF_Session

| Details                                                                                                                                                                                                                  | Expiration | HttpOnly | SameSite | Required? |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | -------- | -------- | --------- |
| [CSRF](https://www.cloudflare.com/learning/security/threats/cross-site-request-forgery/) token used on the `cloudflareaccess.com` [team domain](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name) | 4 hours    | Yes      | None     | Required  |

### CF_AppSession

| Details                                                                                                                                                                     | Expiration | HttpOnly | SameSite | Required? |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------- | -------- | --------- |
| [CSRF](https://www.cloudflare.com/learning/security/threats/cross-site-request-forgery/) token used per application domain, scoped to individual applications behind Access | 24 hours   | Yes      | None     | Required  |

### CF_Device

| Details                                                                                                                                                                                                                                                                                                                                           | Expiration | HttpOnly | SameSite | Required? |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -------- | -------- | --------- |
| Cookie set on the `cloudflareaccess.com` [team domain](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name), used to prevent abuse of [one-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) and [multi-factor authentication](/cloudflare-one/access-controls/access-settings/independent-mfa/) flows | 30 days    | Yes      | Strict   | Required  |

## Cookie settings

Cloudflare Access provides optional security settings that can be added to the browser cookies generated by Access for an authenticated user.

- [SameSite](#samesite-attribute)
- [HttpOnly flag](#httponly)
- [Binding cookie](#binding-cookie)
- [Cookie path](#cookie-path-attribute)
- [Eager redirect cookie](#eager-redirect-cookie)

To enable these settings:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate the application you would like to configure and select **Configure**.
3. Select **Advanced settings** and scroll down to **Cookie settings**.
4. Configure the desired cookie settings.
5. Select **Save**.

### SameSite Attribute

The [`SameSite`](https://web.dev/samesite-cookies-explained/) Attribute selector restricts the cookie to only being sent if the cookie's defined site matches the site being requested in the browser. This adds protection against [cross-site request forgery (CSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery).

The selector options are:

- **None** - Cookies will be sent in all contexts, including cross-origin requests.
- **Lax** - Cookies are allowed to be sent with top-level navigations and will be sent along with GET requests initiated by third party websites.
- **Strict** - Cookies will only be sent in a first-party context and not be sent along with requests initiated by third party websites.

Refer to the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value) for more information.

:::caution

If you are receiving the `ERR_TOO_MANY_REDIRECTS` errors, make sure your `SameSite` setting is set to None or Lax. Setting the `SameSite` setting to Strict can result in too many redirects.

:::

#### When not to use SameSite

Do not enable `SameSite` restrictions if you have additional sites or applications that rely on a specific application's authorization cookie.

### HttpOnly

The `HttpOnly` flag is a cookie attribute that prevents the cookie from being accessed by any client-side scripts, reducing the likelihood of Cross-Site Scripting (XSS) attacks. This flag is enabled by default.

#### When not to use HttpOnly

Do not enable `HttpOnly` if:

- You are using the Access application for non-browser based tools (such as SSH or RDP).
- You have software that relies on being able to access a user's cookie generated by Access.

### Binding cookie

The binding cookie (`CF_Binding`) is an optional cookie issued when a user successfully authenticates. The binding cookie is sent by the user's browser and tied to a specific application's `CF_Authorization` cookie. This cookie is stripped at Cloudflare's network and never forwarded to the origin server.

The `CF_Authorization` cookie cannot be used without the associated binding cookie, which prevents a stolen `CF_Authorization` cookie from being reused by an attacker. If a request arrives at Cloudflare's network with a valid `CF_Authorization` cookie but without the expected binding cookie, Cloudflare rejects the request.

#### When not to use Binding Cookie

Do not enable Binding Cookie if:

- You are using the Access application for non-browser based tools (such as SSH or RDP).
- You have enabled [incompatible Cloudflare products](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/#product-compatibility) on the application domain, such as [Zaraz](/zaraz) or [Google tag gateway](/google-tag-gateway/). Enabling Binding Cookie alongside these products can cause an authentication redirect loop (`ERR_TOO_MANY_REDIRECTS`).
- You have turned on [Authenticate with Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/) for the application.

### Cookie Path Attribute

The Cookie Path Attribute adds the application's path URL to the `CF_Authorization` cookie. When enabled, a user who logs in to `example.com/path1` must re-authenticate to access `example.com/path2`. When disabled, the `CF_Authorization` cookie is only scoped to the domain and subdomain.

### Eager redirect cookie

When turned on, the Eager redirect cookie setting preemptively sets a `CF_Authorization` cookie for every concrete domain in a multi-domain application. Access redirects the browser through each domain after the user first authenticates. This setting is turned on by default for new applications.

:::caution
Browser behavior can become unreliable when the redirect chain exceeds 10 redirects. If your application has more than 10 domains, turn off this setting or thoroughly test the authentication flow before you deploy it. When turned off, Access sets the cookie as a user visits each domain.
:::

## Allow third-party cookies in the browser

By default, some browsers block all third-party cookies in private browsing mode, including the `CF_Authorization` cookie. For XHR requests to work in private windows, you will need to exempt your application and <GlossaryTooltip term="team domain">team domain</GlossaryTooltip> from the browser's tracking protection system.

To enable third-party cookies for an Access application:

<Details header="Chrome">

1. Go to **Settings** > **Privacy and security** > **Cookies and other site data**.
2. Under **Sites that can always use cookies**, add the following URLs:
   - Hostname of your Access application (for example, `https://jira.site.com`)
   - `https://<your-team-name>.cloudflareaccess.com`

</Details>

<Details header="Safari">

1. Go to **Safari** > **Settings** > **Privacy**.
2. Deselect **Block all cookies**.

</Details>

<Details header="Firefox">

1. Go to **Settings** > **Privacy & Security**.
2. Scroll down to **Cookies and Site Data**.
3. Select **Manage Exceptions**.
4. Enter the URL of your Access application (for example, `https://jira.site.com`) and select **Allow**.
5. Enter `https://<your-team-name>.cloudflareaccess.com` and select **Allow**.
6. Select **Save Changes**.

</Details>

<Details header="Brave">

1. Go to `brave://settings/cookies`.
2. Under **Sites that can always use cookies**, add the following URLs:
   - Hostname of your Access application (for example, `https://jira.site.com`)
   - `https://<your-team-name>.cloudflareaccess.com`

</Details>

# Validate JWTs

import { GlossaryTooltip, TypeScriptExample } from "~/components";

When Cloudflare sends a request to your origin, the request will include an [application token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/) as a `Cf-Access-Jwt-Assertion` request header. Requests made through a browser will also pass the token as a `CF_Authorization` cookie.

Cloudflare signs the token with a key pair unique to your account. You should validate the token with your public key to ensure that the request came from Access and not a malicious third party. We recommend validating the `Cf-Access-Jwt-Assertion` header instead of the `CF_Authorization` cookie, since the cookie is not guaranteed to be passed.

## Access signing keys

The public key for the signing key pair is located at `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs`, where `<your-team-name>` is your Cloudflare One <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

By default, Access rotates the signing key every 6 weeks. This means you will need to programmatically or manually update your keys as they rotate. Previous keys remain valid for 7 days after rotation to allow time for you to make the update.

You can also manually rotate the key using the [API](/api/resources/zero_trust/subresources/access/subresources/keys/methods/rotate/). This can be done for testing or security purposes.

As shown in the example below, `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs` contains two public keys: the current key used to sign all new tokens, and the previous key that has been rotated out.

- `keys`: both keys in JWK format
- `public_cert`: current key in PEM format
- `public_certs`: both keys in PEM format

```txt
{
  "keys": [
    {
      "kid": "1a1c3986a44ce6390be42ec772b031df8f433fdc71716db821dc0c39af3bce49",
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "e": "AQAB",
      "n": "5PKw-...-AG7MyQ"
    },
    {
      "kid": "6c3bffef71bb0a90c9cbef3b7c0d4a1c7b4b8b76b80292a623afd9dac45d1c65",
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "e": "AQAB",
      "n": "pwVn...AA6Hw"
    }
  ],
  "public_cert": {
    "kid": "6c3bffef71bb0a90c9cbef3b7c0d4a1c7b4b8b76b80292a623afd9dac45d1c65",
    "cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- "
  },
  "public_certs": [
    {
      "kid": "1a1c3986a44ce6390be42ec772b031df8f433fdc71716db821dc0c39af3bce49",
      "cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- "
    },
    {
      "kid": "6c3bffef71bb0a90c9cbef3b7c0d4a1c7b4b8b76b80292a623afd9dac45d1c65",
      "cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- "
    }
  ]
}
```

:::note[Avoid key rotation issues]

- Validate tokens using the external endpoint rather than saving the public key as a hard-coded value.
- Do not fetch the current key from `public_cert`, since your origin may inadvertently read an expired value from an outdated cache. Instead, match the `kid` value in the JWT to the corresponding certificate in `public_certs`.
  :::

## Verify the JWT manually

To verify the token manually:

1. Copy the JWT from the `Cf-Access-Jwt-Assertion` request header.

2. Go to [jwt.io](https://jwt.io/).

3. Select the RS256 algorithm.

4. Paste the JWT into the **Encoded** box.

5. In the **Payload** box, ensure that the `iss` field points to your team domain (`https://<your-team-name>.cloudflareaccess.com`). `jwt.io` uses the `iss` value to fetch the public key for token validation.

6. Ensure that the page says **Signature Verified**.

You can now trust that this request was sent by Access.

## Programmatic verification

You can run an automated script on your origin server to validate incoming requests. The provided sample code gets the application token from a request and checks its signature against your public key. You will need to insert your own team domain and Application Audience (AUD) tag into the sample code.

### Get your AUD tag

Cloudflare Access assigns a unique AUD tag to each application. The `aud` claim in the token payload specifies which application the JWT is valid for.

To get the AUD tag:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Configure** for your application.
3. From **Additional settings**, copy the **Application Audience (AUD) Tag**.

You can now paste the AUD tag into your token validation script. The AUD tag will never change unless you delete or recreate the Access application.

### Cloudflare Workers example

When Cloudflare Access is in front of your [Worker](/workers/), use [`ctx.access`](/workers/configuration/cloudflare-access/#read-authenticated-user-identity-with-ctxaccess) to confirm that Access authenticated the request. You can also use it to read the signed-in user's identity. You do not need to parse or validate the JWT. Before using this option, review the [`ctx.access` limitations](/workers/configuration/cloudflare-access/#ctxaccess-limitations) for Static Assets and Service Bindings.

If you need to validate the JWT manually, read it from the `Cf-Access-Jwt-Assertion` header. The following code validates the token using the [jose npm package](https://www.npmjs.com/package/jose):

<TypeScriptExample>

```ts
import { jwtVerify, createRemoteJWKSet } from "jose";

interface Env {
	POLICY_AUD: string;
	TEAM_DOMAIN: string;
}

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
		// Verify the POLICY_AUD environment variable is set
		if (!env.POLICY_AUD) {
			return new Response("Missing required audience", {
				status: 403,
				headers: { "Content-Type": "text/plain" },
			});
		}

		// Get the JWT from the request headers
		const token = request.headers.get("cf-access-jwt-assertion");

		// Check if token exists
		if (!token) {
			return new Response("Missing required CF Access JWT", {
				status: 403,
				headers: { "Content-Type": "text/plain" },
			});
		}

		try {
			// Create JWKS from your team domain
			const JWKS = createRemoteJWKSet(
				new URL(`${env.TEAM_DOMAIN}/cdn-cgi/access/certs`)
			);

			// Verify the JWT
			const { payload } = await jwtVerify(token, JWKS, {
				issuer: env.TEAM_DOMAIN,
				audience: env.POLICY_AUD,
			});

			// Token is valid, proceed with your application logic
			return new Response(
				`Hello ${payload.email || "authenticated user"}!`,
				{
					headers: { "Content-Type": "text/plain" },
				}
			);
		} catch (error) {
			// Token verification failed
			const message = error instanceof Error ? error.message : "Unknown error";
			return new Response(`Invalid token: ${message}`, {
				status: 403,
				headers: { "Content-Type": "text/plain" },
			});
		}
	},
};
```

</TypeScriptExample>

#### Required environment variables

Add these [environment variables](/workers/configuration/environment-variables/) to your Worker:
	- `POLICY_AUD`: Your application's [AUD tag](#get-your-aud-tag)
	- `TEAM_DOMAIN`: `https://<your-team-name>.cloudflareaccess.com`, where `<your-team-name>` is replaced with your actual <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

You can set these variables by adding them to your Worker's [Wrangler configuration file](/workers/wrangler/configuration/), or via the Cloudflare dashboard under **Workers & Pages** > **your-worker** > **Settings** > **Environment Variables**.

### Golang example

```go
package main

import (
    "context"
    "fmt"
    "net/http"

    "github.com/coreos/go-oidc/v3/oidc"
)

var (
    ctx        = context.TODO()
    teamDomain = "https://test.cloudflareaccess.com"
    certsURL   = fmt.Sprintf("%s/cdn-cgi/access/certs", teamDomain)

    // The Application Audience (AUD) tag for your application
    policyAUD = "4714c1358e65fe4b408ad6d432a5f878f08194bdb4752441fd56faefa9b2b6f2"

    config = &oidc.Config{
        ClientID: policyAUD,
    }
    keySet   = oidc.NewRemoteKeySet(ctx, certsURL)
    verifier = oidc.NewVerifier(teamDomain, keySet, config)
)

// VerifyToken is a middleware to verify a CF Access token
func VerifyToken(next http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        headers := r.Header

        // Make sure that the incoming request has our token header
        //  Could also look in the cookies for CF_AUTHORIZATION
        accessJWT := headers.Get("Cf-Access-Jwt-Assertion")
        if accessJWT == "" {
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte("No token on the request"))
            return
        }

        // Verify the access token
        ctx := r.Context()
        _, err := verifier.Verify(ctx, accessJWT)
        if err != nil {
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(fmt.Sprintf("Invalid token: %s", err.Error())))
            return
        }
        next.ServeHTTP(w, r)
    }
    return http.HandlerFunc(fn)
}

func MainHandler() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("welcome"))
    })
}

func main() {
    http.Handle("/", VerifyToken(MainHandler()))
    http.ListenAndServe(":3000", nil)
}
```

### Python example

`pip` install the following:

- flask
- requests
- PyJWT
- cryptography

```python
from flask import Flask, request
import requests
import jwt
import json
import os
app = Flask(__name__)


# The Application Audience (AUD) tag for your application
POLICY_AUD = os.getenv("POLICY_AUD")

# Your CF Access team domain
TEAM_DOMAIN = os.getenv("TEAM_DOMAIN")
CERTS_URL = "{}/cdn-cgi/access/certs".format(TEAM_DOMAIN)

def _get_public_keys():
    """
    Returns:
        List of RSA public keys usable by PyJWT.
    """
    r = requests.get(CERTS_URL)
    public_keys = []
    jwk_set = r.json()
    for key_dict in jwk_set['keys']:
        public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key_dict))
        public_keys.append(public_key)
    return public_keys

def verify_token(f):
    """
    Decorator that wraps a Flask API call to verify the CF Access JWT
    """
    def wrapper():
				# Check for the POLICY_AUD environment variable
				if not POLICY_AUD:
					return "missing required audience", 403

        token = ''
        if 'CF_Authorization' in request.cookies:
            token = request.cookies['CF_Authorization']
        else:
            return "missing required cf authorization token", 403
        keys = _get_public_keys()

        # Loop through the keys since we can't pass the key set to the decoder
        valid_token = False
        for key in keys:
            try:
                # decode returns the claims that has the email when needed
                jwt.decode(token, key=key, audience=POLICY_AUD, algorithms=['RS256'])
                valid_token = True
                break
            except:
                pass
        if not valid_token:
            return "invalid token", 403

        return f()
    return wrapper


@app.route('/')
@verify_token
def hello_world():
    return 'Hello, World!'


if __name__ == '__main__':
    app.run()
```

### JavaScript (Node.js) example

```javascript
const express = require("express");
const jose = require("jose");

// The Application Audience (AUD) tag for your application
const AUD = process.env.POLICY_AUD;

// Your CF Access team domain
const TEAM_DOMAIN = process.env.TEAM_DOMAIN;
const CERTS_URL = `${TEAM_DOMAIN}/cdn-cgi/access/certs`;

const JWKS = jose.createRemoteJWKSet(new URL(CERTS_URL));

// verifyToken is a middleware to verify a CF authorization token
const verifyToken = async (req, res, next) => {
	// Check for the AUD environment variable
	if (!AUD) {
		return res.status(403).send({
			status: false,
			message: "missing required audience",
		});
	}

	const token = req.headers["cf-access-jwt-assertion"];

	// Make sure that the incoming request has our token header
	if (!token) {
		return res.status(403).send({
			status: false,
			message: "missing required cf authorization token",
		});
	}

	try {
		const result = await jose.jwtVerify(token, JWKS, {
			issuer: TEAM_DOMAIN,
			audience: AUD,
		});

		req.user = result.payload;
		next();
	} catch (err) {
		return res.status(403).send({
			status: false,
			message: "invalid token",
		});
	}
};

const app = express();

app.use(verifyToken);

app.get("/", (req, res) => {
	res.send("Hello World!");
});

app.listen(3333);
```

# Adobe Acrobat Sign

This guide covers how to configure [Adobe Acrobat Sign](https://helpx.adobe.com/sign/using/enable-saml-single-sign-on.html) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Adobe Acrobat Sign account
- A [claimed domain](https://helpx.adobe.com/sign/using/claim-domain-names.html) in Adobe Acrobat Sign

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Adobe Sign` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Copy the **Access Entity ID or Issuer**, **Public key**, and **SSO endpoint**.
7. Keep this window open without selecting **Select configuration**. You will finish this configuration in step [3. Finish adding a SaaS application to Cloudflare One](#3-finish-adding-a-saas-application-to-cloudflare-one).

## 2. Add a SAML SSO provider to Adobe Sign

1. In Adobe Acrobat Sign, select your profile picture > your name > **Account Settings** > **SAML Settings**.
2. Turn **SAML Allowed** on.
3. Enter a hostname (for example, `yourcompanyname`). Users can use this URL or `https://secure.adobesign.com/public/login` to sign in via SSO.
4. (Optional) For **Single Sign On Login Message**, enter a custom message (for example, `Log in via SSO`). The default message is **Sign in using your corporate credentials**.
5. Fill in the following fields:
   - **Entity ID/Issuer URL**: Access Entity ID or Issuer from application configuration in Cloudflare One.
   - **Login URL/SSO Endpoint**: SSO endpoint from application configuration in Cloudflare One.
   - **IdP Certificate**: Public key from application configuration in Cloudflare One. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
6. Copy the **Entity ID/SAML Audience** and **Assertion Consumer URL**.
7. Select **Save**.

## 3. Finish adding a SaaS application to Cloudflare One

1. In your open Cloudflare One window, fill in the following fields:
   - **Entity ID**: Entity ID/SAML Audience from Adobe Acrobat Sign SAML SSO configuration.
   - **Assertion Consumer Service URL**: Assertion Consumer URL from Adobe Acrobat Sign SAML SSO configuration.
   - **Name ID format**: _Email_
2. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
3. Save the application.

## 4. Test the integration and finalize configuration

1. Open an incognito browser window and go to your Adobe Sign hostname URL or `https://secure.adobesign.com/public/login`. Select the option to sign in via SSO (**Sign in using your corporate credentials** if you have not configured a custom message). You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

:::note

If you receive an error while testing SSO integration, go to your profile picture > your name > **Account Settings** > **SAML Errors** for more information.

:::

2. Once this is successful, you can make sign in via SSO mandatory. Select your profile picture > your name > **Account Settings** > **SAML Settings**, and then turn on **SAML Mandatory**. Keeping **Allow Acrobat Sign Account Administrators to log in using their Acrobat Sign Credentials** turned on will allow administrators to log in even if your account experiences SSO issues.

# Area 1

import { Render } from "~/components";

<Render file="contact-support" product="email-security" />

[Cloudflare Area 1](https://www.cloudflare.com/products/zero-trust/email-security/) is an email security platform that protects your organization's inbox from phishing, spam, and other malicious messages. This guide covers how to configure Area 1 as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to your Area 1 account
- Your user's email in Area 1 matches their email in Cloudflare One

## 1. Add Area 1 to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **SaaS application**.

4. In the **Application** field, enter `Area 1` and select **Area 1**. (Area 1 is not currently listed in the default drop-down menu.)

5. Enter the following values for your application configuration:
   | | |
   | ---------------------------------- | -------------------------------------------------- |
   | **Entity ID** | `https://horizon.area1security.com` |
   | **Assertion Consumer Service URL** | `https://horizon.area1security.com/api/users/saml` |
   | **Name ID Format** | _Email_ |

6. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.

7. Save the application.

## 2. Configure SSO for Area 1

Finally, you will need to configure Area 1 to allow users to log in through Cloudflare Access.

1. In your [Area 1 portal](https://horizon.area1security.com/), go to **Settings** > **SSO**.

2. Turn on **Single Sign On**.

3. (Optional) To require users to sign in through Access, set **SSO Enforcement** to _All_. When SSO is enforced, users will no longer be able to sign in with their Area 1 credentials.

4. In **SAML SSO Domain**, enter `<your-team-name>.cloudflareaccess.com`.

5. Get your Metadata XML file:
   1. In Cloudflare One, copy the **SSO Endpoint** for your application.

      ![Copy SSO settings for a SaaS application from Cloudflare One](~/assets/images/cloudflare-one/applications/saas-sso-endpoint.png)

   2. In a new browser tab, paste the **SSO Endpoint** and append `/saml-metadata` to the end of the URL. For example, `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/saml/<app-id>/saml-metadata`.

   3. Copy the resulting metadata.

6. Return to the Area 1 portal and paste the metadata into **Metadata XML**.

   ![Configure SSO in the Area 1 portal](~/assets/images/cloudflare-one/applications/area1-sso-config.png)

7. Select **Update Settings**.

If you added the application to your App Launcher, you can test the integration by going to `<your-team-name>.cloudflareaccess.com`.

# Asana

This guide covers how to configure [Asana](https://help.asana.com/hc/en-us/articles/14075208738587-Authentication-and-access-management-options-for-paid-plans#gl-saml) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Super admin access to an Asana Enterprise, Enterprise+, or Legacy Enterprise account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Asana_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://app.asana.com/`
   - **Assertion Consumer Service URL**: `https://app.asana.com/-/saml/consume`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint** and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Add a SAML SSO provider to Asana

1. In Asana, select your profile picture > **Admin console** > **Security** > **SAML authentication**.
2. Under **SAML options**, select _Optional_.
3. Fill in the following fields:
   - Sign-in page URL: SSO endpoint from application configuration in Cloudflare One.
   - X.509 certificate: Public key from application configuration in Cloudflare One. Wrap the public key in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
4. Select **Save changes**.

## 3. Test the integration and require SSO

1. Open an incognito browser window and go to your Asana URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

2. After this is successful, you may want to require users to log in via SSO. In Asana, select your profile picture > **Admin console** > **Security** > **SAML authentication**. Under **SAML options**, select **Required for all members, except guest accounts**.

# Atlassian Cloud

This guide covers how to configure [Atlassian Cloud](https://support.atlassian.com/security-and-access-policies/docs/configure-saml-single-sign-on-with-an-identity-provider/) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to an Atlassian Cloud account
- Atlassian Guard Standard subscription
- A [domain](https://support.atlassian.com/user-management/docs/verify-a-domain-to-manage-accounts/) verified in Atlassian Cloud

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Atlassian_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Copy the **Access Entity ID or Issuer**, **Public key**, and **SSO endpoint**.
7. Keep this window open. You will finish this configuration in step [4. Finish adding a SaaS application to Cloudflare One](#4-finish-adding-a-saas-application-to-cloudflare-one).

## 2. Create a x.509 certificate

1. Paste the **Public key** in a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

## 3. Configure an identity provider and SAML SSO in Atlassian Cloud

1.  In Atlassian Cloud, go to **Security** > **Identity providers**.
2.  Select **Other provider** > **Choose**.
3.  For **Directory name**, enter your desired name. For example, you could enter `Cloudflare Access`.
4.  Select **Add** > **Set up SAML single sign-on** > **Next**.

        :::note
        This screen will advise you to create an authentication policy before proceeding. You will do this in step [5. Create an application policy to test integration](#5-create-an-authentication-policy-to-test-integration).
        :::

5.  Fill in the following fields:
    - **Identity provider Entity ID**: Access Entity ID or Issuer from application configuration in Cloudflare One.
    - **Identity provider SSO URL**: SSO endpoint from application configuration in Cloudflare One.
    - **Public x509 certificate**: Paste the entire x.509 certificate from step [2. Create a x.509 certificate](#2-create-a-x509-certificate).
6.  Select **Next**.
7.  Copy the **Service provider entity URL** and **Service provider assertion consumer service URL**.
8.  Select **Next**.
9.  Under **Link domain**, select the domain you want to use with SAML SSO.
10. Select **Next** > **Stop and save SAML**.

## 4. Finish adding a SaaS application to Cloudflare One

1. In your open Cloudflare One window, fill in the following fields:
   - **Entity ID**: Service provider entity URL from Atlassian Cloud SAML SSO set-up.
   - **Assertion Consumer Service URL**: Service provider assertion consumer service URL from Atlassian Cloud SAML SSO set-up.
   - **Name ID format**: _Email_
2. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
3. Save the application.

## 5. Create an authentication policy to test integration

To enable SSO for users in Atlassian Cloud, create an [Atlassian authentication policy](https://support.atlassian.com/security-and-access-policies/docs/configure-authentication-policies-for-your-organization/):

1. In Atlassian Cloud, go to **Security** > **Authentication policies**.
2. Select **Add policy**.
3. Under **Directory**, select the identity provider you used to configure SAML SSO.
4. For **Policy name**, enter your desired name.
5. Select **Add**.
6. In **Settings**, turn on **Enforce single sign-on**.
7. In **Members**, select **Add members**.
8. In **Individual Users**, select your desired test user(s) in the dropdown, and select **Add members**.
9. In **Settings**, select **Update** > **Update**.

## 6. Test the integration

Open an incognito browser window and log in with the credentials of the test user you added to the test authentication policy. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider. When this is successful, turn on **Enforce single sign-on** in your desired authentication policy, or add the desired users to the application policy created in step [5. Create an Application Policy to test Integration](#5-create-an-authentication-policy-to-test-integration).

# AWS

This guide covers how to configure [AWS](https://docs.aws.amazon.com/singlesignon/latest/userguide/manage-your-identity-source-idp.html) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to an AWS account

## 1. Get AWS URLs

1. In the AWS admin panel, search for `IAM Identity Center`.
2. Go to **IAM Identity Center** > **Settings**.
3. In the **Identity source** tab, select the **Actions** dropdown and select _Change identity source_.
4. Change the identity source to **External identity provider**.
5. Copy the values shown in **Service provider metadata**. You will need these values when configuring the SaaS application in Cloudflare One.

Next, we will obtain **Identity provider metadata** from Cloudflare One.

## 2. Add a SaaS application to Cloudflare One

1. In a separate tab or window, open the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Amazon AWS_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: IAM Identity Center issuer URL
   - **Assertion Consumer Service URL**: IAM Identity Center Assertion Consumer Service (ACS) URL
   - **Name ID format**: _Email_
7. (Optional) Additional SAML attribute statements can be passed from your IdP to AWS SSO. To learn more about AWS Attribute mapping, refer to [Attribute mappings - AWS Single Sign-On](https://docs.aws.amazon.com/singlesignon/latest/userguide/attributemappingsconcept.html#supportedidpattributes).
8. AWS supports uploading a metadata XML file. To download your SAML metadata from Access:
   1. Copy the **SAML Metadata endpoint**.
   2. In a separate browser window, go to the SAML Metadata endpoint (`https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/saml/xxx/saml-metadata`).
   3. Save the page as `access_saml_metadata.xml`.
9. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
10. Save the application.

## 3. Complete AWS configuration

1. Return to the **IAM Identity Center** > **Settings** > **Change identity source** tab.

2. Under **IdP SAML metadata**, upload your `access_saml_metadata.xml` file.

3. Select **Next** to review settings, type **ACCEPT** and select **Change identity source** to confirm changes.

4. Confirm that **Provisioning** is set to _Manual_.

:::caution[Important]

Access for SaaS does not currently support [SCIM provisioning](/cloudflare-one/team-and-resources/users/scim/). Make sure that:

1. Users are created in both your identity provider and AWS.
2. Users have matching usernames in your identity provider and AWS.
3. Usernames are email addresses. This is the only format AWS supports with third-party SSO providers.
   :::

## 4. Test the integration

To test the connection, go to your **AWS access portal URL**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

# Braintree

This guide covers how to configure [Braintree](https://developer.paypal.com/braintree/articles/guides/single-sign-on-sso) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Braintree production or sandbox account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Braintree` and select the textbox that appears below.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields with temporary values:
   - **Entity ID**: `placeholder`
   - **Assertion Consumer Service URL**: `https://www.placeholder.com`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint** and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Enable SSO Configuration in Braintree

1. In Braintree, create a [support ticket](https://developer.paypal.com/braintree/help).
2. In **Search Issues**, enter `Login and password issues` and select the corresponding value.
3. In **Issue Details**, fill in the following:
   - **Merchant ID**: Your Braintree Merchant ID. This is the 16-digit value that follows `/merchants/`in your Braintree Control Panel URL.
   - **Email domain(s) to be used in user IDs**: The email domain(s) that should be allowed to sign in to your account via SSO.
   - **Single Sign-on HTTP POST Binding URL**: SSO endpoint from application configuration in Cloudflare One
   - **Certificate for validation**: Public key from application configuration in Cloudflare One.
4. Select whether you are using a **Production** or **Sandbox** account.
5. Fill out the **Your contact information** fields and select **Submit a help request**.
6. When you receive an email stating SSO has been successfully configured for your account, you can proceed to the next step.

## 3. Finish adding a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Braintree** > **Edit** > **Overview**.
3. Replace the temporary values for **Entity ID** and **Assertion Consumer Service URL** with the link provided in the successful SSO configuration email from Braintree support. You will use the same link for both values.
4. Select **Save Application**.

## 4. Test the integration and add SSO users

1. In your Braintree Control Panel, select the **settings** icon > **Team**.
2. Select your desired test user.
3. Under **Single Sign-On**, select **Enable**.
4. Open an incognito browser window. In the address bar, paste `https://id.sandbox.braintreegateway.com` for a sandbox account or
   `https://id.braintreegateway.com` for a production account.
5. In **Your corporate email address** field, type your test user's email. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
6. Upon successful sign-in, you can enable SSO for other users using steps 4.1 - 4.3.

# Coupa

This guide covers how to configure [Coupa](https://compass.coupa.com/en-us/products/product-documentation/integration-technical-documentation/coupa-core-user-authentication/coupa-saml-sso-setup) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Coupa Stage or Production account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Coupa` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**:
     `sso-stg1.coupahost.com` for a stage account or `sso-prd1.coupahost.com` for a production account
   - **Assertion Consumer Service URL**: `https://sso-stg1.coupahost.com/sp/ACS.saml2` for a stage account or `https://sso-prd1.coupahost.com/sp/ACS.saml2` for a production account
   - **Name ID format**: _Email_
7. Copy the **Access Entity ID or Issuer** and **SAML Metadata Endpoint**.
8. In **Default relay state**, enter `https://<your-subdomain>.coupahost.com/sessions/saml_post`.
9. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
10. Save the application.

## 2. Download the metadata file

1. Paste the SAML metadata endpoint from application configuration in Cloudflare One in a web browser.
2. Follow your browser-specific steps to download the URL's contents as an `.xml` file.

## 3. Add a SAML SSO provider in Coupa

1. In Coupa, go to **Setup** > **Company Setup** > **Security Controls**.
2. Under **Sign in using SAML**, turn on **Sign in using SAML**.
3. In **Upload IdP metadata**, select **Choose File**, and upload the `.xml` file you downloaded in step [2. Download the metadata file](#2-download-the-metadata-file).
4. Turn on **Advanced Options**.
5. For **Sign in page URL** and **Timeout URL**, enter `https://sso-stg1.coupahost.com/sp/startSSO.ping?PartnerIdpId=<access-entity-id-or-issuer>&TARGET=https://<your-subdomain>.coupahost.com/sessions/saml_post` using the Access Entity ID or Issuer from application configuration in Cloudflare One.
6. Select **Save**.

## 3. Create a test user and test the integration

1. In Coupa, go to **Setup** > **Company Setup** > **Users**.
2. Select **Create**, then enter the user details for your test user. For **Login** and **Single Sign-On ID**, enter the user's email address.
3. Select **Save**.
4. Open an incognito browser window and go to your Coupa URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
5. Once the login is successful, you can configure other users for SSO by adding their email to the **Single Sign-On ID** field in **Setup** > **Company Setup** > **Users** > user's name.

:::note

You can use the following URL to bypass SSO and login via a username and password: `https://<your-subdomain>.coupahost.com/sessions/support_login`.

:::

# Digicert

This guide covers how to configure [Digicert](https://docs.digicert.com/en/certcentral/manage-account/saml-admin-single-sign-on-guide/configure-saml-single-sign-on.html) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Digicert account
- [SAML](https://docs.digicert.com/en/certcentral/manage-account/saml-admin-single-sign-on-guide/saml-single-sign-on-prerequisites.html) enabled in your Digicert account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Digicert` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://www.digicert.com/account/sso/metadata`
   - **Assertion Consumer Service URL**: `https://www.digicert.com/account/sso/`
   - **Name ID format**: _Email_
7. Copy the **SAML Metadata endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Add a SAML SSO provider in Digicert

1. In Digicert, select **Settings** > **Single Sign-On** > **Set up SAML**.
2. Under **How will you send data from your IDP?**, turn on **Use a dynamic URL**.
3. Under **Use a dynamic URL**, paste the SAML Metadata endpoint from application configuration in Cloudflare One.
4. Under **How will you identify a user?**, turn on **NameID**.
5. Under **Federation Name**, enter a name (for example, `Cloudflare Access`). Your users will select this name when signing in.
6. Select **Save SAML Settings**.

## 3. Test and Enable SSO in Digicert

1. In Digicert, select **Settings** > **Single Sign-On**.
2. Copy the **SP Initiated Custom SSO URL**.
3. Paste the URL into an incognito browser window and sign in. Upon successful sign in, SAML SSO is fully enabled.
4. (Optional) By default, users can choose to sign in directly or with SSO. To require SSO sign in, go to **Account** > **Users**. Turn on **Only allow this user to log in through SAML/OIDC SSO** in the user details of the desired user.

:::note

Users can sign in using service provider initiated SSO by using the **SP Initiated Custom SSO URL**. Alternatively, users can go to `www.digicert.com/account`, select **Sign in with SSO**, and enter the name of the identity provider configured in step [2. Add a SAML SSO provider in Digicert](#2-add-a-saml-sso-provider-in-digicert).

:::

# DocuSign

This guide covers how to configure [Docusign](https://support.docusign.com/s/document-item?bundleId=rrf1583359212854&topicId=ozd1583359139126.html) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Docusign account that has Single Sign-On available
- A [domain](https://support.docusign.com/s/document-item?bundleId=rrf1583359212854&topicId=gso1583359141256.html) verified in Docusign

## 1. Create the Access for SaaS application

1.  In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2.  Select **Create new application**.

3.  Select **SaaS application**.

4.  Use the following configuration:
    - Set the **Application** to _DocuSign_.
    - Put placeholder values in **EntityID** and **Assertion Consumer Service URL** (for example, `https://example.com`). We'll come back and update these.
    - Set **Name ID Format** to: _Unique ID_.

5.  DocuSign requires SAML attributes to do Just In Time user provisioning. Ensure you are collecting SAML attributes from your IdP:
    - Group
    - username
    - department
    - firstName
    - lastName
    - phone

6.  These IdP SAML values can then be mapped to the following DocuSign SAML attributes:
    - Email
    - Surname
    - Givenname

7.  Set an Access policy (for example, create a policy based on _Emails ending in @example.com_).

8.  Copy and save the **SSO Endpoint**, **Entity ID** and **Public Key**.

9.  Transform the **Public Key** into a fingerprint:

        1. Copy the **Public Key** Value.

        2. Paste the **Public Key** into VIM or another code editor.

        3. Wrap the value in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

        4. Set the file extension to `.crt` and save.

## 2. Configure your DocuSign SSO instance

1. Ensure you have a domain claimed in DocuSign.

2. From the DocuSign Admin dashboard, select **Identity Providers**.

3. On the Identity Providers page, select **ADD IDENTITY PROVIDER**. Use the following mappings from the saved Access Application values:
   - **Name**: Pick your desired name.
   - **Identity Provider Issuer**: Entity ID.
   - **Identity Provider Login URL**: Assertion Consumer Service URL.

4. Save the Identity Provider.

5. Upload your certificate to the _DocuSign Identity Provider_ menu.

6. Configure your SAML Attribute mappings. The Attribute Names should match the values in **IdP Value** in your Access application.

7. Go back to the Identity Provider's screen and select **Actions** > **Endpoints**. Copy and save the following:
   - Service Provider Issuer URL.
   - Service Provider Assertion Consumer Service URL.

## 3. Finalize your Cloudflare configuration

1. Go back to your DocuSign application under **Access controls** > **Applications**.
2. Select **Edit**.
3. Use the following mappings:
   - EntityID->Service Provider Issuer URL.
   - Assertion Consumer Service URL -> Service Provider Assertion Consumer Service URL.
4. Save the application.

When ready, enable the SSO for your DocuSign account and you will be able to login to DocuSign via Cloudflare SSO and your Identity Provider.

# Dropbox

This guide covers how to configure [Dropbox](https://help.dropbox.com/security/sso-admin) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Dropbox Advanced, Business Plus, or Enterprise account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select `Dropbox`.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `Dropbox`
   - **Assertion Consumer Service URL**: `https://www.dropbox.com/saml_login`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint** and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create a certificate file

1. Paste the **Public key** in a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
3. Set the file extension as `.pem` and save.

## 3. Add a SAML SSO provider to Dropbox

1. In Dropbox, go to your profile picture > **Settings** > **Admin Console** > **Security** > **Single sign-on**.
2. For **Single sign-on**, select _Optional_.
3. Select **Add Identity provider sign-in URL**.
4. Paste the SSO endpoint from application configuration in Cloudflare One and select **Done**.
5. Select **Add X.509 certificate** and upload the `.pem` file from step [2. Create a certificate file](#2-create-a-certificate-file).
6. Copy **SSO sign-in URL**. This is your custom Dropbox SSO URL.
7. Select **Save**.

## 3. Test the integration and require SSO

1. Open an incognito browser window and go to your custom Dropbox SSO URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

2. After this is successful, you may want to require users to log in via SSO. Go to your profile picture > **Settings** > **Admin Console** > **Security** > **Single sign-on**. For **Single sign-on**, select _Required_. Dropbox will send an email to your users notifying them of the change.

# Generic OIDC application

import { Render } from "~/components";

This page provides generic instructions for setting up a SaaS application in Cloudflare Access using the OpenID Connect (OIDC) authentication protocol.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to the account of the SaaS application

## 1. Get SaaS application URL

In your SaaS application account, obtain the **Redirect URL** (also known as the callback URL). This is the SaaS endpoint where users are redirected to after they authenticate with Cloudflare Access.

Some SaaS applications provide the Redirect URL after you [configure the SSO provider](#3-configure-sso-in-your-saas-application).

## 2. Add your application to Access

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **SaaS application**.

4. Select your **Application** from the drop-down menu. If your application is not listed, enter a custom name in the **Application** field and select the textbox that appears below.

5. Select **OIDC**.

6. Select **Add application**.

7. In **Scopes**, select the user attributes that you want Access to send in the ID token. For more information about configuring OIDC scopes and claims, refer to [OIDC claims](#oidc-claims).

8. In **Redirect URLs**, enter the callback URL obtained from the SaaS application.

9. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.

10. Copy the following values to input into your SaaS application. Different SaaS applications may require different sets of input values.
    | Field | Description |
    | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | Client secret | Credential used to authorize Access as an SSO provider |
    | Client ID | Unique identifier for this Access application |
    | Configuration endpoint | If supported by your SaaS application, you can configure OIDC using this endpoint instead of manually entering the URLs listed below. <br/> `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<client-id>/.well-known/openid-configuration` |
    | Issuer | Base URL for this OIDC integration <br/> `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<client-id>` |
    | Token endpoint | Returns the user's ID token <br/> `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<client-id>/token` |
    | Authorization endpoint | URL where users authenticate with Access <br/> `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<client-id>/authorization` |
    | Key endpoint | Returns the current public keys used to [verify the Access JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/) <br/> `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<client-id>/jwks` |
    | User info endpoint | Returns all user claims in JSON format <br/> `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<client-id>/userinfo` |

11. <Render file="access/add-access-policies" product="cloudflare-one" />

12. <Render file="access/access-choose-idps-default" product="cloudflare-one" />

13. (Optional) Go to **Additional settings** to customize the application experience:

	- **App Launcher customization**: Configure how this application appears to users in the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/). If **Show application in App Launcher** is enabled, then you must enter an **App Launcher URL**. The App Launcher URL is provided by the SaaS application. It may match the base URL portion of **Redirect URL** (`https://<INSTANCE-NAME>.example-app.com`) but could be a different value.
	- <Render file="access/access-block-page" product="cloudflare-one" />

14. Select **Create**.

## 3. Configure SSO in your SaaS application

Next, configure your SaaS application to require users to log in through Cloudflare Access. Refer to your SaaS application documentation for instructions on how to configure a third-party OIDC SSO provider.

## 4. Test the integration

Open an incognito browser window and go to the SaaS application's login URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

## OIDC claims

OIDC claims refer to the user identity characteristics that Cloudflare Access shares with your OIDC SaaS application upon successful authentication. An OIDC scope defines a set of OIDC claims. By default, Cloudflare Access passes all [standard claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) that are included in the `openid`, `email`, `profile`, and `groups` scopes (if available).

| Scope     | Description                                                       |
| --------- | ----------------------------------------------------------------- |
| `openid`  | Includes a unique identifier for the user (required).             |
| `email`   | Includes the user's email address.                                |
| `profile` | Includes the user's name and all custom OIDC claims from the IdP. |
| `groups`  | Include the user's IdP group membership.                          |

In your Access application, you can configure the OIDC scopes and claims that Access sends to the SaaS provider. For example, you can remove the `groups` scope if your SaaS application does not need to receive user group information.

### Filter groups

In **Group filter regex**, you can enter a regular expression to define the identity provider groups that you want to include in the `groups` scope. For example, if you enter the expression `(^TEAM-Engineering-.$)|(^TEAM-Product-.$)`, only groups with names like TEAM-Engineering-A or TEAM-Product-B would get passed to the SaaS application.

### Add claims

To add additional OIDC claims onto the ID token sent to your SaaS application, configure the following fields for each claim:

    - **Name**: OIDC claim name
    - **Scope**: Select the OIDC scope where this claim should be included. In most cases, we recommend selecting `profile` since it already includes other custom claims from the IdP.
    - **IdP claim**: The identity provider value that should map to this OIDC claim. You can select any [SAML attribute](/cloudflare-one/integrations/identity-providers/generic-saml/#saml-headers-and-attributes) or [OIDC claim](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that was configured in a Zero Trust IdP integration.
    - **Required**: If a claim is marked as required but is not provided by an IdP, Cloudflare will fail the authentication request and show an error page.
    - **Add per IdP claim**: (Optional) If you turned on multiple identity providers for the SaaS application, you can choose different attribute mappings for each IdP. These values will override the parent **IdP claim**.

## Advanced settings

### Access token lifetime

The OIDC Access token authorizes users to connect to the SaaS application through Cloudflare Access. You can set an **Access token lifetime** to determine the window in which the token can be used to establish authentication with the SaaS application — if it expires, the user must re-authenticate through Cloudflare Access. To balance security and user convenience, Cloudflare recommends configuring a short Access token lifetime in conjunction with a longer **Refresh token lifetime** (if supported by your application). When the access token expires, Cloudflare will use the refresh token to obtain a new access token after checking the user's identity against your Access policies. When the refresh token expires, the user will need to log back in to the identity provider. The refresh token lifetime should be less than your [global session duration](/cloudflare-one/access-controls/access-settings/session-management/), otherwise the global session would take precedence.

:::note

<Render
	file="access/saas-apps/saas-sessions"
	product="cloudflare-one"
	params={{ session: "OIDC Access tokens", token: "Access token" }}
/>
:::

### OIDC flows

Some SaaS applications require SSO providers to provide tokens to the browser without backend authentication. Access for SaaS supports the following OIDC flows:

- **No additional OIDC flows**: (Default) Recommended unless your application requires additional flows.
- **Hybrid flows**: Used by applications that require information from the ID token before authenticating the user.
- **Implicit flows**: (Not recommended) Typically used by frontend applications that cannot store secrets and which do not support **PKCE without client secret**.

Cloudflare allows various `response_type` values in the authorization request depending on the selected flow. For example, the implicit flow allows Cloudflare to return the ID token, Access token, or both the ID token and Access token from the Authorization endpoint.

| `response_type` values | Default flow | Hybrid flow | Implicit flow |
| ---------------------- | ------------ | ----------- | ------------- |
| `code`                 | ✅           | ✅          | ❌            |
| `id_token`             | ❌           | ✅          | ✅            |
| `token`                | ❌           | ✅          | ✅            |

To include `id_token` in the authorization request, turn on **Return ID Token from Authorization Endpoint**. To include `token`, turn on **Return Access Token from Authorization Endpoint**

:::note
[Refresh tokens](#access-token-lifetime) are not supported with Hybrid or Implicit flows.
:::

# Generic SAML application

import { Render, Details } from "~/components";

This page provides generic instructions for setting up a SaaS application in Cloudflare Access using the SAML authentication protocol.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to the account of the SaaS application

## 1. Get SaaS application URLs

Obtain the following URLs from your SaaS application account:

- **Entity ID**: A unique URL issued for your SaaS application, for example `https://<your-domain>.my.salesforce.com`.
- **Assertion Consumer Service URL**: The service provider's endpoint for receiving and parsing SAML assertions.

## 2. Add your application to Access

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **SaaS application**.

4. Select your **Application** from the drop-down menu. If your application is not listed, enter a custom name in the **Application** field and select the textbox that appears below.

5. Select **SAML**.

6. Select **Add application**.

7. Enter the **Entity ID** and **Assertion Consumer Service URL** obtained from your SaaS application account.

8. Select the **Name ID Format** expected by your SaaS application (usually _Email_).

9. (Optional) Configure any additional [SAML attribute statements](#saml-attributes) required by your SaaS application.

10. Copy the **SSO endpoint**, **Access Entity ID or Issuer**, and **Public key**.

:::note[IdP groups]

If you are using Okta, Microsoft Entra ID (formerly Azure AD), Google Workspace, or GitHub as your IdP, Access will automatically send a SAML attribute titled `groups` with all of the user's associated groups as attribute values.
:::

11. <Render file="access/add-access-policies" product="cloudflare-one" />

12. <Render file="access/access-choose-idps-default" product="cloudflare-one" />

13. (Optional) Go to **Additional settings** to customize the application experience:

	- **App Launcher customization**: Configure how this application appears to users in the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/).
	- <Render file="access/access-block-page" product="cloudflare-one" />

14. Select **Create**.

## 3. Configure SSO in your SaaS application

Next, configure your SaaS application to require users to log in through Cloudflare Access. Refer to your SaaS application documentation for instructions on how to configure a third-party SAML SSO provider. You will need the following values from the Cloudflare One:

- **SSO endpoint**
- **Access Entity ID or Issuer**
- **Public key**

You can either manually enter this data into your SaaS application or upload a metadata XML file. The metadata is available at the URL: `<SSO endpoint>/saml-metadata`.

### Validate SAML Response

When acting as a SAML identity provider, Cloudflare will sign both the SAML Response and the SAML Assertion using the SHA-256 algorithm. The SaaS application can validate this signature using the **Public key** that you upload to the SaaS application.

## 4. Test the integration

Open an incognito browser window and go to the SaaS application's login URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

## SAML attributes

[SAML attributes](/cloudflare-one/integrations/identity-providers/generic-saml/#saml-headers-and-attributes) refer to the user identity characteristics that Cloudflare Access shares with your SAML SaaS application upon successful authentication. By default, Cloudflare Access passes the following attributes (if available) to the SaaS application:

- `id` - UUID of the user's Access identity
- `name` - Full name of the user (for example, `John Doe`)
- `email` - User's email address
- `groups` - Identity provider group membership

In Access for SaaS, you can add additional SAML attributes or customize the SAML statement sent to the SaaS application. This allows you to integrate SaaS applications which have specific SAML attribute requirements.

### SAML attribute statements

To send additional SAML attributes to your SaaS application, configure the following fields for each attribute:

	- **Name**: SAML attribute name
	- **SAML friendly name**: (Optional) A human readable name for the SAML attribute
	- **Name format**: Specify the **Name** format expected by the SaaS application:
			- `Unspecified`: (default) No specific format required.
			- `URI`: Name is in a format such as `urn:ietf:params:scim:schemas:core:2.0:User:userName` or `urn:oid:2.5.4.42`.
			- `Basic`: Name is a normal string such as `userName`.
	- **IdP claim**: The identity provider value that should map to this SAML attribute. You can select any [SAML attribute](/cloudflare-one/integrations/identity-providers/generic-saml/#saml-headers-and-attributes) or [OIDC claim](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that was configured in a Cloudflare One IdP integration.
	- **Required**: If an attribute is marked as required but is not provided by an IdP, Cloudflare will fail the authentication request and show an error page.
	- **Add per IdP claim**: (Optional) If you turned on multiple identity providers for the SaaS application, you can choose different attribute mappings for each IdP. These values will override the parent **IdP claim**.

### JSONata attribute transforms

In **Advanced settings** > **Transformation**, you can enter a [JSONata](https://jsonata.org/) script that modifies a copy of the [User Registry identity](/cloudflare-one/team-and-resources/users/users/). This is useful for setting default values, excluding email addresses, or ensuring usernames meet arbitrary criteria. Access will send the modified user identity to the SaaS application as SAML attributes.

This corresponds to the `saml_attribute_transform_jsonata` field in the [Access applications API](/api/resources/zero_trust/subresources/access/subresources/applications/methods/create/).

:::note
JSONata transformations are not compatible with [SAML attribute statements](#saml-attribute-statements). JSONata transformations will override any specified SAML attributes.
:::

For example, the following JSONata script merges group names into a list and adds an `eduPersonPrincipalName` field which maps to the user email.

```txt title = "JSONata expression"
$merge([$, {"groups": groups.name, 'eduPersonPrincipalName': email}])
```

Here is an example of a user identity before applying the JSONata transform:

```json title= "User identity before JSONata transform"
{
  "account_id": "699d98642c564d2e855e9661899b7252",
  "amr": [
    "pwd"
  ],
  "auth_status": "NONE",
  "common_name": "",
  "device_id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
  "device_sessions": {
    "49e653db-991e-11ee-af26-2243bf8c3428": {
      "last_authenticated": 1703004275
    }
  },
  "devicePosture": {
    "8534a230-e85e-4183-8964-a4b7dcf72986": {
      "rule_name": "Warp",
      "success": true,
      "type": "warp"
    }
  },
  "email": "jdoe@company.com",
  "gateway_account_id": "bTSquyUGwLQjYJn8cI8S1h6M6wU",
  "geo": {
    "country": "US"
  },
  "groups": [
    {
      "id": "12fdf91a-fb23-41b3-995a-de2f72c61d0e",
      "name": "IdentityProtection-RiskyUser-RiskLevel-low"
    },
    {
      "id": "12348f47-8234-4860-a03f-c2a1513f267b",
      "name": "Global Administrator"
    },
    {
      "id": "11235980-87d7-4917-b0aa-74c01914c40e",
      "name": "Application Administrator"
    }
  ],
  "iat": 1659474397,
  "id": "OidHvkPt-I-13IBSnd77UJ8cHgsrUpjs3W6_4t6ES7M",
  "idp": {
    "id": "b08e8c0c-a75d-4b3f-8e7b-cd427b7c7b47",
    "type": "azureAD"
  }
}
```

Result after applying the example JSONata script:

```json output
{
  "account_id": "699d98642c564d2e855e9661899b7252",
  "amr": [
    "pwd"
  ],
  "auth_status": "NONE",
  "common_name": "",
  "device_id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
  "device_sessions": {
    "49e653db-991e-11ee-af26-2243bf8c3428": {
      "last_authenticated": 1703004275
    }
  },
  "devicePosture": {
    "8534a230-e85e-4183-8964-a4b7dcf72986": {
      "rule_name": "Warp",
      "success": true,
      "type": "warp"
    }
  },
  "email": "jdoe@company.com",
  "gateway_account_id": "bTSquyUGwLQjYJn8cI8S1h6M6wU",
  "geo": {
    "country": "US"
  },
  "groups": [
    "IdentityProtection-RiskyUser-RiskLevel-low",
    "Global Administrator",
    "Application Administrator"
  ],
  "iat": 1659474397,
  "id": "OidHvkPt-I-13IBSnd77UJ8cHgsrUpjs3W6_4t6ES7M",
  "idp": {
    "id": "b08e8c0c-a75d-4b3f-8e7b-cd427b7c7b47",
    "type": "azureAD"
  },
  "eduPersonPrincipalName": "jdoe@company.com"
}
```

For more JSONata transform use cases, refer to the following examples.

<Details header="Remove groups attribute">

The following JSONata script removes the `groups` SAML attribute. This can be useful if your SaaS application does not need to receive user group information.

```txt title="JSONata expression"
$ ~> |$|{}, ['groups']|
```

Result after applying the JSONata transform:
```json output
{
  "account_id": "699d98642c564d2e855e9661899b7252",
  "amr": [
    "pwd"
  ],
  "auth_status": "NONE",
  "common_name": "",
  "device_id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
  "device_sessions": {
    "49e653db-991e-11ee-af26-2243bf8c3428": {
      "last_authenticated": 1703004275
    }
  },
  "devicePosture": {
    "8534a230-e85e-4183-8964-a4b7dcf72986": {
      "rule_name": "Warp",
      "success": true,
      "type": "warp"
    }
  },
  "email": "jdoe@company.com",
  "gateway_account_id": "bTSquyUGwLQjYJn8cI8S1h6M6wU",
  "geo": {
    "country": "US"
  },
  "iat": 1659474397,
  "id": "OidHvkPt-I-13IBSnd77UJ8cHgsrUpjs3W6_4t6ES7M",
  "idp": {
    "id": "b08e8c0c-a75d-4b3f-8e7b-cd427b7c7b47",
    "type": "azureAD"
  }
}
```
</Details>

<Details header="Rename groups field and remove group ID">

The following JSONata script changes the `groups.name` field from `name` to `group_name` and removes the `groups.id` field:

```txt title="JSONata expression"
{
  "account_id": account_id,
  "amr": amr,
  "auth_status": auth_status,
  "common_name": common_name,
  "devicePosture": devicePosture,
  "device_id": device_id,
  "device_sessions": device_sessions,
  "email": email,
  "gateway_account_id": gateway_account_id,
  "geo": geo,
	"groups": $map($.groups, function($group) {
    {"group_name": $group.name}}),
  "iat": iat,
  "id": id,
  "idp": idp
}
```

Result after applying the JSONata transform:

```json output
{
  "account_id": "699d98642c564d2e855e9661899b7252",
  "amr": [
    "pwd"
  ],
  "auth_status": "NONE",
  "common_name": "",
  "devicePosture": {
    "8534a230-e85e-4183-8964-a4b7dcf72986": {
      "rule_name": "Warp",
      "success": true,
      "type": "warp"
    }
  },
  "device_id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
  "device_sessions": {
    "49e653db-991e-11ee-af26-2210bf8c3428": {
      "last_authenticated": 1703004275
    }
  },
  "email": "jdoe@company.com",
  "gateway_account_id": "bTSquyUGwLQjYJn8cI8S1h6M6wU",
  "geo": {
    "country": "US"
  },
  "groups": [
    {
      "group_name": "IdentityProtection-RiskyUser-RiskLevel-low"
    },
    {
      "group_name": "Global Administrator"
    },
    {
      "group_name": "Application Administrator"
    }
  ],
  "iat": 1659474397,
  "id": "OidHvkPt-I-13IBSnd77UJ8cHgsrUpjs3W6_4t6ES7M",
  "idp": {
    "id": "b08e8c0c-a75d-4b3f-8e7b-cd427b7c7b47",
    "type": "azureAD"
  }
}
```

</Details>

<Details header="Filter groups by name">

The following JSONata script filters groups to those that match a regular expression.

```txt title="JSONata expression"
$merge([$, { "groups": $filter(groups, function($v) { $contains($v.name, /Administrator/) }) }])
```

Result after applying the JSONata transform:

```json output
{
  "account_id": "699d98642c564d2e855e9661899b7252",
  "amr": [
    "pwd"
  ],
  "auth_status": "NONE",
  "common_name": "",
  "device_id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
  "device_sessions": {
    "49e653db-991e-11ee-af26-2243bf8c3428": {
      "last_authenticated": 1703004275
    }
  },
  "devicePosture": {
    "8534a230-e85e-4183-8964-a4b7dcf72986": {
      "rule_name": "Warp",
      "success": true,
      "type": "warp"
    }
  },
  "email": "jdoe@company.com",
  "gateway_account_id": "bTSquyUGwLQjYJn8cI8S1h6M6wU",
  "geo": {
    "country": "US"
  },
  "groups": [
    {
      "id": "12348f47-8234-4860-a03f-c2a1513f267b",
      "name": "Global Administrator"
    },
    {
      "id": "11235980-87d7-4917-b0aa-74c01914c40e",
      "name": "Application Administrator"
    }
  ],
  "iat": 1659474397,
  "id": "OidHvkPt-I-13IBSnd77UJ8cHgsrUpjs3W6_4t6ES7M",
  "idp": {
    "id": "b08e8c0c-a75d-4b3f-8e7b-cd427b7c7b47",
    "type": "azureAD"
  }
}
```
</Details>

### NameID transform

By default, Access sends the user's email address as the SAML `NameID`. Some SaaS applications require a different value, such as an employee ID, a modified email address, or a username from a legacy system.

You can customize the `NameID` by setting the `name_id_transform_jsonata` field on the SaaS application via the [Access applications API](/api/resources/zero_trust/subresources/access/subresources/applications/methods/create/). This field accepts a [JSONata](https://jsonata.org/) expression that evaluates against the user's identity and must return a single string value. The result replaces the default `NameID` in the SAML assertion.

:::note
The NameID transform is only available through the API. To configure it, update the `saas_app.name_id_transform_jsonata` field on your Access application.
:::

For example, to modify the user's email so that it includes a `+sandbox` suffix (useful when connecting multiple instances of the same SaaS app):

```bash title="Set a NameID transform via the API"
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/access/apps/{app_id} \
--header "Authorization: Bearer {api_token}" \
--header "Content-Type: application/json" \
--data '{
  "saas_app": {
    "auth_type": "saml",
    "name_id_transform_jsonata": "$substringBefore(email, '\''@'\'') & '\''+sandbox@'\'' & $substringAfter(email, '\''@'\'')"
  }
}'
```

Given a user with the email `jdoe@company.com`, this expression produces a `NameID` of `jdoe+sandbox@company.com`.

<Details header="Use employee ID as NameID">

To send a non-email attribute such as an employee ID, reference the attribute name directly in the JSONata expression. The attribute must be available in the user's identity from the IdP.

```bash title="Set employee_id as NameID"
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/access/apps/{app_id} \
--header "Authorization: Bearer {api_token}" \
--header "Content-Type: application/json" \
--data '{
  "saas_app": {
    "auth_type": "saml",
    "name_id_transform_jsonata": "employee_id"
  }
}'
```

Given a user identity that contains `"employee_id": "efgh5678"`, the `NameID` sent in the SAML assertion will be `efgh5678`.

</Details>

<Details header="Remove NameID transform">

To revert to the default behavior (sending the user's email as the `NameID`), set the field to an empty string:

```bash title="Remove NameID transform"
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/access/apps/{app_id} \
--header "Authorization: Bearer {api_token}" \
--header "Content-Type: application/json" \
--data '{
  "saas_app": {
    "auth_type": "saml",
    "name_id_transform_jsonata": ""
  }
}'
```

</Details>

# GitHub Enterprise Cloud

This guide covers how to configure [GitHub Enterprise Cloud](https://docs.github.com/en/enterprise-cloud@latest/admin/managing-iam/using-saml-for-enterprise-iam/configuring-saml-single-sign-on-for-your-enterprise) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- A GitHub Enterprise Cloud subscription
- Access to a GitHub account as an organization owner

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _GitHub_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://github.com/orgs/<your-organization>`
   - **Assertion Consumer Service URL**: `https://github.com/orgs/<your-organization>/saml/consume`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint**, **Access Entity ID or Issuer**, and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create an X.509 certificate

1. Paste the **Public key** in a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

## 3. Configure an identity provider and SAML SSO in GitHub Enterprise Cloud

1. In your GitHub organization page, go to **Settings** > **Authentication security**.
2. Under **SAML single sign-on**, turn on **Enable SAML authentication**.
3. Fill in the following fields:
   - **Sign on URL**: SSO endpoint from application configuration in Cloudflare One.
   - **Issuer**: Access Entity ID or Issuer from application configuration in Cloudflare One.
   - **Public certificate**: Paste the entire x.509 certificate from step [2. Create a x.509 certificate](#2-create-a-x509-certificate).

## 4. Test the integration

Select **Test SAML configuration**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
When this is successful, select **Save**.

You can also turn on **Require SAML SSO authentication for all members of your organization** if you want to enforce SSO login with Cloudflare Access.

# Google Cloud

import { GlossaryTooltip } from "~/components";

This guide covers how to configure [Google Cloud](https://support.google.com/cloudidentity/topic/7558767) as a SAML application in Cloudflare One.

:::caution

When configuring Google Cloud with Access, the following limitations apply:

- Users will not be able to log in using [Google](/cloudflare-one/integrations/identity-providers/google/) or [Google Workspace](/cloudflare-one/integrations/identity-providers/google-workspace/) as an identity provider after Google Cloud is configured with Access.

- The integration of Access as a single sign-on provider for your Google Cloud account does not work for Google super admins. It will work for other users.
  :::

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Google Workspace account
- [Cloud Identity Free or Premium](https://support.google.com/cloudidentity/answer/7389973) set up in your organization's Google Cloud account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Google Cloud_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `google.com`
   - **Assertion Consumer Service URL**: `https://www.google.com/a/<your_domain.com>/acs`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint**, **Access Entity ID or Issuer**, and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create a x.509 certificate

1. Paste the Public key from application configuration in Cloudflare One into a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
3. Set the file extension as `.crt` and save.

## 3. Create an SSO provider in Google Cloud

1. In your [Google Admin console](https://admin.google.com/), go to **Security** > **Authentication** > **SSO with third party IdP**.
2. Select **Third-party SSO profile for your organization** > **Add SSO Profile**.
3. Turn on **Set up SSO with third-party identity provider**.
4. Fill in the following information:
   - **Sign-in page URL**: SSO endpoint from application configuration in Cloudflare One.
   - **Sign-out page URL**: `https://<team-name>.cloudflareaccess.com/cdn-cgi/access/logout`, where `<team-name>` is your Cloudflare One <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
   - **Verification certificate**: Upload the `.crt` certificate file from step [2. Create a x.509 certificate](#2-create-a-x509-certificate).
5. (Optional) Turn on **Use a domain specific issuer**. If you select this option, Google will send an issuer specific to your Google Cloud domain (`google.com/a/<your_domain.com>` instead of the standard `google.com`).

## 4. Test the integration

Open an incognito browser window and go to your Google Cloud URL (`https://console.cloud.google.com/a/<your_domain.com>`). Sign in using credentials that do not belong to a super admin account.

## Troubleshooting

`Error: "G Suite - This account cannot be accessed because the login credentials could not be verified."`

If you see this error, it is likely that the public key and private key do not match. Confirm that your certificate file includes the correct public key.

# Google Workspace

import { GlossaryTooltip } from "~/components";

This guide covers how to configure [Google Workspace](https://support.google.com/a/topic/7579248?ref_topic=7556686&sjid=14539485562330725560-NA) as a SAML application in Cloudflare One.

:::note

The integration of Access as a single sign-on provider for your Google Workspace account does not work for Google super admins. It will work for other users.

:::

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Google Workspace account

## 1. Create an application in Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **SaaS application**.

4. Fill in the following information:
   - **Application**: _Google_.
   - **Entity ID**: Use the value provided to you by Google when [configuring your SAML SSO provider](https://saml-doc.okta.com/SAML_Docs/How-to-Enable-SAML-2.0-in-Google-Apps.html).
   - **Assertion Consumer Service URL**: `https://www.google.com/a/<your_domain.com>/acs`, where `<your_domain.com>` is your Google Workspace domain.
   - **Name ID Format**: _Email_.

:::caution

When you put your Google Workspace behind Access, users will not be able to log in using [Google](/cloudflare-one/integrations/identity-providers/google/) or [Google Workspace](/cloudflare-one/integrations/identity-providers/google-workspace/) as an identity provider. To secure Google Workspace behind Access and avoid an [authentication loop](/cloudflare-one/access-controls/troubleshooting/#google-workspace-redirect-loop), you must configure a different identity provider (not Google or Google Workspace) for authentication.
:::

5. [Create an Access policy](/cloudflare-one/access-controls/policies/) for your application. For example, you could allow users with an `@your_domain.com` email address.

6. Copy the **SSO endpoint**, **Access Entity ID or Issuer**, and **Public key**. These values will be used to configure Google Workspace.

7. Save the application.

## 2. Create a certificate from your public key

1. Copy and then paste your **Public key** into a text editor.

2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`. For example,

   ```txt
   -----BEGIN CERTIFICATE-----
   <PUBLIC_KEY>
   -----END CERTIFICATE-----
   ```

3. Set the file extension as `.crt` and save.

## 3. Create an SSO provider in Google Workspace

1. Log in to your [Google Admin console](https://admin.google.com/).
2. Go to **Security** > **Authentication** > **SSO with third party IdP**.
3. Select **Third-party SSO profile for your organization**.
4. Enable **Set up SSO with third-party identity provider**.
5. Fill in the following information:
   - **Sign-in page URL**: Copy and then paste your **SSO endpoint** from Cloudflare One.
   - **Sign-out page URL**: `https://<team-name>.cloudflareaccess.com/cdn-cgi/access/logout`, where `<team-name>` is your Cloudflare One <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
   - **Verification certificate**: Upload the certificate file containing your public key.
6. (Optional) Enable **Use a domain specific issuer**. If you select this option, Google will send an issuer specific to your Google Workspace domain (`google.com/a/<your_domain.com>` instead of the standard `google.com`).

## 4. Test the integration

1. In your [Google Admin console](https://admin.google.com/), go to **Apps** > **Google Workspace** > **Gmail** > **Setup**.
2. Copy your Gmail **Web address**.
3. Open an incognito browser window and go to your Gmail web address (for example, `https://mail.google.com/a/<your_domain.com>`).

An Access login screen should appear.

## Troubleshooting

`Error: "G Suite - This account cannot be accessed because the login credentials could not be verified."`

If you see this error, it is likely that the public key and private key do not match. Confirm that your certificate file includes the correct public key.

# Grafana Cloud

This guide covers how to configure [Grafana Cloud](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/authorization/#configure-oauth-20-with-generic-oauth) as an OIDC application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Grafana Cloud account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application**.
3. Select **SaaS application**.
4. For **Application**, enter `Grafana Cloud` and select the corresponding textbox that appears.
5. For the authentication protocol, select **OIDC**.
6. Select **Add application**.
7. In **Scopes**, select the attributes that you want Access to send in the ID token.
8. In **Redirect URLs**, enter `https://<your-grafana-domain>/login/generic_oauth`.
9. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.
10. Copy the **Client secret**, **Client ID**, **Token endpoint**, and **Authorization endpoint**.
11. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
12. (Optional) In **Experience settings**, configure [App Launcher settings](/cloudflare-one/access-controls/access-settings/app-launcher/) by turning on **Enable App in App Launcher** and, in **App Launcher URL**, entering `https://<your-grafana-domain>/login`.
13. Save the application.

## 2. Add a SSO provider to Grafana Cloud

1. In Grafana Cloud, select the **menu** icon > **Administration** > **Authentication** > **Generic OAuth**.
2. (Optional) For **Display name**, enter a new display name (for example, `Cloudflare Access`). Users will select **Sign in with (display name)** when signing in via SSO.
3. Fill in the following fields:
   - **Client Id**: Client ID from application configuration in Cloudflare One
   - **Client secret**: Client secret from application configuration in Cloudflare One
   - **Scopes**: Delete `user:email` and enter the scopes configured in Cloudflare One
   - **Auth URL**: Authorization endpoint from application configuration in Cloudflare One
   - **Token URL**: Token endpoint from application configuration in Cloudflare One
4. Select **Save**.

## 3. Test the integration

Open an incognito browser window and go to your Grafana domain (`https://<your-grafana-domain>/login`). Select **Sign in with (display name)**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

# Grafana

This guide covers how to configure [Grafana](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/) as an OIDC application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Grafana account

:::note
You can also configure OIDC SSO for Grafana using a [configuration file](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/#configure-generic-oauth-authentication-client-using-the-grafana-configuration-file) instead of using Grafana's user interface (UI), as documented in this guide.
:::

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application**.
3. Select **SaaS application**.
4. For **Application**, select _Grafana_.
5. For the authentication protocol, select **OIDC**.
6. Select **Add application**.
7. In **Scopes**, select the attributes that you want Access to send in the ID token.
8. In **Redirect URLs**, enter `https://<your-grafana-domain>/login/generic_oauth`.
9. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.
10. Copy the **Client secret**, **Client ID**, **Token endpoint**, and **Authorization endpoint**.
11. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
12. (Optional) In **Experience settings**, configure [App Launcher settings](/cloudflare-one/access-controls/access-settings/app-launcher/) by turning on **Enable App in App Launcher** and, in **App Launcher URL**, entering `https://<your-grafana-domain>/login`.
13. Save the application.

## 2. Add a SSO provider to Grafana

1. In Grafana, select the **menu** icon > **Administration** > **Authentication** > **Generic OAuth**.
2. (Optional) For **Display name**, enter a new display name (for example, `Cloudflare Access`). Users will select **Sign in with (display name)** when signing in via SSO.
3. Fill in the following fields:
   - **Client Id**: Client ID from application configuration in Cloudflare One
   - **Client secret**: Client secret from application configuration in Cloudflare One
   - **Scopes**: Delete `user:email` and enter the scopes configured in Cloudflare One
   - **Auth URL**: Authorization endpoint from application configuration in Cloudflare One
   - **Token URL**: Token endpoint from application configuration in Cloudflare One
4. Select **Save**.

## 3. Test the integration

Log out, then select **Sign in with (display name)**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

# Greenhouse Recruiting

This guide covers how to configure [Greenhouse Recruiting](https://support.greenhouse.io/hc/en-us/articles/360040753811-Configure-single-sign-on-SSO-for-Greenhouse-Recruiting) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to an Advanced or Expert Greenhouse Recruiting site

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Greenhouse` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Copy the **SAML Metadata endpoint**.
7. Keep this window open. You will finish this configuration in step [4. Finish adding a SaaS application to Cloudflare One](#4-finish-adding-a-saas-application-to-cloudflare-one).

## 2. Download the metadata file

1. Paste the SAML Metadata endpoint from application configuration in Cloudflare One in a web browser.
2. Follow your browser-specific steps to download the URL's contents as an `.xml` file.

## 3. Add a SAML SSO provider to Greenhouse

1. In Greenhouse Recruiting, go to the **Configure** icon > **Dev Center** > **Single sign-on**.
2. Copy the **SSO Assertion Consumer URL**.
3. Under **Upload XML file**, select **Choose a file**, and upload the `.xml` file created in step [2. Download the metadata file](#2-download-the-metadata-file).
4. Change the **Entity ID** to `greenhouse.io`.
5. Keep this window open without selecting **Begin testing**. You will finish this configuration in step [5. Test the integration and finalize configuration](#5-test-the-integration-and-finalize-configuration).

## 4. Finish adding a SaaS application to Cloudflare One

1. In your open Cloudflare One window, fill in the following fields:
   - **Entity ID**: `greenhouse.io`
   - **Assertion Consumer Service URL**: SSO Assertion Consumer URL from SSO configuration in Greenhouse Recruiting.
   - **Name ID format**: _Email_
2. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
3. Save the application.

## 5. Test the integration and finalize configuration

1. In your open Greenhouse Recruiting window, select **Begin Testing** > **Proceed**.
2. Open an incognito browser window and go to your Greenhouse Recruiting URL. Choose the SSO login option. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
3. Once SSO sign in is successful, go to the **Configure** icon > **Dev Center** > **Single sign-on**.
4. Select **Finalize Configuration**.
5. In the text field, enter `CONFIGURE`.
6. Select **Finalize**. Now, users will only be able to sign in with SSO.

# Hubspot

This guide covers how to configure [Hubspot](https://knowledge.hubspot.com/account-security/set-up-single-sign-on-sso) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Hubspot Enterprise plan account

## 1. Configure Hubspot

1. Go to **Settings** > **Account**, then go to **Defaults** > **Security**.
2. Select _Single Sign-on_.
3. Copy the values for _Audience URI_ and _Sign on URL_.

## 2. Configure Cloudflare Access

1. In Cloudflare One, go to **Access controls** > **Applications**, select **Create new application**, and select **SaaS application**.

2. Set the **Application type** to _Hubspot_.

3. Use the following Hubspot field mappings:

   | Hubspot values | Cloudflare values              |
   | -------------- | ------------------------------ |
   | Audience URI   | Entity ID                      |
   | Sign On URL    | Assertion Consumer Service URL |

4. Set **NameID** to _Email_.

5. Add any desired [Access policies](/cloudflare-one/access-controls/policies/) to your application.

6. Copy the **SSO endpoint** and **Access Entity ID**.

7. Save the application.

## 3. Create a x.509 certificate

1. Paste the **Public key** in a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

## 4. Finalize Hubspot configuration

1. Use the following field mappings:

   | Cloudflare value | Hubspot value                        |
   | ---------------- | ------------------------------------ |
   | SSO endpoint     | Identity Provider Single Sign-on URL |
   | Entity ID        | Identity Provider Identifier         |
   | Public key       | Certificate                          |

2. Select **Verify** to validate the integration.

Your configuration is now complete. Hubspot SSO can be switched on for specific users or the entire account.

# SaaS applications

import { DirectoryListing } from "~/components"

Cloudflare Access allows you to add an additional authentication layer to your SaaS applications. When you integrate a SaaS application with Access, users log in to the application with Cloudflare as the Single Sign-On provider. The user is then redirected to the configured identity providers for that application and are only granted access if they pass your Access policies.

Cloudflare integrates with the majority of SaaS applications that support the SAML or OIDC authentication protocol. If you do not see your application listed below, refer to our [generic SAML](/cloudflare-one/access-controls/applications/http-apps/saas-apps/generic-saml-saas/) or [generic OIDC](/cloudflare-one/access-controls/applications/http-apps/saas-apps/generic-oidc-saas/) guide and consult your SaaS application's documentation.

<DirectoryListing />

# Ironclad

This guide covers how to configure [Ironclad](https://support.ironcladapp.com/hc/articles/12286012625559-Set-Up-Generic-SSO-SAML-Integration) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Ironclad site

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Ironclad` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Copy the **SSO Endpoint** and **Public key**.
7. Keep this window open. You will finish this configuration in step [3. Finish adding a SaaS application to Cloudflare One](#3-finish-adding-a-saas-application-to-cloudflare-one).

## 2. Add a SAML SSO provider to Ironclad

1. In Ironclad, select your profile picture > **Company settings** > **Integrations** > **SAML**.
2. Select **Add SAML Configuration** > **Show Additional IdP Settings**.
3. Copy the **Callback** value.
4. Fill in the following fields:
   - **Entry Point**: SSO endpoint from application configuration in Cloudflare One.
   - **Identity Provider Certificate**: Public key from application configuration in Cloudflare One. The key will automatically be wrapped in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
5. Select **Save**.

## 3. Finish adding a SaaS application to Cloudflare One

1. In your open Cloudflare One window, fill in the following fields:
   - **Entity ID**: `ironcladapp.com`
   - **Assertion Consumer Service URL**: Callback from Ironclad SAML SSO set-up.
   - **Name ID format**: _Email_
2. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
3. Save the application.

## 4. Add a test user to Ironclad and test the integration

1. In Ironclad, select your profile picture > **Company settings** > **Users & Groups**.
2. Select **Invite User**.
3. For **Email addresses**, add your desired email address for your test user.
4. For **Sign-in Method**, ensure **Sign in with (your-team-domain.cloudflareaccess.com)** is selected
5. Select **Invite**.
6. In the invitation email sent to the test user, select **Join now**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
7. Once this is successful, you can contact your account team or `support@ironcladapp.com` to migrate existing users to SSO login.

# Jamf Pro

This guide covers how to configure [Jamf Pro](https://learn.jamf.com/en-US/bundle/jamf-pro-documentation-current/page/Single_Sign-On.html) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Jamf Pro account

## 1. Collect Jamf Pro information

1. In Jamf Pro, go to **Settings** > **Systems** > **Single Sign-On** > **Edit**.
2. Copy the pre-populated URL in **Entity ID**.
3. Paste the URL in a web browser to download the Jamf metadata file.
4. Open the `metadata.xml` file in a text editor, and copy the values for **Entity ID** and **Assertion Consumer Service**.

## 2. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Jamf` or `Jamf Pro` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: Entity ID value from Jamf Pro metadata file.
   - **Assertion Consumer Service URL**: Assertion Consumer Service value from Jamf Pro metadata file.
   - **Name ID format**: _Email_
7. Copy the **SAML Metadata endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 3. Edit Access SAML Metadata

1. Paste the **SAML Metadata endpoint** from application configuration in Cloudflare One into a browser.
2. Copy the file and paste it into a text editor.
3. Change `WantAuthnRequestsSigned="true"` to `WantAuthnRequestsSigned="false"`.
4. Set the file extension as `.xml` and save.

## 4. Add a SAML SSO provider to Jamf Pro

1. In Jamf Pro, go to **Settings** > **Single Sign-On** > **Edit**.
2. In Identity Provider menu, select **Other**.
3. Label **Other provider** as `Cloudflare`.
4. Fill in the following fields:
   - **Entity ID**: Entity ID from Jamf Pro metadata file.
   - **Identity Provider Metadata Source**: Select **Metadata File** and upload the `.xml` file from step [2. Edit Access SAML Metadata](#2-add-a-saas-application-to-cloudflare-one).
   - **Identity Provider User Mapping**: _Name ID_
   - **Jamf Pro User Mapping**: _Email_
5. Turn on **Single Sign On**.

:::note
The Failover Login URL located on this page can be used to log in if your SSO does not work.
:::

## 5. Test the Integration

Log out of Jamf Pro and open an incognito browser window. Go to your Jamf Pro URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

# Miro

import { Render, TabItem, Tabs } from "~/components";

This guide covers how to configure [Miro](https://help.miro.com/hc/articles/360017571414-Single-sign-on-SSO) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Miro Business or Enterprise plan account
- A [verified domain](https://help.miro.com/hc/articles/360034831793-Domain-control) added to your Miro account (Enterprise plan), or be prepared to do so during SSO configuration (Business or Enterprise plan)

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Miro` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://miro.com/`
   - **Assertion Consumer Service URL**: `https://miro.com/sso/saml`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint** and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Add a SAML SSO provider to Miro

<Tabs> <TabItem label="business plan">

<Render
	file="access/saas-apps/miro-sso"
	product="cloudflare-one"
	params={{ one: "**Security**", two: "Enter" }}
/>

</TabItem>

<TabItem label="enterprise plan">

<Render
	file="access/saas-apps/miro-sso"
	product="cloudflare-one"
	params={{
		one: "**Security and Compliance** > **Authentication** > **Single sign-on**",
		two: "If you have not previously [verified the domain](https://help.miro.com/hc/articles/360034831793-Domain-control), enter",
	}}
/>

</TabItem> </Tabs>

## 3. Test the integration

In the Miro SAML/SSO configuration page, select **Test SSO Configuration**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider. If the login is successful, you will receive a **SSO configuration test was successful** message.

:::note
When testing the integration, you do not have to use an email from a domain you have configured for SSO or a user configured in Miro. The only requirement is that the user is already configured in your identity provider.
:::

# PagerDuty

This guide covers how to configure [PagerDuty](https://support.pagerduty.com/docs/sso) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a PagerDuty site

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _PagerDuty_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://<your-subdomain>.pagerduty.com`
   - **Assertion Consumer Service URL**: ` https://<your-subdomain>.pagerduty.com/sso/saml/consume`
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint** and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create a x.509 certificate

1. Paste the **Public key** in a text editor.
2. Amend the public key so each row is a maximum of 64 characters long. Originally, each full row of the public key is 65 characters long.
3. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

## 3. Add a SAML SSO provider to PagerDuty

1. In PagerDuty, select your profile picture and go to **Account Settings** > **Single Sign-on**.
2. Turn on **SAML**.
3. In **X.509 Certificate**, paste the entire x.509 certificate from step [2. Create a x.509 certificate](#2-create-a-x509-certificate).
4. In **Login URL**, paste the SSO endpoint from application configuration in Cloudflare One.
5. Select **Save Changes**.

## 4. Test the integration and finalize SSO configuration

1. Open an incognito window and paste your PagerDuty URL into the address bar. Select **Sign In With Single Sign-On**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
2. In an incognito window, paste your PagerDuty URL and select **Sign In With Single Sign-On**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
3. Once SSO sign in is successful, select your profile picture and go to **Account Settings** > **Single Sign-on**.
4. Turn off **Allow username/password login** and select **Save Changes**. Now, users will only be able to sign in with SSO.

# Pingboard

This guide covers how to configure [Pingboard](https://support.pingboard.com/hc/en-us/articles/360046585994-Set-Up-a-Custom-SSO-Solution) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Pingboard account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Pingboard` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `http://app.pingboard.com/sp`
   - **Assertion Consumer Service URL**: `https://sso-demo.pingboard.com/auth/saml/consume`
   - **Name ID format**: _Email_
7. Copy the **SAML Metadata endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Add a SAML SSO provider to Pingboard

1. In Pingboard, go to **Account** > **Add-Ons**.
2. Under **Third-Party Integrations**, select **Custom SSO**.
3. In a web browser, paste the SAML Metadata endpoint you copied from the application configuration in Cloudflare One. Next, copy the contents of the displayed page.
4. In Pingboard, under **IdP Metadata**, paste the contents from the SAML Metadata endpoint.
5. (Optional) Under **Sign in with**, enter a name (for example, `Cloudflare Access`). Your users will select this name when signing in.

## 3. Test the integration

Open an incognito browser window and go to your Pingboard URL. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

# Salesforce (OIDC)

import { Render } from "~/components";

This guide covers how to configure [Salesforce](https://help.salesforce.com/s/articleView?id=sf.sso_provider_openid_connect.htm&type=5) as an OpenID Connect (OIDC) application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Salesforce account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Salesforce_.
4. For the authentication protocol, select **OIDC**.
5. Select **Add application**.
6. In **Scopes**, select the attributes that you want Access to send in the ID token.
7. In **Redirect URLs**, enter the callback URL obtained from Salesforce (`https://<your-domain>.my.salesforce.com/services/authcallback/<URL Suffix>`). Refer to [Add a SSO provider to Salesforce](#2-add-a-sso-provider-to-salesforce) for instructions on obtaining this value.
8. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.
9. Copy the following values:
   - **Client ID**
   - **Client Secret**
   - **Authorization endpoint**
   - **Token endpoint**
   - **User info endpoint**
10. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
11. (Optional) In **Experience settings**, configure [App Launcher settings](/cloudflare-one/access-controls/access-settings/app-launcher/) by turning on **Enable App in App Launcher** and, in **App Launcher URL**, entering `https://<your-domain>.my.salesforce.com`.
12. Save the application.

## 2. Add a SSO provider to Salesforce

1. In Salesforce, go to **Setup**.
2. In the **Quick Find** box, enter `auth` and select **Auth providers**.
3. Select **New**.
4. For the provider type, select **OpenID Connect**.
5. Enter a name for the SSO provider (for example, `Cloudflare Access`).
6. Fill in the following fields with values obtained from Cloudflare Access:
   - **Consumer Key**: Client ID
   - **Consumer Secret**: Client Secret
   - **Authorize Endpoint URL**: Authorization endpoint
   - **Token endpoint URL**: Token endpoint
   - **User Info Endpoint URL**: User info endpoint
   - **Token Issuer**: Issuer
7. (Optional) Enable **Use Proof Key for Code Exchange** if you enabled it in Access.
8. In **Default Scopes**, enter a space-separated list of the scopes you configured in Access (for example, `openid email profile groups`).
9. Select **Save**.
10. Copy the **Callback URL**:
    ```txt
    https://<your-domain>.my.salesforce.com/services/authcallback/<URL Suffix>
    ```
11. In Cloudflare One, paste the Callback URL into the **Redirect URL** field.

To test the integration, open an incognito browser window and go to the **Test-Only Initialization URL** ( `https://<your-domain>.my.salesforce.com/services/auth/test/<URL Suffix>`)

## 3. Enable Single Sign-On in Salesforce

1. <Render file="access/saas-apps/salesforce-sso" product="cloudflare-one" />
2. (Optional) To require users to login with Cloudflare Access:
   1. In the **Quick Find** box, enter `single sign-on` and select **Single Sign-On Settings**.
   2. Turn on **Disable login with Salesforce credentials**.

To test, open an incognito browser window and go to your Salesforce domain (`https://<your-domain>.my.salesforce.com`).

# Salesforce (SAML)

import { Details, Render } from "~/components";

This guide covers how to configure [Salesforce](https://help.salesforce.com/s/articleView?id=sf.sso_saml.htm&type=5) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Salesforce account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Salesforce_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://<your-domain>.my.salesforce.com` or `https://<your-domain>.my.salesforce.com?so=<your-salesforce-org-id>`, if your account was created before summer 2019 or does not have a My Domain subdomain.
   - **Assertion Consumer Service URL**: `https://<your-domain>.my.salesforce.com` or `https://<your-domain>.my.salesforce.com?so=<your-salesforce-org-id>`, if your account was created before summer 2019 or does not have a My Domain subdomain.
   - **Name ID format**: _Email_

:::note
If you are unsure of which URL to use in the **Entity ID** and **Assertion Consumer Service URL** fields, you can check your Salesforce account's metadata. In Salesforce, go to the **Single Sign-On Settings** page and select **Download Metadata**. In this file, you will find the correct URLs to use.
:::

7. Copy the **SSO endpoint**, **Public key**, and **Access Entity ID or Issuer**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create a certificate file

1. Paste the **Public key** in a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.
3. Set the file extension as `.crt` and save.

## 3. Add a SAML SSO provider to Salesforce

1. In Salesforce, go to **Setup**.
2. In the **Quick Find** box, enter `single sign-on` and select **Single Sign-On Settings**.
3. In **SAML Single Sign-On Settings**, select **New**.
4. Fill in the following fields:
   - **Name:** Name of the SSO provider (for example, `Cloudflare Access`). Users will select this name when signing in to Salesforce.
   - **API name:** (this will pre-populate)
   - **Issuer:** Paste the Access Entity ID or Issuer from application configuration in Cloudflare One.
   - **Identity Provider Certificate**: Upload the `.crt` certificate file from [2. Create a certificate file](#2-create-a-certificate-file).
   - **Entity ID**: `https://<your-domain>.my.salesforce.com`
   - **SAML Identity type:** If the user's Salesforce username is their email address, select _Assertion contains the User's Salesforce username_. Otherwise, select _Assertion contains the Federation ID from the User object_ and make sure the user's Federation ID matches their email address.

   <Details header="Configure Federation IDs">
   	1. In the **Quick Find** box, enter `users` and select **Users**. 2. Select
   	the user. 3. Verify that the user's **Federation ID** matches the email
   	address used to authenticate to Cloudflare Access.
   </Details>
   - **Identity Provider Login URL**: SSO endpoint provided in Cloudflare One
   for this application.

5. Select **Save**.

## 4. Enable Single Sign-On in Salesforce

1. Configure Single Sign-On settings:
   1. In the **Quick Find** box, enter `single sign-on` and select **Single Sign-On Settings**.
   2. (Optional) To require users to login with Cloudflare Access, turn on **Disable login with Salesforce credentials**.
   3. Turn on **SAML Enabled**.
   4. Turn on **Make federation ID case-insensitive**.
2. <Render file="access/saas-apps/salesforce-sso" product="cloudflare-one" />

To test, open an incognito browser window and go to your Salesforce domain (`https://<your-domain>.my.salesforce.com`).

# ServiceNow (OIDC)

This guide covers how to configure [ServiceNow](https://docs.servicenow.com/bundle/washingtondc-platform-security/page/integrate/single-sign-on/task/create-OIDC-configuration-SSO.html) as an OIDC application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a ServiceNow account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `ServiceNow` and select the corresponding textbox that appears.
4. For the authentication protocol, select **OIDC**.
5. Select **Add application**.
6. In **Scopes**, select the attributes that you want Access to send in the ID token.
7. In **Redirect URLs**, enter `https://<INSTANCE-NAME>.service-now.com/navpage.do`.
8. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.
9. Copy the **Client secret** and **Client ID**.
10. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
11. (Optional) In **Experience settings**, configure [App Launcher settings](/cloudflare-one/access-controls/access-settings/app-launcher/) by turning on **Enable App in App Launcher** and, in **App Launcher URL**, entering `https://<INSTANCE-NAME>.service-now.com`.
12. Save the application.

## 2. Add the Multiple Provider Single Sign-On Installer Plugin to ServiceNow

1. In ServiceNow, select **All**.
2. In the search bar, enter `System Applications`, and under **All Available Applications**, select **All**.
3. In the search bar, enter `Integration - Multiple Provider Single Sign-On Installer`.
4. Select **Install**.
5. Ensure that **Install now** is selected, and select **Install**.

## 3. Add and Test an OIDC SSO provider in ServiceNow

1. Select **All**.
2. In the search bar enter `Multi-Provider SSO`, and select **Identity Providers**.
3. Select **New** > **OpenID Connect**.
4. In the pop-up, fill in the following fields:
   - **Name**: Name of the SSO (for example, `Cloudflare Access`). Unless otherwise configured, users will select this name when signing in to ServiceNow.
   - **Client ID**: **Client ID** from application configuration in Cloudflare One.
   - **Client Secret**: **Client Secret** from application configuration in Cloudflare One.
   - **Well Known Configuration URL**: `https://<TEAM-DOMAIN>.cloudflareaccess.com/cdn-cgi/access/sso/oidc/<CLIENT-ID>/.well-known/openid-configuration`.
5. Select **Import**.
6. Ensure **Active** is turned on
7. Turn on **Show as Login option**, and for **SSO label** enter a label for the user login screen, if desired.
8. Select **Update**.

## 4. Test the integration

For SSO to appear on the login screen, you must have [account recovery](https://docs.servicenow.com/bundle/washingtondc-platform-security/page/integrate/single-sign-on/concept/sso-acct-recovery.html) enabled and configured for at least one admin account. After account recovery is configured, log out of ServiceNow and open an incognito browser window. Go to your ServiceNow URL. Select the SSO name you just configured, which will prompt you to sign in with your identity provider. When the integration is successful, you can go back to the OIDC configuration screen to turn on **Default** and/or **Auto Redirect IDP**.

# ServiceNow (SAML)

This guide covers how to configure [ServiceNow](https://docs.servicenow.com/bundle/washingtondc-platform-security/page/integrate/single-sign-on/task/t_CreateASAML2Upd1SSOConfigMultiSSO.html) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a ServiceNow account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `ServiceNow` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://<INSTANCE-NAME>.service-now.com`
   - **Assertion Consumer Service URL**: `https://<INSTANCE-NAME>.service-now.com/navpage.do`
   - **Name ID format**: _Email_
7. Copy the **SAML Metadata endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Add the Multiple Provider Single Sign-On Installer Plugin to ServiceNow

1. In ServiceNow, select **All**.
2. In the search bar, enter `System Applications`, and under **All Available Applications**, select **All**.
3. In the search bar, enter `Integration - Multiple Provider Single Sign-On Installer`.
4. Select **Install**.
5. Ensure that **Install now** is selected, and select **Install**.

## 3. Add and Test a SAML SSO provider in ServiceNow

1. Select **All**.
2. In the search bar enter `Multi-Provider SSO`, and select **Identity Providers**.
3. Select **New** > **SAML**.
4. In the pop-up, ensure that **URL** is selected.
5. Paste the **SAML Metadata endpoint** from application configuration in Cloudflare One in the empty field.
6. Select **Import**.
7. (Optional) Change the **Name** field to a more recognizable name.
8. Turn off **Sign AuthnRequest**.
9. Select **Update**.
10. In the pop-up, select **Cancel** and then **>**.
11. Select the **Name** of the configuration you just completed.
12. Select **Test Connection**.
13. If the test succeeds, select **Activate**.

# Slack

import { TabItem, Tabs } from "~/components";

This guide covers how to configure [Slack](https://slack.com/help/articles/203772216-SAML-single-sign-on) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Slack Business+ or Enterprise Grid plan account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Slack_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `https://slack.com`
   - **Assertion Consumer Service URL**: `https://<YOUR_DOMAIN>.slack.com/sso/saml`
   - **Name ID format**: The format expected by Slack, usually _Email_
7. Copy the **SSO endpoint**, **Access Entity ID or Issuer**, and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create a x.509 certificate

1. Paste the **Public key** in a text editor.
2. Wrap the certificate in `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

## 3. Add a SAML SSO provider to Slack

<Tabs> <TabItem label="business+ plan">

1. In Slack, go to **Settings & administrations** > **Workspace settings** > **Authentication**.
2. Select **Configure**.
3. Turn on **Test**. Configuration changes will not apply until **Configure** is turned on.
4. Fill in the following fields:
   - **Service Provider Issuer URL**: Ensure set to `https://slack.com`.
   - **SAML SSO URL**: SSO endpoint from application configuration in Cloudflare One.
   - **Identity Provider Issuer**: Access Entity ID or Issuer from application configuration in Cloudflare One.
   - **Public Certificate**: Paste the entire x.509 certificate from step [2. Create a x.509 certificate](#2-create-a-x509-certificate).
5. Under **Advanced Options**, select **Expand**.
6. For **AuthnContextClassRef**, ensure _urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport_ is selected.
7. Ensure **Sign the AuthnRequest** is turned off.
8. For **SAML Response Signing**, turn on **Sign the Response** and **Sign the Assertion**.
9. In the main configuration page under **Settings**, choose whether SSO is _required_, _partially required_, or _optional_ for workspace members.
10. (Optional) Under **Customize**, enter a **Sign in Button Label**.
11. Test your set-up. If all works well, turn **Test** to **Configure**.

</TabItem>

<TabItem label="enterprise grid plan">

1. In Slack, go to **Settings & administration** > **Organization settings** > **Security** > **SSO Settings**.
2. For **SSO name**, enter your desired name.
3. Fill in the following fields:
   - **SAML 2.0 Endpoint URL**: SSO endpoint from application configuration in Cloudflare One.
   - **Identity Provider Issuer URL**: Access Entity ID or Issuer from application configuration in Cloudflare One.
   - **Service Provider Issuer URL**: Ensure set to `https://slack.com`.
   - **x.509 Certificate**: Paste the entire x.509 certificate from step [2. Create a x.509 certificate](#2-create-a-x509-certificate).
4. For **AuthnContextClassRef**, ensure _urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport_ is selected.
5. Ensure **Sign the AuthnRequest** is turned off.
6. For **SAML Response Signing**, turn on **Sign the Response** and **Sign the Assertion**.
7. Select **Test Configuration**.
8. If all works well, select **Turn on SSO** or **Add SSO**.

</TabItem> </Tabs>

# Smartsheet

This guide covers how to configure [Smartsheet](https://help.smartsheet.com/articles/2483123-domain-level-saml-configuration) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Smartsheet Enterprise account
- A [domain](https://help.smartsheet.com/articles/2483051-domain-management) verified in Smartsheet

:::note
In Smartsheet, SSO is configured for a domain. If you have multiple plans using the same domain, the SSO configuration will apply to all Smartsheet users in that domain, regardless of their plan type.
:::

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Smartsheet` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `urn:amazon:cognito:sp:us-east-1_xww1cbP43`
   - **Assertion Consumer Service URL**: `https://saml.authn.smartsheet.com/saml2/idpresponse`
   - **Name ID format**: _Unique ID_
7. Copy the **SAML Metadata endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Create and test a SAML SSO provider in Smartsheet

1. In your Smartsheet Admin Center, go to **Settings** > **Authentication** > **Add a SAML IdP**.
2. In **Other IdP (Customize)**, select **Configure**.
3. Select **Next**.
4. Under **XML URL**, paste the SAML Metadata endpoint from application configuration in Cloudflare One.
5. Under **Name SAML IdP**, enter a name (for example, `Cloudflare Access`).
6. Select **Save & Next**.
7. Select **Verify connection** and sign in via Access. If validation is successful, you will see a **SAML IdP Successfully Connected!** message. Close the configuration verification page.
8. Turn on **I have successfully verified the connection**.
9. Select **Save & Next**.
10. Under **Assign domains to SAML IdP**, select your desired domain.
11. Select **Save and Next** and then **Finish**.

# SparkPost

This guide covers how to configure [SparkPost or SparkPost EU](https://support.sparkpost.com/docs/my-account-and-profile/sso) as a SAML application in Cloudflare Zero Trust.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a SparkPost or SparkPost EU account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `SparkPost` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**:
     - `https://api.sparkpost.com` for SparkPost accounts
     - `https://api.eu.sparkpost.com` for SparkPost EU accounts
     - `https://<api-host>` for SparkPost accounts with dedicated tenants
   - **Assertion Consumer Service URL**:
     - `https://api.sparkpost.com/api/v1/users/saml/consume` for SparkPost accounts
     - `https://api.eu.sparkpost.com/api/v1/users/saml/consume` for SparkPost EU accounts
     - `https://<api-host>/api/v1/users/saml/consume` for SparkPost accounts with dedicated tenants
   - **Name ID format**: _Email_
7. Copy the **SAML Metadata endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Download the metadata file

1. Paste the SAML metadata endpoint from application configuration in Cloudflare One in a web browser.
2. Follow your browser-specific steps to download the URL's contents as an `.xml` file.

## 3. Add a SAML SSO provider to SparkPost

1. In SparkPost, select your profile picture > **Account Settings**.
2. Under **Single Sign-On**, select **Provision SSO**.
3. Under **Upload your Security Assertion Markup Language (SAML)**, select **select a file** and upload the `.xml` file you created in step [2. Download the metadata file](#2-download-the-metadata-file).
4. Select **Provision SSO**.
5. Select **Enable SSO**.

## 4. Add a test user and test the integration

1. In SparkPost, current users must be deleted and re-invited to use SSO. To create a test user, select your profile picture > **Users** > name of the user > **Delete User**. Then, select **Invite User** and fill in the necessary information. Alternatively, invite a new user. An invitation email will be sent.
2. Go to the link sent in the invitation email. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
3. Once SSO is successful, you can turn on SSO for the rest of your current users by deleting and then re-inviting them.

:::note

The SparkPost SSO login link is `https://app.sparkpost.com/auth/sso`. Alternatively, you can go to the usual sign in page and select **Log in with Single Sign-On**.
:::

# Tableau Cloud

This guide covers how to configure [Tableau Cloud](https://help.tableau.com/current/online/en-us/saml_config_site.htm) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Tableau Cloud site

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Tableau_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Copy the **SAML Metadata endpoint**.
7. Keep this window open. You will finish this configuration in step [4. Finish adding a SaaS application to Cloudflare One](#4-finish-adding-a-saas-application-to-cloudflare-one).

## 2. Download the metadata file

1. Paste the SAML Metadata endpoint from application configuration in Cloudflare One in a web browser.
2. Follow your browser-specific steps to download the URL's contents as an `.xml` file.

## 3. Add a SAML SSO provider to Tableau Cloud

1. In Tableau Cloud, go to **Settings** > **Authentication**.
2. Turn on **Enable an additional authentication method**. For **select authentication type**, select _SAML_.
3. Under **1. Get Tableau Cloud metadata**, copy the **Tableau Cloud entity ID** and **Tableau Cloud ACS URL**.
4. Under **4. Upload metadata to Tableau**, select **Choose a file**, and upload the `.xml` file created in step [2. Download the metadata file](#2-download-the-metadata-file)
5. Under **5. Map attributes**, turn on **Full name**. For **Name (full name)**, enter `name`.
6. (Optional) Choose whether users who are accessing embedded views will **Authenticate in a separate pop-up window** or **Authenticate using an inline frame**.
7. Select **Save Changes**.

## 4. Finish adding a SaaS application to Cloudflare One

1. In your open Cloudflare One window, fill in the following fields:
   - **Entity ID**: Tableau Cloud entity ID from Tableau Cloud SAML SSO set-up.
   - **Assertion Consumer Service URL**: Tableau Cloud ACS URL from Tableau Cloud SAML SSO set-up.
   - **Name ID format**: _Email_
2. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
3. Save the application.

## 5. Test the integration and set default authentication type

1. In Tableau Cloud, go to **Settings** > **Authentication**.
2. Under **7. Test Configuration**, select **Test Configuration**.
3. Sign in. If your sign-in is successful, **You are now signed in as (username)** will appear at the top of the page.
4. Close the pop-up window.
5. (Optional) Under **Default Authentication Type for Embedded Views**, turn on **cloudflareaccess.com (SAML)**. You can also configure the default authentication type for individual users under **Users** > **Actions** > **Authentication**.

# Workday

This guide covers how to configure [Workday](https://doc.workday.com/admin-guide/en-us/authentication-and-security/authentication/saml/dan1370796470811.html?toc=1.5.1) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Workday account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, enter `Workday` and select the corresponding textbox that appears.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: `http://www.workday.com`
   - **Assertion Consumer Service URL**: `https://<your-environment>.myworkday.com/<your-tenant>/login-saml.flex` for a production account or `https://<your-environment>-impl.myworkday.com/<your-tenant>/login-saml.flex` for a preview sandbox account
   - **Name ID format**: _Email_
7. Copy the **SSO endpoint**, **Access Entity ID or Issuer**, and **Public key**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Download the metadata file

1. Paste the SAML Metadata endpoint from application configuration in Cloudflare One in a web browser.
2. Follow your browser-specific steps to download the URL's contents as an `.xml` file.

## 3. Add a SAML SSO provider to Workday

1. In Workday, go to **Account Administration** > **Actions** > **Edit Tenant Setup - Security**.
2. Under **SAML Setup**, turn on **Enable SAML Authentication**.
3. In the **SAML Identity Providers** table, select **+**.
4. Fill in the following fields:
   - **Identity Provider Name**: Your desired name for the identity provider (for example, `Cloudflare Access`)
   - **Issuer**: Access Entity ID or Issuer from application configuration in Cloudflare One
   - **IdP SSO Service URL**: SSO endpoint from application configuration in Cloudflare One
5. Under **x509 Certificate**, select the menu icon > **Create x509 Public Key**.
6. Under **Name**, enter a unique name (for example, `access`).
7. Under **Certificate**, paste the Public key from application configuration in Cloudflare One.
8. Select **OK**.
9. If you want to enable SP-initiated login (login initiated by going to your Workday URL), fill in the following fields:
   - **SP Initiated**: Turn on.
   - **Service Provider ID**: `http://www.workday.com`
   - **Sign SP-initiated request**: Turn off.
10. Under **Single Sign-On**, add one or both of the following entries to the **Redirection URLs** grid. For each entry, if your user groups will use the same authentication option to sign in, select **Single URL**. If they will use different authentication options, select **Authentication selector**.
    - IdP-initiated SSO: Under **Login Redirect URL**, enter `<your-team-name>.cloudflareaccess.com`.
    - SP-initiated SSO: Under **Login Redirect URL**, enter `https://<your-environment>/<your-tenant/login-saml2.htmld`.

## 4. Test the integration

:::note
If you encounter a situation where one or more users get locked out of Workday, the user can use this backup URL provided by Workday to sign in with their username and password: `https://<your-workday-url>/login.flex?redirect=n`.
:::

1. In Workday, create an [authentication rule](https://doc.workday.com/admin-guide/en-us/authentication-and-security/authentication/authentication-policies/dan1370796466772.html).
2. Under **Authentication Conditions**, add conditions that will apply only to your test user.
3. Under **Allowed Authentication Types**, select **Specific**, then **SAML**.
4. Select **Done**.
5. Complete the following step:
   - **If you have enabled SP-initiated login**: Open an incognito browser window, go to your Workday URL, and enter your test user's email. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
   - **If you have not enabled SP-initiated login**: Go to your App Launcher at `https://<cloudflare-team-name>.cloudflareaccess.com`. Select the **Workday** tile. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.
6. Once login is successful, you can configure your security settings further, such as adding [user groups](https://doc.workday.com/admin-guide/en-us/authentication-and-security/configurable-security/security-groups/user-based-security-groups/dan1370796695367.html?toc=2.2.12.0) or [authentication rules](https://doc.workday.com/admin-guide/en-us/authentication-and-security/authentication/authentication-policies/dan1370796466772.html) to configure different login rules for different groups of users.

# Zendesk

This guide covers how to configure [Zendesk](https://support.zendesk.com/hc/en-us/articles/4408887505690-Enabling-SAML-single-sign-on#topic_u54_wc3_z2b) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to your Zendesk account

## Configure Zendesk and Cloudflare

1. Go to your Zendesk administrator dashboard, typically available at `<yourdomain>.zendesk.com/admin/security/sso`.

2. In a separate tab or window, open the [Cloudflare dashboard](https://dash.cloudflare.com/), select your account, and go to **Zero Trust** > **Access controls** > **Applications**.

3. Select **Create new application**, then choose **SaaS application**.

4. Input the following values in the Cloudflare One application configuration:

   | Cloudflare One field               | Value                                           |
   | ---------------------------------- | ----------------------------------------------- |
   | **Entity ID**                      | `https://<yoursubdomain>.zendesk.com`           |
   | **Assertion Consumer Service URL** | contents of **SAML SSO URL** in Zendesk account |
   | **Name ID Format**                 | _Email_                                         |

5. (Optional) Configure these Attribute Statements to include a user's first and last name:

   | Cloudflare attribute name | IdP attribute value                                               |
   | ------------------------- | ----------------------------------------------------------------- |
   | `<first name>`            | `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname` |
   | `<last name>`             | `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname`   |

   Zendesk will [use the user's email address as their name](https://support.zendesk.com/hc/en-us/articles/203663676#topic_dzb_gl5_2v) if the name is not provided.

6. To determine who can access Zendesk, [create an Access policy](/cloudflare-one/access-controls/policies/).

7. Copy the **SSO Endpoint** and **Public Key**.

8. Transform the public key into a fingerprint:
   1. Open a [fingerprint calculator](https://www.samltool.com/fingerprint.php).

   2. Paste the **Public Key** into **X.509 cert**.

   3. Wrap the value with `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`.

   4. Set **Algorithm** to _SHA256_ and select **Calculate Fingerprint**.

   5. Copy the **Formatted FingerPrint** value.

9. Add the Cloudflare values to the following Zendesk fields:

   | Cloudflare IdP field                        | Zendesk field               |
   | ------------------------------------------- | --------------------------- |
   | **SSO Endpoint**                            | **SAML SSO URL**            |
   | **Public Key** (transformed to fingerprint) | **Certificate Fingerprint** |

10. Go to `https://<yourdomain>.zendesk.com/admin/security/staff_members` and enable **External Authentication** > **Single Sign On**.

Users should now be able to log in to Zendesk if their Email address exists in the Zendesk user list.

# Zoom

This guide covers how to configure [Zoom](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0060673) as a SAML application in Cloudflare One.

## Prerequisites

- An [identity provider](/cloudflare-one/integrations/identity-providers/) configured in Cloudflare One
- Admin access to a Zoom Business, Education, or Enterprise account
- An [associated domain](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0066259) configured in your Zoom account
- A [vanity URL](https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0061540) configured in your Zoom account

## 1. Add a SaaS application to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application** > **SaaS application**.
3. For **Application**, select _Zoom_.
4. For the authentication protocol, select **SAML**.
5. Select **Add application**.
6. Fill in the following fields:
   - **Entity ID**: ` https://<your-vanity-url>.zoom.us`
   - **Assertion Consumer Service URL**: `https://<your-vanity-url>.zoom.us/saml/SSO`
   - **Name ID format**: _Email_
7. Copy the **Access Entity ID or Issuer**, **Public key**, and **SSO endpoint**.
8. Configure [Access policies](/cloudflare-one/access-controls/policies/) for the application.
9. Save the application.

## 2. Add a SAML SSO provider in Zoom

1. In Zoom, go to **Advanced** > **Single Sign-On**.
2. For **Vanity URL**, select the vanity URL you want to configure SSO for.
3. Fill out the following fields:
   - **Sign in page URL**: SSO endpoint from application configuration in Cloudflare One
   - **Identity Provider Certificate**: Public key from application configuration in Cloudflare One
   - **Service Provider (SP) Entity ID**: `yourvanityurl.zoom.us` (no `https://`)
   - **Issuer (DP Entity ID)**: Access Entity ID or Issuer from application configuration in Cloudflare One
4. For **Binding**, select _http-redirect_.
5. For **Signature Hash Algorithm**, ensure **SHA-256** is selected.
6. Under **Security**, turn off **Sign SAML request** and **Sign SAML logout request**.
7. Select **Save Changes**.
8. Go to **Advanced** > **Security**.
9. Under **Sign-in Methods**, ensure **Allow users to sign in with Single Sign-On (SSO)** is turned on.

## 3. Test the integration

Open an incognito browser window, go to your Zoom vanity URL, and select **Sign in**. You will be redirected to the Cloudflare Access login screen and prompted to sign in with your identity provider.

Once this is successful, you can require SSO for users in your associated domain(s) by completing the following steps:

1. In Zoom, go to **Advanced** > **Security**.
2. Under **Sign-in Methods**, turn on **Require users to sign in with SSO if their e-mail address belongs to one of the domains below**.
3. Under **Select Domains**, turn on the domains that you want to require SSO for.
4. (Optional) Under **Specify users who can bypass SSO sign-in**, add your desired users.
5. Select **Save**.

# Browser-rendered terminal

import { Render } from "~/components";

Cloudflare can render SSH, VNC, and RDP applications in a browser without the need for client software or end-user configuration changes. For SSH and VNC, user email prefixes must match their username on the server. RDP leverages your existing Windows usernames and passwords for authenticating to the Windows server; Cloudflare does not manage any credentials on the Windows server.

## Limitations

- Browser rendering is only supported for [self-hosted public applications](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/), not private IPs or hostnames.
- You can only render a browser-rendered terminal on domains and subdomains, not on specific paths.
- <Render file="access/self-hosted-app/ssh-sessions" product="cloudflare-one" />
- Cloudflare uses TLS to secure the egress RDP connection to your Windows server. We do not currently validate the chain of trust.

## Turn on browser rendering

### SSH and VNC

To turn on browser rendering for an SSH or VNC application:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate the SSH or VNC application you created when [connecting the server to Cloudflare](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/). Select **Configure**.
3. Turn on **Allow access through browser-based RDP, SSH, or VNC sessions**, then select _SSH_ or _VNC_.
	<Render file="access/self-hosted-app/browser-rendering-policies" product="cloudflare-one" />
4. Select **Save**.

When users authenticate and visit the URL of the application, Cloudflare will render a terminal in their browser.

### RDP

To set up browser-rendering for RDP, refer to our [browser-based RDP guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-browser/).

### SSH key exchange algorithms

Cloudflare's browser-rendered SSH terminal supports the following Key Exchange (KEX) algorithms:

    - `curve25519-sha256@libssh.org`
    - `curve25519-sha256`
    - `ecdh-sha2-nistp256`
    - `ecdh-sha2-nistp384`
    - `ecdh-sha2-nistp521`

For browser-rendered SSH connections to work, you may need to update the `sshd_config` file on your server to accept these algorithms.

# Non-HTTP applications

import { Render, Stream } from "~/components";

Cloudflare offers both client-based and clientless ways to grant secure access to non-HTTP applications.

:::note
Non-HTTP applications require [connecting your private network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/) to Cloudflare. For more details, refer to our [Replace your VPN](/learning-paths/replace-vpn/connect-private-network/) implementation guide.
:::

<Stream
	id="f13b085ed4d28a9dbb8faf19ae986125"
	title="SASE - Secure remote access to your critical infrastructure"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/205bdeb4-3e37-4b04-f430-8b5c06a9b300/public"
/>

## Cloudflare One Client

Users can connect by installing the Cloudflare One Client on their device and enrolling in your Zero Trust organization. Remote devices connect to your applications as if they were on your private network. By default, all devices enrolled in your organization can access any private route. To restrict access, [create a self-hosted application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) for a private IP range, port range, and/or hostname and build [Access policies](/cloudflare-one/access-controls/policies/) or [Gateway firewall rules](/cloudflare-one/traffic-controls/network-policies/) that allow or block specific users.

If you would like to define how users access specific infrastructure servers within your network, [create an infrastructure application](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/) in Access for Infrastructure. Access for Infrastructure provides an additional layer of control and visibility over how users access non-HTTP applications, including:

- Define fine-grained policies to govern who has access to specific servers and exactly how a user may access that server.
- Eliminate SSH keys by using short-lived certificates to authenticate users.
- Export SSH command logs to a storage service or SIEM solution using [Logpush](/cloudflare-one/insights/logs/logpush/).

## Clientless access

Clientless access methods are suited for organizations that cannot deploy the Cloudflare One Client or need to support third-party contractors where installing a client is not possible. Clientless access requires onboarding a domain to Cloudflare and configuring a public hostname in order to make the server reachable. Command logging is not supported.

### Browser-rendered terminal

Cloudflare's [browser-based terminal](/cloudflare-one/access-controls/applications/non-http/browser-rendering/) allows users to connect over SSH, RDP, and VNC without any configuration. When users visit the public hostname URL (for example, `https://ssh.example.com`) and log in with their Access credentials, Cloudflare will render a terminal in their browser. For RDP connections, users must authenticate to the Windows server using their Windows username and password in addition to being authenticated by Cloudflare Access.

### Client-side cloudflared

Users can log in to the application by installing `cloudflared` on their device and running a hostname-specific command in their terminal. For more information, refer to [cloudflared authentication](/cloudflare-one/access-controls/applications/non-http/cloudflared-authentication/).

## Related resources

To connect to an application over a specific protocol, refer to these tutorials:

- [SSH](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/)
- [SMB](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/smb/)
- [RDP](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/)

# Add an infrastructure application

import {
	APIRequest,
	Badge,
	Details,
	Tabs,
	TabItem,
	Render,
} from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>  | All plans                                                     |

| System   | Availability |
| -------- | ------------ |
| Windows  | ✅           |
| macOS    | ✅           |
| Linux    | ✅           |
| iOS      | ✅           |
| Android  | ✅           |
| ChromeOS | ✅           |

</Details>

Access for Infrastructure gives you granular control over how users access individual servers, clusters, or databases. You can configure how users authenticate to the resource and control the ports, protocols, and usernames they can use.

You can also organize targets with tags and define applications that match targets by hostname, tag, or both. Access logs and command logs help you audit access and support compliance workflows.

:::note
Access for Infrastructure currently supports [SSH](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/). To connect using other protocols, [add a self-hosted private application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/). For browser-based SSH, RDP, or VNC, refer to [browser-rendered terminal](/cloudflare-one/access-controls/applications/non-http/browser-rendering/).
:::

## Prerequisites

- [Connect your infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/) to Cloudflare using `cloudflared` or Cloudflare Mesh.
- [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on user devices in Traffic and DNS mode.

## 1. Add a target

<Render
	file="access/add-target-generic"
	product="cloudflare-one"
/>

### Tag targets

You can attach key-value [resource tags](/resource-tagging/) to infrastructure targets. Use them to organize targets by environment, team, region, or other metadata.

You can then define infrastructure applications that automatically cover any target with matching values.

You can manage tags inline when you create or edit a target or through the [Resource Tagging API](/resource-tagging/how-to/manage-tags/).

Each tag key can only appear once on a target. For example, a target can have `environment:production` or `environment:staging`, but not both.

### Filter and sort targets by tag

You can filter and sort targets by tag values.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Targets**. Use the filter and sort controls to narrow the list by tag key or value.

</TabItem>
<TabItem label="API">

To filter targets by tag, add one or more `tag` query parameters:

```bash
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/infrastructure/targets?tag=environment:production&tag=team:platform" \
  -H "Authorization: Bearer $API_TOKEN"
```

Use `:` between the tag key and value in each filter. If you add multiple `tag` parameters, Cloudflare applies AND logic.

To sort by tag key, use the `order` and `direction` parameters:

```bash
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/infrastructure/targets?order=tag:environment&direction=asc" \
  -H "Authorization: Bearer $API_TOKEN"
```

The sort parameter also uses `:` in `order=tag:<key>` because it identifies the tag field to sort by.

</TabItem>
</Tabs>

## 2. Add an infrastructure application

<Render file="access/add-infrastructure-app" product="cloudflare-one" />

## 3. (Recommended) Modify order of precedence in Gateway

<Render
	file="access/modify-gateway-policy-precedence-infra"
	product="cloudflare-one"
	params={{ selector: "Access Infrastructure Target" }}
/>

## 4. (Optional) Require independent MFA

You can require independent MFA before users connect with SSH. The application configuration selects the supported infrastructure authenticators: PIV key (`piv_key`), FIDO2 key (`ssh_fido2_key`), or both.

Application-level settings define the default authenticators and [session duration](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#session-duration). A policy can define custom settings for specific users or usernames.

For setup instructions, refer to [Enforce MFA for infrastructure applications](/cloudflare-one/access-controls/policies/mfa-requirements/#infrastructure-applications).

## 5. Configure the server

Certain protocols require configuring the server to trust connections through Access for Infrastructure. For more information, refer to the protocol-specific tutorial:

- [SSH](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#7-configure-ssh-server)

For SSH, this includes trusting the Cloudflare SSH CA and, if your server restricts certificate principals, [authorizing the SSH usernames](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#confirm-the-account-authorizes-the-certificate-principal) you configured on the target.

## 6. Connect as a user

Users connect to the target's IP address using their preferred client software. The user must be logged into the Cloudflare One Client on their device, but no other system configuration is required. You can optionally configure a [private DNS resolver](/cloudflare-one/traffic-controls/resolver-policies/) to allow connections to the target's private hostname.

### Connect to different VNET

To connect to targets that are in different VNETS, users will need to [switch their connected virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/#connect-to-a-virtual-network) in the Cloudflare One Client.

:::note
If a user is connected to a target in VNET-A and needs to connect to a target in VNET-B, switching their VNET will not break any existing connections to targets within VNET-A. At present, connections are maintained between VNETs.
:::

### Display available targets

<Details header="Feature availability">

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2024.9.346.0           |
| macOS    | ✅           | 2024.9.346.0           |
| Linux    | ✅           | 2024.9.346.0           |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

Users can use `warp-cli` to display a list of targets they can access. On the device, open a terminal and run the following command:

<Render file="tunnel/warp-cli-target-list" product="cloudflare-one" />

You can optionally add flags to filter the output. For example:

```sh
warp-cli target list --attribute hostname=do-target --username root
```

To view all available filters, type `warp-cli target list --help`.

## Revoke a user's session

To revoke a user's access to all infrastructure targets, you can either [revoke the user from Zero Trust](/cloudflare-one/access-controls/access-settings/session-management/#per-user) or revoke their device. Cloudflare does not currently support revoking a user's session for a specific target.

## Tag targets

Attach key-value [resource tags](/resource-tagging/) to targets to organize and filter them or build [target criteria](#target-criteria) for your applications.

To tag a target, you need an API token with the `Zero Trust Write` [permission](/fundamentals/api/reference/permissions/). No additional tag-specific permissions are required.

To add tags to a target, make a `PUT` request to the [Infrastructure Access Targets](/api/resources/zero_trust/subresources/access/subresources/infrastructure/subresources/targets/methods/update/) endpoint. Include a `tags` object:

<APIRequest
	path="/accounts/{account_id}/infrastructure/targets/{target_id}"
	method="PUT"
	json={{
		hostname: "infra-target-example",
		ip: {
			ipv4: {
				ip_addr: "198.51.100.10",
				virtual_network_id: "c77b744e-acc8-428f-9257-6878c046ed55",
			},
		},
		tags: {
			environment: "production",
			team: "platform",
		},
	}}
/>

Each target supports one value per tag key. For more information on managing tags, refer to [Resource Tagging](/resource-tagging/how-to/manage-tags/).

## Granular target permissions

Infrastructure Access supports granular read permissions through [Cloudflare's role-based access control](/fundamentals/manage-members/roles/). Administrators can assign read-only roles scoped to specific targets instead of granting account-wide access. When a user with a scoped role calls the targets list API, the response is automatically filtered to only include the targets they have permission to view.

This is useful for organizations that want to give teams visibility into their own infrastructure targets without exposing the full target inventory.

## Target criteria

Use target criteria to define which targets an infrastructure application covers. Each target criteria entry includes a protocol, a port, and selectors that match targets by hostname, tag, or both.

The `target_attributes` selector only supports `hostname` in both the legacy and operator-based formats. Cloudflare rejects any other `target_attributes` key.

A target can only store one value for each tag key. This limit does not apply to target criteria. For example, an application can match both `environment:production` and `environment:staging` in `include`, `require`, or `exclude`.

### Operators

| Operator | Logic | Description |
| --- | --- | --- |
| `include` | OR | Target must match at least one included selector. |
| `require` | AND | Target must match all required selectors. |
| `exclude` | NOT(OR) | Target is rejected if it matches any excluded selector. |

Combined evaluation: **(any include) AND (all requires) AND NOT (any excludes)**.

### Legacy format

You can continue to use the flat `target_attributes` format for existing hostname-only applications. This only matters if you manage applications through the API or Terraform. For each target criteria entry, choose one format: either flat `target_attributes` or operator-based `include`, `require`, and `exclude`.

## Infrastructure policy selectors

The following [Access policy selectors](/cloudflare-one/access-controls/policies/#selectors) are available for securing infrastructure applications:

- Email
- Emails ending in
- SAML group
- Country
- Authentication method
- Device posture
- Entra group, GitHub organization, Google Workspace group, Okta group

# Private network applications (legacy)

:::caution
The Private Network application type can no longer be created from the dashboard. If you do not already have a legacy private network application, use a [self-hosted application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) to secure a private IP address instead.
:::

Existing **Private Network** applications continue to function and can still be managed. These applications were originally configured with the following steps:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications** > **Add an application**.

2. Select **Private Network**.

3. Name your application.

4. For **Application type**, select _Destination IP_.

5. For **Value**, enter the IP address for your application (for example, `10.128.0.7`).
   :::note
   If you would like to create a policy for an IP/CIDR range instead of a specific IP address, you can build a [Gateway Network policy](/cloudflare-one/traffic-controls/network-policies/) using the **Destination IP** selector.
   :::

6. Configure your [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) visibility and logo.

7. Select **Next**. You will see two auto-generated Gateway Network policies: one that allows access to the destination IP and another that blocks access.

8. Modify the policies to include additional identity-based conditions. For example:

   - **Policy 1**

     | Selector       | Operator      | Value            | Logic | Action |
     | -------------- | ------------- | ---------------- | ----- | ------ |
     | Destination IP | in            | `10.128.0.7`     | And   | Allow  |
     | User Email     | matches regex | `.*@example.com` |       |        |

   - **Policy 2**

     | Selector       | Operator | Value        | Action |
     | -------------- | -------- | ------------ | ------ |
     | Destination IP | in       | `10.128.0.7` | Block  |

   Policies are evaluated in [numerical order](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence), so a user with an email ending in @example.com will be able to access `10.128.0.7` while all others will be blocked. For more information on building network policies, refer to our [dedicated documentation](/cloudflare-one/traffic-controls/network-policies/).

9. Select **Add application**.

Your application will appear on the **Applications** page.

# Secure a private IP or hostname

import { Render, GlossaryTooltip, } from "~/components";

You can configure a self-hosted Access application to manage access to specific IPs or hostnames on your private network.

:::note
This feature replaces the legacy [private network app type](/cloudflare-one/access-controls/applications/non-http/legacy-private-network-app/).
:::

## Prerequisites

- Private IPs and hostnames are reachable over the Cloudflare One Client, Cloudflare WAN (formerly Magic WAN) or Browser Isolation. For more details, refer to [Connect a private network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/).
- Private hostnames route to your custom DNS resolver through [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) or [Gateway resolver policies](/cloudflare-one/traffic-controls/resolver-policies/).
- Public IPs and hostnames can be used to define a private application, however the IP or hostname must route through Cloudflare via [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/), [Cloudflare Mesh](/mesh/), or [Cloudflare WAN](/cloudflare-wan/configuration/how-to/configure-routes/).
- (Optional) Turn on [Gateway TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) if you want to use Access JWTs to manage [HTTPS application sessions](#https-applications).

## Add your application to Access

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **Self-hosted and private**.

4.  To add an application using its private IP:
		1. Select **Add private IP**.
		2. In **IP address**, enter the private IP or CIDR range that represents the application (for example, `10.0.0.1` or `172.16.0.0/12`).
		3. In **Port**, enter a single port or a port range used by your application (for example, `22` or `8000-8099`).

			Comma-separated lists of ports (such as `80, 443`) are not supported. To add multiple ports for a specific IP, you can select **Add private IP** and repeat the IP address with the other port. Alternatively, create a new Access application for the other port.

5. To add an application using its private hostname:
	1. Select **Add private hostname**.
	2. In **Hostname**, enter the private hostname of the application (for example, `wiki.internal.local`). You can use [wildcards](/cloudflare-one/access-controls/policies/app-paths/) with private hostnames to protect multiple parts of an application that share a root path.
	3. In **Port**, enter a single port or a port range used by your application (for example, `22` or `8000-8099`).

	:::note
	- **HTTPS applications**: Private hostnames explicitly set to port `443` (not including port ranges such as `441-444`) must have a valid Server Name Indicator (SNI).
	- **Non-HTTPS applications**:  Private hostnames on non-`443` ports do not require a valid SNI value and will be assigned an <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>. Ensure that the following IP addresses are not blocked by any firewalls or excluded from Gateway traffic:

		<Render file="gateway/egress-selector-cgnat-ips" product="cloudflare-one"/>

		For more details on private hostname routing, refer to [Connect a private hostname](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/#prerequisites)

6.  <Render file="access/add-access-policies" product="cloudflare-one" />
7.  Configure how users will authenticate:

    <ol>
    <li>
    Select the [identity providers](/cloudflare-one/integrations/identity-providers/) you want to enable for your application.
    </li>
    <li>
    (Recommended) If you plan to only allow access via a single IdP, turn on **Apply instant authentication**. End users will not be shown the [Cloudflare Access login page](/cloudflare-one/reusable-components/custom-pages/access-login-page/). Instead, Cloudflare will redirect users directly to your SSO login event.
    </li>
    <li> (Recommended) Turn on <b>Authenticate with Cloudflare One Client</b> to allow users to authenticate to the application using their <a href="/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/">Cloudflare One Client session identity</a>. Turn this on if your application is not in the browser and cannot handle a <code>302</code> redirect. </li>
    </ol>

8. <Render file="access/configure-independent-mfa" product="cloudflare-one" />

9. <Render file="access/session-duration" product="cloudflare-one" />

   If the application is non-HTTPS or you do not have TLS decryption turned on, the session is tracked by the Cloudflare One Client per application.

10. (Optional) Go to the **Additional settings** tab to customize the application experience:

	- **App Launcher customization**: Configure how this application appears to users in the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/).
	- **Allow clientless access**: Allow users to access this private hostname or IP without the Cloudflare One Client. Users who pass your Access policies will see a tile in their App Launcher which points to a prefixed URL such as `https://<your-teamname>.cloudflareaccess.com/browser/https://wiki.internal.local/`. The link will route traffic to the application through [Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/). This setting is useful for users on unmanaged devices or contractors who cannot install a device client.

		:::note
		Ensure your [remote browser permissions](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/) allow users of this application to open Clientless Web Isolation links.
		:::

	- <Render file="access/access-block-page" product="cloudflare-one" />
	- The following settings only apply to private hostnames and require [Gateway TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/):
		<Render file="access/self-hosted-app/advanced-settings" product="cloudflare-one" />


11. Select **Create**.

Users can now connect to your private application after authenticating with Cloudflare Access.

## Authentication flow

The authentication experience depends on the application's protocol.

### HTTPS applications

If [Gateway TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) is turned on and a user is accessing an HTTPS application on port `443`, Cloudflare Access will present a login page in the browser and issue an [application token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/) to your origin. This is the same cookie-based authentication flow used by [self-hosted public apps](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).

If [Gateway TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) is turned off, session management is [handled in the Cloudflare One Client](#other-non-http-applications) instead of in the browser.

:::note
If your origin uses a self-signed or otherwise untrusted certificate, create a Gateway HTTP Allow policy for the origin with the [untrusted certificate action](/cloudflare-one/traffic-controls/http-policies/#untrusted-certificates) set to _Pass through_. Otherwise, Gateway will block the connection before it reaches Access.
:::

### Plaintext HTTP applications

For applications served over plaintext HTTP on port `80`, Cloudflare Access presents an in-browser login page and issues an [application token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/), the same as HTTPS applications with Gateway TLS decryption. Gateway TLS decryption is not required because the traffic is already unencrypted.

### Other non-HTTP applications

For applications that are not HTTP or HTTPS (for example, SSH, RDP, or arbitrary TCP/UDP), the Cloudflare One Client manages the session. When authentication is required, the operating system displays an `Authentication required` notification from the Cloudflare One Client. Cloudflare One Client notifications must be allowed in the operating system settings. When the user selects the notification, the Cloudflare One Client opens a browser window with your Access login page.

<Render file="gateway/client-notifications-os" product="cloudflare-one" />

While authentication is pending, the Cloudflare icon in the macOS menu bar or Windows system tray indicates that action is required. If the user misses the notification, they can open the Cloudflare One Client and select **Profile** > **Account information** > **Re-authenticate**. In version 2026.1 and earlier, select **Preferences** > **Account** > **Re-Authenticate Session**. After authentication, retry the connection.

## Order of precedence

### Access vs Gateway policies

By default, Cloudflare will evaluate Access application policies after evaluating all [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/). To evaluate Access applications before or after specific Gateway policies:

<ol>
<li>
In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**. In **Network**, [create a Network policy](/cloudflare-one/traffic-controls/network-policies/) with the following configuration:

    | Selector                                    | Operator | Value     | Action |
    | ------------------------------------------- | -------- | --------- | ------ |
    | Self-hosted Access App with Private Address | is       | _Present_ | Allow  |

</li>

<li>
	Update the policy's [order of
	precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence)
	using the dashboard or API.
</li>

</ol>

:::note
Users must pass the policies in your Access application before they are granted access. The Gateway Allow policy is strictly for routing and connectivity purposes.
:::

### Private hostname vs private IP

An Access application defined by a private hostname takes precedence over an Access application defined by a private IP. For example, assume App-1 points to `wiki.internal.local` and App-2 points to `10.0.0.1`, but `wiki.internal.local` resolves to `10.0.0.1`. Users who go to `wiki.internal.local` will never match App-2; they will be allowed or blocked strictly based on App-1 Access policies (and [Gateway policies](#access-vs-gateway-policies)).

### Overlapping private application destinations

When a request matches destinations in multiple self-hosted private applications, Access selects the application with the most specific matching destination. The application creation order and the order shown in the dashboard do not affect which application is selected.

Access compares destination attributes in the following order:

1. **Hostname or IP specificity:** A matching private hostname takes precedence over any private IP or CIDR. Between matching hostnames, a hostname with more labels takes precedence. For hostnames with the same number of labels, exact labels take precedence over wildcard labels, and partial wildcards take precedence over a full `*` label. Between matching IPs and CIDRs, the destination with fewer IP addresses takes precedence. An exact IPv4 or IPv6 address is the most specific IP destination.
2. **Port specificity:** For otherwise equal destinations, a single port takes precedence over a port range. A smaller port range takes precedence over a larger range, and any port restriction takes precedence over a destination that applies to all ports.
3. **Virtual network specificity:** A destination restricted to a virtual network takes precedence over an otherwise equal destination that applies to all virtual networks. For example, assume App-1 matches `10.0.0.0/24` on all virtual networks and App-2 matches the same CIDR only on the `staging` virtual network. A user connected to `staging` matches App-2, while a user connected to a different virtual network matches App-1.
4. **Protocol specificity:** A destination restricted to TCP or UDP takes precedence over an otherwise equal destination that applies to both protocols.

Destination specificity is evaluated before port specificity. For example, `10.0.1.0/24` on all ports takes precedence over `10.0.0.0/16` on a single port. Access evaluates policies only for the application selected by this matching order.

:::note
This matching order applies to destinations configured on **Self-hosted and private** applications. It does not apply to [legacy private network applications](/cloudflare-one/access-controls/applications/non-http/legacy-private-network-app/).
:::

## Limitations

### Browser Isolation is not compatible with apps on non-`443` ports

<Render file="access/self-hosted-app/browser-isolation-non-443" product="cloudflare-one"/>

### Google Chrome restricts access to private hostnames

<Render file="gateway/egress-selector-chrome-issue" product="cloudflare-one"/>

# Short-lived certificates (legacy)

import { Render } from "~/components";

:::note
Not recommended for new deployments. We recommend using [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) to configure short-lived certificates for SSH.
:::

<Render
	file="access/short-lived-certs-intro"
	product="cloudflare-one"
	params={{ intro: "Cloudflare Access can replace" }}
/>

## 1. Secure the server behind Cloudflare Access

Cloudflare Access short-lived certificates can work with any modern SSH server, whether it is behind Access or not. However, we recommend putting your server behind Access for added security and features, such as auditability and browser-based terminals.

To secure your server behind Cloudflare Access:

1. [Connect the server to Cloudflare](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) as a published application.
2. Create a [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for the server.

## 2. Ensure Unix usernames match user SSO identities

Cloudflare Access will take the identity from a token and, using short-lived certificates, authorize the user on the target infrastructure.

<Render file="ssh/usernames" product="cloudflare-one" />

## 3. Generate a short-lived certificate public key

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Service credentials** > **SSH**.

2. Select **Add a certificate**.

3. In the **Application** dropdown, choose the Access application that represents your SSH server.

4. Select **Generate certificate**. A new row will appear in the short-lived certificates table with the name of your Access application.

5. Select the short-lived certificate for your application.

6. Copy its **CA public key**. You can return to copy this public key at any time.

## 4. Save your public key

1. Copy the public key generated from the dashboard in Step 3.

<Render file="ssh/public-key" product="cloudflare-one" />

## 5. Modify your `sshd_config` file

<Render file="ssh/modify-sshd" product="cloudflare-one" />

## 6. Restart your SSH server

<Render file="ssh/restart-server" product="cloudflare-one" />

## 7. Connect as a user

### Configure your client SSH config

On the client side, [configure your device](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/) to use Cloudflare Access to reach the protected machine. To use short-lived certificates, you must include the following settings in your SSH config file (`~/.ssh/config`).

To save time, you can use the following cloudflared command to print the required configuration command:

```sh
cloudflared access ssh-config --hostname vm.example.com --short-lived-cert
```

If you prefer to configure manually, this is an example of the generated SSH config:

```txt
Match host vm.example.com exec "/usr/local/bin/cloudflared access ssh-gen --hostname %h"
    HostName vm.example.com
    ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
    IdentityFile ~/.cloudflared/vm.example.com-cf_key
    CertificateFile ~/.cloudflared/vm.example.com-cf_key-cert.pub
```

### Connect through a browser-based terminal

End users can connect to the SSH session without any configuration by using Cloudflare's browser-based terminal. To enable, refer to [Browser-rendered terminal](/cloudflare-one/access-controls/applications/non-http/browser-rendering/).

By default, the browser-based terminal prompts the user for a username/password login. If you would like to use certificate based authentication, make sure you have [created a short-lived certificate](#3-generate-a-short-lived-certificate-public-key) for the specific Access application configured for browser-rendered SSH.

---

Your SSH server is now protected behind Cloudflare Access — users will be prompted to authenticate with your identity provider before they can connect.

# Arbitrary TCP

Cloudflare Access provides a mechanism for end users to authenticate with their single sign-on (SSO) provider and connect to resources over arbitrary TCP without being on a virtual private network (VPN).

## Requirements

- A Cloudflare account
- A site active on Cloudflare
- The `cloudflared` daemon installed on the host and client machines

> Cloudflare Access requires you to first [add a site](https://dash.cloudflare.com/sign-up) to Cloudflare. You can use any site you have registered; the site does not need to be the same one you use for customer traffic and it does not need to match sites in your internal DNS.
>
> Adding the site to Cloudflare requires changing your domain's authoritative DNS to point to Cloudflare's nameservers. Once configured, all requests to that hostname will be sent to Cloudflare's network first, where Access policies can be applied.

## **Connect the host to Cloudflare**

### 1. Install the Cloudflare daemon on the host machine

The Cloudflare daemon, `cloudflared`, will maintain a secure, persistent, outbound-only connection from the machine to Cloudflare. Arbitrary TCP traffic will be proxied over this connection using [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/).

Follow [these instructions](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) to download and install `cloudflared` on the machine hosting the resource.

### 2. Authenticate the Cloudflare daemon

Run the following command to authenticate `cloudflared` into your Cloudflare account.

```sh
cloudflared tunnel login
```

`cloudflared` will open a browser window and prompt you to login to your Cloudflare account. If you are working on a machine that does not have a browser, or a browser window does not launch, you can copy the URL from the command-line output and visit the URL in a browser on any machine.

Once you login, Cloudflare will display the sites that you added to your account. Select the site where you will create a subdomain to represent the resource. For example, if you plan to share the service at `tcp.site.com` select `site.com` from the list.

Once selected, `cloudflared` will download a wildcard certificate for the site. This certificate will allow `cloudflared` to create a DNS record for a subdomain of the site.

### 3. Secure the subdomain with Cloudflare Access

Next, protect the subdomain you plan to register with a Cloudflare Access policy. Follow [these instructions](/cloudflare-one/access-controls/policies/) to build a new policy to control who can connect to the resource.

For example, if you share the resource at `tcp.site.com`, build a policy to only allow your team members to connect to that subdomain.

### 4. Connect the resource to Cloudflare

`cloudflared` can proxy connections to nonstandard ports.

Run the following command to connect the resource to Cloudflare, replacing the `tcp.site.com` and `7870` values with your site and port.

```sh
cloudflared tunnel --hostname tcp.site.com --url tcp://localhost:7870
```

`cloudflared` will confirm that the connection has been established. The process needs to be configured to stay alive and autostart. If the process is terminated, end users will not be able to connect.

## **Connect from a client machine**

### 1. Install the Cloudflare daemon on the client machine

Follow the same steps above to download and install `cloudflared` on the client desktop that will connect to the resource. `cloudflared` will need to be installed on each user device that will connect.

### 2. Connect to the resource

Run the following command to create a connection from the device to Cloudflare. Any available port can be specified.

```sh
cloudflared access tcp --hostname tcp.site.com --url localhost:9210
```

This command can be wrapped as a desktop shortcut so that end users do not need to use the command line.

Point the client application to the selected port.

When the client launches, `cloudflared` will launch a browser window and prompt the user to authenticate with your SSO provider.

**Common issues**

- Ensure that the machine's firewall permits egress on ports 80 and 443, otherwise `cloudflared` will return an error.

# Enable automatic cloudflared authentication

When users connect to an Access application through `cloudflared`, the browser prompts them to allow access by displaying this page:

![Access request prompt page displayed after logging in with cloudflared.](~/assets/images/cloudflare-one/applications/non-http/access-screen.png)

Automatic `cloudflared` authentication allows users to skip this login page if they already have an active IdP session.

To enable automatic `cloudflared` authentication:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate your application and select **Configure**.
3. Go to **Authentication**.
4. Turn on **Allow automatic Cloudflared authentication**.
5. Select **Save**.

This option will still prompt a browser window in the background, but authentication will now happen automatically.

# Client-side cloudflared

With Cloudflare Zero Trust, users can connect to non-HTTP applications via a public hostname without installing the Cloudflare One Client. This method requires you to onboard a domain to Cloudflare and install `cloudflared` on both the server and the user's device.

Users log in to the application by running a `cloudflared access` command in their terminal. `cloudflared` will launch a browser window and prompt the user to authenticate with your identity provider.

:::note

Automated services should only authenticate with `cloudflared` if they cannot use a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/). Cloudflared authentication relies on WebSockets to establish a connection. WebSockets have a known limitation where persistent connections may close unexpectedly. We recommend either a [Service Auth policy](/cloudflare-one/access-controls/policies/#service-auth) or using [Warp to Tunnel routing](/cloudflare-one/access-controls/applications/non-http/) in these instances.
:::

For examples of how to connect to Access applications with client-side `cloudflared`, refer to these tutorials:

- [Connect through Access using a CLI](/cloudflare-one/tutorials/cli/)
- [Connect through Access using kubectl](/cloudflare-one/tutorials/kubectl/)
- [Connect to SSH with client-side cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-cloudflared-authentication/)
- [Connect over RDP with cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/#connect-to-rdp-server-with-cloudflared-access)
- [Connect over SMB with cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/smb/)
- [Connect over arbitrary TCP with cloudflared](/cloudflare-one/access-controls/applications/non-http/cloudflared-authentication/arbitrary-tcp/)

# Application paths

Application paths define the URLs protected by an Access policy. When adding a self-hosted application to Access, you can choose to protect the entire website by entering its apex domain, or alternatively, protect specific subdomains and paths.

## Policy inheritance

Cloudflare Zero Trust allows you to create unique rules for parts of an application that share a root path. Imagine an example application is deployed at `dashboard.com/eng` that anyone on the engineering team should be able to access. However, a tool deployed at `dashboard.com/eng/exec` should only be accessed by the executive team.

When multiple rules are set for a common root path, the more specific rule takes precedence. For example, when setting rules for `dashboard.com/eng` and `dashboard.com/eng/exec` separately, the more specific rule for `dashboard.com/eng/exec` takes precedence, and no rule is inherited from `dashboard.com/eng`. If no separate, specific rule is set for `dashboard.com/eng/exec`, it will inherit any rules set for `dashboard.com/eng`.

## Wildcards

When you create an application for a specific subdomain or path, you can use asterisks (`*`) as wildcards. Wildcards allow you to extend the application you are creating to multiple subdomains or paths in a given apex domain.

### Examples

#### Match all subdomains of an apex domain

A wildcard in the **Subdomain** field only matches that specific subdomain level. It does not cover the apex domain or multiple levels of the subdomain. If you want to cover multiple subdomain levels, you can use multiple wildcards.

| Application     | Covers                                       | Does not cover |
| --------------- | -------------------------------------------- | -------------- |
| `*.example.com` | `alpha.example.com` <br/> `beta.example.com` | `example.com` <br/> `foo.bar.example.com`  |

#### Match all paths of an apex domain

To protect an apex domain and all of the paths under it, leave the **Path** field empty. Alternatively, use a wildcard in the **Path** field.

| Application                            | Covers                                                           | Does not cover      |
| -------------------------------------- | ---------------------------------------------------------------- | ------------------- |
| `example.com` <br/> or `example.com/*` | `example.com` <br/> `example.com/alpha` <br/> `example.com/beta` | `alpha.example.com` |

#### Match multi-level subdomains

Using a wildcard in the **Subdomain** field does not cover the parent subdomain nor the apex domain.

| Application          | Covers                                                 | Does not cover                          |
| -------------------- | ------------------------------------------------------ | --------------------------------------- |
| `*.test.example.com` | `alpha.test.example.com` <br/> `beta.test.example.com` | `test.example.com`  <br/> `example.com` |

#### Partially match subdomains

Using a wildcard at the beginning or end of the **Subdomain** field does not cover multiple levels of the subdomain.

| Application         | Covers                                           | Does not cover          |
| ------------------- | ------------------------------------------------ | ----------------------- |
| `*test.example.com` | `test.example.com` <br/> `alphatest.example.com` | `beta.test.example.com` |

#### Match multi-level paths

Using a wildcard in the **Path** field does not cover the parent path nor the apex domain.

| Application           | Covers                                                | Does not cover                          |
| --------------------- | ----------------------------------------------------- | --------------------------------------- |
| `example.com/alpha/*` | `example.com/alpha/one` <br/> `example.com/alpha/two` | `example.com/alpha` <br/> `example.com` |

#### Partially match paths

Using a wildcard in the middle of the **Path** field covers multiple segments of the URL.

| Application            | Covers                                                                               |
| ---------------------- | ------------------------------------------------------------------------------------ |
| `example.com/foo*/bar` | `example.com/foo/bar`<br/> `example.com/food/bar` <br/> `example.com/food/stuff/bar` |

### Limitations

* At most one wildcard in between each dot in the **Subdomain**. For example, `foo*bar*baz.example.com` is not allowed.
* At most one wildcard in between each slash in the **Path**. For example, `example.com/foo*bar*baz` is not allowed.

## Subdomain setups

[Subdomain setups](/dns/zone-setups/subdomain-setup/) allow you to manage a child domain separately from its parent domain. In Access application paths, your configured child domains will appear in the **Domain** dropdown menu. If you [split out a subdomain](/dns/zone-setups/subdomain-setup/setup/) which already has an Access application, you will need to re-save the Access application to associate it with the new child domain.

## Unsupported URLs

### Port numbers

Port numbers are not supported in Access application paths. If a request includes a port number in the URL, Access will strip the port number and redirect the request to the default HTTP/HTTPS port.

### Query strings

Query strings (such as`?foo=bar`) are not supported in Access application paths.

### Anchor links

Since anchor links are processed by the browser and not the server, Access applications do not support `#` characters in the URL. For example, requests to `dashboard.com/#settings` will redirect to `dashboard.com`.

# Common policies

import { Details, Render, Tabs, TabItem, APIRequest } from "~/components";

The following Cloudflare Access policies are commonly used to secure applications.

Refer to the [Access policies page](/cloudflare-one/access-controls/policies/) for a comprehensive list of available actions, rule types, and selectors. To learn how to create and manage policies, refer to [Manage Access policies](/cloudflare-one/access-controls/policies/policy-management/).

## Allow employees by email domain

The most basic Access policy grants access to anyone who authenticates with an email address belonging to your organization. This is a good starting point when you first protect an application with Access and want to restrict it to employees using your corporate [identity provider](/cloudflare-one/integrations/identity-providers/).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render file="access/example-allow-email-domain" product="cloudflare-one" />

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Allow employees by email domain",
		decision: "allow",
		include: [
			{
				email_domain: {
					domain: "example.com",
				},
			},
		],
	}}
/>

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "allow_employees_by_email_domain" {
  account_id = var.cloudflare_account_id
  name       = "Allow employees by email domain"
  decision   = "allow"
  include = [{
    email_domain = {
      domain = "example.com"
    }
  }]
}
```

</TabItem> </Tabs>

You can add multiple email domains to the Include rule if your organization uses more than one domain (for example, `@example.com` and `@example.co.uk`).

## Allow employees from specific countries

Organizations that operate in specific regions or need to comply with data residency requirements can restrict application access to users in approved countries. This policy is useful when you want to limit where employees can connect from, while still allowing exceptions for individual users such as traveling executives.

Because Require rules use AND logic, you cannot add multiple countries directly to a single Require rule — that would require the user to be in all countries simultaneously. Instead, first create a [rule group](/cloudflare-one/access-controls/policies/groups/) that lists the approved countries:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Rule type | Selector | Value                       |
| --------- | -------- | --------------------------- |
| Include   | Country  | `United States`, `Portugal` |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/groups"
	method="POST"
	json={{
		name: "Approved countries",
		include: [
			{
				geo: {
					country_code: "US",
				},
			},
			{
				geo: {
					country_code: "PT",
				},
			},
		],
	}}
/>

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_group`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_group) resource:

```tf
resource "cloudflare_zero_trust_access_group" "approved_countries" {
  account_id = var.cloudflare_account_id
  name       = "Approved countries"
  include = [
    {
      geo = {
        country_code = "US"
      }
    },
    {
      geo = {
        country_code = "PT"
      }
    },
  ]
}
```

</TabItem> </Tabs>

Then reference the rule group in your Access policy:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action | Rule type | Selector         | Value                                      |
| ------ | --------- | ---------------- | ------------------------------------------ |
| Allow  | Include   | Emails ending in | `@example.com`                             |
|        | Require   | Rule group       | `Approved countries`                       |
|        | Exclude   | Email            | `user-1@example.com`, `user-2@example.com` |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Allow employees from specific countries",
		decision: "allow",
		include: [
			{
				email_domain: {
					domain: "example.com",
				},
			},
		],
		require: [
			{
				group: {
					id: "<APPROVED_COUNTRIES_GROUP_ID>",
				},
			},
		],
		exclude: [
			{
				email: {
					email: "user-1@example.com",
				},
			},
			{
				email: {
					email: "user-2@example.com",
				},
			},
		],
	}}
/>

Replace `<APPROVED_COUNTRIES_GROUP_ID>` with the `id` returned when you created the rule group above. To look up existing groups, use the [List Access groups](/api/resources/zero_trust/subresources/access/subresources/groups/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "allow_employees_from_specific_countries" {
  account_id = var.cloudflare_account_id
  name       = "Allow employees from specific countries"
  decision   = "allow"
  include = [{
    email_domain = {
      domain = "example.com"
    }
  }]
  require = [{
    group = {
      id = cloudflare_zero_trust_access_group.approved_countries.id
    }
  }]
  exclude = [
    {
      email = {
        email = "user-1@example.com"
      }
    },
    {
      email = {
        email = "user-2@example.com"
      }
    },
  ]
}
```

The `cloudflare_zero_trust_access_group.approved_countries` reference points to the [`cloudflare_zero_trust_access_group`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_group) resource created above.

</TabItem> </Tabs>

## Require device posture for sensitive applications

For applications that contain sensitive data, you can verify that users connect from managed devices that meet your organization's security baseline. The following example combines identity verification with [device posture checks](/cloudflare-one/reusable-components/posture-checks/) to ensure that the device is running a supported [OS version](/cloudflare-one/reusable-components/posture-checks/client-checks/os-version/) and is connected through the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), which is enforced by the [Require Gateway check](/cloudflare-one/reusable-components/posture-checks/client-checks/require-gateway/).

:::note
Before creating this policy, [create device posture checks](/cloudflare-one/reusable-components/posture-checks/) for each requirement and [enable the Require Gateway posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/require-gateway/).
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action | Rule type | Selector       | Value                                                    |
| ------ | --------- | -------------- | -------------------------------------------------------- |
| Allow  | Include   | Okta Groups    | `Full-Time Employees`                                    |
|        | Require   | Gateway        | `Gateway`                                              |
|        | Require   | OS Version | `Latest version of Windows` |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Require device posture for sensitive apps",
		decision: "allow",
		include: [
			{
				okta: {
					name: "Full-Time Employees",
					identity_provider_id: "<OKTA_IDP_ID>",
				},
			},
		],
		require: [
			{
				device_posture: {
					integration_uid: "<GATEWAY_CHECK_ID>",
				},
			},
			{
				device_posture: {
					integration_uid: "<OS_VERSION_CHECK_ID>",
				},
			},
		],
	}}
/>

Replace the `okta` rule with the [appropriate rule for your identity provider](/api/resources/zero_trust/subresources/access/subresources/policies/methods/create/). To get your identity provider ID, use the [List Access identity providers](/api/resources/zero_trust/subresources/access/subresources/identity_providers/methods/list/) endpoint. To get the integration UIDs for your device posture checks, use the [List device posture checks](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "require_device_posture" {
  account_id = var.cloudflare_account_id
  name       = "Require device posture for sensitive apps"
  decision   = "allow"
  include = [{
    okta = {
      name                 = "Full-Time Employees"
      identity_provider_id = cloudflare_zero_trust_access_identity_provider.okta.id
    }
  }]
  require = [
    {
      device_posture = {
        integration_uid = cloudflare_zero_trust_device_posture_rule.gateway_check.id
      }
    },
    {
      device_posture = {
        integration_uid = cloudflare_zero_trust_device_posture_rule.os_version_check.id
      }
    },
  ]
}
```

- Replace the `okta` rule with the appropriate [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource for your identity provider. To configure the identity provider resource, refer to [Identity providers](/cloudflare-one/integrations/identity-providers/).
- To configure the [`cloudflare_zero_trust_device_posture_rule`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_posture_rule) resources referenced above, refer to [Device posture checks](/cloudflare-one/reusable-components/posture-checks/).

</TabItem> </Tabs>

To reuse these device requirements across multiple applications, create a [rule group](/cloudflare-one/access-controls/policies/groups/) called "Corporate device requirements" that contains the posture checks. You can then reference this rule group in the Require field of any policy.

## Require MFA for high-security applications

For applications that handle financial data, production infrastructure, or other high-value resources, you can require that users authenticate with multi-factor authentication (MFA) in addition to their identity provider credentials. This ensures that a compromised password alone is not sufficient to gain access.

Access supports two approaches to enforcing MFA:

### Identity provider-based MFA

If your identity provider reports the authentication method used during login, you can add an **Authentication method** selector to require a specific MFA method such as a hardware security key.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action | Rule type | Selector              | Value          |
| ------ | --------- | --------------------- | -------------- |
| Allow  | Include   | Okta Groups           | `Employees`    |
|        | Require   | Authentication method | `Security key` |
|        | Require   | Gateway               | _(enabled)_    |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Require MFA for high-security apps",
		decision: "allow",
		include: [
			{
				okta: {
					name: "Employees",
					identity_provider_id: "<OKTA_IDP_ID>",
				},
			},
		],
		require: [
			{
				auth_method: {
					auth_method: "swk",
				},
			},
			{
				device_posture: {
					integration_uid: "<GATEWAY_CHECK_ID>",
				},
			},
		],
	}}
/>

The `auth_method` value uses [RFC 8176](https://datatracker.ietf.org/doc/html/rfc8176#section-2) authentication method reference values. For example, `swk` represents a software-secured key (security key). Replace the `okta` rule with the [appropriate rule for your identity provider](/api/resources/zero_trust/subresources/access/subresources/policies/methods/create/). To get your identity provider ID, use the [List Access identity providers](/api/resources/zero_trust/subresources/access/subresources/identity_providers/methods/list/) endpoint. To get `<GATEWAY_CHECK_ID>`, use the [List device posture checks](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "require_mfa" {
  account_id = var.cloudflare_account_id
  name       = "Require MFA for high-security apps"
  decision   = "allow"
  include = [{
    okta = {
      name                 = "Employees"
      identity_provider_id = cloudflare_zero_trust_access_identity_provider.okta.id
    }
  }]
  require = [
    {
      auth_method = {
        auth_method = "swk"
      }
    },
    {
      device_posture = {
        integration_uid = cloudflare_zero_trust_device_posture_rule.gateway_check.id
      }
    },
  ]
}
```

The `auth_method` value uses [RFC 8176](https://datatracker.ietf.org/doc/html/rfc8176#section-2) authentication method reference values. For example, `swk` represents a software-secured key (security key).

- Replace the `okta` rule with the appropriate [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource for your identity provider. To configure the identity provider resource, refer to [Identity providers](/cloudflare-one/integrations/identity-providers/).
- To configure the [`cloudflare_zero_trust_device_posture_rule`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_posture_rule) resource referenced above, refer to [Device posture checks](/cloudflare-one/reusable-components/posture-checks/).

</TabItem> </Tabs>

### Independent MFA

If you want to enforce MFA directly in Access without relying on your IdP, you can use [independent MFA](/cloudflare-one/access-controls/access-settings/independent-mfa/). Independent MFA is not configured through policy selectors. Instead, you first [turn on independent MFA](/cloudflare-one/access-controls/access-settings/independent-mfa/#turn-on-independent-mfa) at the organization level, then enable it for specific applications or policies through a settings panel. Access will prompt users for a second factor (such as a security key, authenticator app, or biometrics) after they authenticate with your IdP.

For the full details on both approaches, refer to [Enforce MFA](/cloudflare-one/access-controls/policies/mfa-requirements/).

## Allow contractor access with email-based authentication

When you collaborate with external contractors or partners who are not part of your corporate identity provider, you can grant them access using a [one-time PIN (OTP)](/cloudflare-one/integrations/identity-providers/one-time-pin/). OTP sends a short-lived code to the contractor's email address, allowing them to authenticate without needing an account in your IdP.

:::note
Before creating this policy, [enable OTP as a login method](/cloudflare-one/integrations/identity-providers/one-time-pin/#set-up-otp) in your identity provider settings.
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action | Rule type | Selector         | Value                                    |
| ------ | --------- | ---------------- | ---------------------------------------- |
| Allow  | Include   | Emails ending in | `@contractor-a.com`, `@contractor-b.com` |
|        | Require   | Login methods    | `One-time PIN`                           |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Allow contractor access with OTP",
		decision: "allow",
		include: [
			{
				email_domain: {
					domain: "contractor-a.com",
				},
			},
			{
				email_domain: {
					domain: "contractor-b.com",
				},
			},
		],
		require: [
			{
				login_method: {
					id: "<OTP_IDENTITY_PROVIDER_ID>",
				},
			},
		],
	}}
/>

To get the ID of your OTP identity provider, use the [List Access identity providers](/api/resources/zero_trust/subresources/access/subresources/identity_providers/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "allow_contractor_access_with_otp" {
  account_id = var.cloudflare_account_id
  name       = "Allow contractor access with OTP"
  decision   = "allow"
  include = [
    {
      email_domain = {
        domain = "contractor-a.com"
      }
    },
    {
      email_domain = {
        domain = "contractor-b.com"
      }
    },
  ]
  require = [{
    login_method = {
      id = cloudflare_zero_trust_access_identity_provider.otp.id
    }
  }]
}
```

To configure the [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource for OTP (configured with `type = "onetimepin"`), refer to [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/).

</TabItem> </Tabs>

:::caution
Adding `Login Methods: One-time PIN` as an Include rule without restricting email domains allows anyone with any email address to receive a code and access the application. Always pair OTP with specific email domains or an email list in the Include rule.
:::

## Isolate contractor access to internal applications

When contractors or other external users need to view internal applications but should not be able to download, copy, or transfer data to their unmanaged devices, you can serve the application in a [remote browser](/cloudflare-one/remote-browser-isolation/). This gives external users read-only visibility into the application while keeping sensitive data from leaving your environment.

:::note
Before creating this policy, you must turn on [Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/).
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action | Rule type | Selector         | Value                                    |
| ------ | --------- | ---------------- | ---------------------------------------- |
| Allow  | Include   | Emails ending in | `@contractor-a.com`, `@contractor-b.com` |

**Additional settings**: Turn on **Isolate application**.

</TabItem> <TabItem label="API">

First, enable Clientless Web Isolation on your account if you have not already:

<APIRequest
	path="/accounts/{account_id}/gateway/configuration"
	method="PATCH"
	json={{
		settings: {
			browser_isolation: {
				url_browser_isolation_enabled: true,
			},
		},
	}}
/>

Then, create the Access policy with `isolation_required` set to `true`:

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Isolate contractor access",
		decision: "allow",
		include: [
			{
				email_domain: {
					domain: "contractor-a.com",
				},
			},
			{
				email_domain: {
					domain: "contractor-b.com",
				},
			},
		],
		isolation_required: true,
	}}
/>

</TabItem> <TabItem label="Terraform">

First, configure the [`cloudflare_zero_trust_gateway_settings`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_gateway_settings) resource to enable Clientless Web Isolation on your account if you have not already:

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Zero Trust Write`

</Details>

```tf
resource "cloudflare_zero_trust_gateway_settings" "gateway_settings" {
  account_id = var.cloudflare_account_id
  settings = {
    browser_isolation = {
      url_browser_isolation_enabled = true
    }
  }
}
```

Then, configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource with `isolation_required` set to `true`:

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

```tf
resource "cloudflare_zero_trust_access_policy" "isolate_contractor_access" {
  account_id         = var.cloudflare_account_id
  name               = "Isolate contractor access"
  decision           = "allow"
  isolation_required = true
  include = [
    {
      email_domain = {
        domain = "contractor-a.com"
      }
    },
    {
      email_domain = {
        domain = "contractor-b.com"
      }
    },
  ]
}
```

</TabItem> </Tabs>

To restrict what users can do inside the isolated session, create a companion [Gateway HTTP policy](/cloudflare-one/traffic-controls/http-policies/) that matches traffic to the application domain. Set the action to **Isolate** and disable interactive controls in the [policy settings](/cloudflare-one/remote-browser-isolation/isolation-policies/#policy-settings).

<Details header="Example Gateway HTTP policy">

| Selector | Operator | Value              | Action  |
| -------- | -------- | ------------------ | ------- |
| Domain   | in       | `wiki.example.com` | Isolate |

**Policy settings**:

| Setting        | Value        |
| -------------- | ------------ |
| Copy           | Do not allow |
| Paste          | Do not allow |
| Keyboard       | Do not allow |
| File downloads | Do not allow |
| File uploads   | Do not allow |
| Printing       | Do not allow |

</Details>

For more information, refer to [Isolate self-hosted application](/cloudflare-one/access-controls/policies/isolate-application/).

## Block requests from high-risk countries

If your organization restricts access from certain countries due to internal policy or regulatory requirements such as [OFAC sanctions](https://orpa.princeton.edu/export-controls/sanctioned-countries) or [ITAR regulations](https://www.tradecompliance.pitt.edu/embargoed-and-sanctioned-countries), you can create a Block policy that denies access from those regions. Adding a corporate IP allowlist as an Exclude rule ensures that employees connecting through trusted office networks are not inadvertently blocked.

:::note
Before creating this policy, [create a list](/cloudflare-one/reusable-components/lists/) with your approved IP ranges.
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render file="access/example-block-country" product="cloudflare-one" />

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Block requests from high-risk countries",
		decision: "deny",
		include: [
			{
				geo: {
					country_code: "RU",
				},
			},
		],
		exclude: [
			{
				ip_list: {
					id: "<CORPORATE_IP_ALLOWLIST_ID>",
				},
			},
		],
	}}
/>

To get the ID of your IP list, use the [List Zero Trust lists](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "block_high_risk_countries" {
  account_id = var.cloudflare_account_id
  name       = "Block requests from high-risk countries"
  decision   = "deny"
  include = [{
    geo = {
      country_code = "RU"
    }
  }]
  exclude = [{
    ip_list = {
      id = cloudflare_zero_trust_list.corporate_ip_allowlist.id
    }
  }]
}
```

To configure the [`cloudflare_zero_trust_list`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_list) resource referenced above (configured with `type = "IP"`), refer to [Lists](/cloudflare-one/reusable-components/lists/).

</TabItem> </Tabs>

Block policies are best used together with [Allow policies](#allow-employees-by-email-domain) to carve out exceptions. Because Access denies all requests by default, users who do not match a Block policy are still denied unless they match an Allow policy.

## Exclude high-risk users

If your organization uses [Cloudflare User Risk Scores](/cloudflare-one/team-and-resources/users/risk-score/) to flag users with anomalous behavior, you can exclude high-risk users from accessing sensitive applications. This is useful as a dynamic safeguard that automatically restricts access when a user's behavior triggers a risk level change, without requiring manual intervention.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render file="access/example-exclude-risk-score" product="cloudflare-one" />

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Exclude high-risk users",
		decision: "allow",
		include: [
			{
				email_domain: {
					domain: "example.com",
				},
			},
		],
		exclude: [
			{
				user_risk_score: {
					user_risk_score: ["high"],
				},
			},
		],
	}}
/>

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "exclude_high_risk_users" {
  account_id = var.cloudflare_account_id
  name       = "Exclude high-risk users"
  decision   = "allow"
  include = [{
    email_domain = {
      domain = "example.com"
    }
  }]
  exclude = [{
    user_risk_score = {
      user_risk_score = ["high"]
    }
  }]
}
```

</TabItem> </Tabs>

In this example, any user scored as high risk is excluded even if they match the Include rule. To learn how risk scores are calculated and how to configure risk behaviors, refer to [User risk score](/cloudflare-one/team-and-resources/users/risk-score/).

## Authenticate a service using a service token

Automated services such as CI/CD pipelines, monitoring systems, and backend APIs need to access protected applications without an interactive login. Service Auth policies allow machine-to-machine communication by authenticating requests that present valid [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) headers. For additional security, you can restrict the token to requests from specific IP ranges, ensuring the token can only be used from known infrastructure.

:::note
Before creating this policy, [create a service token](/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token).
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action       | Rule type | Selector      | Value              |
| ------------ | --------- | ------------- | ------------------ |
| Service Auth | Include   | Service Token | `My service token` |
|              | Require   | IP ranges     | `192.0.2.0/24`     |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Authenticate service with service token",
		decision: "non_identity",
		include: [
			{
				service_token: {
					token_id: "<SERVICE_TOKEN_ID>",
				},
			},
		],
		require: [
			{
				ip: {
					ip: "192.0.2.0/24",
				},
			},
		],
	}}
/>

To get the ID of your service token, use the [List service tokens](/api/resources/zero_trust/subresources/access/subresources/service_tokens/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "authenticate_service_with_token" {
  account_id = var.cloudflare_account_id
  name       = "Authenticate service with service token"
  decision   = "non_identity"
  include = [{
    service_token = {
      token_id = cloudflare_zero_trust_access_service_token.my_service_token.id
    }
  }]
  require = [{
    ip = {
      ip = "192.0.2.0/24"
    }
  }]
}
```

To configure the [`cloudflare_zero_trust_access_service_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_service_token) resource referenced above, refer to [Service tokens](/cloudflare-one/access-controls/service-credentials/service-tokens/).

</TabItem> </Tabs>

## Authenticate a service using mutual TLS

For environments that require certificate-based authentication, you can use [mutual TLS (mTLS)](/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/) to verify that a connecting client presents a valid certificate with an expected identity. mTLS is useful for authenticating automated systems and IoT devices that do not use an identity provider, or as an additional authentication factor for team members who also log in through an IdP.

:::note
Before creating this policy, [upload a certificate authority (CA)](/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/#add-mtls-authentication-to-your-access-configuration) to your Access configuration.
:::

To restrict access to a specific client, use the **Common Name** selector to match the identity in the client certificate:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render file="access/example-service-auth-mtls" product="cloudflare-one" />

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Authenticate service with mTLS",
		decision: "non_identity",
		include: [
			{
				common_name: {
					common_name: "John Doe",
				},
			},
		],
	}}
/>

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "authenticate_service_with_mtls" {
  account_id = var.cloudflare_account_id
  name       = "Authenticate service with mTLS"
  decision   = "non_identity"
  include = [{
    common_name = {
      common_name = "John Doe"
    }
  }]
}
```

</TabItem> </Tabs>

To allow any client presenting a valid certificate signed by your CA, use the **Valid Certificate** selector. This selector is useful when you trust all certificates issued by your CA and do not need to check a specific Common Name.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action       | Rule type | Selector          |
| ------------ | --------- | ----------------- |
| Service Auth | Include   | Valid Certificate |

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Authenticate service with valid certificate",
		decision: "non_identity",
		include: [
			{
				certificate: {},
			},
		],
	}}
/>

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "authenticate_service_with_valid_certificate" {
  account_id = var.cloudflare_account_id
  name       = "Authenticate service with valid certificate"
  decision   = "non_identity"
  include = [{
    certificate = {}
  }]
}
```

</TabItem> </Tabs>

## Require purpose justification for sensitive applications

For applications such as database admin tools, production consoles, or HR systems, you can require users to provide a written reason each time they access the application. This creates an audit trail that helps security teams understand why access was requested. The justification prompt appears after the user authenticates and before they reach the application. For more information, refer to [Require purpose justification](/cloudflare-one/access-controls/policies/require-purpose-justification/).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Action | Rule type | Selector       | Value                                                 |
| ------ | --------- | -------------- | ----------------------------------------------------- |
| Allow  | Include   | Okta Groups    | `IT Administrators`                                   |
|        | Require   | Gateway        | `Gateway`                                         |
|        | Require   | OS Version | `Latest version of Windows`       |

**Additional settings**: Turn on **Purpose justification**.

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Require purpose justification for sensitive apps",
		decision: "allow",
		include: [
			{
				okta: {
					name: "IT Administrators",
					identity_provider_id: "<OKTA_IDP_ID>",
				},
			},
		],
		require: [
			{
				device_posture: {
					integration_uid: "<GATEWAY_CHECK_ID>",
				},
			},
			{
				device_posture: {
					integration_uid: "<WINDOWS_VERSION_CHECK_ID>",
				},
			},
		],
		purpose_justification_required: true,
		purpose_justification_prompt: "Please enter a justification for accessing this application.",
	}}
/>

Replace the `okta` rule with the [appropriate rule for your identity provider](/api/resources/zero_trust/subresources/access/subresources/policies/methods/create/). To get your identity provider ID, use the [List Access identity providers](/api/resources/zero_trust/subresources/access/subresources/identity_providers/methods/list/) endpoint. To get the integration UIDs for your device posture checks, use the [List device posture checks](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/list/) endpoint.

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "require_purpose_justification" {
  account_id                      = var.cloudflare_account_id
  name                            = "Require purpose justification for sensitive apps"
  decision                        = "allow"
  purpose_justification_required  = true
  purpose_justification_prompt    = "Please enter a justification for accessing this application."
  include = [{
    okta = {
      name                 = "IT Administrators"
      identity_provider_id = cloudflare_zero_trust_access_identity_provider.okta.id
    }
  }]
  require = [
    {
      device_posture = {
        integration_uid = cloudflare_zero_trust_device_posture_rule.gateway_check.id
      }
    },
    {
      device_posture = {
        integration_uid = cloudflare_zero_trust_device_posture_rule.windows_version.id
      }
    },
  ]
}
```

- Replace the `okta` rule with the appropriate [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource for your identity provider. To configure the identity provider resource, refer to [Identity providers](/cloudflare-one/integrations/identity-providers/).
- To configure the [`cloudflare_zero_trust_device_posture_rule`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_posture_rule) resources referenced above, refer to [Device posture checks](/cloudflare-one/reusable-components/posture-checks/).

</TabItem> </Tabs>

You can combine purpose justification with [temporary authentication](/cloudflare-one/access-controls/policies/temporary-auth/) to additionally require approval from a designated reviewer before granting access.

## Bypass a public endpoint

Some applications have endpoints that must be publicly reachable, such as OAuth callback URLs, webhook receivers, or health check paths. You can create a Bypass policy scoped to a specific [application path](/cloudflare-one/access-controls/policies/app-paths/) to disable Access enforcement for that endpoint only. For example, if your application is `app.example.com`, you could create a separate Access application for `app.example.com/oauth/callback` and apply the following Bypass policy:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render file="access/example-bypass-everyone" product="cloudflare-one" />

</TabItem> <TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Bypass public endpoint",
		decision: "bypass",
		include: [
			{
				everyone: {},
			},
		],
	}}
/>

</TabItem> <TabItem label="Terraform">

<Details header="Required API token permissions">

At least one of the following [token permissions](/fundamentals/api/reference/permissions/) is required:

- `Access: Apps and Policies Write`

</Details>

Configure the [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource:

```tf
resource "cloudflare_zero_trust_access_policy" "bypass_public_endpoint" {
  account_id = var.cloudflare_account_id
  name       = "Bypass public endpoint"
  decision   = "bypass"
  include = [{
    everyone = {}
  }]
}
```

</TabItem> </Tabs>

:::caution
Bypass disables all Access security controls and request logging for matching traffic. Scope Bypass policies as narrowly as possible and never use them as a persistent access mechanism for users or services. If you need to allow automated traffic while maintaining authentication and logging, use a [Service Auth](#authenticate-a-service-using-a-service-token) policy instead.
:::

# External Evaluation rules

import { GlossaryTooltip, Example, WranglerConfig, DashButton } from "~/components";

With Cloudflare Access, you can create Allow or Block policies which evaluate the user based on custom criteria. This is done by adding an **External Evaluation** rule to your policy. The **External Evaluation** selector requires two values:

- **Evaluate URL** — the API endpoint containing your business logic.
- **Keys URL** — the key that Access uses to verify that the response came from your API

After the user authenticates with your identity provider, Access sends the user's identity to the external API at **Evaluate URL**. The external API returns a True or False response to Access, which will then allow or deny access to the user. To protect against man-in-the-middle attacks, Access signs all requests with your Access account key and checks that responses are signed by the key at **Keys URL**.

You can set up External Evaluation rules using any API service, but to get started quickly we recommend using [Cloudflare Workers](/workers/).

## Set up external API and key with Cloudflare Workers

### Prerequisites

- [Workers account](/workers/get-started/guide/)
- Install [npm](https://docs.npmjs.com/getting-started)
- Install [Node.js](https://nodejs.org/en/)
- Application protected by Access

### 1. Create a new Worker

1. Open a terminal and clone our example project.

   ```sh
   npm create cloudflare@latest my-worker -- --template https://github.com/cloudflare/workers-access-external-auth-example
   ```

2. Go to the project directory.

   ```sh
   cd my-worker
   ```

3. Create a [Workers KV namespace](/kv/concepts/kv-namespaces/) to store the key. The binding name should be `KV` if you want to run the example as written.

   ```sh
   npx wrangler kv namespace create "KV"
   ```

   The command will output the binding name and KV namespace ID, for example

   ```txt
     [[kv_namespaces]]
      binding = "KV"
      id = "YOUR_KV_NAMESPACE_ID"
   ```

4. Open the [Wrangler configuration file](/workers/wrangler/configuration/) in an editor and insert the following:

   - `[[kv_namespaces]]`: Add the output generated in the previous step.
   - `<TEAM_NAME>`: your Cloudflare One <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

  <WranglerConfig>

   ```jsonc
   {
     "$schema": "./node_modules/wrangler/config-schema.json",
     "name": "my-worker",
     "workers_dev": true,
     "compatibility_date": "$today",
     "main": "index.js",
     "upload_source_maps": true,
     "kv_namespaces": [
       {
         "binding": "KV",
         "id": "YOUR_KV_NAMESPACE_ID"
       }
     ],
     "vars": {
       "TEAM_DOMAIN": "<TEAM_NAME>.cloudflareaccess.com",
       "DEBUG": false
     }
   }
   ```
	 </WranglerConfig>

### 2. Program your business logic

1. Open `index.js` and modify the `externalEvaluation` function to perform logic on any identity-based data sent by Access.

:::note

- Sample code is available in our [GitHub repository](https://github.com/cloudflare/workers-access-external-auth-example).
- To view a list of identity-based data fields, log in to your Access application and append `/cdn-cgi/access/get-identity` to the URL. For example, if `www.example.com` is behind Access, visit `https://www.example.com/cdn-cgi/access/get-identity`.
  :::

2. Deploy the Worker to Cloudflare's global network.

   ```sh
   npx wrangler deploy
   ```

The Worker will be deployed to your `*.workers.dev` subdomain at `my-worker.<YOUR_SUBDOMAIN>.workers.dev`.

### 3. Generate a key

To generate an RSA private/public key pair:

1. Open a browser and go to `https://my-worker.<YOUR_SUBDOMAIN>.workers.dev/keys`.

2. (Optional) Verify that the key has been stored in the `KV` namespace:
   1. In the Cloudflare dashboard, go to the **Workers KV** page.
			<DashButton url="/?to=/:account/workers/kv/namespaces" />
   2. Select **View** next to `my-worker-KV`.

Other key formats (such as DSA) are not supported at this time.

### 4. Create an External Evaluation rule

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.

2. Edit an existing policy or select **Add a policy**.

3. Add the following rule to your policy:

| Rule Type | Selector            | Evaluate URL                                      | Keys URL                                               |
| --------- | ------------------- | ------------------------------------------------- | ------------------------------------------------------ |
| Include   | External Evaluation | `https://my-worker.<YOUR_SUBDOMAIN>.workers.dev/` | `https://my-worker.<YOUR_SUBDOMAIN>.workers.dev/keys/` |

4. Save the policy.

5. Go to **Access controls** > **Applications** and edit the application for which you want to apply the External Evaluation rule.

6. In the **Policies** tab, add the policy that contains the External Evaluation rule.

7. Select **Save**.

When a user logs in to your application, Access will now check their email, device, location, and other identity-based data against your business logic.

### Troubleshooting the Worker

To debug your External Evaluation rule:

1. Go to your Worker directory.

   ```sh
   cd my-worker
   ```

2. Open the [Wrangler configuration file](/workers/wrangler/configuration/) in an editor and set the `debug` variable to `TRUE`.

3. Deploy your changes.

   ```sh
   npx wrangler deploy
   ```

4. Next, start a session to output realtime logs from your Worker.

   ```sh
   wrangler tail -f pretty
   ```

5. Log in to your Access application.

   The session logs should show an incoming and outgoing JWT. The incoming JWT was sent by Access to the Worker API, while the outgoing JWT was sent by the Worker back to Access.

6. To decode the contents of a JWT, you can copy the token into [jwt.io](https://jwt.io/).

   The incoming JWT should contain the user's identity data. The outgoing JWT should look similar to:

   ```js
   {
   "success": true,
   "iat": 1655409315,
   "exp": 1655409375,
   "nonce": "9J2E9Xg6wYj8tlnA5MV4Zgp6t8rzmS0Q"
   }
   ```

   Access checks the outgoing JWT for all of the following criteria:

   - Token was signed by **Keys URL**.
   - Expiration date has not elapsed.
   - API returns `"success": true`.
   - `nonce` is unchanged from the incoming JWT. The `nonce` value is unique per request.

   If any condition fails, the External Evaluation rule evaluates to false.

# Rule groups

import { Render } from "~/components";

A rule group is a collection of Access rules that can be configured once and then quickly applied across many Access policies. Rule groups use the same [rule types](/cloudflare-one/access-controls/policies/#rule-types) and [selectors](/cloudflare-one/access-controls/policies/#selectors) shown in the Access policy builder.

:::note
Rule groups are distinct from groups in your identity provider, like Okta groups. Rule groups can contain a mix of individual users, groups from identity providers, and service authentication options like service tokens.
:::

## Create a rule group

<Render file="access/rule-group" product="cloudflare-one" />

## Use cases

### IP-based rules

We recommend using rule groups to define any IP address-based rules you configure in policies. Keeping IP addresses in one place allows you to modify or remove addresses once, rather than in each policy, and reduces the potential for mistakes.

:::note
If adding more than one IP address or range to a rule group, use an Include rule for the IPs. If you do not use an Include rule, the policy will require traffic to originate from all ranges.
:::

### Country requirements

You can create a rule group that consists of countries to allow or block. Access will treat the countries in the Include rule with an OR logical operator. When building policies for an Access application, you can assign this rule group to a Require policy to require at least one of the countries inside of the group. For an example policy, refer to [Require rules with OR operators](/cloudflare-one/access-controls/policies/#require-rules-with-or-operators).

# Policies

import { Render } from "~/components";

Cloudflare Access determines who can reach your application by applying the Access policies you configure.

Every Access policy has four building blocks:

- [**Actions**](#actions): What happens when a user matches the policy (Allow, Block, Bypass, or Service Auth)
- [**Rule types**](#rule-types): How criteria are combined (Include, Require, or Exclude)
- [**Selectors**](#selectors): The attributes being checked (for example, email domain, country, or device posture)
- **Values**: The specific values to match against (for example, `@example.com`)

## Cloudflare Access policy actions {/* actions */}

Actions let you grant or deny permission to a certain user or user group. You can set only one action per policy.

### Allow

The Allow action in Cloudflare Access allows users that meet certain criteria to reach an application behind Access.

The following table shows an example Cloudflare Access Allow policy that lets any user with an `@example.com` email address, as validated against an IdP, reach the application:

<Render file="access/example-allow-email-domain" product="cloudflare-one" />

You can add a Require rule in the same policy action to enforce additional checks. Finally, if the policy contains an Exclude rule, users meeting that definition are prevented from reaching the application.

For example, the following table shows an Allow policy with Require and Exclude rules. This configuration lets any user from Portugal with an `@team.com` email address, as validated against an IdP, reach the application, except for `user-1` and `user-2`:

| Action | Rule type | Selector         | Value                                |
| ------ | --------- | ---------------- | ------------------------------------ |
| Allow  | Include   | Country          | `Portugal`                           |
|        | Require   | Emails Ending In | `@team.com`                          |
|        | Exclude   | Email            | `user-1@team.com`, `user-2@team.com` |

### Block

The Block action in Cloudflare Access prevents users who meet certain criteria from reaching an application. For example, the following table shows a Block policy that blocks requests from Russian source IPs that are not on your [list of approved IPs](/cloudflare-one/reusable-components/lists/).

<Render file="access/example-block-country" product="cloudflare-one" />

Block policies are best used in conjunction with [Allow policies](#allow) as a way to carve out exceptions in those Allow policies. Since Access is deny by default, users who do not match a Block policy will still be denied access unless they explicitly match an Allow policy.

### Bypass

The Bypass action in Cloudflare Access disables Access enforcement for specific traffic.

:::caution[Warning]

Bypass does not enforce any Access security controls and requests are not logged. Bypass policies should be tested before deploying to production. Consider using [Service Auth](/cloudflare-one/access-controls/policies/#service-auth) if you would like to enforce policies and maintain logging without requiring user authentication.

As Bypass does not enforce Access security controls, Bypass policies do not support identity-based [rule types](/cloudflare-one/access-controls/policies/#rule-types). When making Bypass policies, you will not be able to apply certain identity-based [selectors](/cloudflare-one/access-controls/policies/#selectors) (such as email).

:::

The Bypass action disables any Access enforcement for traffic that meets the defined rule criteria. Bypass is typically used to enable applications that require specific endpoints to be public.

For example, some applications have an endpoint under the `/admin` route that must be publicly routable. In this situation, you could create an Access application for the domain `test.example.com/admin/<your-url>` and add the Bypass policy shown in the following table:

<Render file="access/example-bypass-everyone" product="cloudflare-one" />

As part of implementing a Zero Trust security model, Cloudflare does not recommend using Bypass to grant direct permanent access to your internal applications. To enable seamless and secure access for on-network employees, use Cloudflare Tunnel to [connect your private network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/) and have users connect through the Cloudflare One Client.

:::note

When applying a Bypass action, security settings revert to the defaults configured for the zone and any configured Page Rules. If **Always use HTTPS** is enabled for the site, then traffic to the bypassed destination continues in HTTPS. If **Always use HTTPS** is disabled, traffic is HTTP.

:::

#### Bypass policy product incompatibility

Bypass policies which contain [device posture check](/cloudflare-one/reusable-components/posture-checks/) rules will not function when:

- [Zaraz](/zaraz/) is enabled for the zone protected by Access
- A [Worker](/workers/) intercepts the request

To work around these limitations and bypass Access, we recommend changing the policy action to [Service Auth](#service-auth).

### Service Auth

Service Auth rules in Cloudflare Access enforce authentication flows that do not require an identity provider IdP login, such as service tokens and mutual TLS.

The following table shows an example Cloudflare Access Service Auth policy configuration:

| Action       | Rule type | Selector          |
| ------------ | --------- | ----------------- |
| Service Auth | Include   | Valid certificate |

## Cloudflare Access rule types {/* rule-types */}

Rule types work like logical operators and determine how your criteria are combined to evaluate a user. All Access policies must contain at least one Include rule. This Include rule defines the initial pool of eligible users who can access an application. You can then add Exclude and Require rules to narrow the scope.

### Include

The Include rule in Cloudflare Access is similar to an OR logical operator. In case more than one Include rule is specified, users need to meet only one of the criteria.

### Exclude

The Exclude rule in Cloudflare Access works like a NOT logical operator. A user meeting any Exclusion criteria will not be allowed access to the application.

### Require

The Require rule in Cloudflare Access works like an AND logical operator. A user must meet all specified Require rules to be allowed access.

#### Require rules with OR operators

By default, any values added to a Require rule are concatenated by an AND operator. For example, let's say you want to grant access to an application to both the full-time employees and the contractors, and only the ones based in specific countries — say Portugal and the United States. If you set up a rule with the following configuration:

| Action | Rule type | Selector         | Value                                 |
| ------ | --------- | ---------------- | ------------------------------------- |
| Allow  | Require   | Country          | `United States`, `Portugal`           |
|        | Require   | Emails ending in | `@cloudflare.com`, `@contractors.com` |

This policy requires the user to be in the United States AND Portugal simultaneously, and have an email ending in both `@cloudflare.com` AND `@contractors.com`. Therefore, nobody will have access to the application.

**Solution:** Use a [rule group](/cloudflare-one/access-controls/policies/groups/) to convert AND logic to OR logic within a Require rule.

1.  Create a rule group called `Country requirements` that includes users in Portugal OR the United States:

        | Rule type | Selector | Value                       |
        | --------- | -------- | --------------------------- |
        | Include   | Country  | `United States`, `Portugal` |

2.  Create a policy that requires the rule group, and that also includes users with emails ending in either `@cloudflare.com` OR `@contractors.com`:

        | Action | Rule type | Selector          | Value                                 |
        | ------ | --------- | ----------------- | ------------------------------------- |
        | Allow  | Require   | Rule group         | `Country requirements`                    |
        |        | Include   | Emails ending in  | `@cloudflare.com`, `@contractors.com` |

## Cloudflare Access selectors {/* selectors */}

When you add a rule to your Cloudflare Access policy, you will be asked to specify the criteria, or attributes, you want users to meet. These attributes are available for all Access application types, including [SaaS](/cloudflare-one/access-controls/applications/http-apps/saas-apps/), [self-hosted](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/), and [non-HTTP](/cloudflare-one/access-controls/applications/non-http/) applications.

Non-identity attributes are polled continuously, meaning they are evaluated with each new HTTP request for changes during the [user session](/cloudflare-one/access-controls/access-settings/session-management/). If you have configured [SCIM provisioning](/cloudflare-one/team-and-resources/users/scim/), you can force a user to re-attest all attributes with Access whenever you revoke the user in the IdP or update their IdP group membership.

| Selector                 | Description                                                                                                                                                                                                                                                                                                                              | Checked at login | Checked continuously<sup>1</sup> | Identity-based selector? |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -------------------------------- | ------------------------ |
| Emails                   | `you@company.com`                                                                                                                                                                                                                                                                                                                        | ✅               | ❌                               | ✅                       |
| Emails ending in         | `@company.com`                                                                                                                                                                                                                                                                                                                           | ✅               | ❌                               | ✅                       |
| External Evaluation      | Allows or denies access based on [custom logic](/cloudflare-one/access-controls/policies/external-evaluation/) in an external API.                                                                                                                                                                                                       | ✅               | ❌                               | ✅                       |
| IP ranges                | `192.168.100.1/24` (supports IPv4/IPv6 addresses and CIDR ranges)                                                                                                                                                                                                                                                                        | ✅               | ✅                               | ❌                       |
| Country                  | Uses the IP address to determine country.                                                                                                                                                                                                                                                                                                | ✅               | ✅                               | ❌                       |
| Everyone                 | Allows, denies, or bypasses access to everyone.                                                                                                                                                                                                                                                                                          | ✅               | ❌                               | ❌                       |
| Common Name              | The request will need to present a valid certificate with an expected common name.                                                                                                                                                                                                                                                       | ✅               | ✅                               | ❌                       |
| Valid Certificate        | The request will need to present any valid client certificate.                                                                                                                                                                                                                                                                           | ✅               | ✅                               | ❌                       |
| Service Token            | The request will need to present the correct service token headers configured for the specific application. Requires the [Service Auth](#service-auth) action.                                                                                                                                                                           | ✅               | ✅                               | ❌                       |
| Any Access Service Token | The request will need to present the headers for any [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) created for this account. Requires the [Service Auth](#service-auth) action.                                                                                                                   | ✅               | ✅                               | ❌                       |
| User Risk Score          | The user's current [risk score](/cloudflare-one/team-and-resources/users/risk-score/) (Low, Medium, High, or Unscored). Matches only the values selected in the rule. This selector only displays for Enterprise plans.                                                                                                                   | ✅               | ✅                               | ✅                       |
| Linked App Token         | Checks for a valid [OAuth access token](/cloudflare-one/access-controls/applications/linked-app-token/) issued to a specific Access application. Requires the [Service Auth](#service-auth) action.                                                                                                                                                   | ✅               | ✅                               | ❌                       |
| Login Methods            | Checks the identity provider used at the time of login.                                                                                                                                                                                                                                                                                  | ✅               | ❌                               | ✅                       |
| Authentication Method    | Checks the [multi-factor authentication](/cloudflare-one/access-controls/policies/mfa-requirements/#identity-provider-based-mfa) method used by the user, if supported by the identity provider. To enforce MFA independently of your IdP, refer to [independent MFA](/cloudflare-one/access-controls/access-settings/independent-mfa/). | ✅               | ❌                               | ✅                       |
| Identity provider group  | Checks the user groups configured with your identity provider (IdP). This selector only displays if you use Microsoft Entra ID, GitHub, Google, Okta, or an IdP that provisions groups with [SCIM](/cloudflare-one/team-and-resources/users/scim/).                                                                                      | ✅               | ❌                               | ✅                       |
| SAML Group               | Checks a SAML attribute name / value pair. This selector only displays if you use a [generic SAML](/cloudflare-one/integrations/identity-providers/generic-saml/) identity provider.                                                                                                                                                     | ✅               | ❌                               | ✅                       |
| OIDC Claim               | Checks an OIDC claim name / value pair. This selector only displays if you use a [generic OIDC](/cloudflare-one/integrations/identity-providers/generic-oidc/) identity provider.                                                                                                                                                        | ✅               | ❌                               | ✅                       |
| Device posture           | Checks device posture signals from the Cloudflare One Client or a third-party service provider. This selector only displays after you create a [device posture check](/cloudflare-one/reusable-components/posture-checks/).                                                                                                              | ✅               | ✅                               | ❌                       |
| Warp                     | Checks that the device is connected to the Cloudflare One Client, including the consumer version. This selector only displays after you enable the [WARP posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/require-warp/).                                                                                 | ✅               | ✅                               | ❌                       |
| Gateway                  | Checks that the device is connected to your Zero Trust instance through the Cloudflare One Client. This selector only displays after you enable the [Gateway posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/require-gateway/).                                                                          | ✅               | ✅                               | ❌                       |
| Cloudflare Account Member | Checks that the user is a member of a specific Cloudflare account. If no account ID is specified, defaults to the current account. This selector only displays if you use the [Cloudflare](/cloudflare-one/integrations/identity-providers/cloudflare/) identity provider.                                                                | ✅               | ❌                               | ✅                       |

<sup>1</sup> For SaaS applications, Access can only enforce policies at the time
of initial sign on and when reissuing the SaaS session. Once the user has
authenticated to the SaaS app, session management falls solely within the
purview of the SaaS app.

## Connection context in Cloudflare Access

Connection context settings allow you to control how users interact with an application after they have been granted access. While [selectors](#selectors) determine who can access an application, connection context settings determine what actions users can take during their session. The available connection context settings depend on the application type.

Connection context is configured per policy, allowing you to grant different permissions to different groups of users. For example, you could allow full-time employees to copy data from a remote RDP session while restricting contractors to read-only access.

| Application type                                                                                                         | Available settings                           |
| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
| [Infrastructure (SSH)](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/)                       | Allowed UNIX usernames                       |
| [Browser-based RDP](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-browser/#connection-settings) | Clipboard controls, file transfer controls |

## Cloudflare Access policy order of execution

Cloudflare Access policies are evaluated based on their action type and order you set. Bypass and Service Auth policies are evaluated first, from top to bottom as shown in the UI. Then, Block and Allow policies are evaluated based on their order from top to bottom.

For example, if you have policies arranged as follows:

- Allow A
- Block B
- Service Auth C
- Bypass D
- Allow E

The policies will execute in this order: Service Auth C > Bypass D > Allow A > Block B > Allow E. Once a user matches an Allow or Block policy, evaluation stops and no subsequent policies can override the decision.

## Common Cloudflare Access misconfigurations

If you add any of the following rules to an Allow policy, anyone will be able to access your application.

### Include everyone

The following table shows a Cloudflare Access policy that includes everyone:
| Rule type | Selector | Value |
| --------- | -------- | ---------- |
| Include | Everyone | `Everyone` |

### Include all valid emails

The following table shows a Cloudflare Access policy that includes all users with valid email login methods:
| Rule type | Selector | Value |
| --------- | ------------- | -------------- |
| Include | Login Methods | `One-time PIN` |

## Additional Cloudflare Access resources

[API and Terraform](/cloudflare-one/api-terraform/) provide programmatic ways to manage your Access policies and configurations.

# Isolate self-hosted application

import { Render } from "~/components";

:::note

Requires [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/).
:::

With Access policies, you can require users to open self-hosted applications in a secure [remote browser](/cloudflare-one/remote-browser-isolation/). Because the remote browser is directly integrated into our Secure Web Gateway platform, [HTTP policies](/cloudflare-one/traffic-controls/http-policies/) can be applied to isolated applications without needing to install the Cloudflare One Client. This allows you to distribute internal applications to unmanaged users while retaining control over sensitive data.

## Prerequisites

<Render file="access/isolation-prereqs" product="cloudflare-one" />

## Enable Browser Isolation

<Render file="access/enable-isolation" product="cloudflare-one" />

## Policies for isolated applications

Traffic to the isolated Access application is filtered by your Gateway [HTTP policies](/cloudflare-one/traffic-controls/http-policies/). Useful policies include:

- [Identity-based policies](/cloudflare-one/traffic-controls/identity-selectors/) to allow or block requests based on user identity.
- [Data Loss Prevention policies](/cloudflare-one/data-loss-prevention/) to log or block transmission of sensitive data.
- [Isolation policies](/cloudflare-one/remote-browser-isolation/isolation-policies/) to disable browser actions such as copy/paste, printing, or file downloads.

For example, if your application is hosted on `internal.site.com`, the following policy blocks users from uploading and downloading credit card numbers within the remote browser:

| Selector    | Operator | Value                   | Logic | Action |
| ----------- | -------- | ----------------------- | ----- | ------ |
| Domain      | in       | `internal.site.com`     | And   | Block  |
| DLP Profile | in       | `Financial Information` |       |        |

## Product compatibility

For a list of products that are incompatible with the **Isolate application** feature, refer to [Product Compatibility](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/#product-compatibility) .

# Enforce MFA

import { GlossaryTooltip, Details, Render } from "~/components";

Cloudflare Access supports two methods of enforcing multi-factor authentication (MFA):

- **[Identity provider-based MFA](#identity-provider-based-mfa)** — Require specific MFA methods reported by your identity provider (IdP).
- **[Independent MFA](#independent-mfa)** — Prompt users for a second factor directly in Access, without relying on a third-party identity provider.

For SSH connections to [infrastructure applications](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/), Access also supports [independent MFA with PIV and FIDO2 keys](#infrastructure-applications).

## Identity provider-based MFA

You can require that users log in with specific MFA methods provided by their identity provider. For example, you can create rules that only allow users to reach a given application if they authenticate with a security key through their IdP.

IdP-based MFA enforcement is only available with the following identity providers:

- [Okta](/cloudflare-one/integrations/identity-providers/okta/)
- [Microsoft Entra ID (formerly Azure AD)](/cloudflare-one/integrations/identity-providers/entra-id/)
- [Generic OIDC](/cloudflare-one/integrations/identity-providers/generic-oidc/)
- [Generic SAML 2.0](/cloudflare-one/integrations/identity-providers/generic-saml/)

To enforce an IdP MFA requirement on an application:

1.  In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2.  Find the application for which you want to enforce MFA and select **Configure**. Alternatively, [create a new application](/cloudflare-one/access-controls/applications/http-apps/).

3.  Go to **Policies**.

4.  If your application already has a policy containing an identity requirement, find it and select **Configure**.

    :::note
    The policy should contain an Include rule that uses identity-based selectors. For example, the Include rule could allow users who are part of a [rule group](/cloudflare-one/access-controls/policies/groups/), email domain, or identity provider group.
    :::

5.  Add the following rule to the policy:

        | Rule type | Selector | Value |
        | ---------- | -------- | ------ |
        | Require    | Authentication method |  `mfa - multiple-factor authentication` |

6.  Save the policy.

:::caution[Important]
If the user fails to present the required MFA method, Cloudflare Access rejects the user, even if they successfully log in to the identity provider with an alternative method.
:::

### Authentication methods in the JWT

When users authenticate with their identity provider, the IdP shares their username with Cloudflare Access. Access writes that value into the <GlossaryTooltip term="JSON web token">JSON Web Token (JWT)</GlossaryTooltip> generated for the user.

Certain identity providers also share the MFA method presented by the user. Access can add these values into the JWT. For example, if the user authenticated with their password and a security key, the IdP can send a confirmation to Cloudflare Access. Access then stores that method in the JWT issued to the user.

Cloudflare Access follows [RFC 8176](https://tools.ietf.org/html/rfc8176), Authentication Method Reference Values, to define authentication methods.

## Independent MFA

Independent MFA prompts users for a second factor directly in Access. This allows you to enforce MFA requirements without relying on your IdP's MFA configuration.

You can configure MFA requirements at three levels:

| Level                                                                            | Description                                                    |
| -------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| [Organization](/cloudflare-one/access-controls/access-settings/independent-mfa/) | Enforce MFA by default for all applications in your account.   |
| [Application](#configure-independent-mfa-for-an-application)                     | Require or turn off MFA for a specific application.            |
| [Policy](#configure-independent-mfa-for-a-policy)                                | Require or turn off MFA for users who match a specific policy. |

MFA settings use this precedence: **Policy** > **Application** > **Organization**.

### Prerequisites

Before you configure independent MFA on applications or policies, you must [turn on independent MFA](/cloudflare-one/access-controls/access-settings/independent-mfa/) at the organization level.

:::tip
At the organization level, you can also [restrict which authenticators can be enrolled using AAGUIDs](/cloudflare-one/access-controls/access-settings/independent-mfa/#restrict-authenticators-by-aaguid) and [skip independent MFA when the identity provider already performed MFA](/cloudflare-one/access-controls/access-settings/independent-mfa/#use-identity-provider-mfa.
:::

### Configure independent MFA for an application

Each application has three MFA options:

| Option                                 | Behavior                                                                                                                                                                                                                                               |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Respect global enforcement setting** | Uses the [organization-level](/cloudflare-one/access-controls/access-settings/independent-mfa/) MFA configuration. If MFA is required globally, users must complete MFA. If MFA is not required globally, users are not prompted. This is the default. |
| **Custom MFA settings**                | Overrides the organization setting with application-specific allowed authenticators and session duration.                                                                                                                                              |
| **Disable MFA**                        | Users are not prompted for independent MFA when accessing this application, even if MFA is required globally.                                                                                                                                          |

To configure MFA for an application:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Find the application you want to configure and select **Configure**.
3. Scroll down to **Authentication** and select the **MFA**.tab.
4. Select one of the following options:
   - To inherit the organization setting, select **Respect global enforcement setting**.
   - To set custom requirements, select **Custom MFA settings**, then configure the [allowed MFA methods](/cloudflare-one/access-controls/access-settings/independent-mfa/#supported-mfa-methods) and [authentication duration](#mfa-session-duration).
   - To exempt the application from MFA, select **Disable MFA**.
5. Select **Save**.

To configure MFA for an infrastructure application, refer to [Infrastructure applications](#infrastructure-applications).

### Configure independent MFA for a policy

Each policy has the same three MFA options described in [Configure independent MFA for an application](#configure-independent-mfa-for-an-application). Policy-level settings override application-level settings.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.
2. Choose an **Allow** policy and select **Configure**.
3. Under **Multi-factor authentication (MFA)**, select an option:
   - To inherit the application or organization setting, select **Respect global enforcement setting**.
   - To set custom requirements for users who match this policy, select **Custom MFA settings**, then configure the [allowed MFA methods](/cloudflare-one/access-controls/access-settings/independent-mfa/#supported-mfa-methods) and [authentication duration](#mfa-session-duration).
   - To exempt users who match this policy from MFA, select **Disable MFA**.
4. Select **Save**.

To configure MFA for an infrastructure application policy, refer to [Infrastructure applications](#infrastructure-applications).

### MFA session duration

The MFA session duration determines how long a successful MFA authentication remains valid. After the MFA session expires, the user must complete MFA again on their next Cloudflare Access login in addition to completing IdP authentication. You can require users to complete MFA on each Access login or set a custom duration. MFA session durations are only checked during the login flow and do not affect a user's existing session.

Access checks MFA sessions from most specific to least specific:

1. **Policy MFA session duration** — If set, applies to users who match the policy.
2. **Application MFA session duration** — If set, applies to all users accessing the application.
3. **Global MFA session duration** — The default for all applications that do not specify their own duration.

#### Require MFA on every login

To require MFA every time a user logs in to an application, set the authentication duration to **Require every login**. This prevents Access from caching a successful MFA session.

- **Organization** — Go to **Zero Trust** > **Access controls** > **Access settings** > **Allow multi-factor authentication (MFA)**. Set **Authentication duration** to **Require every login**. This applies to all applications unless overridden at the application or policy level. For more details, refer to [independent MFA settings](/cloudflare-one/access-controls/access-settings/independent-mfa/).
- **Application** — Go to **Zero Trust** > **Access controls** > **Applications** > select your application > **Configure** > **Authentication** > **MFA** tab. Select **Custom MFA settings** and set **Authentication duration** to **Require every login**.
- **Policy** — Go to **Zero Trust** > **Access controls** > **Policies** > select your policy > **Configure**. Under **Multi-factor authentication (MFA)**, select **Custom MFA settings** and set **Authentication duration** to **Require every login**.

To configure this for an application via the API, first send a `GET` request to retrieve the full application configuration, then send a `PUT` request with the complete application body including the updated `mfa_config`. Set `session_duration` to `"0m"`:

```bash
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/access/apps/{app_id} \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "mfa_config": {
    "mfa_disabled": false,
    "session_duration": "0m"
  }
}'
```

### Precedence example

Consider the following configuration:

```mermaid
flowchart TD
    subgraph org["Organization"]
        orgSettings["**Apply global MFA settings by default**, <br/>**MFA methods**: Authenticator app + Security key, <br/>**Authentication duration**: 24 hours"]
    end

    subgraph appA["Application A"]
        appASettings["**Respect global enforcement setting**<br/>(inherits organization settings)"]
        subgraph policies["Policies"]
            policy1["Policy 1<br/>**Custom MFA settings**,<br/>**MFA methods**: Security keys only,<br/>**Authentication duration**: 1 hour"]
            policy2["Policy 2<br/>**Disable MFA**"]
        end
    end

    subgraph appB["Application B"]
        appBSettings["**Disable MFA**"]
    end

    orgSettings --> appASettings
    orgSettings -.->|"overridden"| appBSettings
    appASettings -.->|"overridden by"| policy1
    appASettings -.->|"overridden by"| policy2
```

In this example:

- Users who access Application A and match Policy 1 must use a security key and re-authenticate every hour.
- Users who access Application A and match Policy 2 are not prompted for MFA.
- Users who access Application A and match neither policy must use an authenticator application or a security key, with a 24-hour session.
- Users who access Application B are not prompted for MFA.

## Infrastructure applications

Infrastructure applications that use SSH support two infrastructure-only MFA methods. `piv_key` uses an enrolled Personal Identity Verification (PIV) key. `ssh_fido2_key` uses an enrolled FIDO2 key. Neither method applies to other Access application types or browser WebAuthn authentication.

You can configure MFA for infrastructure apps at the application level or at the policy level.

### Configure MFA for an infrastructure application

Select PIV key, FIDO2 key, or both when configuring custom MFA. The corresponding API arrays are `["piv_key"]`, `["ssh_fido2_key"]`, and `["piv_key", "ssh_fido2_key"]`.

:::note

<Render
	file="access/infra-mfa-global-settings-warning"
	product="cloudflare-one"
/>
:::

<Details header="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Find your infrastructure application and select **Configure**.
3. Go to the **Authentication** tab and select **MFA**.
4. Select one of the following options:
   - **Respect global enforcement setting** — Uses the [organization-level](/cloudflare-one/access-controls/access-settings/independent-mfa/) MFA configuration. This is the default.
   - **Custom MFA settings** — Override the global setting for this application. Select **PIV key**, **FIDO2 key**, or both as the allowed authenticators, then set a custom MFA session duration.
   - **Disable MFA** — Users are not prompted for MFA when accessing this application.
5. Select **Save**.

</Details>

<Details header="API">

To update MFA settings for an infrastructure application, first send a `GET` request to retrieve the full application configuration, then send a `PUT` request with the complete application body including the `mfa_config` object. The `PUT` request must contain all fields returned by the `GET` to avoid overwriting existing settings.

```bash
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/access/apps/{app_id} \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "mfa_config": {
    "mfa_disabled": false,
    "session_duration": "12h",
    "allowed_authenticators": ["piv_key", "ssh_fido2_key"]
  }
}'
```

| Field                     | Type    | Description                                                                                                                                           |
| ------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mfa_disabled`            | Boolean | If `true`, MFA is not required for this application, even if global settings enforce MFA.                                                             |
| `session_duration`        | String  | Duration before the user must re-authenticate with MFA (for example, `30m`, `1h`, `24h`). Set to `0m` to require MFA on every SSH connection.         |
| `allowed_authenticators`  | Array   | Use `["piv_key"]`, `["ssh_fido2_key"]`, or `["piv_key", "ssh_fido2_key"]`.                                                                            |

</Details>

### Configure MFA for an infrastructure policy

You can set different MFA requirements for different SSH usernames by configuring MFA at the policy level. Policy-level MFA settings override application-level settings.

<Details header="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Find your infrastructure application and select **Configure**.
3. Go to **Policies** and select the policy you want to configure.
4. Under **Multi-factor authentication (MFA)**, select an option:
   - **Respect global enforcement setting** — Inherits the application or organization setting.
   - **Custom MFA settings** — Select **PIV key**, **FIDO2 key**, or both as the allowed authenticators and set a custom MFA session duration for users who match this policy.
   - **Disable MFA** — Users who match this policy are not prompted for MFA.
5. Select **Save**.

</Details>

<Details header="API">

To update MFA settings for a policy, first send a `GET` request to retrieve the full policy configuration, then send a `PUT` request with the complete policy body including the `mfa_config` object. The `mfa_config` object uses the same fields as the [application-level configuration](#configure-mfa-for-an-infrastructure-application).

```bash
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/access/policies/{policy_id} \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "name": "Require MFA for root",
  "decision": "allow",
  "include": [
    {
      "email": {
        "email": "jdoe@company.com"
      }
    }
  ],
  "mfa_config": {
    "mfa_disabled": false,
    "session_duration": "1h",
    "allowed_authenticators": ["piv_key", "ssh_fido2_key"]
  },
  "connection_rules": {
    "ssh": {
      "usernames": ["root"],
      "allow_email_alias": false
    }
  }
}'
```

</Details>

### MFA session duration for SSH

The MFA session duration determines how long users can open new SSH connections without another MFA prompt. Set the duration to `0m` to require MFA for every new connection. Expiration does not terminate an active connection.

MFA sessions are bound to the user's device. If a user switches to a different device, they must re-authenticate regardless of the remaining session duration.

Session duration is evaluated in the following order:

1. **Policy-level duration** — If set, applies to users who match the policy.
2. **Application-level duration** — If no policy-level duration is set, uses the application setting.
3. **Organization-level duration** — If neither policy nor application defines a duration, uses the global setting.

When a user matches multiple policies that each define a session duration, Access uses the **shortest** duration across all matching policies.

### Precedence and conflict resolution

MFA configuration is evaluated from most specific to least specific: **policy** > **application** > **organization**.

| Organization MFA | Application MFA | Policy MFA | Result                                      |
| ---------------- | --------------- | ---------- | ------------------------------------------- |
| Required         | Required        | Required   | MFA required                                |
| Required         | Required        | Disabled   | MFA not required (policy wins)              |
| Required         | Disabled        | (not set)  | MFA not required (application wins)         |
| Required         | (not set)       | (not set)  | MFA required (organization setting applies) |

Organization-level MFA must be enabled for users to enroll PIV keys. Explicit settings at a lower level (policy or application) override higher levels. If no explicit setting exists at a level, the next higher level applies.

# Manage Access policies

import { Tabs, TabItem, APIRequest } from "~/components";

Access policies define the users who can log in to your Access applications. You can create, edit, or delete policies at any time and reuse policies across multiple applications.

## Create a policy

To create a reusable Access policy:

1. In [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.
2. Select **Add a policy**.
3. Enter a **Policy name**.
4. Choose an [**Action**](/cloudflare-one/access-controls/policies/#actions) for the policy.
5. Choose a [**Session duration**](/cloudflare-one/access-controls/access-settings/session-management/) for the policy.
6. Configure as many [**Rules**](/cloudflare-one/access-controls/policies/#rule-types) as needed.
7. (Optional) Configure additional settings for users who match this policy:
   - [Isolate application](/cloudflare-one/access-controls/policies/isolate-application/).
   - [Purpose justification](/cloudflare-one/access-controls/policies/require-purpose-justification/)
   - [Temporary authentication](/cloudflare-one/access-controls/policies/temporary-auth/)
   - [Independent MFA](/cloudflare-one/access-controls/policies/mfa-requirements/#independent-mfa)
8. Select **Save**.

You can now add this policy to an [Access application](/cloudflare-one/access-controls/applications/http-apps/).

## Edit a policy

To make changes to an existing Access policy:

1. In [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.
2. Locate the policy you want to update and select **Configure**.
3. Once you have made the necessary changes, select **Save**.

The updated policy is now in effect for all associated Access applications.

## Delete a policy

To delete a reusable Access policy:

1. In [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies** and locate the policy you want to delete.
2. If the policy is used by an application, remove the policy from all associated applications.
3. Select **Delete**.
4. A pop-up message will ask you to confirm your decision to delete the policy. Select **Delete**.

## Test your policies

You can test your Access policies against all existing user identities in your Zero Trust organization.  For the policy tester to work, users must have logged into the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) or any other Access application at some point in time.

Cloudflare will use the most recent device that was authenticated with Access to test your policies.

### Test a single policy

The Access policy builder allows you to test your rules before saving any changes.

To test an individual Access policy:

1. In [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.
2. Locate the policy you want to test and select **Configure**.
3. Go to **Policy tester** and select **Test policies**.

The policy tester reports the percentage of active users who are allowed or denied access to an application based on this policy. You can expand the test results to view a list of allowed or blocked users.

### Test all policies in an application

You can test your Access application policies against your user population before deploying changes to your users. After saving your changes, you can also perform a more detailed policy test for a specific user.

To test if users have access to an application:

1. In [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate the application you want to test and select **Configure**.
3. Go to **Policies** > **Policy tester**.
4. To test all active users in your organization, select **Test policies**.

		The policy tester reports the percentage of users who are allowed or denied access to this application based on all configured policies. You can expand the test results to view a list of allowed or blocked users.

5. To perform a detailed test on a single user:

	a. If you made any changes to your policies, first save the application.

	b. Select **testing a single user**.

	c. Enter their email address and select **Test policies**.

	The single user test results will show:
		- Whether the user is allowed or denied access to this application based on all configured policies.
		- The user's identity from their most recent Access login attempt.
		- Whether the user matches individual Allow, Block, or Bypass policies.

## Legacy policies

Legacy policies are scoped to a specific application and cannot be added to newly created Access applications.

### Migrate to reusable policies

To migrate legacy policies to reusable policies:

1. [Create a reusable policy](#create-a-policy) that will replace the legacy policy.
2. Go to the Access application associated with the legacy policy.
3. Add the reusable policy to the application and remove the legacy policy. Removing the legacy policy from the application also deletes it.
4. Repeat these steps for each legacy policy. If you have duplicate legacy policies, you can replace them with a single reusable policy.

### Convert a legacy policy

You can use the API to convert a legacy policy into a reusable policy. To convert a legacy policy, make a `PUT` request with an empty request body:

<APIRequest
  path="/accounts/{account_id}/access/apps/{app_id}/policies/{policy_id}/make_reusable"
  method="PUT"
/>

The policy is now removed from the applications endpoint (`/access/apps/$APP_ID/policies`) and managed using the [reusable policies endpoints](/api/resources/zero_trust/subresources/access/subresources/policies/)(`/access/policies/$POLICY_ID`).

# Require purpose justification

Cloudflare Access allows security and IT teams to present users with a purpose justification screen directly after they log in to an Access application. This allows organizations to audit not only for who is accessing their resources, but also for why they are requesting access.

The purpose justification screen will show for any new sessions of an application. For example, if an Access application has a session time of eight hours, a user will see the purpose justification screen once every eight hours.

Configuring a purpose justification screen is done as part of configuring an Access policy.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Choose an application and select **Configure**.
3. Go to **Policies**.
4. Choose an **Allow** policy and select **Configure**.
5. Under **Additional settings**, turn on **Purpose justification**.
6. (Optional) Set a custom purpose justification message. This will appear on the purpose justification screen and will be visible to the user.
7. Save the policy.

Users who match this policy will see the following screen:

![Finalized purpose justification screen displaying custom message.](~/assets/images/cloudflare-one/policies/purpose-justification.png)

# Temporary authentication

import { CURL } from "~/components";

With Cloudflare Access, you can require that users obtain approval before they can access a specific self-hosted application or SaaS application. The administrator will receive an email notification to approve or deny the request. Unlike a typical Allow policy, the user will have to request access at the end of each session. This allows you to define the users who should have persistent access and those who must request temporary access.

## Set up temporary authentication

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Choose a **Self-hosted** or **SaaS** application and select **Configure**.
3. Choose an **Allow** policy and select **Configure**.
4. Under **Additional settings**, turn on [**Purpose justification**](/cloudflare-one/access-controls/policies/require-purpose-justification/).
5. Turn on **Temporary authentication**.
6. Enter the **Email addresses of the approvers**.
	:::note
	Your approvers must be authenticated by Access. If they do not have an active session, Access will verify their identity against your [App Launcher Access policy](/cloudflare-one/access-controls/access-settings/app-launcher/).
	:::
7. Save the policy.

Temporary authentication is now enabled for users who match this policy. You can optionally add a second **Allow** policy for users who should have persistent access. Be sure the policy order is set to allow persistent users through.

## Temporary authentication requests

When a user accesses the application, they will be prompted to enter a purpose justification and submit an access request. The request is automatically emailed to approvers. Alternatively, the user can manually present the approval link to approvers.
![Temporary authentication request page shown to users](~/assets/images/cloudflare-one/policies/temp-auth-request.png)

Approvers will receive a request similar to the example below. The approver can then grant access for a set amount of time, up to a maximum of 24 hours.

![Temporary authentication approval page shown to administrators](~/assets/images/cloudflare-one/policies/temp-auth-approval.png)

## Review temporary authentication logs

Temporary authentication requests have separate logs from Access authentication events. The just-in-time (JIT) request logs include the requester, application, purpose justification, request status, expiration time, approval count, and approver email addresses.

To list requests across your account, send a `GET` request to the JIT request logs endpoint. You can filter the results by `status`, `search`, `since`, and `until`, and use `page` and `per_page` for pagination. Valid status values are `PENDING`, `APPROVED`, `DENIED`, and `CANCELED`.

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/logs/jit_requests?status=APPROVED&page=1&per_page=25"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

To review one request's lifecycle, use its `knock_request_id`. The response lists events in chronological order, including request creation, approver decisions, cancellation, approval completion, and successful login.

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/logs/jit_requests/{knock_request_id}"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

# Service credentials

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Mutual TLS

import { AvailableNotifications, Render, Example } from "~/components";

:::note[Availability]
Access mTLS is available with Enterprise and pay-as-you-go Zero Trust plans. It is not included in the Free plan. Free customers can use [service tokens](/cloudflare-one/access-controls/service-credentials/service-tokens/) to authenticate automated systems.

This page covers Access mTLS for Service Auth policies. [Zone-level mTLS](/api-shield/security/mtls/) is a separate feature with different plan availability.
:::

[Mutual TLS (mTLS) authentication](https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/) requires both the client and the server to present certificates during the TLS handshake. In the Cloudflare Access implementation, the CA you upload is used to verify the client certificate (server certificate verification is handled by standard TLS). Access mTLS serves two purposes:

- **Authenticate devices that do not use an identity provider** — Automated systems and IoT devices can prove their identity by presenting a client certificate instead of logging in through an IdP.
- **Add a second authentication factor** — Team members who log in through an IdP can also be required to present a valid client certificate, providing an additional layer of security.

When you upload a root certificate authority (CA) to Access, only requests from devices with a matching client certificate are allowed through. When a request reaches the application, Access asks the client to present a certificate. If the client cannot present a valid certificate, the request is blocked. If the client presents a valid certificate, Access completes a key exchange to verify.

![mTLS handshake diagram](~/assets/images/cloudflare-one/identity/devices/mtls.png)

:::caution[Important]

The mTLS certificate is used only to verify the client certificate. It does not control the SSL certificate presented during the [server hello](https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/).

:::

## Enforce mTLS authentication

### Prerequisites

- An [Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for the hostname that you would like to secure with mTLS.
- A CA that issues client certificates for your devices.
  <Render file="byo-ca-mtls-cert-requirements" product="ssl" />

### Add mTLS to your Access application

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Service credentials** > **Mutual TLS**.

2. Select **Add mTLS Certificate**.

3. Enter any name for the root CA.

4. In **Certificate content**, paste the contents of your root CA.

	If the client certificate is directly signed by the root CA, you only need to upload the root. If the client certificate is signed by an intermediate certificate, you must upload the entire CA chain (intermediate and root). For example:
	```txt
	-----BEGIN CERTIFICATE-----
	<intermediate.pem>
	-----END CERTIFICATE-----
	-----BEGIN CERTIFICATE-----
	<rootCA.pem>
	-----END CERTIFICATE-----
	```

	Do not include any SSL/TLS server certificates; Access only uses the CA chain to verify the connection between the user's device and Cloudflare.

5. In **Associated hostnames**, enter the fully-qualified domain names (FQDN) that will use this certificate.

   These FQDNs will be the hostnames used for the resources being protected in the [Access policy](/cloudflare-one/access-controls/policies/). You must associate the Root CA with the FQDN that the application being protected uses.

6. Save the policy.

7. Go to **Access controls** > **Policies**.

8. [Create an Access policy](/cloudflare-one/access-controls/policies/policy-management/#create-a-policy) using one of the following [selectors](/cloudflare-one/access-controls/policies/#selectors):
   - **Valid Certificate**: Any client certificate that can authenticate with the Root CA will be allowed to proceed.
   - **Common Name**: Only client certificates with a specific common name will be allowed to proceed.

9. If this is for a client who does not need to log in through an IdP, set the policy **Action** to _Service Auth_.

   <Example>

   **Example mTLS policy**

   <Render file="access/example-service-auth-mtls" product="cloudflare-one" />

   </Example>

10. Save the policy, then go to **Access controls** > **Applications**.

11. Select the application you would like to enforce mTLS on and select **Configure**. The application must be included in the **Associated hostnames** list from Step 5.

12. In the **Policies** tab, add your mTLS policy.

13. Save the application.

You can now authenticate to the application using a client certificate. For instructions on how to present a client certificate, refer to [Test mTLS](#test-mtls).

## Test mTLS

### Test using cURL

To test the application protected by an mTLS policy:

1. First, attempt to curl the site without a client certificate.
   This curl command example is for the site `example.com` that has an [Access application and policy](#add-mtls-to-your-access-application) set for `https://auth.example.com`:

   ```sh
   curl -sv https://auth.example.com
   ```

   Without a client certificate in the request, a `403 forbidden` response displays and the site cannot be accessed.

2. Now, add your client certificate and key to the request:

   ```sh
   curl -sv https://auth.example.com --cert example.pem --key key.pem
   ```

When the authentication process completes successfully, a `CF_Authorization Set-Cookie` header returns in the response.

:::caution

Cloudflare Gateway cannot inspect traffic to mTLS-protected domains. If a device has the Cloudflare One Client turned on and passes HTTP requests through Gateway, access will be blocked unless you [bypass HTTP inspection](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) for the domain.
:::

### Test in a browser

To access an mTLS-protected application in a browser, the client certificate must be imported into your browser's certificate manager. Instructions vary depending on the browser. Your browser may use the operating system's root store or its own internal trust store.

The following example demonstrates how to add a client certificate to the macOS system keychain:

:::caution[Important]

The command adds the client certificate to the trusted store on your device. Only proceed if you are comfortable doing so and intend to keep these testing certificates safeguarded.
:::

1. Navigate to the directory containing the client certificate and key.
   <Render
   	file="keychain-access"
   	product="cloudflare-one"
   	params={{ cert: "client.pem" }}
   />

Assuming your browser uses the macOS system store, you can now connect to the mTLS application through the browser.

## Generate mTLS certificates

You can use open source private key infrastructure (PKI) tools to generate certificates to test the mTLS feature in Cloudflare Access.

### OpenSSL

This section covers how to use [OpenSSL](https://www.openssl.org/) to generate a root and intermediate certificate, and then issue client certificates that can authenticate against the CA chain.

#### Generate the root CA

1. Generate the root CA private key:

   ```sh
    openssl genrsa -aes256 -out rootCA.key 4096
   ```

   When prompted, enter a password to use with `rootCA.key`.

2. Create a self-signed root certificate called `rootCA.pem`:

   ```sh
   openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.pem
   ```

   You will be prompted to enter your private key password and fill in some optional fields. For testing purposes, you can leave the optional fields blank.

#### Generate an intermediate certificate

1. Generate the intermediate CA private key:

   ```sh
    openssl genrsa -aes256 -out intermediate.key 4096
   ```

   When prompted, enter a password to use with `intermediate.key`.

2. Create a certificate signing request (CSR) for the intermediate certificate:

   ```sh
   openssl req -new -sha256 -key intermediate.key -out intermediate.csr
   ```

   You will be prompted to enter your private key password and fill in some optional fields. For testing purposes, you can leave the optional fields blank.

3. Create a CA Extension file called `v3_intermediate_ca.ext`. For example,

   ```txt
   subjectKeyIdentifier = hash
   authorityKeyIdentifier = keyid:always,issuer
   basicConstraints = critical, CA:true
   keyUsage = critical, cRLSign, keyCertSign
   ```

   Make sure that `basicConstraints` includes the `CA:true` property. This property allows the intermediate certificate to act as a CA and sign client certificates.

4. Sign the intermediate certificate with the root CA:

   ```sh
    openssl x509 -req -in intermediate.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out intermediate.pem -days 1825 -sha256 -extfile v3_intermediate_ca.ext
   ```

#### Create a CA chain file

1. Combine the intermediate and root certificates into a single file:

   ```sh
   cat intermediate.pem rootCA.pem > ca-chain.pem
   ```

   The intermediate certificate should be at the top of the file, followed by its signing certificate.

2. Upload the contents of `ca-chain.pem` to Cloudflare Access. For instructions, refer to [Add mTLS to your Access application](#add-mtls-to-your-access-application).

#### Generate a client certificate

1. Generate a private key for the client:

   ```sh
    openssl genrsa -out client.key 2048
   ```

2. Create a CSR for the client certificate:

   ```sh
   openssl req -new -key client.key -out client.csr
   ```

   You will be prompted to fill in some optional fields. For testing purposes, you can set **Common Name** to something like `John Doe`.

3. Sign the client certificate with the intermediate certificate:

   ```sh
    openssl x509 -req -in client.csr -CA intermediate.pem -CAkey intermediate.key -CAcreateserial -out client.pem -days 365 -sha256
   ```

4. Validate the client certificate against the certificate chain:

   ```sh
   openssl verify -CAfile ca-chain.pem client.pem
   ```

   ```sh output
   client.pem: OK
   ```

You can now use the client certificate (`client.pem`) and its key (`client.key`) to [test mTLS](#test-mtls).

### Cloudflare PKI

This guide uses [Cloudflare's PKI toolkit](https://github.com/cloudflare/cfssl) to generate a root CA and client certificates from JSON files.

#### 1. Install dependencies

The process requires two packages from Cloudflare's PKI toolkit:

- `cf-ssl`
- `cfssljson`

You can install these packages from the [Cloudflare SSL GitHub repository](https://github.com/cloudflare/cfssl). You will need a working installation of Go, version 1.12 or later. Alternatively, you can [download the packages](https://github.com/cloudflare/cfssl) directly.
Use the instructions under Installation to install the toolkit, and ensure that you install all of the utility programs in the toolkit.

#### 2. Generate the root CA

1. Create a new directory to store the root CA.

2. Within that directory, create two new files:
   - **CSR**. Create a file named `ca-csr.json` and add the following JSON blob, then save the file.

     ```json
     {
     	"CN": "Access Testing CA",
     	"key": {
     		"algo": "rsa",
     		"size": 4096
     	},
     	"names": [
     		{
     			"C": "US",
     			"L": "Austin",
     			"O": "Access Testing",
     			"OU": "TX",
     			"ST": "Texas"
     		}
     	]
     }
     ```

   - **config**. Create a file named `ca-config.json` and add the following JSON blob, then save the file.

     ```json
     {
     	"signing": {
     		"default": {
     			"expiry": "8760h"
     		},
     		"profiles": {
     			"server": {
     				"usages": ["signing", "key encipherment", "server auth"],
     				"expiry": "8760h"
     			},
     			"client": {
     				"usages": ["signing", "key encipherment", "client auth"],
     				"expiry": "8760h"
     			}
     		}
     	}
     }
     ```

3. Now, run the following command to generate the root CA with those files.

   ```sh
   cfssl gencert -initca ca-csr.json | cfssljson -bare ca
   ```

4. The command will output a root certificate (`ca.pem`) and its key (`ca-key.pem`).

   ```sh
   ls
   ```

   ```sh output
   ca-config.json ca-csr.json ca-key.pem ca.csr  ca.pem
   ```

5. Upload the contents of `ca.pem` to Cloudflare Access. For instructions, refer to [Add mTLS to your Access application](#add-mtls-to-your-access-application).

#### 3. Generate a client certificate

To generate a client certificate that will authenticate against the uploaded root CA:

1. Create a file named `client-csr.json` and add the following JSON blob:

   ```json
   {
   	"CN": "James Royal",
   	"hosts": [""],
   	"key": {
   		"algo": "rsa",
   		"size": 4096
   	},
   	"names": [
   		{
   			"C": "US",
   			"L": "Austin",
   			"O": "Access",
   			"OU": "Access Admins",
   			"ST": "Texas"
   		}
   	]
   }
   ```

2. Now, use the following command to generate a client certificate with the Cloudflare PKI toolkit:

   ```sh
   cfssl gencert -ca=ca.pem -ca-key=ca-key.pem  -config=ca-config.json -profile=client client-csr.json | cfssljson -bare client
   ```

The command will output a client certificate file (`client.pem`) and its key (`client-key.pem`). You can now use these files to [test mTLS](#test-mtls).

#### Create a certificate revocation list

You can use the Cloudflare PKI toolkit to generate a certificate revocation list (CRL), as well. This list will contain client certificates that are revoked.

1. Get the serial number from the client certificate generated earlier. Add that serial number, or any others you intend to revoke, in hex format in a text file. This example uses a file named `serials.txt`.

2. Create the CRL with the following command.

   ```sh
   cfssl gencrl serials.txt ../mtls-test/ca.pem ../mtls-test/ca-key.pem | base64 -D > ca.crl
   ```

You will need to add the CRL to your server or enforce the revocation in a Cloudflare Worker. An example Worker Script can be found on the [Cloudflare GitHub repository](https://github.com/cloudflare/access-crl-worker-template).

<Render file="add-client-certificate-rfc9440" product="ssl" />

<Render file="forward-client-certificate" product="ssl" />

## Known limitations

mTLS does not currently work for:

- Cloudflare Pages site served on a [custom domain](/pages/configuration/custom-domains/)
- Cloudflare R2 public bucket served on a [custom domain](/r2/buckets/public-buckets/#connect-a-bucket-to-a-custom-domain)

## Notifications for mutual TLS certificates

Cloudflare will send the following [notifications](/notifications/) before your mutual TLS certificates expire:

<AvailableNotifications
	product="SSL/TLS"
	notificationFilter="Access mTLS Certificate Expiration Alert"
/>

# Service tokens

import {
	AvailableNotifications,
	Render,
	APIRequest,
	CURL,
	DashButton,
	Steps,
	Tabs,
	TabItem,
} from "~/components";

You can provide automated systems with service tokens to authenticate against your Cloudflare One policies. Cloudflare Access will generate service tokens that consist of a Client ID and a Client Secret. Automated systems or applications can then use these values to reach an application protected by Access.

This section covers how to create, rotate, renew, disable, and revoke a service token. You can also configure Access to manage inactive service tokens automatically.

## Create a service token

<Render file="access/create-service-token" product="cloudflare-one" />

You can now configure your Access applications and [device enrollment permissions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-service-token) to accept this service token. Make sure to set the policy action to [**Service Auth**](/cloudflare-one/access-controls/policies/#service-auth); otherwise, Access will prompt for an identity provider login.

### Client Secret format

As of August 26, 2026, new service token Client Secrets use the format `cfast_[40 alphanumeric characters][8-character checksum]`. The prefix and checksum make the secrets easier for credential scanning tools to identify.

Existing Client Secrets use a 64-character hexadecimal format. These secrets continue to work and do not require rotation. Both formats use the same Client ID and authentication headers.

## Connect your service to Access

### Request

To authenticate to an Access application using your service token, add the following to the headers of any HTTP request:

`CF-Access-Client-Id: <CLIENT_ID>`

`CF-Access-Client-Secret: <CLIENT_SECRET>`

For example,

```sh
curl -H "CF-Access-Client-Id: <CLIENT_ID>" -H "CF-Access-Client-Secret: <CLIENT_SECRET>" https://app.example.com
```

#### Authenticate with a single header

You can configure a self-hosted Access application to accept a service token in a single HTTP header, as an alternative to the `CF-Access-Client-Id` and `CF-Access-Client-Secret` pair of headers. This is useful for authenticating SaaS services that only support sending one custom header in a request (for example, the `Authorization` header).

To authenticate using a single header:

1. Get your existing Access application configuration:

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="GET"
   />

2. Make a `PUT` request with the name of the header you want to use for service token authentication. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request.

   <APIRequest
   	path="/accounts/{account_id}/access/apps/{app_id}"
   	method="PUT"
   	json={{
   		domain: "app.example.com",
   		type: "self_hosted",
   		read_service_tokens_from_header: "Authorization",
   	}}
   />

3. Add the header to any HTTP request. For example,

   ```sh
   curl -H "Authorization: {\"cf-access-client-id\": \"<CLIENT_ID>\", \"cf-access-client-secret\": \"<CLIENT_SECRET>\"}" https://app.example.com
   ```

## Strict service token authentication

Strict service token authentication is a Zero Trust organization setting that applies consistent behavior to requests made with service tokens. When the setting is on, Access handles requests with service token headers as follows:

- If authentication or authorization fails, Access always returns `401` or `403` instead of redirecting the client to the login page with `302`.
- Only Service Auth policies can authorize the request. Access ignores Allow policies and any `CF_Authorization` cookie sent with the request.
- Access does not return a `CF_Authorization` cookie to the client after successful authentication. Subsequent requests should continue to use service token headers.
- Failed requests for recognized service tokens appear in [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/#non-identity-authentication). Access logs expired or disabled tokens, incorrect Client Secrets, and tokens not authorized for the application. It does not log malformed headers or unknown Client IDs.

Zero Trust organizations created on or after `2026-10-05` have strict service token authentication turned on by default and cannot turn it off. Cloudflare recommends that existing organizations turn it on as well.

:::note
Organizations created before `2026-10-05` can turn this setting on or off.
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.

   <DashButton url="/?to=/:account/access-controls/settings" zeroTrust />

2. Under **Manage service tokens**, turn on **Strict service token authentication**.
3. In the confirmation dialog, select **Enable**.
</Steps>

To turn off strict service token authentication, turn off the setting and select **Disable**.

</TabItem> <TabItem label="API">

Send a `PATCH` request to the [Update your Zero Trust organization](/api/resources/zero_trust/subresources/organizations/methods/update/) endpoint:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/organizations"
	method="PATCH"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		strict_service_token_auth: true,
	}}
/>

To turn off strict service token authentication, set `strict_service_token_auth` to `false`.

</TabItem> </Tabs>

## Rotate service token secrets

Rotate a service token secret when you suspect exposure or as part of regular credential rotation. The Client ID remains the same, but Access generates a new Client Secret.

You can set a grace period during which both secrets work. Use this period to update your services before Access revokes the previous secret.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Service credentials** > **Service Tokens**.

   <DashButton url="/?to=/:account/access/service-auth/service-tokens" zeroTrust />

2. Locate the token and select the three dots > **Rotate secret**.
3. In **Keep the current secret valid for**, choose when Access should revoke the current secret. Available grace periods range from one hour to 30 days. To revoke it when you rotate, select _Revoke immediately_.
4. Select **Rotate**.
5. Copy the new Client Secret and update your services before the grace period ends.
</Steps>

</TabItem> <TabItem label="API">

Make a `POST` request to the [Rotate a service token](/api/resources/zero_trust/subresources/access/subresources/service_tokens/methods/rotate/) endpoint. Set `previous_client_secret_expires_at` to an RFC 3339 timestamp when the previous secret should expire:

<APIRequest
	path="/accounts/{account_id}/access/service_tokens/{service_token_id}/rotate"
	method="POST"
	json={{
		previous_client_secret_expires_at: "2030-01-01T00:00:00Z",
	}}
/>

To revoke the previous secret immediately, omit `previous_client_secret_expires_at` from the request.

</TabItem> </Tabs>

## Renew service tokens

Service tokens expire according to the token duration you selected when you created the token.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Service credentials** > **Service Tokens**.
2. Locate the token you want to renew.
3. To extend the token's lifetime by one year, select **Refresh**.
4. To extend the token's lifetime by more than a year:
   1. Select **Edit**.
   2. Choose a new **Service Token Duration**.
   3. Select **Save**. The expiration date will be extended by the selected amount of time.

</TabItem> <TabItem label="API">

To extend the token's lifetime by one year, make a `POST` request to the [Refresh a service token](/api/resources/zero_trust/subresources/access/subresources/service_tokens/methods/refresh/) endpoint:

<APIRequest
	path="/accounts/{account_id}/access/service_tokens/{service_token_id}/refresh"
	method="POST"
/>

To extend the token's lifetime by a custom duration, make a `PUT` request to the [Update a service token](/api/resources/zero_trust/subresources/access/subresources/service_tokens/methods/update/) endpoint with the new `duration`:

<APIRequest
	path="/accounts/{account_id}/access/service_tokens/{service_token_id}"
	method="PUT"
	json={{
		duration: "17520h",
	}}
/>

</TabItem> <TabItem label="Terraform (v5)">

To renew the service token, update the `duration` attribute on the [`cloudflare_zero_trust_access_service_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_service_token) resource and apply the change. Cloudflare resets the expiration relative to the time of the update.

```tf
resource "cloudflare_zero_trust_access_service_token" "example_service_token" {
	account_id = var.cloudflare_account_id
	name       = "Example service token"
	duration   = "17520h"

	lifecycle {
		create_before_destroy = true
	}
}
```

</TabItem> </Tabs>

## Manage inactive service tokens

You can configure Access to automatically disable or delete service tokens that are no longer in use. The setting applies to all service tokens in your Zero Trust account.

Access considers a service token inactive when all of the following are true:

- The token has not successfully authenticated with an Access application during the configured inactivity period.
- The token is older than the configured inactivity period.
- The token is not directly referenced by an Access policy rule.

You can set the inactivity period to a whole number from 30 to 365 days. Disabled tokens remain in your account and can be turned on again. Deleted tokens cannot be recovered.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.

   <DashButton url="/?to=/:account/access-controls/settings" zeroTrust />

2. Under **Manage service tokens**, turn on **Automatically clean up inactive service tokens**.
3. Enter an **Inactivity period** from 30 to 365 days.
4. Choose whether Access should disable or delete inactive tokens.
5. Select **Save**.
</Steps>

</TabItem> <TabItem label="API">

Send a `PATCH` request to update your Zero Trust organization. Set `action` to `disable` or `delete`:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/organizations"
	method="PATCH"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		service_token_inactivity: {
			enabled: true,
			inactivity_threshold_days: 90,
			action: "disable",
		},
	}}
/>

To stop automatic cleanup, set `enabled` to `false`.

</TabItem> </Tabs>

Cleanup runs gradually in the background. An eligible token may not be disabled or deleted immediately.

## Turn a service token on or off

Turn off a service token to temporarily prevent it from authenticating. Access preserves the token so you can turn it on again later.

Turning off a token also stops its previous secret from working during an active rotation grace period.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Service credentials** > **Service Tokens**.

   <DashButton url="/?to=/:account/access/service-auth/service-tokens" zeroTrust />

2. Locate the token and select the three dots.
3. To stop the token from authenticating, select **Disable token** > **Disable**.
4. To restore authentication, select **Enable token** > **Enable**.
</Steps>

</TabItem> <TabItem label="API">

Make a `PUT` request to the [Update a service token](/api/resources/zero_trust/subresources/access/subresources/service_tokens/methods/update/) endpoint. Set `enabled` to `false` to turn off the token:

<APIRequest
	path="/accounts/{account_id}/access/service_tokens/{service_token_id}"
	method="PUT"
	json={{
		name: "<TOKEN_NAME>",
		enabled: false,
	}}
/>

To turn the token on again, set `enabled` to `true`.

</TabItem> </Tabs>

## Revoke service tokens

If you need to revoke access before the token expires, delete the token. Services that rely on a deleted service token can no longer reach your application.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Service credentials** > **Service Tokens**.
2. **Delete** the token you need to revoke.

</TabItem> <TabItem label="API">

Make a `DELETE` request to the [Delete a service token](/api/resources/zero_trust/subresources/access/subresources/service_tokens/methods/delete/) endpoint:

<APIRequest
	path="/accounts/{account_id}/access/service_tokens/{service_token_id}"
	method="DELETE"
/>

</TabItem> <TabItem label="Terraform (v5)">

To revoke the service token, remove the [`cloudflare_zero_trust_access_service_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_service_token) resource from your configuration and run `terraform apply`, or target the resource for destruction:

```sh
terraform destroy -target=cloudflare_zero_trust_access_service_token.example_service_token
```

</TabItem> </Tabs>

:::note

When editing an Access application, selecting **Revoke existing tokens** revokes existing sessions but does not prevent the user from starting a new session. As long as the Client ID and Client Secret are still valid, they can be exchanged for a new token on the next request. To revoke access, you must delete the service token.
:::

## Set a token expiration alert

An alert can be configured to notify a week before a service token expires to allow an administrator to invoke a token refresh.

<AvailableNotifications product="Cloudflare Access" />

To configure a service token expiration alert:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com), go to the **Notifications** page.
   <DashButton url="/?to=/:account/notifications" />
2. Select **Add**.
3. Select _Expiring Access Service Token_.
4. Enter a name for your alert and an optional description.
5. (Optional) Add other recipients for the notification email.
6. Select **Save**.

Your alert has been set and is now visible on the **Notifications** page.

# Access

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/access/. */}

<ProductChangelog product="access" hideEntry="2024-06-16-cloudflare-one" />

## 2025-02-12

**Access policies support filtering**

You can now filter Access policies by their action, selectors, rule groups, and assigned applications.

## 2025-02-11

**Private self-hosted applications and reusable policies GA**

[Private self-hosted applications](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) and [reusable Access policies](/cloudflare-one/access-controls/policies/policy-management/) are now generally available (GA) for all customers.

## 2025-01-21

**Access Applications support private hostnames/IPs and reusable Access policies.**

Cloudflare Access self-hosted applications can now be defined by [private IPs](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/), [private hostnames](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) (on port 443) and [public hostnames](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/). Additionally, we made Access policies into their own object which can be reused across multiple applications. These updates involved significant updates to the overall Access dashboard experience. The updates will be slowly rolled out to different customer cohorts. If you are an Enterprise customer and would like early access, reach out to your account team.

## 2025-01-15

**Logpush for SSH command logs**

Enterprise customers can now use Logpush to export SSH command logs for Access for Infrastructure targets.

## 2024-12-04

**SCIM GA for Okta and Microsoft Entra ID**

Cloudflare's SCIM integrations with [Okta](/cloudflare-one/integrations/identity-providers/okta/#synchronize-users-and-groups) and [Microsoft Entra ID](/cloudflare-one/integrations/identity-providers/entra-id/#synchronize-users-and-groups) (formerly AzureAD) are now out of beta and generally available (GA) for all customers. These integrations can be used for Access and Gateway policies and Zero Trust user management. Note: This GA release does not include [Dashboard SSO SCIM](/fundamentals/account/account-security/scim-setup/) support.

## 2024-10-23

**SSH with Access for Infrastructure**

Admins can now use [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) to manage privileged access to SSH servers. Access for Infrastructure provides improved control and visibility over who accessed what service and what they did during their SSH session. Access for Infrastructure also eliminates the risk and overhead associated with managing SSH keys by using short-lived SSH certificates to access SSH servers.

## 2024-08-26

**Reduce automatic seat deprovisioning minimum to 1 month, down from 2 months.**

Admins can now configure Zero Trust seats to [automatically expire](/cloudflare-one/team-and-resources/users/seat-management/#enable-seat-expiration) after 1 month of user inactivity. The previous minimum was 2 months.

## 2024-06-06

**Scalability improvements to the App Launcher**

Applications now load more quickly for customers with a large number of applications or complex policies.

## 2024-04-28

**Add option to bypass CORS to origin server**

Access admins can [defer all CORS enforcement to their origin server](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/cors/#bypass-options-requests-to-origin) for specific Access applications.

## 2024-04-15

**Zero Trust User identity audit logs**

All user identity changes via SCIM or Authentication events are logged against a user's registry identity.

## 2024-02-22

**Access for SaaS OIDC Support**

Access for SaaS applications can be setup with OIDC as an authentication method. OIDC and SAML 2.0 are now both fully supported.

## 2024-02-22

**WARP as an identity source for Access**

Allow users to log in to Access applications with their WARP session identity. Users need to reauthenticate based on default session durations. WARP authentication identity must be turned on in your device enrollment permissions and can be enabled on a per application basis.

## 2023-12-20

**Unique Entity IDs in Access for SaaS**

All new Access for SaaS applications have unique Entity IDs. This allows for multiple integrations with the same SaaS provider if required. The unique Entity ID has the application audience tag appended. Existing apps are unchanged.

## 2023-12-15

**Default relay state support in Access for SaaS**

Allows Access admins to set a default relay state on Access for SaaS apps.

## 2023-09-15

**App launcher supports tags and filters**

Access admins can now tag applications and allow users to filter by those tags in the App Launcher.

## 2023-09-15

**App launcher customization**

Allow Access admins to configure the App Launcher page within Zero Trust.

## 2023-09-15

**View active Access user identities in the dashboard and API**

Access admins can now view the full contents of a user's identity and device information for all active application sessions.

## 2023-09-08

**Custom OIDC claims for named IdPs**

Access admins can now add custom claims to the existing named IdP providers. Previously this was locked to the generic OIDC provider.

## 2023-08-02

**Azure AD authentication contexts**

Support Azure AD authentication contexts directly in Access policies.

## 2023-06-23

**Custom block pages for Access applications**

Allow Access admins to customize the block pages presented by Access to end users.

# Browser Isolation

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/browser-isolation/. */}

<ProductChangelog
	product="browser-isolation"
	hideEntry="2024-06-16-cloudflare-one"
/>

## 2024-03-21

**Removed third-party cookie dependencies**

Removed dependency on third-party cookies in the isolated browser, fixing an issue that previously caused intermittent disruptions for users maintaining multi-site, cross-tab sessions in the isolated browser.

# CASB

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/casb/. */}

<ProductChangelog product="casb" hideEntry="2024-06-16-cloudflare-one" />

## 2024-06-03

**Atlassian Bitbucket integration**

You can now scan your Bitbucket Cloud workspaces for a variety of contextualized security issues such as source code exposure, admin misconfigurations, and more.

## 2024-05-23

**Data-at-rest DLP for Box and Dropbox**

You can now scan your [Box](/cloudflare-one/integrations/cloud-and-saas/box/#data-loss-prevention-optional) and [Dropbox](/cloudflare-one/integrations/cloud-and-saas/dropbox/#data-loss-prevention-optional) files for DLP matches.

## 2024-04-16

**Export CASB findings to CSV**

You can now export all top-level CASB findings or every instance of your findings to CSV.

# Cloudflare Network Firewall

import { ProductChangelog } from "~/components";

{/* <!-- Actual content lives in /src/content/changelog/cloudflare-network-firewall/. Update the file there for new entries to appear here. For more details, refer to https://developers.cloudflare.com/style-guide/documentation-content-strategy/content-types/changelog/#yaml-file --> */}

<ProductChangelog
	product="cloudflare-network-firewall"
	hideEntry="2024-06-16-cloudflare-one"
/>

## 2024-09-12

**New UI improvements**

The dashboard now displays the order number of custom rules, and improved drag and drop functionality. You can also preview rules on a side panel without leaving the current page.

## 2024-08-16

**Cloudflare Network Firewall Analytics Rule Log Enhancement**

Customers who create a rule in a disabled mode will see the rule as **Log (rule disabled)**.

# Cloudflare One Client

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/warp-releases/. For more details, refer to https://developers.cloudflare.com/style-guide/documentation-content-strategy/content-types/changelog/#yaml-file --> */}

Review recent changes to the Cloudflare One Client (formerly WARP).

<ProductChangelog product="cloudflare-one-client" />

# Digital Experience Monitoring

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/dex/. */}

<ProductChangelog product="dex" hideEntry="2024-06-16-cloudflare-one" />

## 2025-01-24

**IP visibility**

[IP visibility](/cloudflare-one/insights/dex/ip-visibility/) enables admins to inspect the different IP addresses associated with an end-user device. IP types available for review on the Cloudflare dashboard include: the device's private IP, the public IP assigned to the device by the ISP, and the router's (that the device is connected to) private IP.

## 2024-12-19

**Remote captures**

Admins can now collect packet captures (PCAPs) and WARP diagnostic logs from end-user devices. For more information, refer to [Remote captures](/cloudflare-one/insights/dex/diagnostics/client-packet-capture/).

## 2024-05-20

**Last seen ISP**

Admins can view the last ISP seen for a device by going to **My Team** > **Devices**. Requires setting up a [traceroute test](/cloudflare-one/insights/dex/tests/traceroute/).

## 2024-05-13

**DEX alerts**

Admins can now set [DEX alerts](/cloudflare-one/insights/dex/notifications/) using [Cloudflare Notifications](/notifications/). Three new DEX alert types:

- Device connectivity anomaly
- Test latency
- Test low availability

# Data Loss Prevention

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/dlp/. */}

<ProductChangelog product="dlp" hideEntry="2024-06-16-cloudflare-one" />

## 2025-01-15

**Payload log match visibility**

When viewing decrypted payload log matches, DLP now provides more context by listing multiple DLP matches and the matching DLP profile.

## 2024-11-25

**Profile confidence levels**

DLP profiles now support setting a [confidence level](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#confidence-levels) to choose how tolerant its detections are to false positives based on the context of the detection. The higher a profile's confidence level is, the less false positives will be allowed. Confidence levels include Low, Medium, or High. DLP profile confidence levels supersede [context analysis](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#context-analysis).

## 2024-11-01

**Send entire HTTP requests to a Logpush destination**

In addition to [logging the payload](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#log-the-payload-of-matched-rules) from HTTP requests that matched a DLP policy in Cloudflare Logs, Enterprise users can now configure a [Logpush job](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#send-dlp-forensic-copies-to-logpush-destination) to send the entire HTTP request that triggered a DLP match to a storage destination. This allows long-term storage of full requests for use in forensic investigation.

## 2024-09-03

**Exact Data Match multi-entry upload support**

You can now upload files with [multiple columns of data](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/#upload-a-new-exact-data-match-dataset) as Exact Data Match datasets. DLP can use each column as a separate existing detection entry.

## 2024-05-23

**Data-at-rest DLP for Box and Dropbox**

You can now scan your [Box](/cloudflare-one/integrations/cloud-and-saas/box/#data-loss-prevention-optional) and [Dropbox](/cloudflare-one/integrations/cloud-and-saas/dropbox/#data-loss-prevention-optional) files for DLP matches.

## 2024-04-16

**Optical character recognition**

DLP can now [detect sensitive data](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#optical-character-recognition-ocr) in jpeg, jpg, and png files. This helps companies prevent the leak of sensitive data in images, such as screenshots.

# Email security

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/email-security/. */}

<ProductChangelog
	product="email-security-cf1"
	hideEntry="2024-06-16-cloudflare-one"
/>

## 2024-12-19

**Email security expanded folder scanning**

Microsoft 365 customers can now choose to scan all folders or just the inbox when deploying via the Graph API.

## 2024-08-06

**Email security is live**

Email security is now live under Zero Trust.

## 2024-08-06

**Microsoft Graph API deployment.**

Customers using Microsoft Office 365 can set up Email security via Microsoft Graph API.

# Gateway

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/gateway/. */}

<ProductChangelog product="gateway" hideEntry="2024-06-16-cloudflare-one" />

## 2025-02-12

**Upload/Download File Size selectors for HTTP policies**

Gateway and DLP users can now create HTTP policies with the [Download and Upload File Size (MiB)](/cloudflare-one/traffic-controls/http-policies/#download-and-upload-file-size) traffic selectors. This update allows users to block uploads or downloads based on file size.

## 2025-02-02

**The default global Cloudflare root certificate expired on 2025-02-02 at 16:05 UTC**

If you installed the default Cloudflare certificate before 2024-10-17, you must generate a new certificate and activate it for your Zero Trust organization to avoid inspection errors. Refer to [Troubleshooting](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#browser-and-certificate-issues) for instructions and troubleshooting steps.

## 2025-01-08

**Bring your own resolver IP (BYOIP) for DNS locations**

Enterprise users can now [provide an IP address](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#bring-your-own-dns-resolver-ip) for a private DNS resolver to use with [DNS locations](/cloudflare-one/traffic-controls/resolver-endpoints/). Gateway supports bringing your own IPv4 and IPv6 addresses.

## 2024-11-20

**Category filtering in the network policy builder**

Gateway users can now create network policies with the [Content Categories](/cloudflare-one/traffic-controls/network-policies/#content-categories) and [Security Risks](/cloudflare-one/traffic-controls/network-policies/#security-risks) traffic selectors. This update simplifies malicious traffic blocking and streamlines network monitoring for improved security management.

## 2024-10-17

**Per-account Cloudflare root certificate**

Gateway users can now generate [unique root CAs](/cloudflare-one/team-and-resources/devices/user-side-certificates/) for their Zero Trust account. Both generated certificate and custom certificate users must [activate a root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/#activate-a-root-certificate) to use it for inspection. Per-account certificates replace the default Cloudflare certificate, which is set to expire on 2025-02-02.

## 2024-10-10

**Time-based policy duration**

Gateway now offers [time-based DNS policy duration](/cloudflare-one/traffic-controls/dns-policies/timed-policies/#time-based-policy-duration). With policy duration, you can configure a duration of time for a policy to turn on or set an exact date and time to turn a policy off.

## 2024-10-04

**Expanded Gateway log fields**

Gateway now offers new fields in [activity logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) for DNS, network, and HTTP policies to provide greater insight into your users' traffic routed through Gateway.

## 2024-09-30

**File sandboxing**

Gateway users on Enterprise plans can create HTTP policies with [file sandboxing](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/) to quarantine previously unseen files downloaded by your users and scan them for malware.

## 2024-07-30

**UK NCSC indicator feed publicly available in Gateway**

Gateway users on any plan can now use the [PDNS threat intelligence feed](/security-center/indicator-feeds/#publicly-available-feeds) provided by the UK National Cyber Security Centre (NCSC) in DNS policies.

## 2024-07-14

**Gateway DNS filter non-authenticated queries**

Gateway users can now select which endpoints to use for a given DNS location. Available endpoints include IPv4, IPv6, DNS over HTTPS (DoH), and DNS over TLS (DoT). Users can protect each configured endpoint by specifying allowed source networks. Additionally, for the DoH endpoint, users can filter traffic based on source networks and/or authenticate user identity tokens.

## 2024-06-25

**Gateway DNS policy setting to ignore CNAME category matches**

Gateway now offers the ability to selectively ignore CNAME domain categories in DNS policies via the [**Ignore CNAME domain categories** setting](/cloudflare-one/traffic-controls/domain-categories/#ignore-cname-domain-categories) in the policy builder and the [`ignore_cname_category_matches` setting](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/create/) in the API.

## 2024-04-05

**Gateway file type control improvements**

Gateway now offers a more extensive, categorized [list of files](/cloudflare-one/traffic-controls/http-policies/#download-and-upload-file-types) to control uploads and downloads.

# Changelog

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in associated src/content/changelogs/. */}

<ProductChangelog area="Cloudflare One" />

# Risk score

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelogs/risk-score/. */}

<ProductChangelog product="risk-score" hideEntry="2024-06-16-cloudflare-one" />

## 2024-06-14

**SentinelOne signal ingestion**

You can now configure a [predefined risk behavior](/cloudflare-one/team-and-resources/users/risk-score/#predefined-risk-behaviors) to evaluate user risk score using device posture attributes from the [SentinelOne integration](/cloudflare-one/integrations/service-providers/sentinelone/).

# Cloudflare Tunnel

import { ProductChangelog, Render } from "~/components";

{/* <!-- All changelog entries live in src/content/changelog/tunnel/. */}

<ProductChangelog
	product="tunnel"
	hideEntry="2024-06-16-cloudflare-one"
/>

## 2024-10-17

**Simplified WARP Connector deployment**

You can now deploy WARP Connector using a simplified, guided workflow similar to `cloudflared` connectors. For detailed instructions, refer to the [WARP Connector documentation](/mesh/).

## 2024-10-10

**Bugfix for --grace-period**

The new `cloudflared` build [2024.10.0](https://github.com/cloudflare/cloudflared/releases/tag/2024.10.0) has a bugfix related to the [--grace-period](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#grace-period) tunnel run parameter. `cloudflared` connectors will now abide by the specified waiting period before forcefully closing connections to Cloudflare's network.

## 2024-08-06

**cloudflared builds available in GitHub for Apple silicon**

macOS users can now download `cloudflared-arm64.pkg` directly from [GitHub](https://github.com/cloudflare/cloudflared/releases), in addition to being available via Homebrew.

# Scan for sensitive data

import { Render } from "~/components";

:::note
Requires Cloudflare CASB and Cloudflare DLP.
:::

You can use [Cloudflare Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/) to discover if files stored in a SaaS application contain sensitive data. To perform DLP scans in a SaaS app, first configure a [DLP profile](#configure-a-dlp-profile) (a set of patterns that define what counts as sensitive data) with the data patterns you want to detect, then [add the profile](#enable-dlp-scans-in-casb) to a CASB integration.

## Supported integrations

<Render file="casb/casb-dlp-integrations" product="cloudflare-one" />

## Configure a DLP profile

You may either use DLP profiles predefined by Cloudflare, or create your own custom profiles based on regex, predefined detection entries, datasets, and document fingerprints.

### Configure a predefined profile

<Render
	file="data-loss-prevention/predefined-profile"
	product="cloudflare-one"
/>

Your DLP profile is now ready to use with CASB.

### Build a custom profile

<Render file="data-loss-prevention/custom-profile" product="cloudflare-one" />

Your DLP profile is now ready to use with CASB.

For more information, refer to [Configure a DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/).

## Enable DLP scans in CASB

### Add a new integration

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Cloud & SaaS**.
2. Select **Add integration** and choose a [supported integration](#supported-integrations).
3. During the setup process, you are prompted to select DLP profiles for the integration.
4. Select **Save integration**.

CASB will scan every publicly accessible file in the integration for text that matches the DLP profile. The initial scan may take up to a few hours to complete.

### Modify an existing integration

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Cloud & SaaS**.
2. Choose a [supported integration](#supported-integrations) and select **Configure**.
3. Under **DLP profiles**, select the profiles that you want the integration to scan for.
4. Select **Save integration**.

If you enable a DLP profile from the **Manage integrations** page, CASB will only scan publicly accessible files that have had a modification event since enabling the DLP profile. Modification events include changes to the following attributes:

- Contents of the file
- Name of the file
- Visibility of the file (only if changed to publicly accessible)
- Owner of the file
- Location of the file (for example, moved to a different folder)

:::caution
If you add a DLP profile to an existing integration, CASB only scans files modified after you enabled the profile. To scan all files, you must enable the DLP profile during the [integration setup flow](#add-a-new-integration).
:::

## Limitations

DLP in CASB will only scan:

- [Text-based files](/cloudflare-one/data-loss-prevention/#supported-file-types) such as documents, spreadsheets, and PDFs. Images are not supported.
- Files less than or equal to 100 MB in size.
- Java and R source code files that are at least 5 KB. Smaller files in these languages are skipped.

# Custom finding types

import { Details, Steps } from "~/components";

In addition to providing predefined finding types to detect security issues within SaaS and cloud applications, Cloudflare CASB allows custom finding types to be defined from scratch, providing full control over the security conditions CASB detects.

## Prerequisites

- A [Cloud or SaaS integration](/cloudflare-one/integrations/cloud-and-saas/) has been added.
- Familiarity with [Rego](https://www.openpolicyagent.org/docs/policy-language).

## Standard and custom finding types

Cloudflare CASB provides two ways that security risks are detected. In [Cloudflare One](https://one.dash.cloudflare.com), they are differentiated in **Cloud & SaaS findings** > **Finding types library** as entries with **Origin** value "Standard" or "Custom".

- **Standard finding type** - A predefined finding type created and managed by Cloudflare CASB. It can be inspected and duplicated, but not changed.
- **Custom finding type** - A finding type that is created and managed in the account with Rego expressions.

The following table shows the capabilities of a CASB customer's account for both standard and custom finding types:

| Capability                      | Standard finding types | Custom finding types |
| ------------------------------- | ---------------------- | -------------------- |
| Inspect detection logic         | ✅                     | ✅                   |
| Create posture finding types    | ❌                     | ✅                   |
| Create content finding types    | ❌                     | ❌                   |
| Duplicate posture finding types | ✅                     | ✅                   |
| Duplicate content finding types | ❌                     | ❌                   |
| Edit posture finding types      | ❌                     | ✅                   |
| Edit content finding types      | ❌                     | ❌                   |
| Delete posture finding types    | ❌                     | ✅                   |
| Delete content finding types    | ❌                     | ❌                   |
| Disable posture finding types   | ❌                     | ❌                   |
| Disable content finding types   | ❌                     | ❌                   |

## How custom finding types work

### Example scenario

A security team at a company that uses Google Workspace wants to have visibility into when a group for executives, the board, or the legal team is open to people outside the company. The company names these groups with the prefixes `exec-`, `board-`, and `legal-`, and one of them, the outside counsel group, is allowed to have external members. Given that this depends on the company's own group naming convention and exception, CASB does not include a standard finding type for this exact scenario, so the team creates a custom finding type.

1. **Define the finding type.** In the Finding types library, the team creates a custom finding type. They name it "Sensitive group open to outsiders", set the severity to High, choose Google Workspace as the provider, and write a Rego expression that returns "fail" when a group whose address starts with `exec-`, `board-`, or `legal-` allows external members (unless it is the outside counsel group), lets anyone join, or lets anyone post.
2. **See the results.** As CASB receives updates for the company's Google Workspace groups, it evaluates each one. Groups that match appear as finding instances under **Posture Findings**, so the team can see which sensitive groups are open. Groups with other names are never flagged.
3. **Take action.** The team creates a [CASB policy](/cloudflare-one/cloud-and-saas-findings/policies/) for this finding type that sends a webhook to their messaging admin automation. Each new finding instance now triggers the automation to restrict the group.

The following sections describe each part of this flow in detail.

### Finding type fields

A custom finding type consists of the following fields:

| Field                 | Description                                                                                                                                 |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **Finding Type Name** | The display name of the finding type.                                                                                                       |
| **Description**       | A definition for what the finding type detects against.                                                                                     |
| **Severity**          | The impact of the finding type: Low, Medium, High, or Critical.                                                                             |
| **Provider**          | The [integration vendor](/cloudflare-one/integrations/cloud-and-saas/) the finding type detects against.                                    |
| **Asset Class**       | The set of asset fields available to the detection logic. These fields appear under **Available asset fields** in the finding type builder. |
| **Category Type**     | The classification of the findings it generates.                                                                                            |
| **Integrations**      | The integrations CASB evaluates this finding type against. Select **Apply to all integrations** to use all of them.                         |
| **Rego Package**      | The detection logic CASB uses to generate findings.                                                                                         |

CASB evaluates a custom finding type against an asset when it is created or updated and exists within the set of configured integrations. They are not run retroactively against existing assets.

Continuing the [example scenario](#example-scenario), one board group has an external member, and a new executive group is open to anyone:

| Day       | Event                                                                                     | Finding created?                                                                                                                |
| --------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Sunday    | The `board-directors@example.com` group allows external members and is updated.           | No. The "Sensitive group open to outsiders" finding type does not exist yet.                                                    |
| Monday    | The security team creates the "Sensitive group open to outsiders" finding type.           | No. CASB does not evaluate `board-directors`, because it has not changed since the finding type was created.                    |
| Tuesday   | The `exec-staff@example.com` group is created and anyone can join it.                     | Yes. CASB evaluates the new group and creates a finding instance.                                                               |
| Wednesday | A member is added to `board-directors`.                                                   | Yes. CASB evaluates the group again and creates a finding instance, because it still allows external members.                   |
| Thursday  | The `legal-outside-counsel@example.com` group, which allows external members, is updated. | No. CASB evaluates the group and its result is "pass", because it is the approved exception, so no finding instance is created. |

## Rego detection logic

Custom finding types are written in [Rego](https://www.openpolicyagent.org/docs/policy-language), the open-source policy language used by Open Policy Agent (OPA). For an introduction to Rego, refer to the [OPA documentation](https://www.openpolicyagent.org/docs/policy-language). CASB uses Rego v1 and supports a restricted subset of [Rego built-ins](https://www.openpolicyagent.org/docs/policy-reference/builtins) and operations.

The following expression is the one the security team writes in the [example scenario](#example-scenario). It detects sensitive groups that are open to people outside the company:

```txt
sensitive_prefixes := ["exec-", "board-", "legal-"]
external_exceptions := {"legal-outside-counsel@example.com"}

default result := "pass"

email := lower(input.asset.email)

sensitive if strings.any_prefix_match(email, sensitive_prefixes)

result := "fail" if {
	sensitive
	input.asset.allowExternalMembers == true
	not email in external_exceptions
}

result := "fail" if {
	sensitive
	input.asset.whoCanJoin == "ANYONE_CAN_JOIN"
}

result := "fail" if {
	sensitive
	input.asset.whoCanPostMessage == "ANYONE_CAN_POST"
}
```

This expression works as follows:

- `sensitive_prefixes` and `external_exceptions` hold the company's values. They are written into the expression, because CASB cannot fetch lists from another system during evaluation.
- `default result := "pass"` sets the result to "pass" for every asset unless another rule in the expression changes it. Therefore, assets that do not match are never flagged.
- `email := lower(input.asset.email)` lowercases the group's address, so that `Board-Directors@example.com` is treated the same as `board-directors@example.com`.
- `sensitive` is true when the address starts with any of the three prefixes. `strings.any_prefix_match` checks the whole list in one call. Groups with other names never match.
- `result := "fail" if { ... }` changes the result to "fail" when everything inside the braces is true. This will create a finding instance.
- The first rule checks if sensitive groups allow external members without explicit approvals.
- The second rule checks for sensitive groups that let anyone join.
- The third rule checks for sensitive groups that let anyone post.

### Rego guidelines for custom finding type expressions

- The expression must define a `result` that returns either "pass" or "fail". A "fail" result means the asset matches the finding type and creates a finding instance.
- Add `default result := "pass"` so assets that do not match a fail condition return "pass".
- No need to add a package declaration or any import statements, this includes `import rego.v1`. CASB adds these automatically, and the editor does not accept them.
- Fields from the selected asset class can be read from `input.asset`.
- Some asset classes include related assets, such as a file and its owner. When available, reference fields from related assets through `input.associated`.

### Rego validation

Select **Validate** in the finding type builder to check a Rego expression before creating the finding. Validation compiles the expression and checks it against the asset schema. It reports any syntax errors or unsupported Rego built-ins it finds.

Validation does not execute the expression against data. To confirm the expression detects the intended conditions, [verify the finding type's behavior](#verify-custom-finding-type-behavior) after it is created.

### Supported Rego built-ins

Custom finding types support the following Rego built-ins and operators. Any built-in not listed here is unsupported.

<Details header="Supported built-ins and operators">

| Category                   | Built-ins                                                                                                                                                                                                                                                                                                                    |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Operators                  | `=`, `==`, `!=`, `>`, `>=`, `<`, `<=`, `in`                                                                                                                                                                                                                                                                                  |
| Aggregates                 | `count`, `max`, `min`, `product`, `sort`, `sum`                                                                                                                                                                                                                                                                              |
| Arrays                     | `array.concat`, `array.flatten`, `array.reverse`, `array.slice`                                                                                                                                                                                                                                                              |
| Conversion                 | `to_number`                                                                                                                                                                                                                                                                                                                  |
| Encoding and serialization | `base64.decode`, `base64.encode`, `base64.is_valid`, `base64url.decode`, `base64url.encode`, `base64url.encode_no_pad`, `hex.decode`, `hex.encode`, `json.is_valid`, `json.marshal`, `json.marshal_with_options`, `json.unmarshal`, `urlquery.decode`, `urlquery.decode_object`, `urlquery.encode`, `urlquery.encode_object` |
| Glob and graph             | `glob.match`, `glob.quote_meta`, `graph.reachable`, `graph.reachable_paths`, `walk`                                                                                                                                                                                                                                          |
| CIDR                       | `net.cidr_contains`, `net.cidr_contains_matches`, `net.cidr_intersects`, `net.cidr_is_valid`, `net.cidr_merge`                                                                                                                                                                                                               |
| Numbers                    | `abs`, `ceil`, `div`, `floor`, `minus`, `mul`, `plus`, `rem`, `round`                                                                                                                                                                                                                                                        |
| JSON and objects           | `json.filter`, `json.remove`, `object.filter`, `object.get`, `object.keys`, `object.remove`, `object.subset`, `object.union`, `object.union_n`                                                                                                                                                                               |
| Regular expressions        | `regex.globs_match`, `regex.is_valid`, `regex.match`, `regex.replace`, `regex.template_match`                                                                                                                                                                                                                                |
| Semantic versions          | `semver.compare`, `semver.is_valid`                                                                                                                                                                                                                                                                                          |
| Sets                       | `and`, `intersection`, `or`, `union`                                                                                                                                                                                                                                                                                         |
| Strings                    | `concat`, `contains`, `endswith`, `format_int`, `indexof`, `indexof_n`, `lower`, `replace`, `split`, `sprintf`, `startswith`, `strings.any_prefix_match`, `strings.any_suffix_match`, `strings.count`, `substring`, `trim`, `trim_left`, `trim_prefix`, `trim_right`, `trim_space`, `trim_suffix`, `upper`                   |
| Time                       | `time.add_date`, `time.clock`, `time.date`, `time.diff`, `time.format`, `time.now_ns`, `time.parse_duration_ns`, `time.parse_ns`, `time.parse_rfc3339_ns`, `time.weekday`                                                                                                                                                    |
| Types                      | `is_array`, `is_boolean`, `is_null`, `is_number`, `is_object`, `is_set`, `is_string`, `type_name`                                                                                                                                                                                                                            |
| Units                      | `units.parse`, `units.parse_bytes`                                                                                                                                                                                                                                                                                           |
| URI                        | `uri.is_valid`, `uri.parse`                                                                                                                                                                                                                                                                                                  |
| UUID                       | `uuid.parse`, `uuid.rfc4122`                                                                                                                                                                                                                                                                                                 |

</Details>

### Rego references

Custom finding type expressions use Rego v1 syntax and the Cloudflare-supported Rego built-ins listed in this section.

- [Rego policy language](https://www.openpolicyagent.org/docs/policy-language) - Rego language documentation.
- [OPA built-ins](https://www.openpolicyagent.org/docs/policy-reference/builtins) - Full list of OPA built-ins. Custom finding types support a subset of this list.
- [OPA style guide](https://www.openpolicyagent.org/docs/style-guide) - General guidance for writing Rego expressions.
- [OPA Playground](https://play.openpolicyagent.org/) - Sandbox for writing and testing general Rego expressions.

:::note
The OPA documentation and Playground include built-ins that custom finding types do not support. An expression that works in the Playground may fail validation in Cloudflare One. Use only the built-ins listed on this page.
:::

## Create a custom finding type

<Steps>
1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Finding types library**.
2. Select **Create finding type**.
3. Under **General Information**, enter a finding type name and description. Then, select a severity.
4. Under **Scope Definition**, select a provider and asset class.
5. Choose whether the finding type applies to all integrations for the provider or to selected integrations.
6. Under **Detection Logic**, enter the Rego expression. Use **Available asset fields** to identify supported fields.
7. Select **Validate**. Resolve each validation error before continuing.
8. Select **Create finding type**.
</Steps>

CASB applies the custom finding type when it receives future asset updates within the selected scope. Matching assets appear as posture finding instances.

## Verify custom finding type behavior

To confirm that a custom finding type is detecting matches, check its finding instances.

<Steps>
1. In **Cloud & SaaS findings**, go to **Posture Findings**.
2. Find the custom finding type.
3. Refer to the **Instances** column to confirm that matches have been found. The **Instances** column shows how many assets currently match.
4. Select **Manage** to view the matching assets.
</Steps>

:::note
CASB evaluates assets only when it receives asset creations or updates. Assets that have not changed since the finding type was created are not evaluated, so they do not appear in **Instances**.
:::

If no instances appear after assets in the finding type's integrations have been created or updated, perform the following checks:

- Check the specified provider
- Check the specified asset class
- Check the specified integration scope
- Check the written field names in the written Rego expression
- Check the field types used in the written Rego expression
- Check the expected output of the written Rego expression

## Custom finding types restrictions and limits

- An account can have up to 250 custom finding types.
- Up to 25 custom finding types can apply to one integration.
- Custom finding types support posture finding types. They do not support Data Loss Prevention (DLP) content finding types.
- New and updated custom finding types do not apply retroactively.
- The complete Rego policy, including Cloudflare-added declarations, can be up to 4 KiB.
- Network access is not available during Rego expression evaluation.
- Package declarations and import declarations are not accepted in the editor.

## Use custom finding types in policies

Custom finding types can be used in [CASB policies](/cloudflare-one/cloud-and-saas-findings/policies/) the same way standard finding types are used. When a [policy is created](/cloudflare-one/cloud-and-saas-findings/policies/#create-a-policy), the custom finding type is selected from the **Finding type** list.

### Example scenario

Continuing the previous [example](#example-scenario), the security team wants a group restricted automatically each time CASB finds a sensitive group that is open to people outside the company.

1. **Create the policy.** The team creates a CASB policy and selects "Sensitive group open to outsiders" from the **Finding type** list.
2. **Set the scope.** The policy is set to **Apply to all integrations**, so it covers every Google Workspace integration the finding type is evaluated against.
3. **Choose the action.** The policy sends a webhook to the team's messaging admin automation.
4. **See it work.** When a group is created or updated and CASB evaluates it as "fail", CASB creates a finding instance. The policy then fires the webhook, and the automation restricts the group's membership and posting settings.

### Guidelines for using policies with custom finding types

- Custom finding types can be used for policies that trigger webhooks.
- A policy can only act on findings within the integrations it selects, or from all integrations if **Apply to all integrations** is set. It can only act on integrations within the custom finding type's integration scope. The two lists do not need to match.
- If the [integration scope of the custom finding type](#edit-a-custom-finding-type) is edited to remove an integration, CASB no longer creates finding instances for that integration, so the policy has nothing to act on there.
- If the custom finding type is [quarantined](#quarantined-custom-finding-types), CASB creates no new finding instances, so the policy does not trigger.
- A custom finding type cannot be deleted while a policy uses it. [Delete the policy](/cloudflare-one/cloud-and-saas-findings/policies/#edit-turn-off-or-delete-a-policy) first, then [delete the custom finding type](#delete-a-custom-finding-type).

## Manage custom finding types

Standard or custom posture finding types can be duplicated, supported fields on a custom finding type can be edited, and a custom finding type that is no longer needed can be deleted.

### Duplicate a finding type

Open a standard posture finding type to inspect its detection logic. If the logic is a useful starting point, duplicate the finding type to create an editable copy.

#### Example scenario

Continuing the [previous example](#example-scenario), leadership asks that the executive and board groups be treated as Critical, with no exceptions, while the legal groups stay at High. Severity cannot be edited after creation, so the security team duplicates the finding type instead.

1. **Duplicate.** The team selects **Duplicate** on "Sensitive group open to outsiders". CASB copies the name, description, severity, provider, asset class, integration scope, and Rego expression into the builder.
2. **Adjust.** They rename the copy "Executive and board group open to outsiders" and set the severity to Critical. In the Rego expression, they change `sensitive_prefixes` to `["exec-", "board-"]`, and remove `external_exceptions` and the `not email in external_exceptions` line.
3. **Create.** They select **Validate**, then **Create finding type**. The copy is a new custom finding type.

The original "Sensitive group open to outsiders" keeps running unchanged. For this company specifically, an `exec-` or `board-` group that breaks a rule now receives a finding from both finding types, one at High and one at Critical.

#### Steps

<Steps>
1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Finding types library**.
2. In the finding type menu, select **Duplicate**.
3. Review the copied name, description, severity, provider, asset class, integration scope, and Rego expression.
4. Change the copied fields as needed.
5. Select **Validate**. Resolve each validation error before continuing.
6. Select **Create finding type**.
</Steps>

The duplicate finding type is a separate custom finding type. Changes to the duplicate do not affect the original finding type.

### Edit a custom finding type

Only the following fields can be edited after a custom finding type is created:

- Name
- Description
- Integration scope

To change the provider, asset class, severity, or Rego expression, [duplicate the finding type](#duplicate-a-finding-type).

<Steps>
1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Finding types library**.
2. In the custom finding type menu, select **Edit**.
3. Update the name, description, or integration scope.
4. Select **Save**.
</Steps>

### Delete a custom finding type

<Steps>
1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Finding types library**.
2. In the custom finding type menu, select **Delete**.
3. Confirm the deletion.
</Steps>

Deleting a custom finding type will stop all evaluations against future asset updates and deletes the associated finding instances.

Deleting a finding instance cannot be undone.

### Disable a custom finding type

Custom finding types cannot be disabled. To stop CASB from evaluating a custom finding type, [delete it](#delete-a-custom-finding-type). Deleting a custom finding type cannot be undone and deletes its existing finding instances.

To stop a [CASB policy](/cloudflare-one/cloud-and-saas-findings/policies/) that uses a custom finding type from triggering, turn off the policy instead. Refer to [Edit, turn off, or delete a policy](/cloudflare-one/cloud-and-saas-findings/policies/#edit-turn-off-or-delete-a-policy).

## Quarantined custom finding types

Quarantine is a status CASB applies when a custom finding type's expression cannot be evaluated safely.

A quarantined custom finding type displays a warning icon next to its entry in **Cloud & SaaS findings** > **Finding types library**.

While a custom finding type is quarantined:

- Existing finding instances remain visible.
- CASB does not evaluate the expression when it receives asset updates, so no new finding instances are created.
- Other custom and standard finding types on the same integrations continue to run normally.

The **Policies** list does not show when a policy uses a quarantined custom finding type.

### Why a custom finding is quarantined

A custom finding type will be quarantined if CASB is unable to evaluate the finding type against assets. Common causes include:

- Nested iteration over large arrays, such as comparing every item in a list to every other item.
- Timing out due to evaluation against large or deeply nested asset fields.
- Running regular expressions against every element of a large collection when a simpler check, such as `startswith` or `endswith`, is sufficient.

**Validate** compiles the expression and checks it against the asset schema, but does not run the expression against asset data. An expression can pass validation and still be quarantined later.

### Manually quarantine a custom finding type

A custom finding type cannot be manually quarantined, and the status cannot be removed, from the dashboard or the API.

Editing a quarantined custom finding type's name, description, or integration scope does not remove the status.

### Resolve a quarantined custom finding

Custom finding type expressions cannot be edited. To resolve a quarantined custom finding type, duplicate it and correct the expression in the copy:

<Steps>
1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Finding types library**.
2. In the quarantined custom finding type menu, select **Duplicate**.
3. Rewrite the expression to reduce its runtime and memory use. Refer to [Why a custom finding is quarantined](#why-a-custom-finding-is-quarantined).
4. Select **Validate**. Resolve each validation error before continuing.
5. Select **Create finding type**. The duplicate is a new custom finding type and is not quarantined.
6. If a [CASB policy](/cloudflare-one/cloud-and-saas-findings/policies/) uses the quarantined custom finding type, update the policy to use the new custom finding type.
7. [Delete the quarantined custom finding type](#delete-a-custom-finding-type).
</Steps>

:::note
A quarantined custom finding type still counts toward the limits of 250 custom finding types per account and 25 per integration. If a limit has been reached, copy the expression and delete the quarantined custom finding type before the replacement is created.
:::

If the cause of the quarantine cannot be identified, contact [Cloudflare Support](/support/contacting-cloudflare-support/). Provide the custom finding type ID and as much detail as possible.

## Example expressions

### Iterate over an array

For the Anthropic `AnthropicComplianceRole` asset class, the following expression detects a role with the Cowork permission:

```txt
default result := "pass"

result := "fail" if {
	some permission in input.asset.permissions
	permission.action == "cowork"
}
```

### Handle a nullable value

For the Anthropic `AnthropicComplianceUser` asset class, the following expression detects a non-null email in the `example.com` domain:

```txt
default result := "pass"

result := "fail" if {
	input.asset.email != null
	regex.match(`(?i)@example\.com$`, input.asset.email)
}
```

### Use an associated asset

For the Google Workspace `GoogleWorkspaceLicenseAssignment` asset class, the following expression detects a non-suspended, non-administrator user with an AI Ultra license:

```txt
ai_ultra_sku_id := "1010470008"

default result := "pass"

result := "fail" if {
	input.asset.skuId == ai_ultra_sku_id
	input.associated.user.isAdmin == false
	input.associated.user.isSuspended == false
}
```

# Cloud and SaaS findings

import { GlossaryTooltip, Render } from "~/components";

:::note[Availability]
Available for all Zero Trust users.

Free users can configure up to two CASB integrations. You must upgrade to an Enterprise plan to view the details of a finding instance.
:::

Cloudflare's [Cloud Access Security Broker](https://www.cloudflare.com/learning/access-management/what-is-a-casb/) (CASB) connects to SaaS application and cloud environment APIs to scan for security issues that can occur after a user has successfully logged in. These include misconfigurations (such as overly permissive sharing settings), unauthorized user activity, <GlossaryTooltip term="shadow IT" link="https://www.cloudflare.com/learning/access-management/what-is-shadow-it/">shadow IT</GlossaryTooltip>, and other data security issues.

For a list of supported finding types, refer to [Cloud and SaaS integrations](/cloudflare-one/integrations/cloud-and-saas/). You can also send posture finding instances to external systems with [CASB webhooks](/cloudflare-one/integrations/cloud-and-saas/webhooks/).

## Finding terminology

CASB uses the following terms for posture findings:

| Term             | Definition                                                                                                                                                                                                         |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Finding type     | A definition of a specific security issue that CASB detects. It includes detection logic and metadata for one vendor and asset class, such as files or users. One finding type can apply to multiple integrations. |
| Posture finding  | A summary of detections of one finding type within one integration. Each row in **Posture Findings** represents a finding and groups its instances.                                                                |
| Finding instance | An individual occurrence of a posture finding affecting a specific asset within that integration. One asset can have instances of different finding types.                                                         |

For example, CASB detects publicly viewable files in two Google Workspace integrations. **Posture Findings** shows two findings for the **Google Workspace: File publicly accessible with view access** finding type, one per integration. Each affected file, such as `budget.xlsx`, is a separate finding instance within its integration.

Content findings use a different grouping. Each row in **Content Findings** represents one asset within an integration and groups its matching [Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/) profiles.

To review detected findings and their affected assets, refer to [Manage findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/).

## Manage CASB integrations

<Render file="casb/manage-integrations" product="cloudflare-one" />

# Manage findings

import { Steps, TabItem, Tabs } from "~/components";

Cloudflare CASB detects security issues involving users, stored files, and application settings. In Cloudflare One, you can review posture and content findings and take action on affected assets. For definitions of finding types, posture findings, and finding instances, refer to [Finding terminology](/cloudflare-one/cloud-and-saas-findings/#finding-terminology).

## Prerequisites

- You have added a [Cloud and SaaS integration](/cloudflare-one/integrations/cloud-and-saas/).
- Your scan has surfaced at least one security finding.

## Posture findings

Posture findings include misconfigurations, unauthorized user activity, and other data security issues. Each finding summarizes one finding type within one integration. Its finding instances identify the affected assets. The same finding type can appear in separate rows for different integrations.

To view details about the posture findings that CASB found:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Choose **SaaS** or **Cloud**.
3. To view details about a finding, select the finding name.

Cloud & SaaS findings will display details about your posture finding, including the finding type, [severity level](#severity-levels), number of instances, associated integration, current status, and date detected. For more information on each instance of the finding, select **Manage**.

To manage the finding's visibility, you can update the finding's [severity level](#severity-levels) or [hide the finding](#hide-findings) from view. You can also [send a posture finding instance to a webhook](#send-webhook). Some findings also provide a remediation guide to resolve the issue.

To detect conditions that Cloudflare's standard finding types do not cover, refer to [Custom finding types](/cloudflare-one/cloud-and-saas-findings/custom-finding-types/).

### Severity levels

Cloudflare CASB labels each finding with one of the following severity levels:

| Severity level | Urgency                                                                      |
| -------------- | ---------------------------------------------------------------------------- |
| Critical       | Suggests the finding is something your team should act on today.             |
| High           | Suggests the finding is something your team should act on this week.         |
| Medium         | Suggests the finding should be reviewed sometime this month.                 |
| Low            | Suggests the finding is informational or part of a scheduled review process. |

#### Change the severity level

You can change the severity level for a finding at any time in case the default assignment does not suit your environment:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Locate the finding you want to modify and select **Manage**.
3. In the severity level drop-down menu, choose your desired setting (_Critical_, _High_, _Medium_, or _Low_).

The new severity level will only apply to the posture finding within this specific integration. If you added multiple integrations of the same application, the other integrations will not be impacted by this change.

## Content findings

Content findings identify potential data exposure detected by [Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/). Each content finding represents one asset within an integration and groups all matching DLP profiles for that asset.

To view details about the content findings that CASB found:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Content Findings**.
2. Choose **SaaS** or **Cloud**.
3. To view details about a finding, select the finding name.

Cloud & SaaS findings will display details about your content finding, including the file name, a link to the file, matching DLP profiles, associated integration, and date detected.

AWS users can configure a [compute account](/cloudflare-one/integrations/cloud-and-saas/aws-s3/#compute-account) to scan for data security resources within their S3 resources.

## View shared files

Posture finding instances and content findings for some integrations (such as [Microsoft 365](/cloudflare-one/integrations/cloud-and-saas/microsoft-365/#file-sharing) and [Box](/cloudflare-one/integrations/cloud-and-saas/box/#file-sharing)) may link to an inaccessible file. To access the actual shared file:

<Tabs> <TabItem label="Posture finding">

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Choose **SaaS** or **Cloud**.
3. Locate the individual finding, then select **Manage**.
4. In **Active Instances**, select the file name.
5. In **Shared Links**, select the linked file instance.

</TabItem>

<TabItem label="Content finding">

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Content Findings**.
2. Choose **SaaS** or **Cloud**.
3. Select the file name of the detected asset.
4. In **Sharing details**, select the linked file instance.

</TabItem> </Tabs>

## Hide findings

After reviewing your findings, you may decide that certain posture findings are not applicable to your organization. Cloudflare CASB allows you to remove findings or individual instances of findings from your list of active issues. CASB will continue to scan for these issues, but any detections will appear in a separate tab.

- **Ignore a finding** — Moves the posture finding for one integration from **Active** to **Ignored**. Current and future detections of that finding type within the integration appear under the ignored finding. Ignoring does not affect findings of the same type in other integrations.
- **Hide an instance** — Moves one finding instance from **Active** to **Hidden**. Future detections of the same finding type for that asset within the integration go to the **Hidden** tab automatically.

### Ignore a finding

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Locate the active finding you want to hide.
3. In the three-dot menu, select **Move to ignore**.

The finding's status will change from **Active** to **Ignored**. CASB will continue to scan for these findings and report detections. You can change ignored findings back to **Active** with the same process at any time.

### Hide an instance of a finding

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Choose the active finding you want to hide, then select **Manage**.
3. In **Active**, find the instance you want to hide.
4. In the three-dot menu, select **Move to hidden**.

The instance will be moved from **Active** to **Hidden** within the finding. If CASB detects the same issue for that asset again, it appears in the **Hidden** tab. You can move hidden instances back to the **Active** tab at any time.

## Send webhook

After you configure one or more [CASB webhooks](/cloudflare-one/integrations/cloud-and-saas/webhooks/), you can send posture finding instances to external systems such as chat platforms, ticketing systems, SIEMs, SOAR tools, and custom automation services.

CASB webhooks currently support posture finding instances only.

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Choose **SaaS** or **Cloud**.
3. Choose the finding you want to review, then select **Manage**.
4. In **Active Instances**, select an instance.
5. In the instance details panel, select **Send webhook**.
6. Choose the webhook destination or destinations you want to use.
7. Select **Send webhooks**.

Cloudflare queues webhook sends in the background. A success message means that Cloudflare accepted the request for delivery.

To validate a destination before sending a live finding instance, use **Test delivery** from the [Webhooks](/cloudflare-one/integrations/cloud-and-saas/webhooks/) page.

## Remediate findings

In addition to detecting issues with SaaS and cloud applications, CASB can remediate supported posture finding instances directly in applications.

### Configure remediation permissions

Before you can remediate finding instances, [add a new integration](/cloudflare-one/integrations/cloud-and-saas/) and choose _Read-Write mode_ during setup. Alternatively, you can update an existing integration:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Cloud & SaaS findings** > **Integrations**.
2. Choose your integration, then select **Configure**.
3. In **Integration permissions**, choose _Read-Write mode_.
4. Select **Update integration**. CASB will redirect you to your Microsoft 365 configuration.
5. Sign in to your organization, then select **Accept**.

CASB can now remediate instances of supported finding types directly.

### Remediate a finding

To remediate a finding instance:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Choose a finding whose type supports remediation, then select **Manage**.
3. In **Active Instances**, select an instance.
4. In **Remediation details**, choose a remediation action to take.

CASB will begin remediating the instance.

### Remediate inherited file permissions

CASB can remove supported permissions applied directly to a Microsoft 365 or Google Workspace resource. If a file inherits access from a parent folder or Shared Drive, change the permission on that parent resource. Changing the file itself does not remove the inherited access.

CASB does not automatically change permissions on parent resources. Changing a parent permission can affect every downstream file and folder that inherits it.

#### Microsoft 365

CASB may show a separate finding instance for the parent folder that gives the file its permission. The remediation details may include **Parent Folder Access** links. Open the linked instance to remediate the parent folder separately.

When an inherited permission prevents remediation, CASB displays:

> Resource has inherited permissions. Remediate permissions at the folder or organizational level.

#### Google Workspace

For Google Workspace, CASB does not identify the exact parent resource. Asset details may show the associated Shared Drive, but not the originating folder or permission.

When an inherited permission prevents remediation, CASB displays:

> Resource has inherited permissions from a parent resource. Direct remediation of the parent is not yet supported for Google Workspace.

For example, a file inherits public access when its parent folder allows **Anyone with the link**. Change the **General access** setting on the parent folder to resolve the finding instance.

A file can also inherit external access from a Shared Drive member. Change the relevant parent permission or Shared Drive membership to resolve the finding instance.

To remediate an inherited Google Workspace permission:

<Steps>
1. In Google Drive, open the affected file and select **Share** or **Manage access**.
2. Find the public or external access associated with the finding instance.
3. If Google Drive identifies the parent, open that resource. Otherwise, inspect each parent folder until you find where the permission is configured directly.
4. For Shared Drive content, also inspect the membership and sharing settings.
5. Review the impact on downstream resources before changing the permission.
6. Change the permission at its source.
</Steps>

CASB updates the finding instance after it receives updated asset data. The failed child-resource remediation remains in the remediation history.

### Manage remediated findings

To review remediation progress, go to **Cloud & SaaS findings** > **Posture Findings** and open the affected finding instance. Each remediation status applies only to that instance:

| Status     | Description                                                                                              |
| ---------- | -------------------------------------------------------------------------------------------------------- |
| Pending    | CASB has set the finding instance to be remediated.                                                      |
| Processing | CASB is remediating the finding instance.                                                                |
| Validating | CASB completed the remediation and is waiting to confirm that the issue affecting the asset is resolved. |
| Completed  | CASB remediated the finding instance and confirmed that the issue is resolved.                           |
| Failed     | CASB could not remediate the finding instance.                                                           |
| Rejected   | CASB does not have the correct permissions to remediate the finding instance.                            |

CASB may take up to 48 hours to validate a remediation.

If the status is **Completed**, remediation succeeded. If the status is **Failed** or **Rejected**, remediation failed, and you can select the finding instance to take action again. A **Rejected** status indicates that CASB does not have the correct permissions to remediate the instance.

CASB will log remediation actions in **Logs** > **Admin**. For more information, refer to [Cloudflare One Logs](/cloudflare-one/insights/logs/).

To automatically remediate matching finding instances without reviewing each one, refer to [Remediation Policies](/cloudflare-one/cloud-and-saas-findings/policies/).

# Remediation Policies

import { APIRequest, Details, Steps, TabItem, Tabs } from "~/components";

:::note[Availability]
Requires a paid Cloudflare CASB plan. Remediation Policies are not available for free CASB integrations.
:::

Use CASB policies to automatically remediate a finding instance or send a webhook as soon as CASB detects it. A policy defines the vendor, integrations, finding type match, and action Cloudflare should take. For definitions of finding types and instances, refer to [Finding terminology](/cloudflare-one/cloud-and-saas-findings/#finding-terminology).

Policies build on [manual remediation](/cloudflare-one/cloud-and-saas-findings/manage-findings/#remediate-findings) and [CASB webhooks](/cloudflare-one/integrations/cloud-and-saas/webhooks/). Instead of each finding instance needing to be actioned manually, a configured policy automatically triggers action on all newly discovered matching finding instances.

## Prerequisites

- A configured [Cloud or SaaS integration](/cloudflare-one/integrations/cloud-and-saas/).
- [Read-Write permissions](/cloudflare-one/cloud-and-saas-findings/manage-findings/#configure-remediation-permissions) on the integration, required for remediation actions.
- A [configured webhook destination](/cloudflare-one/integrations/cloud-and-saas/webhooks/#create-a-webhook), required for webhook actions.

## How policies work

When CASB detects a finding instance, it checks whether the instance matches a customer-configured policy. If a policy matches, Cloudflare runs the action configured in the policy against that instance automatically.

A policy can run a remediation action, send a webhook, or both.

## Find the IDs for a policy

When you create a policy with the API or Terraform, you reference other objects by ID:

- **Finding type ID**: Use the [List finding types](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/finding_types/methods/list/) endpoint.
- **Remediation type ID**: Use the [List remediation types](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/finding_types/subresources/remediation_types/methods/list/) endpoint for the finding type. Only [some finding types](#run-remediations) support remediation.
- **Webhook ID**: Use the ID of a [configured webhook](/cloudflare-one/integrations/cloud-and-saas/webhooks/#create-a-webhook). In Terraform, reference the `id` of a `cloudflare_zero_trust_casb_webhook` resource or data source.
- **Integration IDs**: Required only when the policy does not apply to all integrations. Use the [List integrations](/api/resources/zero_trust/subresources/casb/subresources/integrations/methods/list/) endpoint.

## Create a policy

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Policies**.
2. Select **Create a policy**.
3. Under **Basic information**, enter a **Policy name**. Optionally, enter a **Description**.
4. Under **Choose how you want to trigger the policy**, select a **Vendor**.
5. Select one or more **Integrations**, or select **Apply to all integrations** to apply the policy to every integration for the vendor.
6. Select a **Finding type**. Only finding types available for the selected vendor and integrations appear here.
7. Under **Define what to do with findings that match your trigger**, choose one or both actions:
   - **Run Remediation** to have Cloudflare perform a first-party remediation action against the SaaS integration API. This option is only available for select finding types.
   - **Send webhooks** to send a notification to one or more webhook destinations.
8. Under **Status**, turn on **Enable policy**.
9. Select **Create policy**.

</TabItem> <TabItem label="API">

Make a `POST` request to the [Create a policy](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/policies/methods/create/) endpoint. A policy must include at least one action, and can include at most one remediation:

<APIRequest
	path="/accounts/{account_id}/data-security/posture/policies"
	method="POST"
	json={{
		display_name: "Auto-remediate public files",
		description: "Remove public access and notify the SIEM",
		enabled: true,
		applies_to_all_integrations: true,
		finding_type_id: "<FINDING_TYPE_ID>",
		actions: {
			remediation_types: [{ remediation_type_id: "<REMEDIATION_TYPE_ID>" }],
			webhook_configs: [{ webhook_config_id: "<WEBHOOK_ID>" }],
		},
	}}
/>

To limit the policy to specific integrations, set `applies_to_all_integrations` to `false` and provide an `integration_ids` array of [integration IDs](#find-the-ids-for-a-policy).

</TabItem> <TabItem label="Terraform (v5)">

<Steps>

1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
   - `Zero Trust Write`

2. Create a policy using the [`cloudflare_zero_trust_casb_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_casb_policy) resource. The following example remediates matching findings and sends them to the `siem` webhook from [Create a webhook](/cloudflare-one/integrations/cloud-and-saas/webhooks/#create-a-webhook). If you manage the webhook outside Terraform, use its ID or the `cloudflare_zero_trust_casb_webhook` data source instead:

   ```tf
   resource "cloudflare_zero_trust_casb_policy" "public_files" {
     account_id                  = var.cloudflare_account_id
     display_name                = "Auto-remediate public files"
     description                 = "Remove public access and notify the SIEM"
     enabled                     = true
     applies_to_all_integrations = true
     finding_type_id             = "<FINDING_TYPE_ID>"

     actions = {
       remediation_types = [{
         remediation_type_id = "<REMEDIATION_TYPE_ID>"
       }]
       webhook_configs = [{
         webhook_config_id = cloudflare_zero_trust_casb_webhook.siem.id
       }]
     }
   }
   ```

   To limit the policy to specific integrations, set `applies_to_all_integrations = false` and list the [integration IDs](#find-the-ids-for-a-policy):

   ```tf
     applies_to_all_integrations = false
     integration_ids             = ["<INTEGRATION_ID_1>", "<INTEGRATION_ID_2>"]
   ```

   Terraform reports an error at `terraform plan` if `applies_to_all_integrations` is `false` and `integration_ids` is empty.

</Steps>

</TabItem> </Tabs>

Policies will be in effect for all newly discovered finding instances going forward. New or updated policies are not applied retroactively to existing finding instances.

CASB policies appear in a list showing each policy's name, integration, finding type, webhook, remediation action, status, and the time it last ran.

### Run remediations

Remediation actions perform a first-party fix directly against the integration's API, such as revoking a public file share.

CASB currently supports remediation actions for Microsoft 365 and Google Workspace file and folder finding types. If a finding type does not support remediation, **Run Remediation** displays **No automated remediation available for this finding type** and cannot be enabled.

<Details header="Supported finding types for remediation">

**Google Workspace:**

- File publicly accessible with edit access
- File publicly accessible with view access
- File shared outside company with edit access
- File shared outside company with view access
- File shared company-wide with edit access
- File shared company-wide with view access
- File publicly accessible with edit access with DLP Profile match
- File publicly accessible with view access with DLP Profile match
- File shared outside company with edit access with DLP Profile match
- File shared outside company with view access with DLP Profile match
- File shared company-wide with edit access with DLP Profile match
- File shared company-wide with view access with DLP Profile match

**Microsoft 365:**

- File publicly accessible with edit access
- File publicly accessible with view access
- File shared company-wide with edit access
- File shared company-wide with view access
- File publicly accessible with edit access with DLP Profile match
- File publicly accessible with view access with DLP Profile match
- File shared company-wide with edit access with DLP Profile match
- File shared company-wide with view access with DLP Profile match

</Details>

Remediation requires [Read-Write permissions](/cloudflare-one/cloud-and-saas-findings/manage-findings/#configure-remediation-permissions) on the integration. If the integration only has Read permissions, upgrade the integration before the policy can remediate matching finding instances.

For more information, refer to [Manage remediated findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#manage-remediated-findings).

### Send webhooks

Webhook actions send the finding instance to a previously configured webhook destination. Use this to route finding instances to systems such as Slack, Microsoft Teams, Jira, ServiceNow, Tines, or a custom HTTPS endpoint.

When a policy sends a webhook, the payload uses the same format as a webhook sent manually from a finding instance. For the payload structure and field descriptions, refer to [Payload format](/cloudflare-one/integrations/cloud-and-saas/webhooks/#payload-format).

## Edit, turn off, or delete a policy

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Policies**.
2. Select the policy to update.
3. Modify the policy's basic information, trigger, or actions.
4. Select **Save changes**.

To turn a policy on or off, use the **Enable policy** toggle under **Status**. Each policy displays its status as **Enabled** or **Disabled** in the policy list. A disabled policy stops matching new finding instances until you turn it on again.

To delete a policy, open the policy and select **Delete**.

</TabItem> <TabItem label="API">

To update a policy, make a `PUT` request to the [Update a policy](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/policies/methods/update/) endpoint. Include `display_name`, `enabled`, `applies_to_all_integrations`, and `actions`. The request replaces the existing policy, so include every action you want to keep. To turn the policy off, set `enabled` to `false`:

<APIRequest
	path="/accounts/{account_id}/data-security/posture/policies/{policy_id}"
	method="PUT"
	json={{
		display_name: "Auto-remediate public files",
		enabled: false,
		applies_to_all_integrations: true,
		actions: {
			webhook_configs: [{ webhook_config_id: "<WEBHOOK_ID>" }],
		},
	}}
/>

You cannot change a policy's finding type. To use a different finding type, create a new policy.

To delete a policy, make a `DELETE` request to the [Delete a policy](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/policies/methods/delete/) endpoint:

<APIRequest
	path="/accounts/{account_id}/data-security/posture/policies/{policy_id}"
	method="DELETE"
/>

</TabItem> <TabItem label="Terraform (v5)">

To update a policy, change its attributes and run `terraform apply`. To turn the policy off, set `enabled = false`.

Changing `finding_type_id` replaces the policy: Terraform deletes the existing policy and creates a new one.

To delete a policy, remove the resource from your configuration and run `terraform apply`, or target the resource for destruction:

```sh
terraform destroy -target=cloudflare_zero_trust_casb_policy.public_files
```

</TabItem> </Tabs>

## Manage existing policies with Terraform

### Import a policy

To bring a policy created in the dashboard or API under Terraform management, import it using your account ID and the policy ID:

```sh
terraform import cloudflare_zero_trust_casb_policy.public_files '<ACCOUNT_ID>/<POLICY_ID>'
```

### Look up policies

Use the [`cloudflare_zero_trust_casb_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/data-sources/zero_trust_casb_policy) data source to read a single policy, or [`cloudflare_zero_trust_casb_policies`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/data-sources/zero_trust_casb_policies) to list all policies in an account:

```tf
data "cloudflare_zero_trust_casb_policy" "public_files" {
  account_id = var.cloudflare_account_id
  policy_id  = "<POLICY_ID>"
}

data "cloudflare_zero_trust_casb_policies" "all" {
  account_id = var.cloudflare_account_id
}
```

## Logs

Every policy produces two categories of logs, available under **Insights** in Cloudflare One:

- **Admin Activity logs** record changes to a policy definition, including who created, edited, or disabled the policy, and when.
- **Cloud & SaaS Security policies logs** record the runtime outcome of each policy invocation, including the finding instance that triggered the policy, the asset acted on, whether the action succeeded or failed, and the error returned by the vendor if it failed (for example, a `401 Unauthorized` response or a rate limit error).

For compliance reporting, the Cloud & SaaS Security policies log ties a specific finding instance to a specific automated action and timestamp.

For more information, refer to [Cloudflare One Logs](/cloudflare-one/insights/logs/).

## Limitations

- Remediation actions in a policy are only available for Microsoft 365 and Google Workspace file and folder finding types.
- CASB cannot remove permissions inherited from a parent resource by remediating the affected child. For vendor-specific behavior and manual remediation steps, refer to [Remediate inherited file permissions](/cloudflare-one/cloud-and-saas-findings/manage-findings/#remediate-inherited-file-permissions).
- A policy only applies to new instances of a finding type detected after the policy is created.

## Troubleshooting

For help diagnosing issues, refer to [CASB troubleshooting](/cloudflare-one/integrations/cloud-and-saas/troubleshooting/casb/).

# Troubleshoot CASB

Use this guide to troubleshoot common issues with Cloud Access Security Broker (CASB).

This guide covers troubleshooting steps for CASB integrations and webhooks. For integration-specific issues, refer to the integration's documentation.

## Integration fails to connect or returns an error

Integration connection problems are the most common issue during CASB setup. If you receive an error such as "There was an error creating the integration" or are redirected back to the dashboard without the integration appearing, follow these steps.

### Check permissions in the third-party application

Ensure the account you are using to authorize the integration has the necessary administrative privileges in the third-party application (for example, **Global Administrator** for Microsoft 365, **Super Admin** for Google Workspace, or **Organization Owner** for GitHub). Insufficient permissions are the leading cause of setup failures.

### Clear previous installations

If the SaaS application was previously integrated with a different Cloudflare account, you must manually revoke the old Cloudflare application from within the SaaS provider's admin console.

- **For Microsoft 365**: Go to **Microsoft 365 admin center** > **Enterprise applications** and delete the existing Cloudflare One application.
- **For Google Workspace**: Go to **Google Admin Console** > **Security** > **Access and data control** > **API controls** and remove the Cloudflare app from third-party app access.
- **For GitHub**: Go to your organization's **Settings** > **Third-party access** and revoke the Cloudflare CASB application.

After cleaning up the old app, wait a few minutes and then try the integration process again from the Cloudflare One dashboard.

### Verify OAuth permissions

During setup, CASB will ask you to approve a set of permissions. The permissions requested are required for the CASB service to scan for misconfigurations and, if you choose, to take remediation actions. While some permissions may seem broad (for example, `write` access), they are necessary for actions like quarantining a file or modifying sharing settings. Refer to the specific integration guide for a detailed list of required permissions.

## Findings are stale or not updating after remediation

A common point of confusion is when a resolved issue (for example, when a file is made private, or when a user is suspended) continues to appear as an active finding in the CASB dashboard.

### Understand scan frequency

CASB integrations do not provide real-time updates. Scans are performed periodically to discover new findings and validate the status of existing ones. The initial scan can take several hours, and subsequent scans run approximately every 24-48 hours.

### Force a re-scan

To trigger a new scan:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Integrations**.
2. Find your integration and select **Configure**.
3. Turn off **Scan for findings**.
4. After a few minutes, turn on **Scan for findings** again.

This action will queue a fresh scan of your integration. Allow several hours for your findings to reflect the new results.

## Remediation action fails in the dashboard

If you attempt to use a one-click remediation action (such as "Make private") on a finding, it may result in a **Failed** status, often with a timeout error.

### Verify permissions

The remediation failure may be due to the permissions for the Cloudflare app being changed or revoked in the SaaS application after the initial setup. Re-validate the integration to ensure all required permissions are still granted.

### Remediate manually

As a workaround, remediate the finding directly within the SaaS application (for example, change the file's sharing settings in Google Drive). CASB will clear the finding from the dashboard after the next successful scan.

## Webhook test or delivery fails

If Cloudflare cannot deliver a test request or a posture finding instance to your destination, follow these steps.

### Check destination requirements

Verify that the destination URL uses `https://` and is publicly reachable. Cloudflare rejects destinations that resolve to localhost, loopback, private, or other reserved addresses.

### Check authentication settings

Ensure that the webhook's authentication method matches what your receiver expects. Re-enter any bearer token, Basic auth credentials, static headers, or signing secret if needed.

### Understand delivery timing

Test delivery sends a test request immediately, but posture finding instance sends are queued in the background. A success message means that Cloudflare accepted the request for delivery.

## CASB is generating false positives

CASB may incorrectly flag items, such as flagging internally-shared files as public or archived Google Workspace users as inactive.

### Review finding details

Carefully examine the evidence provided in the finding. An object's status in the SaaS platform may not be accurate.

### Report the issue

If you confirm the finding is a false positive, report the behavior to Cloudflare Support. Provide the finding ID (visible in the finding's detail view) and as much detail as possible. This helps the Support team refine the detection logic for all customers.

### Hide the finding

While Cloudflare investigates the issue, you can [ignore the finding or hide individual instances](/cloudflare-one/cloud-and-saas-findings/manage-findings/#hide-findings) to remove it from your active list and reduce noise.

# Cloud Workload Security

import { LinkButton } from "~/components";

Cloud Workload Security (CWS) helps you prevent risky cloud changes. Apply policy controls to API requests before they reach your cloud provider.

:::note[Zero Trust subscription required]
Your account must have a [Zero Trust subscription](/cloudflare-one/setup/#2-create-a-zero-trust-organization). The Zero Trust Free plan is sufficient.
:::

Use CWS to:

- Define policies to allow approved operations and block risky requests.
- Inspect request content, including parameters and request-body fields.
- Enforce guardrails across the cloud resources your policies cover.

For example, block requests that would make an R2 bucket public. Allow requests that keep the bucket private.

<LinkButton
	href="/cloudflare-one/cloud-workload-security/get-started/"
	variant="primary"
>
	Get started
</LinkButton>

## How it works

You manage CWS resources in the Cloudflare dashboard. A proxy you run in your environment loads a deployment's configuration and evaluates requests routed through it.

Workloads select requests, policies check them, and policy sets group those checks for a deployment.

To understand how these entities validate requests, refer to [Request Validation](/cloudflare-one/cloud-workload-security/concepts/request-validation/).

## CWS entities

### Workloads

A [workload](/cloudflare-one/cloud-workload-security/guides/configure-policies/#create-a-workload) is a reusable filter that selects a group of requests. For example, select Cloudflare API traffic or requests for production accounts. Include or exclude workloads in a policy set to choose where its policies apply.

### Policies

A [policy](/cloudflare-one/cloud-workload-security/guides/configure-policies/#create-a-policy) combines a request filter with a Rego check. Rego is the language used to write the check. Its Boolean `valid` rule determines whether a request passes. For example, reject a request that would make an R2 bucket public.

### Policy sets

A [policy set](/cloudflare-one/cloud-workload-security/guides/configure-policies/#create-a-policy-set) groups policies and defines their workload scope. Include or exclude workloads to choose which requests it covers. Each policy's own filter still applies.

Turn on **Enforce** for policies that should block failed checks. Attach the policy set to a deployment to apply it.

### Deployments

A [deployment](/cloudflare-one/cloud-workload-security/get-started/#create-a-deployment) is the configuration that a proxy loads. It references policy sets and can include a domain ACL and Basic Auth credentials. Its **Default mode** applies when no policy matches a request. Its **On error action** configures policy error handling.

### Proxies

A [CWS proxy](/cloudflare-one/cloud-workload-security/get-started/#set-up-the-proxy-locally) is the process that enforces a deployment's configuration. Run it in your environment using a deployment ID and Cloudflare API credential. Route client API requests through it to apply your controls. The proxy refreshes its configuration and records request decisions.

### Domain ACLs

A [domain access control list (ACL)](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/#create-a-domain-acl) controls which destinations a deployment can reach. It uses domain-specific allow or block rules and a default action. Allowing a destination does not bypass request policies.

### Basic Auth credentials

A [Basic Auth credential](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/#create-a-basic-auth-credential) identifies a client using the proxy. It has a username and uses password secrets for authentication. Workload and policy filters can match this identity.

These credentials are separate from the proxy's Cloudflare API credential and the client's cloud-provider credentials.

### Credential secrets

A [secret](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/#rotate-a-secret) is a password associated with a Basic Auth credential. A credential can have multiple secrets with optional expiration times. Use overlapping secrets so clients can switch passwords before the old one expires or is removed.

### Rego modules

A [Rego module](/cloudflare-one/cloud-workload-security/guides/policy-reference/#create-a-rego-module) stores reusable policy logic under a `lib.` package. Policies import its rules instead of duplicating the same checks. For example, share a private-bucket check across several policies.

### Rego metadata

[Rego metadata](/cloudflare-one/cloud-workload-security/guides/policy-reference/#create-rego-metadata) stores shared JSON data that policies can read. Use it for settings such as whether buckets must remain private. Update the shared value without rewriting each policy.

### Tenant structures

A [tenant structure](/cloudflare-one/cloud-workload-security/guides/policy-reference/#create-a-tenant-structure) describes the hierarchy of your cloud resources. Amazon Web Services (AWS) structures contain roots, organizational units, and accounts. Cloudflare structures contain organizations, accounts, and zones. Give the structure a package path to let Rego policies read its nodes.

## Target security profiles with workloads

Use workloads to apply different security profiles to different environments. For example, production accounts can require stricter policies than non-production accounts.

Create separate policy sets for production and non-production. Use workload filters to target AWS accounts or organizational units, or Cloudflare accounts or organizations.

Include the matching workload in each policy set. Attach both policy sets to the same deployment.

## Next steps

- [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/) to define workloads and attach policy sets.
- [Manage traffic scope](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/) to configure domain access control lists and Basic Auth credentials.
- [Manage policy reference resources](/cloudflare-one/cloud-workload-security/guides/policy-reference/) to reuse Rego code, metadata, and cloud hierarchies.
- [Proxy configuration](/cloudflare-one/cloud-workload-security/configuration/) to choose authentication, connection, and caching settings.
- [Troubleshooting](/cloudflare-one/cloud-workload-security/troubleshooting/) to diagnose proxy, client, and policy issues.

For availability, proxy deployment options, and connection requirements, refer to [Platform](/cloudflare-one/cloud-workload-security/platform/).

# Concepts

[Request Validation](/cloudflare-one/cloud-workload-security/concepts/request-validation/) explains how workload scope, policy filters, Rego checks, and enforcement settings determine request decisions.

To configure these controls, follow [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/). To connect your first proxy, follow [Get started](/cloudflare-one/cloud-workload-security/get-started/).

# Request Validation

Cloud Workload Security (CWS) uses workloads and policies to validate API requests. Workloads define request scope. Policies define the checks, and policy sets connect scope to checks. A deployment selects the policy sets to apply.

For a definition of each resource, refer to [CWS entities](/cloudflare-one/cloud-workload-security/#cws-entities).

## How the concepts work together

Request validation combines scope, checks, and enforcement settings:

```mermaid
flowchart TD
    accTitle: CWS request validation
    accDescr: A deployment selects policy sets. Workloads define request scope, policy filters select matching requests, and Rego valid rules validate them. Modules, metadata, and tenant structures supply shared logic and data. Enforce settings and deployment defaults determine whether requests are allowed or blocked.

    A["Deployment's policy sets"] --> B["Workload scope<br/>Included and excluded requests"]
    B --> C["Policy filters<br/>Select applicable policies"]
    C -->|Matching policies| D["Rego validation<br/>Each policy's valid rule"]
    C -->|No matching policy| F["Request decision<br/>Enforce and deployment defaults"]
    E["Rego modules, metadata,<br/>and tenant structures"] -.-> D
    D --> F
```

## Select applicable policies

### Deployment and policy sets

The deployment selects the policy sets available for evaluation. Each set groups policies and specifies included and excluded workloads.

### Workload scope

A request must match an included workload when inclusions are configured. It must not match an excluded workload. An empty inclusion list adds no inclusion restriction.

### Policy filter

Within that scope, each policy's filter selects the requests to check. For example, a workload can select Cloudflare API traffic while a policy filter selects one R2 operation. Workload scope and policy filter must both match for the policy to apply.

## Validate with Rego

Each matching policy evaluates a Boolean rule named `valid`. A `true` result passes that policy's check. A `false` result fails it. Use Rego to validate request content, such as whether a change would make a bucket public.

Policies can reuse shared logic and data:

- [Rego modules](/cloudflare-one/cloud-workload-security/guides/policy-reference/#create-a-rego-module) supply reusable checking logic.
- [Rego metadata](/cloudflare-one/cloud-workload-security/guides/policy-reference/#create-rego-metadata) supplies shared JSON values.
- [Tenant structures](/cloudflare-one/cloud-workload-security/guides/policy-reference/#create-a-tenant-structure) describe cloud resource hierarchies. A package path makes their nodes available to Rego.

## Apply enforcement and defaults

Each policy's **Enforce** setting within its policy set determines how its check result affects the request:

| Condition                                               | Effect                                                                   |
| ------------------------------------------------------- | ------------------------------------------------------------------------ |
| A matching policy returns `true`.                       | That check passes. Another enforcing policy can still block the request. |
| A matching policy returns `false` with **Enforce** on.  | Block the request.                                                       |
| A matching policy returns `false` with **Enforce** off. | Record the failed check without blocking on that policy's result.        |
| No policy matches the request.                          | Apply the deployment's **Default mode**.                                 |

More than one policy can match a request. A failed enforcing policy blocks even when another policy passes. A failed Rego check is not an unmatched request, so it does not fall back to **Default mode**.

Configure policy error handling with the deployment's **On error action** setting.

## Example: keep R2 buckets private

Suppose a workload selects Cloudflare API requests. Its policy set contains a policy for changing R2 bucket public access. The policy filter selects that operation. Rego permits it only when `input.http.request.body.enabled` is `false`.

An attempt to turn on public access matches the filter but fails Rego validation. With **Enforce** on, CWS blocks the request. For a runnable guardrail example, follow [Get started](/cloudflare-one/cloud-workload-security/get-started/).

## Related controls

[Domain ACLs](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/#create-a-domain-acl) control which destinations a deployment can reach. Allowing a destination does not bypass workload scope or policy checks.

[Basic Auth credentials](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/#create-a-basic-auth-credential) provide client identities that workload and policy filters can match. The cloud provider still applies its own authentication and permissions.

To configure scope and enforcement, follow [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/). To inspect decisions, refer to [Read proxy logs](/cloudflare-one/cloud-workload-security/troubleshooting/#read-proxy-logs).

# Proxy configuration

Use `config.yaml` to configure the Cloud Workload Security (CWS) proxy process. Start with the complete local configuration in [Get started](/cloudflare-one/cloud-workload-security/get-started/#configure-the-proxy), then replace the relevant blocks with the options in this reference.

Either API authentication mode can be combined with direct or chained outbound connections and any cache mode.

The file requires three top-level objects:

| Object  | Purpose                                                             |
| ------- | ------------------------------------------------------------------- |
| `cws`   | Account, deployment, API authentication, and configuration refresh. |
| `proxy` | Listener, logging, TLS certificates, and outbound proxy settings.   |
| `redis` | Process-local cache limits and optional shared Redis storage.       |

Unknown settings are rejected, including unknown fields within these objects. Examples containing one object are partial configurations. Retain the other required objects from your file.

## Validate a configuration

After editing your configuration, validate it before starting the proxy:

```sh
cws-proxy --config config.yaml --config-test
```

Successful validation prints `CWS proxy configuration is valid`. This checks local settings. It does not fetch the deployment, verify credentials, connect to Redis, or prove the listener can start.

Restart the proxy after changing `config.yaml` or its process environment. Dashboard policy changes are loaded through configuration refresh instead.

## API connection and authentication

The `cws` object connects the proxy to one account and deployment. Its settings are:

| Setting                  | Required or default                        | Description                                                                              |
| ------------------------ | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
| `api_base_url`           | Required                                   | Use `https://api.cloudflare.com/client/v4`.                                              |
| `account_id`             | Required                                   | Account ID containing the deployment.                                                    |
| `deployment_id`          | Required                                   | ID of the deployment whose configuration the proxy loads.                                |
| `api_poll_interval_secs` | `30`                                       | Seconds between deployment configuration refreshes. Must be an integer of at least `30`. |
| `api_token_env`          | Required for API token authentication      | Name of the environment variable containing the API token.                               |
| `api_key_env`            | Required for Global API Key authentication | Name of the environment variable containing the Global API Key.                          |
| `api_key_email`          | Required for Global API Key authentication | Literal account email associated with that key.                                          |

Choose exactly one authentication mode. For API token authentication, omit `api_key_env` and `api_key_email`. For Global API Key authentication, supply both key fields and omit `api_token_env`. Do not set unused authentication fields to `null`.

### API token

Use an API token with the **Cloud Workload Security Deployment** permission for the intended account. For token creation, follow [Create the proxy API token](/cloudflare-one/cloud-workload-security/get-started/#create-the-proxy-api-token).

Replace `<ACCOUNT_ID>` and `<DEPLOYMENT_ID>` with your resource IDs:

```yaml title="API token authentication"
cws:
  api_base_url: "https://api.cloudflare.com/client/v4"
  api_poll_interval_secs: 30
  api_token_env: CWS_DEPLOYMENT_TOKEN
  account_id: "<ACCOUNT_ID>"
  deployment_id: "<DEPLOYMENT_ID>"
```

Supply the token through `CWS_DEPLOYMENT_TOKEN` in the proxy process environment. `api_token_env` stores the variable's name, not its value. This credential loads CWS configuration. Clients still need their own cloud-provider credentials.

### Global API Key

For Global API Key authentication, replace the entire `cws` object with this form. Replace the email and resource placeholders:

```yaml title="Global API Key authentication"
cws:
  api_base_url: "https://api.cloudflare.com/client/v4"
  api_poll_interval_secs: 30
  api_key_env: CWS_GLOBAL_API_KEY
  api_key_email: "<ACCOUNT_EMAIL>"
  account_id: "<ACCOUNT_ID>"
  deployment_id: "<DEPLOYMENT_ID>"
```

Supply the key through `CWS_GLOBAL_API_KEY` in the proxy process environment.

### Environment references

`api_base_url`, `account_id`, and `deployment_id` also accept complete environment references such as `${CWS_ACCOUNT_ID}`. The referenced variable must exist and contain a nonblank value. Embedded substitutions and shell expressions are not supported.

For example, replace the two ID values in your existing `cws` object with:

```yaml title="Account and deployment references"
account_id: "${CWS_ACCOUNT_ID}"
deployment_id: "${CWS_DEPLOYMENT_ID}"
```

Other strings remain literal. For `api_token_env`, `api_key_env`, and Redis `url_env`, use the variable name directly, without `${...}`. Certificate paths and `api_key_email` do not expand environment references.

## Listener and logging

The `proxy` object requires `logging`, `tls_interception`, and `upstream`. Its listener and logging options are:

| Setting                   | Default        | Description                                                                                                               |
| ------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `proxy.listen_addr`       | `0.0.0.0:8080` | IP address and port for the HTTP proxy listener. Hostnames are not accepted. Use brackets for IPv6, such as `[::1]:8080`. |
| `proxy.logging.format`    | `json`         | Log format: `json` or `text`.                                                                                             |
| `proxy.logging.verbosity` | `INFO`         | Log level: `CRITICAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`, or `TRACE`. Values are case-sensitive.                           |

Keep `127.0.0.1:8080` for clients on the same host or network namespace. For a shared proxy, choose an address clients can reach and restrict network access to intended clients. `0.0.0.0` listens on all IPv4 interfaces. It is not a destination address for clients.

An empty `logging: {}` uses JSON at `INFO`. For human-readable logging, replace `proxy.logging` with:

```yaml title="Text logging"
logging:
  format: text
  verbosity: INFO
```

For log events, decision fields, and redaction guidance, refer to [Read proxy logs](/cloudflare-one/cloud-workload-security/troubleshooting/#read-proxy-logs).

## TLS certificates and trust

The `proxy.tls_interception` object requires all three settings:

| Setting          | Value                               | Description                                                                                       |
| ---------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------- |
| `ca_cert`        | Nonblank file path                  | CA certificate used to sign certificates presented to clients.                                    |
| `ca_private_key` | Nonblank file path                  | Matching CA private key. Keep this key on the proxy host.                                         |
| `extra_root_cas` | List of unique, nonblank file paths | Additional trusted roots for the proxy's outbound TLS connections. Use `[]` when none are needed. |

Clients trust `ca_cert`, not the private key. For CA creation and client setup, follow [Prepare a certificate authority](/cloudflare-one/cloud-workload-security/get-started/#prepare-a-certificate-authority).

Paths are resolved from the proxy process's working directory, not the configuration file's directory. Use absolute paths when those directories differ.

If your network inspects outbound TLS, add its approved CA certificate to `extra_root_cas`. Each file supplies only its first certificate, so use a separate PEM file for each additional root. This setting does not configure client trust or the CA used to sign intercepted certificates.

## Outbound proxy chaining

Use `proxy.upstream.proxy` when CWS must reach destinations through another HTTP proxy. These settings belong to the CWS proxy process. They are separate from the client's `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables.

The outbound proxy settings are:

| Setting       | Default | Description                                                                                                                             |
| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `http_proxy`  | `null`  | Upstream proxy URL for destination port `80`.                                                                                           |
| `https_proxy` | `null`  | Upstream proxy URL for destination port `443`.                                                                                          |
| `ca_cert`     | `null`  | Additional CA certificate file trusted when connecting to an `https://` upstream proxy. Only the first certificate in the file is read. |
| `no_proxy`    | `null`  | Unique destination rules that connect directly instead of using the upstream proxy. An empty list also means no exceptions.             |

Both proxy URL fields accept `http://` or `https://`. The field chooses the destination traffic. The URL scheme chooses whether CWS uses TLS to connect to the upstream proxy. For example, `https_proxy: "http://proxy.example.com:8080"` routes port `443` through an HTTP proxy.

Configure each traffic type independently. Setting only `http_proxy` does not route port `443` through it. Omit a field or use `null` for direct connections of that type. `proxy.upstream.proxy: {}` uses direct connections for both types.

Proxy URLs can include Basic Auth credentials. Supply such configuration through your secret-management process. Do not confuse these credentials with the deployment API credential or client identity.

### Chain both traffic types

Replace `proxy.upstream` with this block, substituting your upstream proxy URL and CA path:

```yaml title="Upstream proxy with direct exceptions"
upstream:
  proxy:
    http_proxy: "https://proxy.example.com:8443"
    https_proxy: "https://proxy.example.com:8443"
    ca_cert: /path/to/upstream-proxy-ca.pem
    no_proxy:
      - api.example.com
      - .internal.example.com
      - 192.0.2.10
      - 2001:db8::/32
```

`ca_cert` establishes trust in the upstream proxy. `tls_interception.extra_root_cas` establishes additional trust for outbound destination certificates. These serve different connections.

### Direct-connection rules

`no_proxy` supports these rule types:

| Rule                              | Matches                                     |
| --------------------------------- | ------------------------------------------- |
| `api.example.com`                 | That hostname only.                         |
| `.example.com`                    | The parent domain and its subdomains.       |
| `192.0.2.10` or `2001:db8::10`    | That IP address.                            |
| `192.0.2.0/24` or `2001:db8::/32` | IP-address destinations in that CIDR range. |

Hostname comparisons ignore case. Entries are destination names or addresses, not URLs or `host:port` pairs. Use hostname rules for named destinations rather than assuming a CIDR rule resolves their DNS names.

An exception bypasses the additional upstream proxy, not CWS policy evaluation. Domain ACLs and applicable request policies still apply.

## Cache and Redis

CWS always has a bounded process-local memory cache. Redis adds shared storage. Without Redis, cached values are local to that process and are lost when it exits.

Choose the connection mode that matches your Redis service:

| Mode                            | `enabled` | `cluster_mode` | `auth_type` | Additional required settings                     |
| ------------------------------- | --------- | -------------- | ----------- | ------------------------------------------------ |
| Memory only                     | `false`   | Not needed     | Not needed  | None.                                            |
| Standalone Redis                | `true`    | `false`        | `basic`     | `url_env`.                                       |
| Redis Cluster                   | `true`    | `true`         | `basic`     | `url_env`.                                       |
| Standalone ElastiCache with IAM | `true`    | `false`        | `iam`       | `url_env`, `iam_cluster_name`, and `iam_region`. |
| ElastiCache cluster with IAM    | `true`    | `true`         | `iam`       | `url_env`, `iam_cluster_name`, and `iam_region`. |

### Memory only

This is the configuration used in Get started:

```yaml title="Memory-only cache"
redis:
  enabled: false
```

Redis connection and authentication settings are unused in this mode. The memory-cache limits still apply.

### Redis with URL credentials

Replace the `redis` object with:

```yaml title="Standalone Redis"
redis:
  enabled: true
  url_env: CWS_REDIS_URL
  cluster_mode: false
  auth_type: basic
```

Inject the Redis connection URL into the proxy environment variable named `CWS_REDIS_URL`. The URL uses `redis://` or `rediss://` and includes the Redis username and password when required. `rediss://` uses TLS.

For Redis Cluster, set `cluster_mode: true`. The environment variable can contain one seed URL or comma-separated seed URLs. Each seed needs a complete URL, and all seeds must use the same scheme.

### ElastiCache IAM

Replace the `redis` object with this form. Substitute your replication-group name and AWS region:

```yaml title="ElastiCache IAM authentication"
redis:
  enabled: true
  url_env: CWS_REDIS_URL
  cluster_mode: false
  auth_type: iam
  iam_cluster_name: "<REPLICATION_GROUP_NAME>"
  iam_region: "<AWS_REGION>"
```

Set `cluster_mode: true` for an ElastiCache cluster. The connection URL must use `rediss://`, include the ElastiCache username, and omit a password. All seed URLs must use the same username. The proxy obtains AWS credentials through the default AWS credential provider chain and generates IAM authentication tokens. The proxy's AWS identity needs permission to connect to that ElastiCache user and replication group.

When Redis is enabled, startup requires a successful Redis connection. A missing connection variable, invalid authentication, or exhausted connection retries prevents startup. Local `--config-test` validation does not test that connection.

### Redis settings reference

The `redis` settings and defaults are:

| Setting                    | Default               | Description                                                                                                                              |
| -------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`                  | Required              | `false` for process-local memory only; `true` adds Redis storage.                                                                        |
| `url_env`                  | `null`                | Environment variable name containing the Redis URL. Required and nonblank when enabled.                                                  |
| `cluster_mode`             | `false` when disabled | Required when enabled. Use `true` for Redis Cluster and `false` for standalone Redis.                                                    |
| `auth_type`                | `basic`               | `basic` for credentials from the URL, or `iam` for ElastiCache IAM.                                                                      |
| `iam_cluster_name`         | `null`                | ElastiCache replication-group name. Required and nonblank for enabled IAM authentication.                                                |
| `iam_region`               | `null`                | AWS region used to sign IAM tokens. Required and nonblank for enabled IAM authentication.                                                |
| `max_retries`              | `3`                   | Additional connection or command attempts after the first attempt. Integer from `0` through `5`.                                         |
| `backoff_secs`             | `1`                   | Whole seconds between connection or command attempts. Integer from `0` through `5`.                                                      |
| `namespace`                | `cws`                 | Prefix for proxy-internal cache keys. Must be nonblank, omit the trailing `:`, and not equal `cws_data` or start with `cws_data:`.       |
| `cache_item_size`          | `0`                   | Maximum bytes read from Redis for a non-internal key. `0` removes this read limit. Does not limit process-local or proxy-internal reads. |
| `memory_cache_max_entries` | `10000`               | Maximum process-local cache entries. Must be a positive integer.                                                                         |
| `memory_cache_max_bytes`   | `67108864`            | Maximum combined process-local key and value bytes, equivalent to 64 MiB. Must be a positive integer.                                    |

Both memory limits apply whether Redis is enabled or disabled. A zero `cache_item_size` does not remove those limits.

The proxy appends `:` to the internal namespace. Policy cache keys use a separate account-and-deployment-scoped namespace, `cws_data:<ACCOUNT_ID>:<DEPLOYMENT_ID>:`. Changing `redis.namespace` does not replace that policy-key isolation.

# Get started

import { DashButton, Steps, TabItem, Tabs } from "~/components";

Run a Cloud Workload Security (CWS) proxy locally and block requests that make an R2 bucket public. This walkthrough uses an empty test bucket in your Cloudflare account.

The proxy and `cf` client run on the same Linux machine. For pipeline runners, application hosts, and shared proxies, refer to [Proxy deployment options](/cloudflare-one/cloud-workload-security/platform/#proxy-deployment-options).

## Prerequisites

:::note[Zero Trust subscription required]
Your account must have a [Zero Trust subscription](/cloudflare-one/setup/#2-create-a-zero-trust-organization). The Zero Trust Free plan is sufficient.
:::

- A Linux machine or virtual machine with APT or DNF, `curl`, and `sudo` access.
- A Cloudflare account with access to manage CWS resources and create account API tokens.
- R2 access in the same account and permission to create and configure a test bucket.
- The [Cloudflare CLI installed and signed in](/cf/get-started/). Use Node.js 24.5 or later for the client proxy settings in this guide.
- OpenSSL and a terminal on the proxy host.

## Set up the proxy locally

<Steps>
1. ### Install the proxy

   Install `cws-proxy` using your Linux distribution's package manager:

   <Tabs>
     <TabItem label="APT (deb package)">
       ```sh
       sudo curl -fsSLo /usr/share/keyrings/cloudflare-cws.gpg \
         https://downloads.cac.cloudflare.com/packages/keys/cws-package-signing.gpg

       sudo curl -fsSLo /etc/apt/sources.list.d/cws-proxy.sources \
         https://downloads.cac.cloudflare.com/packages/cws-proxy.sources

       sudo apt update
       sudo apt install cws-proxy
       ```
     </TabItem>
     <TabItem label="DNF (rpm package)">
       ```sh
       sudo rpm --import \
         https://downloads.cac.cloudflare.com/packages/keys/cws-package-signing.asc

       sudo curl -fsSLo /etc/yum.repos.d/cws-proxy.repo \
         https://downloads.cac.cloudflare.com/packages/cws-proxy.repo

       sudo dnf install cws-proxy
       ```
     </TabItem>
   </Tabs>

   Confirm that the installed command is available:

   ```sh
   cws-proxy --help
   ```

   For the proxy's Beta terms, license notices, and acknowledgments, refer to [Legal](/cloudflare-one/cloud-workload-security/legal/).

2. ### Create the guardrail policy

   In the Cloudflare dashboard, select your account and go to **Cloud Workload Security** > **Policies**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/policies" />

   Select **Add policy** and name it `restrict-r2-public-access`. In **Filters**, select **Wirefilter** and enter:

   ```txt
   application.name == "Cloudflare" and application.service == "r2" and application.operation == "r2-put-bucket-public-policy"
   ```

   In **Rego source**, enter this policy. It permits the selected operation only when the request turns off public access:

   ```txt title="Rego source"
   package policy.cf_r2_no_public_access

   default valid := false

   valid if {
     input.http.request.body.enabled == false
   }
   ```

   Select **Add policy** to save it.

3. ### Create a policy set

   Go to **Cloud Workload Security** > **Policy sets**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/policy-sets" />

   Select **Add policy set** and name it `r2-private-buckets`. In **Policies**, select **Attach policy**, choose `restrict-r2-public-access`, and select **Attach policies**.

   Turn on **Enforce** for the policy. Leave **Workloads** empty for this walkthrough, then select **Add policy set**.

4. ### Create a deployment

   Go to **Cloud Workload Security** > **Deployments**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/deployments" />

   Select **Add deployment** and name it `cws-r2-test`. In **Policy sets**, select **Attach policy set**, choose `r2-private-buckets`, and select **Attach policy sets**.

   In **Deployment settings**, choose *Default allow* for **Default mode**. This permits requests outside the guardrail's filter. Select **Add deployment** to save it.

   Open the deployment and copy its **ID**. Also copy your [Cloudflare Account ID](/fundamentals/account/find-account-and-zone-ids/).

5. ### Create the proxy API token

   In the same account, go to **Manage account** > **Account API tokens**.

   <DashButton url="/?to=/:account/api-tokens" />

   Create a custom account token with the **Cloud Workload Security Deployment** permission. Save the token for the proxy configuration.

   This token loads CWS configuration. Your signed-in `cf` client uses its own credentials for R2 requests.

6. ### Prepare a certificate authority

   The proxy needs a certificate authority (CA) certificate and its matching private key. Clients need to trust the CA certificate.

   To create a local test CA, save this configuration as `openssl.cnf`. If you already have a CA pair, use those files instead.

   ```ini title="openssl.cnf"
   [req]
   prompt = no
   distinguished_name = subject
   x509_extensions = v3_ca

   [subject]
   O = Example Inc
   CN = CWS Local Proxy CA

   [v3_ca]
   basicConstraints = critical,CA:true
   keyUsage = critical,keyCertSign,cRLSign
   subjectKeyIdentifier = hash
   authorityKeyIdentifier = keyid:always,issuer
   ```

   Generate the certificate and private key:

   ```sh
   umask 077
   openssl genrsa -out ca-key.pem 4096
   openssl req -config openssl.cnf -key ca-key.pem -new -x509 -days 730 -sha256 -out ca-cert.pem
   ```

   Keep `ca-key.pem` on the proxy host. Configure clients with `ca-cert.pem` only.

7. ### Configure the proxy

   Save this configuration as `config.yaml`. Replace `<ACCOUNT_ID>` and `<DEPLOYMENT_ID>` with the values you copied. Use absolute certificate paths if the files are outside your working directory.

   ```yaml title="config.yaml"
   cws:
     api_base_url: "https://api.cloudflare.com/client/v4"
     api_poll_interval_secs: 30
     api_token_env: "CWS_DEPLOYMENT_TOKEN"
     account_id: "<ACCOUNT_ID>"
     deployment_id: "<DEPLOYMENT_ID>"

   proxy:
     logging:
       format: json
       verbosity: INFO
     listen_addr: "127.0.0.1:8080"
     tls_interception:
       ca_cert: ./ca-cert.pem
       ca_private_key: ./ca-key.pem
       extra_root_cas: []
     upstream:
       proxy:
         http_proxy: null
         https_proxy: null
         ca_cert: null
         no_proxy: null

   redis:
     enabled: false
   ```

   `api_token_env` contains an environment variable name, not the token value. This local example binds the proxy to loopback and does not require a Redis server.

   For other authentication modes, upstream proxies, logging, and Redis options, refer to [Proxy configuration](/cloudflare-one/cloud-workload-security/configuration/).

8. ### Validate and start the proxy

   In the terminal that runs the proxy, set your token and validate the file:

   ```sh
   export CWS_DEPLOYMENT_TOKEN="<DEPLOYMENT_API_TOKEN>"
   cws-proxy --config config.yaml --config-test
   ```

   Successful validation prints `CWS proxy configuration is valid`. It checks the local configuration, not API access.

   Start the proxy in that terminal:

   ```sh
   cws-proxy --config config.yaml
   ```

   Keep the terminal open. Wait for `remote_config_loaded` and `action_mapper_loaded` in the logs before sending requests. Do not point this terminal's `HTTP_PROXY` or `HTTPS_PROXY` variables at the local proxy.

9. ### Route the CLI through the proxy

   Open a second terminal for the client. Replace the certificate path and Account ID, then set:

   ```sh
   export HTTP_PROXY="http://127.0.0.1:8080"
   export HTTPS_PROXY="http://127.0.0.1:8080"
   export NODE_USE_ENV_PROXY=1
   export NODE_EXTRA_CA_CERTS="<ABSOLUTE_PATH_TO_CA_CERT_PEM>"
   export CLOUDFLARE_ACCOUNT_ID="<ACCOUNT_ID>"
   ```

   `NODE_EXTRA_CA_CERTS` points to the CA certificate, not its private key. These variables apply to the `cf` client in this terminal. Make sure `NO_PROXY` and `no_proxy` do not bypass `api.cloudflare.com`.

10. ### Verify the guardrail

    In the client terminal, create an empty test bucket and inspect managed public access:

    ```sh
    cf r2 buckets create --name cws-r2-guardrails-demo
    cf r2 buckets domains managed list --bucket-name cws-r2-guardrails-demo
    ```

    The managed-domain result should contain `"enabled": false`. Use a different bucket name if this name already exists in your account.

    First, confirm the policy permits turning off public access:

    ```sh
    cf r2 buckets domains managed update cws-r2-guardrails-demo --enabled false
    ```

    This request should succeed. Then attempt to turn on public access:

    ```sh
    cf r2 buckets domains managed update cws-r2-guardrails-demo --enabled true
    ```

    The request should fail with HTTP status `403` and a `CWS.Error` response. Check the managed-domain result again to confirm public access remains off.

    If the request succeeds, immediately turn off public access with `--enabled false`. Check that the CLI uses the proxy, the deployment contains your policy set, and **Enforce** is on. For further checks, refer to [Requests expected to be blocked succeed](/cloudflare-one/cloud-workload-security/troubleshooting/#requests-expected-to-be-blocked-succeed).

11. ### Remove the test bucket

    In the client terminal, delete the empty bucket:

    ```sh
    cf r2 buckets delete cws-r2-guardrails-demo
    ```

    Confirm the deletion when prompted.
</Steps>

## Next steps

To apply guardrails outside this local walkthrough, review [Proxy deployment options](/cloudflare-one/cloud-workload-security/platform/#proxy-deployment-options).

To target specific request groups or add more policies, follow [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/). For destination controls, follow [Manage traffic scope](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/).

For startup, certificate, or unexpected request results, refer to [Troubleshooting](/cloudflare-one/cloud-workload-security/troubleshooting/).

# Configure policies

import { DashButton, Steps } from "~/components";

Use workloads to select requests and policies to check them. Group policies in a policy set, then attach that set to a deployment.

You need access to manage Cloud Workload Security in your account. Before attaching a policy set, [create a deployment](/cloudflare-one/cloud-workload-security/get-started/#create-a-deployment). To connect a proxy, follow [Get started](/cloudflare-one/cloud-workload-security/get-started/).

## Create a workload

A workload is a reusable request filter. For example, you can group requests to the Cloudflare API.

Use separate workloads to [target security profiles for production and non-production environments](/cloudflare-one/cloud-workload-security/#target-security-profiles-with-workloads). Include each workload in the policy set for that environment.

<Steps>
1. In the Cloudflare dashboard, select your account and go to **Cloud Workload Security** > **Workloads**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/workloads" />

2. Select **Add workload** and enter a **Workload name**.

3. In **Filters**, select **Wirefilter** and enter a filter. This example matches Cloudflare API requests:

   ```txt
   application.name == "Cloudflare"
   ```

   You can also use **Visual Builder** to select fields, operators, and values. Select an application before selecting its service or operation.

4. Select **Add workload** to save the workload.
</Steps>

## Create a policy

Both **Filters** and **Rego source** are required.

A policy filter selects the requests that run its Rego check. The Rego source must declare a package under `policy.` and a Boolean rule named `valid`.

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Policies**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/policies" />

2. Select **Add policy** and enter a **Policy name**.

3. In **Filters**, select **Wirefilter** and enter the request scope. This example selects R2 API requests:

   ```txt
   application.name == "Cloudflare" and application.service == "r2"
   ```

4. In **Rego source**, enter the check. This example permits only `GET` requests within the selected scope:

   ```txt title="Rego source"
   package policy.r2_read_only

   default valid := false

   valid if {
     input.http.request.method == "GET"
   }
   ```

   Use `input.http.request.body` to inspect parsed request content. For a request-body example, refer to the [R2 public-access guardrail](/cloudflare-one/cloud-workload-security/get-started/#create-the-guardrail-policy).

5. Select **Add policy** to save the policy. Correct any filter or Rego errors before continuing.
</Steps>

## Create a policy set

A policy set groups policies and their workload scope. A request must match an included workload and must not match an excluded workload. Without included workloads, the set has no inclusion restriction.

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Policy sets**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/policy-sets" />

2. Select **Add policy set** and enter a **Policy set name**.

3. In **Workloads**, select **Attach workload**, choose the workloads to include, and confirm the selection. To omit a group of requests, select **Exclude workload**.

4. In **Policies**, select **Attach policy**, choose the policies, and select **Attach policies**.

5. For each policy, turn on **Enforce** to block requests that fail its check. Turn it off to evaluate the policy without blocking on its result.

6. Select **Add policy set** to save the set.
</Steps>

The policy filter still applies within the policy set's workload scope. A workload match does not bypass a policy filter.

## Attach the set to a deployment

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Deployments** and open the deployment.

   <DashButton url="/?to=/:account/one/cloud-workload-security/deployments" />

2. In **Policy sets**, select **Attach policy set**, choose the set, and select **Attach policy sets**.

3. In **Deployment settings**, choose a **Default mode**. *Default allow* permits requests outside every policy's request scope. *Default block* blocks those requests.

4. Select **Save**. Allow a configuration refresh interval before testing through the proxy.
</Steps>

### Default mode and enforcement

A policy matches when its policy set's workload scope and its filter both match. The deployment default mode applies when no policy matches. A `false` Rego result is a failed check, not an unmatched request.

For example, a matching policy with **Enforce** off does not block a `false` result, even with _Default block_. Another enforcing policy can still block the request.

Send one request that passes your policy and one that fails it. Confirm the failing request returns a CWS block response when **Enforce** is on.

To change an existing resource, open it from its list and select **Save** after editing. Changes to a policy or policy set affect deployments that reference it.

# Guides

Use these guides to configure Cloud Workload Security resources:

- [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/): define workloads, write policies, and attach policy sets to deployments.
- [Manage traffic scope](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/): configure domain access control lists and manage Basic Auth secrets.
- [Manage policy reference resources](/cloudflare-one/cloud-workload-security/guides/policy-reference/): create reusable Rego modules, metadata, and tenant structures.

To understand how CWS validates requests, refer to [Request Validation](/cloudflare-one/cloud-workload-security/concepts/request-validation/). To connect your first proxy and test a policy, follow [Get started](/cloudflare-one/cloud-workload-security/get-started/).

# Manage traffic scope

import { DashButton, Steps } from "~/components";

Domain access control lists (ACLs) control which destinations a deployment can reach. Use the Basic Auth page to manage credentials and their secrets. To understand how these controls relate to request policies, refer to [Request Validation](/cloudflare-one/cloud-workload-security/concepts/request-validation/).

You need access to manage Cloud Workload Security in your account.

## Create a domain ACL

A domain ACL contains domain-specific actions and a default action. The default action applies when no domain entry matches.

<Steps>
1. In the Cloudflare dashboard, select your account and go to **Cloud Workload Security** > **Traffic scope** > **Domain ACLs**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/domain-acls" />

2. Select **Add a domain ACL** and enter a **Name**.

3. Choose **Allow** or **Block** for **Default action**.

4. In **Domains**, select **Add domain**. Enter a **Domain** and choose its **Action**. For example, block `api.example.com` with a default action of **Allow**.

5. Select **Add a domain ACL** to save the list.
</Steps>

Use `example.com` for an exact domain match. Use `.example.com` to match the parent domain and its subdomains.

## Attach a domain ACL

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Deployments** and open the deployment.

   <DashButton url="/?to=/:account/one/cloud-workload-security/deployments" />

2. In **Domain ACL**, select from the list and select **Save**.

3. After the proxy refreshes its configuration, test an allowed destination and a blocked destination through it.
</Steps>

An ACL allow decision permits the connection. CWS policies can still block API requests on that connection.

To detach the list, select **None** in **Domain ACL** and select **Save**. Detach an ACL from its deployments before deleting it.

## Create a Basic Auth credential

Basic Auth credentials are separate from the Cloudflare API token used in your proxy configuration.

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Traffic scope** > **Credentials** > **Basic Auth**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/credentials/basic-auth" buttonName="Basic Auth credentials" />

2. Select **Create credential** and enter a **Username**. Use printable characters without spaces or colons.

3. In **Secrets**, select **Add a secret**. Enter **Password** and **Confirm password**. Optionally, set an **Expiration**.

4. In the dialog, select **Add a secret**, then select **Create credential** to save the credential and its initial secret.
</Steps>

The username is read-only after creation. Open the credential to manage its secrets.

## Rotate a secret

Use this procedure for a credential your clients already use.

<Steps>
1. In **Basic Auth**, open the credential.

2. In **Secrets**, select **Add a secret**. Enter and confirm the new password, set an optional expiration, and select **Add a secret**.

3. Allow the deployment configuration to refresh before updating clients to use the new password. Verify requests from those clients before deleting the old secret.

4. For the old secret, select **Delete** and confirm the deletion.
</Steps>

The secret list displays creation and expiration information. It does not display stored passwords.

# Manage policy reference resources

import { DashButton, Steps } from "~/components";

Use **Policy reference** resources to share code and data between policies. You need access to manage Cloud Workload Security in your account.

## Create a Rego module

Rego modules hold reusable library code under `lib.`. Write each policy's `policy.` package and `valid` rule in the policy editor.

<Steps>
1. In the Cloudflare dashboard, select your account and go to **Cloud Workload Security** > **Policy reference** > **Rego modules**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/rego-modules" />

2. Select **Add Rego module** and enter a **Module name**.

3. In **Rego source**, enter library code. This example checks whether a request turns off public access:

   ```txt title="Library module"
   package lib.bucket_rules

   private if {
     input.http.request.body.enabled == false
   }
   ```

   **Package path** is derived from the source and is read-only.

4. Select **Add Rego module** to save the module.
</Steps>

To call the library from a policy, import it and reference its rule:

```txt title="Policy source"
package policy.private_bucket

import data.lib.bucket_rules

default valid := false

valid if {
  bucket_rules.private
}
```

The policy also requires a filter to select the intended operation. Refer to [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/).

## Create Rego metadata

Metadata stores reusable JSON values. An import path such as `metadata.bucket_rules` exposes the value as `data.metadata.bucket_rules` in Rego.

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Policy reference** > **Rego metadata**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/rego-metadata" />

2. Select **Add Rego metadata**. Enter a **Name** and an **Import path** in the format `metadata.<NAME>`.

3. In **Data**, enter valid JSON. For example:

   ```json
   {
     "require_private_buckets": true
   }
   ```

4. Select **Add Rego metadata** to save the value.
</Steps>

For this example, a policy can read `data.metadata.bucket_rules.require_private_buckets`. Update the metadata to change the shared value without editing each policy.

## Create a tenant structure

Tenant structures describe an AWS or Cloudflare resource hierarchy. An optional **Package path** exposes the hierarchy to Rego, such as `data.tenant.aws.production`.

<Steps>
1. In the Cloudflare dashboard, go to **Cloud Workload Security** > **Policy reference** > **Tenant structures**.

   <DashButton url="/?to=/:account/one/cloud-workload-security/tenant-structures" />

2. Select **Add tenant structure**. Enter a **Name** and select **AWS** or **Cloudflare** for **Application**.

3. (Optional) Enter a **Package path** using `tenant.aws.<NAME>` or `tenant.cloudflare.<NAME>`, matching your selected application.

4. In **Nodes**, select **Import nodes** and choose a `.json` file. Review the imported nodes in **Table view** or **JSON view**.

5. Select **Add tenant structure** to save the structure and nodes.
</Steps>

### Node import format

Use a JSON object keyed by each node's native resource ID. Each value contains `node_kind`, an optional `name`, and `parent` for a child node. Do not add a `native_id` field inside the value.

This AWS example places an account under a root. Replace the example IDs with your resource IDs:

```json title="nodes.json"
{
	"r-example": {
		"node_kind": "root",
		"name": "Example organization"
	},
	"123456789012": {
		"node_kind": "account",
		"name": "Production account",
		"parent": "r-example"
	}
}
```

AWS supports `root`, `ou`, and `account` nodes. Cloudflare supports `org`, `account`, and `zone` nodes. Native IDs must be unique, and each parent must exist in the structure.

### Edit nodes

Open an existing tenant structure to add, edit, or delete nodes in **Table view**. Select **Save** to apply the changes. **JSON view** is read-only.

Policies read mounted nodes by native ID. For example, `data.tenant.aws.production.nodes["123456789012"]` returns the example account node.

# Legal

This software is made available as a "Beta Service" under the agreement applicable to your Cloudflare account. By downloading, installing, or using this software, you agree that your use is governed by that agreement. Depending on your subscription type, the applicable agreement is either the Cloudflare Self-Serve Terms of Service (https://www.cloudflare.com/terms/), the Cloudflare Enterprise Terms (https://www.cloudflare.com/enterpriseterms/), or a separately negotiated written agreement between you and Cloudflare. Cloudflare processes personal information as described in its Privacy Policy (https://www.cloudflare.com/privacypolicy/). If you do not agree to the applicable terms, do not download, install, or use this software.

## Third-party licenses

Review the [Third-party licenses](/cloudflare-one/cloud-workload-security/legal/third-party-licenses/) for the Cloud Workload Security proxy.

# Third-party licenses

import { LinkButton } from "~/components";

The Cloud Workload Security (CWS) proxy includes third-party Rust and Go dependencies. These notices cover the Linux ARM64 build with default Cargo features.

## License notices

The full notice file contains license text, copyright notices, and attributions. [View the third-party license notices](/cloudflare-one/cloud-workload-security/legal/third-party-licenses.txt) or download the file.

<LinkButton
	href="/cloudflare-one/cloud-workload-security/legal/third-party-licenses.txt"
	download="THIRD_PARTY_LICENSES.txt"
	variant="secondary"
>
	Download license notices
</LinkButton>

## Acknowledgments

> This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)

> This product includes cryptographic software written by Eric Young (eay@cryptsoft.com)

> This product includes software written by Tim Hudson (tjh@cryptsoft.com)

# Platform

Cloud Workload Security (CWS) is available in Beta. Manage CWS resources in the Cloudflare dashboard after selecting your account.

Your account must have a Zero Trust subscription. The Zero Trust Free plan is sufficient.

You need permission to manage the resources you change. The proxy also needs a Cloudflare API credential for the same account. Refer to [API connection and authentication](/cloudflare-one/cloud-workload-security/configuration/#api-connection-and-authentication) for API token and Global API Key setup.

## Limits

CWS applies these standard resource and relationship limits.

### Entity quotas

Quotas apply per account unless otherwise indicated:

| Entity | Standard limit |
|---|---:|
| Deployments | 5 |
| Policy sets | 10 |
| Workloads | 10 |
| Domain ACLs | 10 |
| Basic Auth credentials | 10 |
| Policies | 50 |
| Rego modules — shared libraries | 10 |
| Rego metadata documents | 10 |
| Tenant structures | 5 |
| Tenant nodes per structure | 100 |

### Per-entity and relationship limits

The following limits apply to individual resources and relationships:

| Constraint | Standard limit |
|---|---:|
| Policy sets attached to a deployment | 10 |
| Basic Auth credentials attached to a deployment | 10 |
| Policies in a policy set | 50 |
| Included workloads in a policy set | 10 |
| Excluded workloads in a policy set | 10 |
| Entries in a domain ACL | 100 |
| Stored secrets per Basic Auth credential, including expired secrets | 10 |
| Tags per deployment, policy, policy set, or workload | 10 |
| Domain ACLs attached to a deployment | 1 |
| Roots in a tenant structure | 1 |

## Proxy deployment options

You run the CWS proxy in your environment. Choose a location that your cloud API clients can reach, and install the `cws-proxy` package using APT or DNF.

### Local machine

Run the proxy and client on the same Linux machine or Linux virtual machine (VM) to test a guardrail. Follow [Set up the proxy locally](/cloudflare-one/cloud-workload-security/get-started/#set-up-the-proxy-locally) for the empty-R2-bucket walkthrough.

### CI/CD runner

Run the proxy alongside jobs in a continuous integration and continuous delivery (CI/CD) pipeline. This applies guardrails to cloud API requests made by applications and deployment scripts.

For this pattern, the runner needs:

- A proxy process that is ready before jobs make cloud API requests. After starting the proxy, check the proxy logs for `remote_config_loaded` and `action_mapper_loaded` to confirm startup.
- The API credential supplied through the CI secret store, and a certificate authority (CA) certificate and private key available to the proxy process.
- Client tools configured to use the proxy endpoint and trust its CA certificate. Apply client proxy settings to the cloud-request steps, separately from the proxy process.

Manage proxy startup and cleanup through your runner's process or service lifecycle. For the `cf` client settings, refer to [Route the CLI through the proxy](/cloudflare-one/cloud-workload-security/get-started/#route-the-cli-through-the-proxy).

### Application host or container

Run the installed proxy as a managed process on a Linux VM or application host. For containers, use a Linux environment with APT or DNF and provide the proxy's configuration, credentials, and CA files.

Configure your application to use the proxy through its supported proxy and CA trust settings. Keep the proxy running while the application makes cloud API requests.

### Shared proxy host

Run a proxy on a host reachable by multiple applications or pipeline jobs. Set `proxy.listen_addr` to a private IP address and port on that host, then configure clients with the corresponding proxy URL.

Restrict network access to the intended clients. Each client needs the CA certificate for trust. Keep the matching private key on the proxy host.

For package installation, refer to [Get started](/cloudflare-one/cloud-workload-security/get-started/). For listener, authentication, upstream proxy, and cache settings, refer to [Proxy configuration](/cloudflare-one/cloud-workload-security/configuration/).

## Traffic requirements

Policies evaluate API requests routed through your CWS proxy. Creating a deployment does not route application traffic automatically.

Clients must use the proxy and trust its CA certificate. Keep the matching private key on the proxy host.

Clients on other hosts or in separate containers need a reachable proxy address. A loopback address such as `127.0.0.1` only reaches processes in the same network namespace.

Domain ACLs control destination access separately from request policies. An allowed destination does not bypass policy checks.

## Configuration refresh

The proxy refreshes its deployment configuration at the interval configured in `cws.api_poll_interval_secs`. The minimum interval is `30` seconds.

After saving policy, policy set, or deployment changes, allow a refresh interval before verifying their effect. Restart the proxy after changing its local configuration file.

If changes do not affect requests, follow [Saved changes do not affect requests](/cloudflare-one/cloud-workload-security/troubleshooting/#saved-changes-do-not-affect-requests).

## Request authentication

Cloud provider authentication still applies to requests sent through the proxy. The proxy uses its Cloudflare API credential to load deployment configuration. Clients still need their own cloud provider credentials.

To identify the source of a block response, follow [Requests receive an unexpected 403](/cloudflare-one/cloud-workload-security/troubleshooting/#requests-receive-an-unexpected-403).

## Licenses

For the proxy's Beta terms, license notices, and acknowledgments, refer to [Legal](/cloudflare-one/cloud-workload-security/legal/).

# Troubleshooting

import { Steps } from "~/components";

Diagnose Cloud Workload Security (CWS) proxy, client, and policy issues. This guide covers the proxy setup in [Get started](/cloudflare-one/cloud-workload-security/get-started/).

:::note
For questions about the Open Beta, contact [cloud-workload-security-open-beta@cloudflare.com](mailto:cloud-workload-security-open-beta@cloudflare.com).
:::

Use the sections to identify your symptom and verify the fix. Read the logs from the terminal or service running the proxy. For the relevant fields, refer to [Read proxy logs](/cloudflare-one/cloud-workload-security/troubleshooting/#read-proxy-logs).

## Proxy does not start

### Symptoms

The proxy exits before accepting connections. The terminal reports a configuration, file, or listener error.

### Possible causes and solutions

#### Invalid local configuration

Validate the configuration file without starting the proxy:

```sh
cws-proxy --config config.yaml --config-test
```

Successful validation prints:

```txt output
CWS proxy configuration is valid
```

Correct the reported YAML or schema error, then validate again. Use the `cws`, `proxy`, and `redis` settings from [Proxy configuration](/cloudflare-one/cloud-workload-security/configuration/). Unknown settings are rejected.

This check validates local configuration. It does not verify the API token, fetch the deployment, or prove the listener can start.

#### Unavailable certificate files or listener address

The proxy needs a certificate authority (CA) certificate and its matching private key. Confirm that `proxy.tls_interception.ca_cert` and `ca_private_key` point to files the proxy process can read. Use absolute paths if you start the proxy from another directory.

Check that `proxy.listen_addr` contains an IP address and port, such as `127.0.0.1:8080`. The address must be available on the proxy host, and another process must not already occupy the port.

#### Package or command is unavailable

If `cws-proxy` is not found, confirm that package installation succeeded in the environment that starts the proxy. Check that the installed command is on that process's `PATH`.

If the operating system cannot execute the command, check that the package is compatible with your Linux distribution and CPU architecture. Refer to [Install the proxy](/cloudflare-one/cloud-workload-security/get-started/#install-the-proxy) for the package setup and available-command check.

### Verify the fix

Start the proxy with `cws-proxy --config config.yaml`. Wait for `remote_config_loaded` and `action_mapper_loaded`, then retry your client request. If configuration loading fails, follow [Proxy cannot load the deployment](/cloudflare-one/cloud-workload-security/troubleshooting/#proxy-cannot-load-the-deployment).

## Proxy cannot load the deployment

### Symptoms

The proxy does not reach `remote_config_loaded`. Logs report a missing credential, an unsuccessful API response, or configuration fetch retries.

### Possible causes and solutions

#### Token environment variable is missing or empty

`cws.api_token_env` contains an environment variable name, not a token value. For the setup in Get started, the name is `CWS_DEPLOYMENT_TOKEN`.

In the terminal that starts the proxy, check whether the variable is populated without printing the token:

```sh
if [ -n "${CWS_DEPLOYMENT_TOKEN:-}" ]; then
  printf '%s\n' "CWS_DEPLOYMENT_TOKEN is set"
else
  printf '%s\n' "CWS_DEPLOYMENT_TOKEN is not set or is empty"
fi
```

The `cws_api_token_missing` error means the configured variable is missing or empty. A service or container must receive the variable in its own process environment. Setting it in the client terminal does not set it in the proxy process.

This presence check does not verify token validity. After correcting the proxy process environment, restart the proxy.

#### Account, deployment, or permission mismatch

Confirm that `cws.account_id` and `cws.deployment_id` identify the intended deployment. The API token must have the **Cloud Workload Security Deployment** permission for that account.

For `401` or `403` API responses, check the token's validity and account permissions. For `404`, check the account and deployment IDs and whether the deployment still exists. Refer to [Create the proxy API token](/cloudflare-one/cloud-workload-security/get-started/#create-the-proxy-api-token).

#### API connectivity or trust failure

For the API, use `https://api.cloudflare.com/client/v4` as `cws.api_base_url`. Check that the proxy host can resolve and reach that origin. Use the logged connection, timeout, or certificate error to identify the failing connection.

:::caution
Do not set client `HTTP_PROXY` or `HTTPS_PROXY` variables in the proxy process environment.
:::

If your network inspects Transport Layer Security (TLS), configure its approved trust roots through `proxy.tls_interception.extra_root_cas`. Client `NODE_EXTRA_CA_CERTS` does not change the proxy's outbound trust.

#### Deployment configuration cannot be parsed or prepared

Local configuration validation does not validate the downloaded deployment bundle. If the API returns successfully but the proxy cannot parse or compile the configuration, [contact the Open Beta team](/cloudflare-one/cloud-workload-security/troubleshooting/#contact-the-open-beta-team) with the proxy version and sanitized error.

### Verify the fix

Restart after changing local configuration or the process environment. Confirm `remote_config_loaded` and `action_mapper_loaded` before retrying your client. Transient startup fetch failures can retry, but a missing credential or invalid configuration needs correction.

## Client connections or certificate verification fail

### Symptoms

The client reports a refused connection or a certificate verification error. It cannot complete a request through the proxy.

### Possible causes and solutions

#### Client uses an unreachable proxy address

Compare the client's proxy address with `proxy.listen_addr`. In the local setup, both use `127.0.0.1:8080`, and `HTTPS_PROXY` uses the `http://` scheme:

```sh
export HTTP_PROXY="http://127.0.0.1:8080"
export HTTPS_PROXY="http://127.0.0.1:8080"
```

A loopback address only reaches the proxy within the same host or network namespace. A client in another container or host needs a reachable proxy address. Confirm that the proxy has finished startup before retrying.

#### Client does not trust the proxy CA

Use the CA files configured for your proxy. Substitute their paths if they differ from `ca-cert.pem` and `ca-key.pem`.

<Steps>
1. In the proxy terminal, inspect the CA certificate's validity dates:

   ```sh
   openssl x509 -in ca-cert.pem -noout -dates
   ```

   Confirm that the current time falls between `notBefore` and `notAfter`. Correct an expired certificate or an incorrect system clock.

2. Compare the public-key digests of the certificate and private key:

   ```sh
   openssl x509 -in ca-cert.pem -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256
   openssl pkey -in ca-key.pem -pubout -outform DER | openssl dgst -sha256
   ```

   The digests must match. These commands print public-key digests, not the private key. If they differ, configure the matching CA certificate and key.

3. In the client terminal, configure trust in the CA certificate. For the Node.js-based `cf` client, set:

   ```sh
   export NODE_EXTRA_CA_CERTS="<ABSOLUTE_PATH_TO_CA_CERT_PEM>"
   ```

   Start a new client process after changing its trust settings. Keep the private key on the proxy host. Other clients need their own supported CA trust configuration.
</Steps>

For CA setup, refer to [Prepare a certificate authority](/cloudflare-one/cloud-workload-security/get-started/#prepare-a-certificate-authority).

### Verify the fix

Retry a read-only request through the proxy, such as the managed-domain list request in [Verify the guardrail](/cloudflare-one/cloud-workload-security/get-started/#verify-the-guardrail). Confirm that connection and certificate verification succeed. Diagnose an HTTP error separately using [Requests receive an unexpected 403](/cloudflare-one/cloud-workload-security/troubleshooting/#requests-receive-an-unexpected-403).

## Requests expected to be blocked succeed

### Symptoms

A client completes a request that your guardrail should block. The proxy may have no corresponding request record, or may record an allowed decision.

### Possible causes and solutions

#### Client bypasses the proxy

Creating a deployment does not route traffic automatically. Confirm that the client process receives the intended proxy settings. Check that `NO_PROXY` and `no_proxy` do not bypass the target origin.

For the documented `cf` setup, use Node.js 24.5 or later. Check the client terminal's version:

```sh
node --version
```

Set `NODE_USE_ENV_PROXY=1` before starting the client. Refer to [Route the CLI through the proxy](/cloudflare-one/cloud-workload-security/get-started/#route-the-cli-through-the-proxy).

A connection record alone does not prove that CWS evaluated an HTTP request. Check for the request decision. An unclassified destination can pass through as a raw tunnel without request-policy evaluation.

#### Policy is not attached, in scope, or enforcing

Compare `cws.deployment_id` with the deployment you edited. Verify the configuration path from deployment to policy:

<Steps>
1. Confirm that the deployment includes the intended policy set.
2. Confirm that the policy set includes the intended policy and has **Enforce** turned on for it.
3. Compare the request with the included and excluded workloads. An empty inclusion list includes all requests before exclusions. An excluded workload match removes the request from the set's scope.
4. Check the policy filter against the request's application, service, operation, and other selected fields. Both workload scope and policy filter must match.
</Steps>

For the editing procedure, refer to [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/). After a configuration refresh, inspect `request.policy_sets` for the evaluated policies, their decisions, and their `enforce` values.

An enforcing policy that returns `false` blocks a matching request. A non-enforcing policy can record a denial without blocking. The deployment **Default mode** applies when no policy matches. For policy evaluation errors, also inspect **On error action** and the error outcome.

#### Request or identity differs from the policy's assumptions

Confirm that the client sends the operation and request body your Rego check expects. If the proxy reports `request_body_inspection_skipped_too_large`, body inspection was skipped and `input.http.request.body` is `null`. A body-dependent check can produce a different result.

If your filters depend on a Basic Auth identity, check the configured credential and secret expiration or rotation. Refer to [Manage traffic scope](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/). The deployment API token does not identify your client or replace its cloud-provider credentials.

### Verify the fix

Use a controlled test request that should pass and another that should fail. Confirm the expected decision and evaluated policy in the proxy logs. For the empty-R2-bucket walkthrough, follow [Verify the guardrail](/cloudflare-one/cloud-workload-security/get-started/#verify-the-guardrail), including restoring private access if the blocking test succeeds.

## Requests receive an unexpected 403

### Symptoms

The client receives HTTP `403`, a `CWS.Error` response, or a rejected CONNECT request.

### Possible causes and solutions

#### Domain ACL blocks the connection

A domain access control list (ACL) can reject CONNECT before request-policy evaluation. Look for `domain_acl_denied_connect`. Inspect the ACL attached to your deployment and its default action.

Check the destination pattern: `example.com` is an exact match, while `.example.com` includes the parent domain and its subdomains. Correct the entry for the intended destination using [Manage traffic scope](/cloudflare-one/cloud-workload-security/guides/manage-traffic-scope/).

An ACL allow decision permits the connection. It does not bypass request policies.

#### CWS rejects the request

`CWS.Error` identifies a CWS request denial. Inspect the related request logs for the reason and evaluated policies. If a matching policy denies, compare its Rego check with the request. Another enforcing policy can block even when one policy passes.

If no policy matches, inspect the deployment **Default mode** and intended request scope. If an evaluation error occurs, inspect the error and deployment **On error action**. Correct the underlying configuration or policy using [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/).

If application classification fails before policy evaluation, the logs may report that failure instead of a normal policy decision. Include that error when [contacting the Open Beta team](/cloudflare-one/cloud-workload-security/troubleshooting/#contact-the-open-beta-team).

#### Cloud provider rejects an allowed request

If `request.outcome` is `allowed` and the response record has `upstream_status: 403`, the cloud provider rejected the forwarded request. Check the client's provider credentials, resource permissions, and provider error message.

The CWS deployment token loads proxy configuration. It does not grant access to cloud-provider resources.

### Verify the fix

Retry an authorized, in-scope request. Confirm the proxy decision and the provider response separately. A `403` status alone does not identify which layer rejected the request.

## Saved changes do not affect requests

### Symptoms

You save a policy, policy set, or deployment change, but subsequent requests behave as before.

### Possible causes and solutions

#### Proxy has not refreshed the intended deployment

Confirm that the running proxy uses the account and deployment you edited. Allow the interval configured in `cws.api_poll_interval_secs` before testing again. Its minimum is `30` seconds.

Look for `polling_remote_config` and any fetch or prepare failure. `remote_config_updated` is not emitted for every applied policy change. Verify changes through subsequent request decisions and evaluated policies.

#### Refresh fails and the proxy retains its previous configuration

`remote_config_fetch_failed_keeping_current` and `remote_config_prepare_failed_keeping_current` mean the refresh failed. The running proxy retains its previous valid deployment configuration.

For fetch failures, check [API access and connectivity](/cloudflare-one/cloud-workload-security/troubleshooting/#proxy-cannot-load-the-deployment). For prepare failures, review the reported configuration error and [contact the Open Beta team](/cloudflare-one/cloud-workload-security/troubleshooting/#contact-the-open-beta-team) if it persists. A saved dashboard change alone does not prove that the proxy applied it.

#### Local configuration or process environment changed

Restart the proxy after changing its local configuration file or credential environment. A configuration poll does not reload those local settings.

### Verify the fix

After a successful refresh or restart, repeat the controlled policy test. Confirm the expected policy set, enforcement state, and request decision in the logs.

## Requests receive an Action Mapper 503

### Symptoms

The response has HTTP `503` and the message `Action Mapper application data is unavailable`. It can also contain `x-cws-proxy-err: Action Mapper unavailable`.

### Possible causes and solutions

#### Application data is unavailable or invalid

Action Mapper classifies API requests for policy evaluation. The proxy rejects inspected HTTP traffic when it cannot use a complete, valid application data set. A successful deployment configuration load does not prove this data is ready.

Look for `initial_action_mapper_fetch_failed`, `initial_action_mapper_load_failed`, or `initial_action_mapper_incomplete` during startup. During operation, check `action_mapper_application_pending`, `action_mapper_poll_failed_keeping_current`, and any `action_mapper_degraded_denied_connect` or `action_mapper_degraded_denied_request` events.

Use the reported error to check API connectivity and authentication. The proxy retries application-data loading. If validation failures persist, [contact the Open Beta team](/cloudflare-one/cloud-workload-security/troubleshooting/#contact-the-open-beta-team) with sanitized log excerpts.

### Verify the fix

At startup, confirm `action_mapper_loaded`. After a runtime failure, confirm recovery in the mapper logs and retry a read-only request. The request should no longer return the Action Mapper unavailable response. An unrelated `503` needs investigation using its own error and logs.

## Requests receive 421 Misdirected Request

### Symptoms

The client receives HTTP `421`. The proxy logs `request_host_tunnel_mismatch`.

### Possible causes and solutions

#### Request destination differs from the CONNECT destination

Check that the request URL, `Host` header, and CONNECT destination agree on the host and effective port. Inspect any client configuration or intermediary that overrides the destination or rewrites `Host`.

Configure the client to send the intended destination consistently. Retain the proxy's destination checks. Refer to the logged `request_authority`, `context_authority`, and `tunnel_authority` fields to identify the disagreement.

### Verify the fix

Start a new client connection and retry the request. Confirm that it no longer produces `421` or `request_host_tunnel_mismatch`, then inspect its policy decision and provider response.

## Dashboard resources cannot be loaded or saved

### Symptoms

The dashboard reports a loading or saving error. A policy or workload form may report invalid filter or Rego input.

### Possible causes and solutions

#### Wrong account, missing permission, or connection failure

Confirm that the dashboard uses the account containing your resources. Check that your account role permits the resource operation. The proxy's deployment token and your dashboard permissions serve different purposes.

For a loading error, check your connection and reload the page. If a resource is missing, confirm that it still exists in the selected account.

#### Invalid filter or Rego source

Correct the displayed field errors. Filters must use supported Wirefilter fields and operators. Put request-body checks in Rego, using `input.http.request.body`.

For dashboard policies, declare a package under `policy.` and a Boolean `valid` rule. Check the syntax and imported library or metadata paths. Refer to [Configure policies](/cloudflare-one/cloud-workload-security/guides/configure-policies/) and [Manage policy reference resources](/cloudflare-one/cloud-workload-security/guides/policy-reference/).

If the operation still fails, [contact the Open Beta team](/cloudflare-one/cloud-workload-security/troubleshooting/#contact-the-open-beta-team) with the sanitized API error code and message, when available. Do not treat a failed save as an applied configuration change.

### Verify the fix

Confirm that the resource saves and reloads successfully. For a policy change, allow a configuration refresh and verify its effect through the proxy.

## Read proxy logs

The configuration in Get started uses JSON logging at `INFO`. Read records from the proxy process that handled the request. Use the timestamp and `cws.request_id`, when present, to relate records.

For evaluated HTTP requests, inspect these events and fields:

| Event or field              | What it tells you                                                                                                  |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `remote_config_loaded`      | Deployment configuration loaded at startup.                                                                        |
| `action_mapper_loaded`      | Application data loaded at startup.                                                                                |
| `request.outcome`           | CWS allowed or denied the evaluated request. An allowed decision does not guarantee provider success.              |
| `request.outcome_reason`    | Reason for a denied request, when present.                                                                         |
| `request.policy_sets`       | JSON-encoded audit of matching policy sets and evaluated policies, including `enforce` and each policy's decision. |
| `request.evaluation_result` | Evaluation result, when available. Use the per-policy audit to understand which policy controlled the outcome.     |
| `response`                  | Response record containing `status` and, for a provider response, `upstream_status`.                               |
| `request.context`           | Redacted policy input for diagnosis. It can be truncated and is not a complete copy of policy input.               |

JSON-encoded log values can be truncated. A truncated audit is not a complete policy list. CONNECT denials and failures before policy evaluation can have their own events. The absence of a normal request record does not, by itself, prove that the client bypassed the proxy.

## Contact the Open Beta team

If the issue persists, contact [the Open Beta team](mailto:cloud-workload-security-open-beta@cloudflare.com) with:

- The failure timestamp and timezone.
- The proxy version, operating system, and CPU architecture.
- The client name, version, and error message.
- The account, deployment, and relevant policy or policy-set IDs.
- The target API operation and expected versus actual result.
- Short, sanitized log excerpts, `cws.request_id`, and any response reference, when available.

To collect the proxy version, run:

```sh
cws-proxy --version
```

Include only the relevant configuration keys. Remove API tokens, passwords, private keys, and credential-bearing headers. Redact cookies, sensitive URLs, request bodies, and metadata before sharing. Review logs even when the proxy has already redacted some fields.

# DLP settings

DLP settings allow you to configure account-level settings that apply across all DLP profiles and policies. These settings are located in **Zero Trust** > **Data loss prevention** > **DLP settings** in the [Cloudflare dashboard](https://dash.cloudflare.com/).

## Optical Character Recognition (OCR)

Optical Character Recognition (OCR) analyzes and interprets text within image files. When turned on, OCR can detect sensitive data within images your users upload.

OCR supports scanning `.jpg`/`.jpeg` and `.png` files between 4 KB and 1 MB in size. Text is encoded in UTF-8 format, including support for non-Latin characters.

To turn on OCR:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **DLP settings**.
2. Turn on **Optical Character Recognition (OCR)**.

## AI context analysis

:::note
AI context analysis only supports Gateway HTTP and HTTPS traffic.
:::

AI context analysis uses a pretrained model to analyze surrounding context and adjust the confidence level of a detection. For example, a number that matches a credit card pattern may receive a lower confidence score if it appears in a context where credit card numbers are unlikely. DLP will log any matches that are above your confidence threshold.

DLP redacts any matched text, then converts the surrounding context into a vector embedding and submits it to [Cloudflare Workers AI](/workers-ai/). Vector embeddings (not raw text) are stored in user-specific private namespaces for up to six months, along with hit count and the [false positive/negative report](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#report-false-and-true-positives-to-ai-context-analysis).

To turn on AI context analysis:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **DLP settings**.
2. Turn on **AI context analysis**.
3. [Add the profile](/cloudflare-one/data-loss-prevention/dlp-policies/#2-create-a-dlp-policy) to a DLP policy.
4. When configuring the DLP policy, turn on [payload logging](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#log-the-payload-of-matched-rules).

AI context analysis results will appear in the payload section of your [DLP logs](/cloudflare-one/data-loss-prevention/dlp-policies/#4-view-dlp-logs). To improve future detections of sensitive data, you need to [report false and true positives](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#report-false-and-true-positives-to-ai-context-analysis).

## Payload encryption key

Before you begin logging DLP payloads, you will need to set a DLP payload encryption public key. DLP uses public-key encryption so that matched sensitive data is readable only by you — Cloudflare does not have access to your private key and cannot decrypt your logs.

### Generate a key pair

You will generate two keys: a public key (uploaded to Cloudflare to encrypt log data) and a private key (kept by you to decrypt log data later).

To generate a public/private key pair in the command line, refer to [Generate a key pair](/waf/managed-rules/payload-logging/command-line/generate-key-pair/).

### Upload the public key to Cloudflare

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **DLP settings**.
2. In the **DLP Payload Encryption public key** field, paste your public key.
3. Select **Save**.

:::note
The matching private key is required to view logs. If you lose your private key, you will need to [generate](#generate-a-key-pair) and [upload](#upload-the-public-key-to-cloudflare) a new public key. The payload of new requests will be encrypted with the new public key. Previously logged data encrypted with the old key will be permanently unreadable.
:::

## Payload log masking

You can control how sensitive data appears in your DLP payload logs by selecting a masking level. This determines how much of the matched content is visible after decryption.

To configure payload log masking:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **DLP settings**.
2. Go to the **Payload log masking** card.
3. Choose one of the following masking levels:
   - **Full Mask (default):** Masks the match while preserving character count and visual formatting. For example, a Social Security Number appears as `***-**-****`.
   - **Partial Mask:** Reveals 25% of the matched content while masking the remainder. For example, `***-**-6789`.
   - **Clear Text:** Stores the full, unmasked match for detailed investigation. For example, `123-45-6789`.

:::note
The masking level is applied at detection time, before the payload is encrypted. Your team will see the selected format when they decrypt the log with your private key.
:::

:::caution
The selected masking level applies to all sensitive data matches found within a payload window — not just the match that triggered the policy.
:::

## Migrate from profile-level settings

OCR and AI context analysis are available at both the profile level (**Data loss prevention** > **Profiles**) and the account level (**Data loss prevention** > **DLP settings**) during the migration period. When both are configured, DLP uses OR logic for evaluation. A match occurs if either the profile-level or account-level setting would trigger a detection.

Profile-level OCR and AI context analysis settings will be deprecated in a future release. We recommend migrating to account-level settings in **DLP settings** to ensure consistent behavior across all profiles.

To migrate:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **DLP settings**.
2. Turn on **Optical Character Recognition (OCR)** and/or **AI context analysis** as needed.
3. Go to **Zero Trust** > **Data loss prevention** > **Profiles**.
4. For each profile with OCR or AI context analysis enabled, edit the profile and turn off the profile-level settings.
5. Select **Save profile**.

# Data loss prevention

import { GlossaryDefinition, Render } from "~/components";

:::note[Availability]
Available as an add-on to Zero Trust Enterprise plans.

Users on Zero Trust Free and Pay-as-you-go plans can use the [Financial Information](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#financial-information) and [Social Security, Insurance, Tax, and Identifier Numbers](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#social-security-insurance-tax-and-identifier-numbers) predefined profiles, [payload logging](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#log-the-payload-of-matched-rules), and [false positive reporting](/cloudflare-one/data-loss-prevention/dlp-policies/#report-false-positives).
:::

<GlossaryDefinition term="Cloudflare Data Loss Prevention (DLP)" />

DLP scans HTTP traffic, SaaS application files, and AI prompts for sensitive data such as credit card numbers, credentials, and personally identifiable information.

Cloudflare does not write scanned content to disk. DLP encrypts and temporarily stores content in memory only. To retain matched content for review, configure [payload logging](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#log-the-payload-of-matched-rules) for encrypted payload copies or a [Logpush destination](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#send-dlp-forensic-copies-to-logpush-destination) to export full matching HTTP requests.

DLP uses [**profiles**](/cloudflare-one/data-loss-prevention/dlp-profiles/) to define what to detect, [**detection entries**](/cloudflare-one/data-loss-prevention/detection-entries/) to identify specific patterns and data, and optional [**Data Classification**](/cloudflare-one/data-loss-prevention/data-classification/) to organize and label findings at scale.

## Data in transit

Data Loss Prevention complements [Secure Web Gateway](/cloudflare-one/traffic-controls/) to detect sensitive data transferred in HTTP requests and responses. DLP scans HTTP bodies (excluding headers), which may include uploaded or downloaded files, chat messages, forms, and other web content.

DLP requires [Gateway HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/) with [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) to read HTTPS bodies in transit. The depth of visibility varies for each site or application. DLP does not scan any traffic that bypasses Cloudflare Gateway (such as traffic that matches a [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policy).

Before deciding what to log or block, use [Passive Detection](/cloudflare-one/data-loss-prevention/passive-detection/) to learn which sensitive data types appear in sampled Gateway traffic and where they are found. Use those findings to create or refine a [Gateway DLP policy](/cloudflare-one/data-loss-prevention/dlp-policies/).

## Data at rest

Data Loss Prevention complements [Cloudflare CASB](/cloudflare-one/integrations/cloud-and-saas/) (Cloud Access Security Broker) to detect sensitive data stored in your SaaS applications. CASB connects directly to SaaS application APIs to retrieve and scan files, rather than reading files as they pass through Cloudflare Gateway. Because of this, Gateway and Cloudflare One Client settings (such as [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policies and [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) configurations) do not affect data at rest scans.

To get started, refer to [Scan SaaS applications with DLP](/cloudflare-one/cloud-and-saas-findings/casb-dlp/).

## AI applications and controls

- **AI Gateway** — Data Loss Prevention integrates with [Cloudflare AI Gateway](/ai-gateway/) to scan AI prompts and responses for sensitive data. To enable this, refer to [Set up DLP for AI Gateway](/ai-gateway/features/dlp/set-up-dlp/). When enabled, DLP inspects the text content of requests sent to AI providers and responses returned from AI models, without requiring Gateway HTTP filtering or TLS decryption.
- **Gateway Application Granular Controls** — [Gateway Application Granular Controls](/cloudflare-one/traffic-controls/http-policies/#granular-controls) let you control specific actions within AI applications without blocking the entire application. You can add the [DLP Profile selector](/cloudflare-one/traffic-controls/http-policies/#dlp-profile) to the same HTTP policy to scan those operations for sensitive data, as described in [Scan HTTP traffic with DLP](/cloudflare-one/data-loss-prevention/dlp-policies/).
- **AI Security for Apps** — DLP complements [AI Security for Apps](/waf/detections/ai-security-for-apps/) by providing profile-based detection of sensitive data in AI traffic, while AI Security for Apps handles large language model (LLM)-specific threats such as prompt injection and unsafe topics. To detect sensitive data alongside AI Security for Apps, configure [DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/) and apply them to your AI traffic policies.

## Email security

Data Loss Prevention integrates with [Cloudflare Email Security](/cloudflare-one/email-security/) to scan [outbound emails](/cloudflare-one/email-security/policies/outbound/outbound-dlp/) for sensitive data. Outbound DLP requires [Microsoft 365](https://www.cloudflare.com/learning/cloud/what-is-microsoft-365/) and uses a client-side Outlook add-in to inspect emails before they are sent.

To get started, refer to [Outbound Data Loss Prevention (DLP)](/cloudflare-one/email-security/policies/outbound/outbound-dlp/).

## Troubleshooting

For help resolving common issues with DLP, refer to [Troubleshoot DLP](/cloudflare-one/data-loss-prevention/troubleshoot-dlp/).

To check how DLP evaluates sample content against your profiles, use [Test scan](/cloudflare-one/data-loss-prevention/test-scan/).

## Supported file types

### Formats

DLP supports reporting and scanning the following file types:

- Text and CSV
- Microsoft Office 2007 and later (`.docx`, `.xlsx`, `.pptx`), including Microsoft 365
- PDF
- ZIP files containing the above

DLP will scan the text contained in text, Microsoft Office, and PDF files.

Refer to the [OCR documentation](/cloudflare-one/data-loss-prevention/dlp-settings/#optical-character-recognition-ocr) for supported image format information.

:::note
ZIP files can be recursively compressed a maximum of 10 times.
:::

# Passive Detection

import { DashButton, Steps } from "~/components";

:::note[Availability]
The detection entries available to Passive Detection depend on your [Zero Trust plan](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/).
:::

Before you create a Data Loss Prevention (DLP) policy, you may not know which sensitive data types appear in your traffic or where they are going. Passive Detection gives you a place to start. It helps you answer those questions before you decide what to log or block.

Passive Detection scans a randomly sampled subset of Gateway HTTP request and response bodies and brings the findings into one dashboard. You can see which detection entries matched, trace where they appeared, and identify where a policy could help. Use what you learn to build a focused policy or review gaps in an existing one.

Passive Detection works without a Gateway DLP policy. It does not change how Gateway handles traffic, and existing policies continue to apply.

## Get started

### Prerequisites

To collect Passive Detection results:

- Route HTTP traffic through Cloudflare Gateway.
- Turn on [Gateway HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/).
- Turn on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) for HTTPS traffic.
- Make sure each detection entry you want to scan is enabled in at least one [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/) in your account. Passive Detection scans entries enabled in any profile in your account, not just profiles selected for a policy.

For uploaded or downloaded files, refer to the [supported file types](/cloudflare-one/data-loss-prevention/#supported-file-types). Traffic that bypasses Gateway or matches a [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policy cannot produce findings.

### View Passive Detection

<Steps>
1. In the Cloudflare dashboard, go to **Zero Trust** > **Insights & Logs** > **Dashboards**.

   <DashButton url="/?to=/:account/analytics/dashboards" zeroTrust />

2. Select **Passive Detection**.
3. Select a time range of 7, 30, or 90 days.
4. (Optional) Select **Add filter** > **Traffic direction**, then select _Request_, _Response_, or _Unknown_.
</Steps>

Use **Request** to see data sent to destinations and **Response** to see data received from them. **Unknown** includes HTTP bodies without a recorded traffic direction. Remove the filter to include all traffic directions. Request and response bodies are counted separately.

The selected time range and traffic direction also apply when you open an entry to investigate it.

## Learn from the data

Start with the account summary for the broad picture. From there, select a detection entry to see where it appeared, the matching profiles, and whether policy enforcement was applied.

### Start with the account summary

The summary cards describe detections within your selected time range and traffic direction:

- **Total detections**: Each entry counts once per HTTP body.
- **Unique traffic with detections**: HTTP bodies with at least one detection, counted once.
- **Entries detected**: Unique DLP entries matched in the selected scope.

For example, one request body that matches two different entries contributes two detections to **Total detections** and one body to **Unique traffic with detections**. These counts do not tell you how many individual sensitive values the body contains.

Use **Detections by data type** to see which entries account for the most detections. Use **Policy coverage** to find entries that may need policy review.

### Find sensitive data

Next, use the detection table to decide what to investigate. Compare entries, then inspect their profiles and destinations. The table contains one row per detection entry found in sampled traffic. Entries without detections do not appear.

Use these fields to compare entries:

| Field              | Meaning                                                                                                                                                        |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Entry              | The DLP detection entry found in sampled traffic.                                                                                                              |
| Profiles           | The profiles associated with the detection.                                                                                                                    |
| Detections         | HTTP bodies where this entry was detected at least once. Multiple occurrences of the same entry in one body count once.                                        |
| URL destinations   | The number of distinct destinations associated with the entry.                                                                                                 |
| Confidence (H/M/L) | Observed detections grouped by high, medium, and low confidence.                                                                                               |
| Policy coverage    | Whether matching profiles were selected for policy enforcement when the bodies were scanned. Refer to [Find policy coverage gaps](#find-policy-coverage-gaps). |
| Last seen          | When the entry was most recently observed during the selected time range.                                                                                      |

All times are reported in UTC.

The same body can contribute confidence counts through more than one matching profile. Confidence counts can therefore add up to more than the number of detected bodies, even within a single confidence level.

### Trace where data goes

After you choose an entry, select it to see where it appeared. The detail view shows detections and confidence over time, followed by the destinations and applications associated with that entry.

Destinations group URLs by host and a normalized path. For example, paths such as `/users/12345` can be grouped under `/users/{id}`. Each destination includes detection counts, confidence, traffic direction, and the last detection time.

Expand a destination to review a selection of matched URLs, their detection counts, and when they were last observed. Use these details to check whether the activity involves an expected application or a destination that needs further investigation.

For example, a detection associated with an unfamiliar file-sharing destination may warrant a closer look. Check whether that destination is approved for the data involved before deciding whether to create a policy.

:::caution
Matched URLs can contain sensitive values in their paths or query strings. Review them before sharing results.
:::

The dashboard shows detection metadata. Original request and response bodies are not stored.

### Find policy coverage gaps

Policy coverage helps you find detections that may need a closer look. A detection counts as covered when at least one matching DLP profile was selected for policy enforcement when the HTTP body was scanned.

Coverage does not confirm that the complete Gateway policy matched or that Gateway logged or blocked the traffic. The dashboard groups coverage into three statuses:

| Status         | Meaning                                                                           |
| -------------- | --------------------------------------------------------------------------------- |
| Covered        | Each detection had at least one matching profile selected for enforcement.        |
| Partly covered | Some detections had a matching profile selected for enforcement and some did not. |
| Not covered    | No detections had a matching profile selected for enforcement.                    |

Start with entries that are **Not covered** or **Partly covered**, then review their destinations and the relevant policies. A coverage gap is a reason to investigate, not a recommendation to block every detection.

## Create a DLP policy

Once you understand what data was detected and where it appeared, turn those findings into a policy that fits your traffic. Focus on the data types and destinations that need protection, rather than applying the same action to every detection.

<Steps>
1. Choose a detection entry to act on. Review its destinations, confidence, and policy coverage to decide whether the activity is expected.
2. Configure a [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/) with the detection entries you want the policy to match, or use an existing profile.
3. (Optional) If Test scan is available for the profile, use [Test scan](/cloudflare-one/data-loss-prevention/test-scan/) to confirm that it detects representative content. Test scan checks the profile on its own. It does not evaluate a Gateway policy.
4. Create a [Gateway DLP policy](/cloudflare-one/data-loss-prevention/dlp-policies/) to log or block matching traffic. Scope it to the relevant destinations, applications, or users.
5. [Test the policy with Gateway traffic](/cloudflare-one/data-loss-prevention/dlp-policies/#3-test-dlp-policy) and confirm that it takes the expected action in [HTTP request logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/).
</Steps>

For an existing policy, use your findings to review its scope and detection settings. [Data security analytics](/cloudflare-one/insights/analytics/data-analytics/) shows activity from DLP policies after they are configured.

## Limitations

- Passive Detection scans a random sample of eligible Gateway HTTP traffic, not every request or response body.
- Counts may be estimated for high-volume detections due to analytics sampling. They describe sampled traffic, not all eligible Gateway traffic.
- The dashboard does not report total sampled traffic or detection rates.
- Destinations are scoped to one detection entry. Passive Detection does not provide an account-wide destination inventory.
- Results do not include user identity.

An empty dashboard does not confirm that sensitive data is absent.

For help with missing results, refer to [Troubleshoot DLP](/cloudflare-one/data-loss-prevention/troubleshoot-dlp/#passive-detection-shows-no-results).

# Troubleshoot DLP

import { Render, Steps } from "~/components";

Use this guide to troubleshoot common issues with Data Loss Prevention (DLP).

To check whether a profile detects specific content without sending traffic through Gateway, use [Test scan](/cloudflare-one/data-loss-prevention/test-scan/).

## DLP policy does not trigger or block content

DLP not inspecting or blocking content is the most common issue reported. If you have configured a [DLP policy](/cloudflare-one/data-loss-prevention/dlp-policies/) but it fails to inspect or block traffic, the cause is almost always that the traffic is not being decrypted. To use DLP to scan the content of HTTPS requests, you must turn on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/).

To turn on TLS decryption:

<Render file="gateway/enable-tls-decryption" product="cloudflare-one" />

Once you turn on TLS decryption, you can create a DLP policy to inspect the content of HTTPS requests. For example:

| Selector    | Operator | Value                 | Logic | Action |
| ----------- | -------- | --------------------- | ----- | ------ |
| Domain      | in       | `box.com`             | And   | Block  |
| DLP Profile | in       | _Credit card numbers_ |       |        |

## Passive Detection shows no results

[Passive Detection](/cloudflare-one/data-loss-prevention/passive-detection/) shows detections from sampled Gateway traffic. If you expect results but the dashboard is empty:

<Steps>
1. Select a time range that includes the traffic you want to investigate.
2. Check **Traffic direction**. Remove the filter to include all traffic directions, or select _Unknown_ to check bodies without a recorded direction.
3. Confirm that the expected detection entry is enabled in at least one DLP profile in the account.
4. Confirm that traffic meets the [Gateway inspection prerequisites](/cloudflare-one/data-loss-prevention/passive-detection/#prerequisites), including HTTP filtering and TLS decryption for HTTPS.
5. Check whether a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) or another routing configuration excludes the traffic from inspection.
</Steps>

An individual request or response body may not be sampled. An empty dashboard does not confirm that sensitive data is absent.

## DLP scans trigger false positives or block legitimate sites

If your DLP policy is blocking access to business-critical applications (such as Zoho, Google, or internal domains) or generating a high number of false positives, your DLP policy is likely too broad. Profiles such as **Credentials and Secrets** are powerful but can be overly aggressive if not scoped correctly.

### Problematic configuration

Applying a sensitive profile to all traffic causes unnecessary blocks. For example:

| Selector    | Operator | Value                     | Action |
| ----------- | -------- | ------------------------- | ------ |
| DLP Profile | in       | _Credentials and Secrets_ | Block  |

### Recommended solution

Make your policies more specific. Instead of a catch-all block, create granular policies that target high-risk destinations or user groups.

This policy only blocks uploads of financial data to file-sharing websites for a specific user group, reducing the risk of false positives on other sites.

| Selector           | Operator | Value                           | Logic | Action |
| ------------------ | -------- | ------------------------------- | ----- | ------ |
| Destination Domain | in       | `dropbox.com`, `wetransfer.com` | And   | Block  |
| DLP Profile        | in       | _Financial Information_         | And   |        |
| User Group Names   | in       | `Finance Team`                  |       |        |

You can also create policies that match trusted applications using the [**Do Not Scan** action](/cloudflare-one/traffic-controls/http-policies/#do-not-scan).

## DLP detections are inconsistent

If DLP detects sensitive data in plain text but not within images or certain applications, check for the following issues:

- **OCR is turned on**: For DLP to scan text within images (such as a picture of a credit card), you must turn on [Optical Character Recognition (OCR)](/cloudflare-one/data-loss-prevention/dlp-settings/#optical-character-recognition-ocr) in DLP settings.
- **Application-specific behavior**: Some applications, such as WhatsApp Web, use protocols or encryption methods (such as WebSocket connections) that Gateway may not be able to fully inspect with HTTP policies.
- **Supported file types**: Content must be in a [supported file type](/cloudflare-one/data-loss-prevention/#supported-file-types) for DLP inspection.

## PII Record profile does not match

The **Personally Identifiable Information (PII) Record** predefined profile does not match isolated values. Unlike most profiles, it matches only when at least three unique detection entries appear in close proximity, which reduces false positives from single, unrelated matches.

If you need to detect an individual data type (such as a single email address or credit card number), use a different predefined profile or a [custom profile](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile) with the specific detection entries you want.

## DLP options are missing or you cannot create custom profiles

If you cannot use the _DLP Profile_ selector when creating an HTTP policy or are blocked from creating a custom DLP profile, it typically means one of two things:

1. Incorrect plan. These features require a Zero Trust Enterprise plan. If you believe your account should have this entitlement, contact your account team to confirm your subscription details.
2. Permissions issue. You may not have the required administrative privileges to configure DLP settings. Check with your Cloudflare account administrator.

# Build a data class

Data classes are reusable classification rules built from detection entries, other data classes, sensitivity levels, and data tags.

Use a data class when you want to combine multiple signals into a single reusable classification rule that can then be added to custom DLP profiles.

## What a data class does

A data class lets you define classification logic separately from a DLP profile.

Instead of rebuilding the same logic in multiple profiles, you can create one reusable data class and apply it wherever you need it.

Data classes can also assign labels to matched content. This lets you connect raw detections to a broader classification model instead of relying only on direct entry matching in a profile.

## Create a data class

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Data classification** > **Data classes**.
2. Select **Create data class**.
3. Enter a name and optional description.
4. Build the detection rules for the data class.
5. Assign the labels you want matching content to receive.
6. Select **Save**.

## Build detection rules

Data classes use a rule builder to combine multiple signals into one classification rule.

You can build rules from:

- [detection entries](/cloudflare-one/data-loss-prevention/detection-entries/)
- other existing data classes

Use logical operators such as `AND` and `OR` to control how those conditions are evaluated.

Because data classes can reference other data classes, you can build reusable classification layers instead of recreating the same logic in multiple places. Cloudflare excludes the current data class from the selector to prevent recursive references.

## Assign labels

After you define the rule logic, choose the labels you want matching content to receive.

You can assign:

- a sensitivity schema and sensitivity level
- a data tag group and one or more data tags

When content matches the data class, Cloudflare applies those labels to the match.

## Use a data class in a DLP profile

After you create a data class, you can add it to a custom DLP profile.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Profiles**.
2. Create or edit a custom DLP profile.
3. In **Data classes**, select **Add data classes**.
4. Choose the data classes you want to include, then select **Confirm**.
5. (Optional) Add direct detection entries or labels to the profile.
6. Select **Save profile**.

Custom DLP profiles can combine direct detection entries, data classes, and labels in the same profile.

# Configure labels and templates

Labels and templates define the classification metadata you can apply to sensitive content in Cloudflare DLP.

Use the **Labels** tab to create and manage sensitivity schemas, sensitivity levels, data tag groups, and data tags. Use the **Templates** tab to review Cloudflare-managed starting points for sensitivity schemas and data tag groups.

## Labels

Labels help you describe matched content in a consistent way.

Data Classification supports two label types:

- **Sensitivity schemas and levels** define an ordered classification hierarchy.
- **Data tag groups and tags** define additional descriptors you can apply to content.

You can use labels directly in custom DLP profiles and assign them through data classes.

### Sensitivity schemas and levels

A sensitivity schema is a named hierarchy of sensitivity levels, such as `Public`, `Internal`, `Confidential`, or `Restricted`.

Each schema contains one or more ordered levels. In custom DLP profiles, selecting a sensitivity level lets you match content at that level or higher within the selected schema.

### Create a sensitivity schema

When creating a sensitivity schema, you can either create a custom schema from scratch or start from a template.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Data classification** > **Labels**.
2. Select **Create labels**.
3. In **Sensitivity schema**, choose one of the following:
   - **Create a custom schema** to define the schema from scratch
   - **Choose a template** to start from a Cloudflare-managed template
4. Enter or review the name and description.
5. Add or update the sensitivity levels you want to include, in order.
6. Select **Save**.

You can edit the resulting sensitivity schema after creation.

### Data tag groups and tags

A data tag group contains related tags you can use to describe content beyond its sensitivity level. For example, a data tag group could contain tags for business function, data owner, or content category.

### Create a data tag group

When creating a data tag group, you can either create a custom group from scratch or start from a template.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Data classification** > **Labels**.
2. Select **Create labels**.
3. In **Data tag group**, choose one of the following:
   - **Create a custom group** to define the group from scratch
   - **Choose a template** to start from a Cloudflare-managed template
4. Enter or review the name and description.
5. Add or update the data tags you want to include.
6. Select **Save**.

You can edit the resulting data tag group after creation.

## Templates

Templates provide Cloudflare-managed starting points for sensitivity schemas and data tag groups.

Templates are not linked objects. When you build from a template, Cloudflare creates a new sensitivity schema or data tag group in your account. After that, you can edit it like any other label object you create.

You can start from a template in either of the following ways:

- from the **Templates** tab, by reviewing a template and selecting **Build with template**
- from the **Labels** tab, by selecting **Create labels** and then **Choose a template** inline during creation

### Build from a template from the Templates tab

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Data classification** > **Templates**.
2. Select a template to review its details.
3. Select **Build with template**.
4. Review and customize the resulting sensitivity schema or data tag group.
5. Select **Save**.

After you build from a template, the resulting object appears in the **Labels** tab and can be used in data classes and DLP profiles.

## Use labels in DLP

After you create labels, you can use them in either of the following ways:

- assign them to content through [Build a data class](/cloudflare-one/data-loss-prevention/data-classification/build-a-data-class/)
- apply them directly in [custom DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/)

In custom DLP profiles, sensitivity levels and data tags can be used directly as profile criteria, even when they are not assigned through a data class.

# Data classification

Data Classification extends Cloudflare DLP with a reusable layer for identifying, organizing, and labeling sensitive content. Instead of building all detection logic directly inside a DLP profile, you can define labels and reusable classification rules, then apply them in custom DLP profiles.

## What is Data Classification?

With Data Classification, you can:

- Define labels such as sensitivity levels and data tags
- Use templates as a starting point for those labels
- Build reusable data classes that combine multiple signals into a single classification rule

This is useful when you want more than direct inspection. Detection entries help identify sensitive content. Data Classification helps organize and label that content so administrators can identify its severity and apply it consistently across DLP profiles.

Templates provide Cloudflare-managed starting points for sensitivity schemas and data tag groups. When you build from a template, Cloudflare creates a new object in your account that you can edit.

## How Data Classification fits with DLP

Data Classification works alongside detection entries and DLP profiles.

| Component | What it does |
| --- | --- |
| Detection entries | Detect specific content such as patterns, datasets, document fingerprints, AI prompt topics, and predefined detections. |
| Labels | Define sensitivity schemas, sensitivity levels, data tag groups, and data tags used to describe matched content. |
| Templates | Provide Cloudflare-managed starting points for sensitivity schemas and data tag groups. |
| Data classes | Build reusable classification rules from detection entries, other data classes, sensitivity levels, and data tags. |
| DLP profiles | Apply detection and classification logic to DLP scanning and enforcement workflows. |

In general, detection entries help identify sensitive content. Data Classification helps organize and label that content so administrators can identify its severity, understand where it exists, and apply it consistently. DLP profiles then apply that logic to scanning and enforcement workflows.

## When to use Data Classification vs DLP profiles

Use detection entries and DLP profiles when you want direct detection and enforcement. For example, if you want to detect a specific regex, dataset, or predefined detection and immediately use it in a policy, building directly with detection entries may be enough.

Use Data Classification when you want a more reusable and structured model. For example, Data Classification is a better fit when you want to:

- standardize sensitivity labels across multiple detections
- organize related detections into a reusable data class
- combine multiple signals into a single classification rule
- reuse the same classification logic across multiple DLP profiles

In summary, use DLP profiles when you want enforcement. Use Data Classification when you want to organize and label sensitive content in a reusable way before applying that logic in DLP workflows.

## Next steps

To get started:

- [Configure labels and templates](/cloudflare-one/data-loss-prevention/data-classification/configure-labels-and-templates/) — Create labels and build from Cloudflare-managed templates.
- [Build a data class](/cloudflare-one/data-loss-prevention/data-classification/build-a-data-class/) — Create reusable classification rules and apply them in custom DLP profiles.
- [Configure DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/) — Apply detection entries, data classes, and labels in DLP scanning workflows.

# Configure detection entries

Detection entries are the reusable detection logic that Cloudflare DLP uses to identify sensitive content in your web traffic and SaaS applications. You can create and manage detection entries independently of [DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/), then add the same entry to one or more custom profiles. You can also use detection entries in [data classes](/cloudflare-one/data-loss-prevention/data-classification/build-a-data-class/).

Detection entries include:

- [Pattern entries](#pattern-entries) — regular expressions used to detect text patterns
- [Predefined detection entries](/cloudflare-one/data-loss-prevention/detection-entries/predefined-detection-entries/) — Cloudflare-managed detections for specific types of sensitive content
- [Exact Data Match datasets](#exact-data-match-datasets) — uploaded datasets of sensitive values to match against, such as customer records or account numbers
- [Custom Wordlist datasets](#custom-wordlist-datasets) — uploaded plaintext datasets used to detect terms such as product names, internal codes, or SKU numbers
- [Document entries](#document-entries) — fingerprints of example documents used to find similar content
- [AI prompt topics](#ai-prompt-topics) — categories of prompts submitted to generative AI tools

## Manage detection entries

In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries** to create, review, and manage detection entries.

The Detection entries section includes dedicated views for different entry types, including **All**, **Pattern**, **Predefined**, **Datasets**, **Documents**, and **AI prompt topics**. You can use search and filters to find specific entries and review details such as type, status, and last updated time.

You can add the same detection entry to multiple custom DLP profiles. When you delete a custom detection entry, Cloudflare lists the profiles that currently use it.

## Test a detection entry

To test whether a detection entry identifies sample content, add the entry to a [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile), then use [Test scan](/cloudflare-one/data-loss-prevention/test-scan/). Test scan confirms whether the selected profile detects the sample. It does not test a Gateway policy or traffic flowing through Gateway.

If the profile does not match the sample, review the detection entry configuration, the profile [match count](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#match-count), and [confidence threshold](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#confidence-thresholds).

## Predefined detection entries

Predefined detection entries are Cloudflare-managed detections for specific types of sensitive content. You can review them from the **Predefined** view in **Detection entries** and add them directly to custom DLP profiles.

For a full list, refer to [Predefined detection entries](/cloudflare-one/data-loss-prevention/detection-entries/predefined-detection-entries/).

## Pattern entries

Pattern entries use regular expressions to detect text patterns in scanned content. You can create pattern entries independently of a DLP profile and reuse them across multiple custom profiles.

Regular expressions are written in Rust. Cloudflare recommends validating your regex with [Rustexp](https://rustexp.lpil.uk/).

DLP detects UTF-8 characters, which can be up to 4 bytes each. Custom text pattern detections are limited to 1024 bytes in length.

DLP does not support regular expressions with `+` or `*` operators because they are prone to exceeding the length limit. For example, the regex pattern `a+` can detect an infinite number of `a` characters. Cloudflare recommends using `a{min,max}` instead, such as `a{1,1024}`.

### Create a pattern entry

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Pattern** tab, select **Add Pattern**.
3. Enter a name. Optionally, add a description.
4. In **Value**, enter the regular expression you want to detect.
5. Select **Validate Regex**.
6. After the regex is validated, select **Save**.

To use a pattern entry, add it as an existing entry to one or more [custom DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile).

To apply organization-specific checks to pattern matches, refer to [Validate data loss prevention matches with Cloudflare Workers](/cloudflare-one/data-loss-prevention/detection-entries/custom-validation-workers/).

## Exact Data Match datasets

Exact Data Match (EDM) datasets protect sensitive information such as names, addresses, phone numbers, and account numbers.

All EDM dataset data is encrypted before reaching Cloudflare. To detect matches, Cloudflare hashes traffic and compares it to hashes from your dataset. Matched data will be redacted in payload logs.

### Prepare Exact Data Match datasets

#### Formatting

To prepare an Exact Data Match dataset for DLP, add your desired data to a multi-column spreadsheet. Each line must be at least six characters long. Entries do not require trailing or final commas.

For compatibility, save your file in either `.csv` or `.txt` format with LF (`\n`) newline characters. DLP does not support CRLF (`\r\n`) newline characters. For information on dataset limits, refer to [Account limits](/cloudflare-one/account-limits/#data-loss-prevention-dlp).

#### Column title cells

DLP will detect and use title cells as column names for Exact Data Match datasets. If multiple columns have the same name, DLP will append a number sign (`#`) and number to their names.

:::caution[Update EDM datasets]
To select which Exact Data Match columns to use, you will need to [reupload any EDM datasets](#manage-existing-exact-data-match-datasets) added prior to column support.
:::

### Upload a new Exact Data Match dataset

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Datasets** tab, select **Add a dataset**.
3. Select **Exact Data Match (EDM)**.
4. Upload your dataset file. Select **Next**.
5. Review and choose the detected columns you want to include. Select **Next**.
6. Name your dataset. Optionally, add a description. Select **Next**.
7. Review the details for your uploaded dataset. Select **Save dataset**.

DLP will encrypt your dataset and save its hash.

The dataset will appear in the list with an **Uploading** status. Once the upload is complete, the status will change to **Complete**. You can then add the dataset as an existing entry to one or more [custom DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile).

### Manage existing Exact Data Match datasets

Uploaded Exact Data Match datasets are read-only. To update a dataset, you must upload a new file to replace the original.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Datasets** tab, select the dataset you want to update.
3. Select **Upload dataset** and choose your updated dataset. Select **Next**.
4. Review and choose the new columns. Select **Next**.
5. Select **Save dataset**.

Your new dataset will replace the original dataset.

:::caution[Remove existing column entries]
If you want to update an Exact Data Match dataset to remove a column in use as an [existing detection entry](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile), you must remove the existing entry from any custom DLP profiles using it before updating the dataset.
:::

## Custom Wordlist datasets

Custom Wordlist (CWL) datasets protect non-sensitive terms such as intellectual property, SKU numbers, and internal project names.

Cloudflare stores data from CWL datasets in plaintext within DLP. Plaintext matches appear in payload logs. Optionally, CWL can detect case-sensitive data.

### Prepare Custom Wordlist datasets

Column title cells may result in false positives in Custom Wordlist datasets and should be removed.

### Upload a new Custom Wordlist dataset

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Datasets** tab, select **Add a dataset**.
3. Select **Custom Wordlist (CWL)**.
4. Name your dataset. Optionally, add a description.
5. In **Upload file**, choose your dataset file.
6. (Optional) In **Settings**, turn on **Enforce case sensitivity** to require matched values to contain exact capitalization.
7. Select **Save**.

DLP will save your dataset in cleartext.

The dataset will appear in the list with an **Uploading** status. Once the upload is complete, the status will change to **Complete**. You can then add the dataset as an existing entry to one or more [custom DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile).

### Manage existing Custom Wordlist datasets

Uploaded Custom Wordlist datasets are read-only. To update a dataset, you must upload a new file to replace the original.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Datasets** tab, select the dataset you want to update.
3. Select **Upload dataset** and choose your updated dataset. Select **Next**.
4. Select **Save dataset**.

Your new dataset will replace the original dataset.

## Document entries

You can upload example documents to detect similar content in your organization's traffic. DLP creates a unique fingerprint of the document and compares traffic against it based on how similar it is to the original. This is useful for detecting specific document types common to your organization, such as contract templates or internal reports, where the content does not reduce to a list of individual values in an uploaded dataset.

DLP stores uploaded documents encrypted at rest in a [Cloudflare R2](/r2/) bucket. To upload sensitive data that is only stored in memory, use [Exact Data Match datasets](#exact-data-match-datasets).

### Prepare document entries

DLP supports documents in `.docx` and `.txt` format. Documents must be under 10 MB.

### Upload a new document entry

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Documents** tab, select **Add a document entry**.
3. Name your document. Optionally, add a description.
4. In **Minimum similarity for matches**, enter a value between 0% and 100%.
5. In **Upload document**, choose and upload your document file.
6. Select **Save**.

The document will appear in the list with a **Pending** status. Once the upload is complete, the status will change to **Complete**. If you created a document entry with Terraform, the status will be **No file** until you upload a file.

To use your uploaded document fingerprint, add it as an existing entry to one or more [custom DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile).

### Manage existing document entries

Uploaded document entries are read-only. To update a document entry, you must upload a new file to replace the original.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Detection entries**.
2. From the **Documents** tab, choose the document you want to update and select **Edit**.
3. (Optional) Update the name and minimum similarity for matches for your document entry. You can also open the existing uploaded document.
4. In **Update document entry**, choose and upload your updated document file.
5. Select **Save**.

Your new document entry will replace the original document entry. If your file upload fails, DLP will still use the original document fingerprint to scan traffic until you delete the entry.

## AI prompt topics

DLP uses [Application Granular Controls](/cloudflare-one/traffic-controls/http-policies/#granular-controls) to detect and categorize prompts submitted to generative AI tools. Application Granular Controls analyzes prompts for both content and user intent. Supported AI prompt protection detections include:

| Detection entry | Description |
| --- | --- |
| Content: PII | Prompt contains personal information such as names, SSNs, or email addresses. |
| Content: Credentials and Secrets | Prompt contains API keys, passwords, or other sensitive credentials. |
| Content: Source Code | Prompt contains actual source code, code snippets, or proprietary algorithms. |
| Content: Customer Data | Prompt contains customer names, projects, business activities, or confidential customer contexts. |
| Content: Financial Information | Prompt contains financial numbers or confidential business data. |
| Intent: PII | Prompt requests specific personal information about individuals. |
| Intent: Code Abuse and Malicious Code | Prompt requests malicious code for attacks, exploits, or harmful activities. |
| Intent: Jailbreak | Prompt attempts to circumvent AI security policies. |

Each detection entry is categorized as either **Content** or **Intent**:

- **Content** — Detects specific text or data in the prompt itself (for example, a user pasting source code or a credit card number into a chat).
- **Intent** — Detects the user's goal or objective for the AI's response (for example, a user asking an AI to generate malicious code or extract personal information).

Intent detection is useful when AI applications have access to internal data sources containing sensitive information through SaaS connectors or Model Context Protocol (MCP) servers.

To use an AI prompt topic, configure the corresponding [predefined DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#ai-prompt) or add it as an existing entry to one or more [custom DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile). AI prompt protection is available for ChatGPT, Google Gemini, Perplexity, and Claude.

# Validate data loss prevention matches with Cloudflare Workers

import {
	DashButton,
	PackageManagers,
	Steps,
	TypeScriptExample,
} from "~/components";

Data loss prevention (DLP) can run a Cloudflare Worker in your account when a pattern matches. Use your Worker to apply organization-specific checks, adjust confidence, or reject the match.

Custom functions appear in the entry editor when this feature is available for your account. You can attach them to custom pattern entries.

## Set up your Workers account

<Steps>

1. Set up Workers in the same [Cloudflare account](/fundamentals/account/create-account/) where you configure DLP. If you have not used Workers before, follow the [Workers getting started guide](/workers/get-started/guide/).
2. Configure a [Workers subdomain](/workers/configuration/routing/workers-dev/) for that account.

</Steps>

## Create a validation Worker

Deploy the Worker to the account where you configure DLP. Export a class named `DlpValidationEntrypoint` with a `validate` method. Import its base class and types from `@cloudflare/zt-types`.

<PackageManagers pkg="@cloudflare/zt-types" />

The method receives a `DlpValidationInput` with the entry ID, match offsets, and request method and URL. Its `excerpt` is base64-encoded and contains the match with surrounding context. The `userId` field is optional.

<TypeScriptExample filename="src/index.ts">

```ts
import {
	DlpValidation,
	type DlpValidationInput,
	type DlpValidationResult,
} from "@cloudflare/zt-types";

export class DlpValidationEntrypoint extends DlpValidation {
	async validate(input: DlpValidationInput): Promise<DlpValidationResult> {
		if (input.entryId === "entry-id-to-reject") {
			return { score: "veto" };
		}

		return { score: 0 };
	}
}
```

</TypeScriptExample>

Replace the example condition with your validation logic. Return a score from `-1` to `1` to adjust confidence. Positive scores increase confidence. Negative scores decrease it. A score of `0` leaves confidence unchanged. Return `"veto"` to reject the match.

## Attach a Worker to a pattern entry

<Steps>

1. Log in to Cloudflare One and go to **Data loss prevention** > **Detection entries**.

   <DashButton url="/?to=/:account/dlp/detection-entries" buttonName="Detection entries" zeroTrust />

2. From the **Pattern** tab, add a pattern entry or edit an existing custom pattern entry.
3. In **Validators**, select **Custom functions**.
4. Select **Add custom function**.
5. Select **Create new**, or select **Use existing** and choose a saved function.
6. For a new function, enter a name and select an eligible Worker.
7. (Optional) For a new function, choose the default behavior for Worker errors or timeouts.
8. For a new function, select **Create**. Select **Save** on the pattern entry.

</Steps>

Default behavior options are _No fallback_, _Veto_, or _Score_. Scores range from `-1` to `1`.

To use the pattern entry in DLP scanning, add it to a [custom DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile).

# Detection entries

Detection entries are the reusable detection logic that Cloudflare DLP uses to identify sensitive content in your web traffic and SaaS applications. You can create and manage detection entries independently of DLP profiles, then add the same entry to one or more custom profiles.

Use the following pages to configure detection entries and review Cloudflare-managed predefined detections.

## Detection entry pages

- [Configure detection entries](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/) — Create and manage pattern entries, datasets, document entries, and AI prompt topics.
- [Validate data loss prevention matches with Cloudflare Workers](/cloudflare-one/data-loss-prevention/detection-entries/custom-validation-workers/) - Attach a Worker in your account to a pattern entry to adjust confidence or reject a match.
- [Predefined detection entries](/cloudflare-one/data-loss-prevention/detection-entries/predefined-detection-entries/) — Review Cloudflare-managed predefined detections and their descriptions.

# Predefined detection entries

Predefined detection entries are Cloudflare-managed detections for specific types of sensitive content. You can review these entries from the **Predefined** view in **Detection entries**.

You can add any predefined detection entry directly to a custom [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile) or [data class](/cloudflare-one/data-loss-prevention/data-classification/build-a-data-class/). Use the following reference to review all predefined detection entries currently supported by Cloudflare DLP.

| Detection entry | Description |
| --- | --- |
| AI Prompt Content: Credentials and Secrets | Prompt contains API keys, passwords, or other sensitive credentials |
| AI Prompt Content: Customer data | Prompt contains customer names, projects, business activities, or confidential customer contexts |
| AI Prompt Content: Financial Information | Prompt contains actual financial numbers or confidential business data |
| AI Prompt Content: PII | Prompt contains personal information (names, SSNs, emails, etc.) |
| AI Prompt Content: Source code | Prompt contains source code, code snippets, or proprietary algorithms |
| AI Prompt Intent: Code Abuse and Malicious Code | Malicious code or attempts to exploit vulnerabilities |
| AI Prompt Intent: Jailbreak | Prompt attempts to circumvent security policies |
| AI Prompt Intent: PII | Prompt requests specific personal information about individuals |
| Amazon AWS Access Key ID | Detects Amazon AWS access key IDs such as `AKIA<ACCESS_KEY_ID>`. |
| Amazon AWS Secret Access Key | Detects potential Amazon AWS secret access keys such as `<AWS_SECRET_ACCESS_KEY>`. |
| American Express Card Number | Detects American Express credit card numbers such as "378282246310005". |
| American Express Text | Detects mentions of the American Express brand name such as "American Express". |
| Australia Address | Detects Australian street addresses with state and postcode such as "100 George St, Sydney NSW 2000". |
| Australia Business (ABN) | Detects Australian Business Numbers (ABN) such as "51 824 753 556". |
| Australia Company (ACN) | Detects Australian Company Numbers (ACN) such as "001 000 004". |
| Australia Medicare | Detects Australian Medicare card numbers such as "2000000006". |
| Australia Passport Number | Detects Australian passport numbers such as "L1234567". |
| Australia Tax File Number | Detects Australian Tax File Numbers (TFN) such as "85 655 734". |
| Austria SSN (SV-Nummer) | Detects Austrian social security numbers (SV-Nummer) such as "1018 010180". |
| Austria Tax ID | Detects Austrian tax identification numbers (Steuernummer) such as "12 345/6789". |
| Austria VAT (UID) | Detects Austrian VAT numbers (UID-Nummer) such as "ATU12345678". |
| Belgium Tax ID (NN) | Detects Belgian national numbers (Numéro National / Rijksregisternummer) such as "85.07.30-000.61". |
| Belgium VAT | Detects Belgian VAT numbers such as "BE0000000097". |
| Bitcoin Wallet | Detects Bitcoin wallet addresses such as "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2". |
| Brazil CNPJ | Detects Brazilian corporate taxpayer registry numbers (CNPJ) such as "11.222.333/0001-81". |
| Brazil CPF (Tax ID) | Detects Brazilian individual taxpayer registry numbers (CPF) such as "529.982.247-25". |
| Bulgaria Uniform Civil (EGN) | Detects Bulgarian Uniform Civil Numbers (EGN) such as "2405500007". |
| C | Detects C source code. |
| C# | Detects C# source code. |
| C++ | Detects C++ source code. |
| Canada Bank Account Number | Detects Canadian bank account numbers (institution + transit + account) such as "12345-678 1234567". |
| Canada Health Number | Detects Canadian provincial health card numbers such as "1234-567-897". |
| Canada Passport | Detects Canadian passport numbers such as "AB123456". |
| Canada PHIN (Manitoba) | Detects Manitoba Personal Health Identification Numbers (PHIN) such as "100000009". |
| Canada Physical Address | Detects Canadian street addresses with postal code such as "100 Main St, Ottawa, ON K1A 0B1". |
| Canada Social Insurance Number | Detects Canadian Social Insurance Numbers (SIN) such as "114 905 474". |
| Chile National ID (RUT) | Detects Chilean national identification numbers (RUT / Rol Único Tributario) such as "12.345.678-5". |
| China ID Card | Detects Chinese resident identity card numbers such as "11010519491231002X". |
| Cloudflare Account Owned API Token | Detects Cloudflare account-owned API tokens such as `cfat_<ACCOUNT_OWNED_API_TOKEN>`. |
| Cloudflare User API Key | Detects Cloudflare user API keys such as `cfk_<USER_API_KEY>`. |
| Cloudflare User API Token | Detects Cloudflare user API tokens such as `cfut_<USER_API_TOKEN>`. |
| Croatia Personal ID (OIB) | Detects Croatian personal identification numbers (OIB) such as "10000000005". |
| CVV Card Number (labeled) | Detects credit card CVV/CVC verification codes in context of the "cvv" keyword, such as "cvv: 033". |
| Denmark Tax (CPR) | Detects Danish personal identification numbers (CPR-nummer) such as "010180-0008". |
| Diners Club Card Number | Detects Diners Club credit card numbers such as "30121690374838". |
| Discord Webhook | Detects Discord webhook URLs such as a webhook URL under `discord.com/api/webhooks/`. |
| Email Address | Detects email addresses such as "test@example.com". |
| Ethereum Wallet | Detects Ethereum wallet addresses such as "0x71C7656EC7ab88b098defB751B7401B5f6d8976F". |
| EU Passport | Detects EU member state passport numbers such as "AB1234567". |
| FDA Active Ingredients | Detects FDA-registered drug active ingredient names such as "ABEMACICLIB". |
| FDA Drug Names | Detects FDA-registered drug names such as "ABACAVIR". |
| Finland Tax ID | Detects Finnish personal identity codes (Henkilötunnus / HETU) such as "311280-888Y". |
| France CNI (National ID) | Detects French national identity card numbers (Carte nationale d'identité) such as "12345678901". |
| France Passport | Detects French passport numbers such as "12AB34567". |
| France Social Security Number | Detects French social security (INSEE) numbers such as "145081849670637". |
| France Tax ID (SPI) | Detects French tax identification numbers (Numéro fiscal SPI) such as "1234567890123". |
| France VAT | Detects French VAT numbers such as "FR12000000000". |
| Full Name | Detects personal full names. |
| Germany Tax ID | Detects German tax identification numbers (Steueridentifikationsnummer) such as "10000000005". |
| Germany VAT | Detects German VAT numbers (USt-IdNr) such as "DE100000003". |
| GitHub PAT | Detects GitHub personal access tokens such as a token beginning with `ghp_`. |
| Go | Detects Go source code. |
| Google GCP API Key | Detects Google Cloud Platform API keys such as `AIza<API_KEY>`. |
| Greece Tax (AFM) | Detects Greek tax identification numbers (AFM) such as "100000003". |
| Haskell | Detects Haskell source code. |
| Hong Kong Identity Card Number | Detects Hong Kong identity card (HKID) numbers such as "F543210(A)". |
| Hungary Tax | Detects Hungarian tax identification numbers (Adóazonosító jel) such as "8000000008". |
| IBAN | Detects International Bank Account Numbers (IBAN) such as "GB94 BARC 1020 1530 0934 59". |
| ICD-10 FY2023 Short Description | Detects ICD-10 FY2023 medical diagnosis terms such as "Typhoid fever, unspecified". |
| ICD-11 Short Description | Detects ICD-11 medical diagnosis terms such as "ABDOMINAL ACTINOMYCOSIS". |
| India Aadhaar | Detects Indian Aadhaar national identification numbers such as "2345 6789 0124". |
| India GST (GSTIN) | Detects Indian Goods and Services Tax identification numbers (GSTIN) such as "22AAAAA0000A1Z5". |
| India PAN Card | Detects Indian Permanent Account Number (PAN) card identifiers such as "ABCDE1234F". |
| India Voter ID | Detects Indian Voter ID (EPIC) numbers such as "ABC1234567". |
| Indonesia Identity Card Number | Detects Indonesian identity card (KTP/NIK) numbers such as "3203012503770011". |
| Indonesia Tax (NPWP) | Detects Indonesian taxpayer identification numbers (NPWP) such as "12.345.678.9-012.345". |
| Ireland Tax (PPS) | Detects Irish Personal Public Service numbers (PPS) such as "1234567FA". |
| Ireland VAT | Detects Ireland VAT numbers such as "IE1234567T". |
| Italy Fiscal Code | Detects Italian fiscal codes (Codice Fiscale) such as "BNZVCN32S10E573Z". |
| Japan Postal Code | Detects a Japanese postal code preceded by the 〒 marker, such as "〒100-0001". Free-form addresses without a 〒 postal-code marker are not matched. |
| Japan My Number (Corp) | Detects Japanese corporate My Number identifiers (Hojin Bango) such as "7000012050002". |
| Japan My Number (Person) | Detects Japanese individual My Number identifiers (Kojin Bango) such as "100000000005". |
| Japan Names (Kanji, labeled) | Detects a kanji personal name only when it immediately follows an inline label such as 氏名, 名前, or Name — for example "氏名: 田中太郎". Bare kanji names with no preceding label (for example a standalone name or one in a spreadsheet column) are not matched. |
| Japan Passport | Detects Japanese passport numbers such as "TZ1234567". |
| Java | Detects Java source code. |
| JavaScript | Detects JavaScript source code. |
| Korea Resident Number (RRN) | Detects South Korean Resident Registration Numbers (RRN) such as "850515-1234567". |
| Lua | Detects Lua source code. |
| Luxembourg Tax | Detects Luxembourg national identification numbers (Matricule) such as "1985010112345". |
| Luxembourg VAT | Detects Luxembourg VAT numbers such as "LU10000053". |
| Malaysian National Identity Card Number | Detects Malaysian national identity card (MyKad) numbers such as "560224-10-8354". |
| Mastercard Card Number | Detects Mastercard credit card numbers such as "5252 5971 4219 4116". |
| Mastercard Text | Detects mentions of the Mastercard brand name such as "Mastercard". |
| Microsoft Azure Client Secret | Detects Microsoft Azure client secrets such as `<AZURE_CLIENT_SECRET>`. |
| MX CLABE (Bank) | Detects Mexican CLABE bank account numbers such as "032180000118359719". |
| MX CURP | Detects Mexican CURP codes such as "GOMC850515HJCRRR05". |
| Netherlands BSN | Detects Dutch citizen service numbers (Burgerservicenummer / BSN) such as "123456782". |
| Netherlands VAT | Detects Dutch VAT numbers (Btw-nummer) such as "NL123456782B01". |
| NPM Token | Detects npm registry access tokens such as a token beginning with `npm_`. |
| NZ NHI Number | Detects New Zealand National Health Index (NHI) numbers such as "ZZZ0016". |
| NZ Tax (IRD) | Detects New Zealand Inland Revenue Department (IRD) tax numbers such as "49-091-850". |
| OpenAI API Key | Detects OpenAI API keys such as a key beginning with `sk-proj-`. |
| Peru Tax (RUC) | Detects Peruvian taxpayer identification numbers (RUC) such as "20000000001". |
| Peru Unique ID (DNI) | Detects Peruvian national identity numbers (DNI) such as "12345678". |
| Philippines Unified Multi-Purpose ID Identity Number | Detects Philippines Unified Multi-Purpose ID (UMID) numbers such as "2460-1501400-1". |
| Poland National ID (PESEL) | Detects Polish national identification numbers (PESEL) such as "85051500006". |
| Poland REGON | Detects Polish National Business Registry numbers (REGON) such as "100000008". |
| Poland Tax (NIP) | Detects Polish tax identification numbers (NIP) such as "123-456-32-18". |
| Portugal Tax (NIF) | Detects Portuguese tax identification numbers (NIF / Número de Contribuinte) such as "100000002". |
| PyPI Token | Detects PyPI package upload tokens such as a token beginning with `pypi-`. |
| Python | Detects Python source code. |
| R | Detects R source code. |
| Rust | Detects Rust source code. |
| Singapore National Registration Identity Card Number | Detects Singapore NRIC/FIN identity card numbers such as "S6792120H". |
| Slack API Token | Detects Slack API tokens such as a token beginning with `xoxb-`. |
| Slack Webhook | Detects Slack incoming webhook URLs such as a webhook URL under `hooks.slack.com/services/`. |
| Spain DNI/NIF | Detects Spanish national identity numbers (DNI/NIF) such as "12345678Z". |
| Spain SSN | Detects Spanish Social Security affiliation numbers (NAF) such as "28 1234567890". |
| Spain Tax (CIF) | Detects Spanish corporate tax identification codes (CIF) such as "A58818501". |
| SSH Private Key | Detects SSH private key material such as "-----BEGIN OPENSSH PRIVATE KEY-----". |
| Stripe Granular Restricted Key | Detects Stripe live-mode restricted API keys such as a key beginning with `rk_live_`. |
| Stripe Standard Secret Key | Detects Stripe live-mode secret API keys such as a key beginning with `sk_live_`. |
| Sweden Tax | Detects Swedish personal identity numbers (Personnummer) such as "811228-9874". |
| SWIFT | Detects SWIFT/BIC business identifier codes such as "PMFAUS66". |
| Swift | Detects Swift source code. |
| Taiwan National Identification Number | Detects Taiwan national identification numbers such as "W171845961". |
| Thai Identity Card Number | Detects Thai national identity card numbers such as "4-8547-01245-28-9". |
| UAE Passport | Detects United Arab Emirates passport numbers such as "A1234567". |
| Union Pay Card Number | Detects UnionPay credit card numbers such as "6250941006528599". |
| Union Pay Text | Detects mentions of the UnionPay brand name such as "Union Pay". |
| United Kingdom National Insurance Number | Detects UK National Insurance Numbers (NINO) such as "OC 66 31 85 C". |
| United Kingdom NHS Number | Detects UK NHS patient identification numbers such as "485-585-0454". |
| United States ABA Routing Number | Detects US ABA bank routing numbers such as "021000021". |
| United States SSN Numeric Detection | Detects US Social Security Numbers such as "123-45-6789". |
| United States SSN Text | Detects mentions of "SSN" or "social security" as a keyword, such as "social security". |
| Unsanitized HAR File | Detects HAR (HTTP Archive) files that may contain unsanitized authentication tokens or session data, such as a HAR file containing `Authorization: Bearer <USER_API_TOKEN>`. |
| Uruguay ID (CI) | Detects Uruguayan national identity numbers (Cédula de Identidad) such as "1.234.500-8". |
| US Driver's License Number | Detects US driver's license numbers such as "CA License: A1234567". |
| US Individual Tax Identification Number (ITIN) | Detects US Individual Taxpayer Identification Numbers (ITIN) such as "934-78-5678". |
| US Mailing Address | Detects US mailing addresses such as "100 First St, Chicago, IL 60601". |
| US Passport Number | Detects US passport numbers such as "A12345678". |
| US Phone Number | Detects US phone numbers such as "555-555-5555". |
| Visa Card Number | Detects Visa credit card numbers such as "4111 1111 1111 1111". |
| Visa Text | Detects mentions of the Visa brand name such as "Visa". |

# Common policies

import { GlossaryTooltip, Render } from "~/components";

The following DLP policies are commonly used to secure sensitive data in uploaded and downloaded files. They are built as [Gateway HTTP policies](/cloudflare-one/traffic-controls/http-policies/) using the [DLP Profile](/cloudflare-one/traffic-controls/http-policies/#dlp-profile) selector.

Before using these policies, complete the [prerequisites for scanning HTTP traffic](/cloudflare-one/data-loss-prevention/dlp-policies/#prerequisites).

## Log uploads/downloads

When you want to monitor where sensitive data is going before enforcing blocks, use the **Allow** action. In a Gateway HTTP policy, all matches — including Allow — are recorded in your [HTTP request logs](/cloudflare-one/data-loss-prevention/dlp-policies/#4-view-dlp-logs). This gives you visibility into sensitive data transfers without disrupting users.

The following example logs any upload or download that matches your enabled [Financial Information](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#financial-information) DLP profile entries when users interact with file sharing applications.

| Selector           | Operator | Value                   | Logic | Action |
| ------------------ | -------- | ----------------------- | ----- | ------ |
| DLP Profile        | in       | _Financial Information_ | And   | Allow  |
| Content Categories | in       | _File Sharing_          |       |        |

## Block file types

<Render file="gateway/policies/block-file-types" product="cloudflare-one" />

For more information on what file formats DLP can scan, refer to [Supported file types](/cloudflare-one/data-loss-prevention/#supported-file-types).

## Block uploads/downloads for specific users

You can configure access on a per-user or group basis by adding [identity-based conditions](/cloudflare-one/traffic-controls/identity-selectors/) to your policies. These selectors match against user attributes from your configured identity provider.

The following example blocks only contractors from uploading/downloading Financial Information to file sharing apps. Users who are not in the _Contractors_ group are not affected by this policy.

| Selector           | Operator | Value                   | Logic | Action |
| ------------------ | -------- | ----------------------- | ----- | ------ |
| DLP Profile        | in       | _Financial Information_ | And   | Block  |
| Content Categories | in       | _File Sharing_          | And   |        |
| User Group Names   | in       | _Contractors_           |       |        |

## Exclude Android applications

Many Android applications (such as Google Drive) use <GlossaryTooltip term="certificate pinning" link="/ssl/reference/certificate-pinning/">certificate pinning</GlossaryTooltip>, which is incompatible with Gateway TLS decryption. These applications verify they are connecting directly to their own servers and will reject Gateway's inspection certificate. If needed, you can create a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) so that the app can continue to function on Android:

1. Set up an [OS version device posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/os-version/) that checks for the Android operating system.

2. Create the following HTTP policy in Gateway:

   | Selector                     | Operator | Value                | Logic | Action         |
   | ---------------------------- | -------- | -------------------- | ----- | -------------- |
   | Application                  | in       | _Google Drive_       | And   | Do Not Inspect |
   | Passed Device Posture Checks | in       | _OS Version Android_ |       |                |

Android users can now use the app, but the app traffic will bypass Gateway inspection entirely — including DLP scanning, HTTP logging, and antivirus scanning.

## Exclude specific sites

In your [DLP logs](/cloudflare-one/data-loss-prevention/dlp-policies/#4-view-dlp-logs), you may find that certain sites routinely trigger DLP detections that do not represent actual data loss (false positives). To exempt these sites from DLP scanning:

1. [Create a list](/cloudflare-one/reusable-components/lists/) of hostnames or URLs.

2. Exclude the list from your DLP policy using the `not in list` operator, which references the list you created in step 1:

   | Selector    | Operator    | Value                   | Logic | Action |
   | ----------- | ----------- | ----------------------- | ----- | ------ |
   | DLP Profile | in          | _Financial Information_ | And   | Block  |
   | Application | in          | _Google Drive_          | And   |        |
   | Domain      | not in list | _Do not DLP - SSN_      |       |        |

# Scan HTTP traffic

import { Badge, GlossaryTooltip } from "~/components";

You can scan HTTP traffic for sensitive data through [Secure Web Gateway](/cloudflare-one/traffic-controls/) policies. To enforce <GlossaryTooltip term="Cloudflare Data Loss Prevention (DLP)">DLP</GlossaryTooltip> policies, first configure a **DLP profile** that defines what sensitive data patterns to detect. Then build a **Gateway HTTP policy** that defines what action to take (allow, block, or log) when Gateway finds matching data.

Before creating a policy, use [Passive Detection](/cloudflare-one/data-loss-prevention/passive-detection/) to explore sensitive data in sampled Gateway traffic. Its findings can help you choose which data types and destinations your policy should cover.

:::note
To scan AI prompts and responses without Gateway HTTP filtering, you can also enable DLP directly on an [AI Gateway](/ai-gateway/features/dlp/).
:::

## Prerequisites

- Set up [Gateway HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/). This routes your users' web traffic through Cloudflare Gateway so it can be inspected.
  - HTTP filtering requires turning on the [Gateway proxy](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy) for TCP traffic.
- Turn on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/#turn-on-tls-decryption). Because most web traffic is encrypted with HTTPS, Gateway must decrypt it before DLP can scan the request body for sensitive data.

## 1. Configure a DLP profile

A DLP profile defines the sensitive data patterns you want to detect — for example, social security number formats, credit card numbers, or custom patterns specific to your organization. Refer to [Configure a DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/). We recommend getting started with a predefined profile.

## 2. Create a DLP policy

DLP Profiles may be used alongside other Cloudflare One rules in a [Gateway HTTP policy](/cloudflare-one/traffic-controls/http-policies/). To start logging or blocking traffic, create a policy for DLP:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**. Select **HTTP**.
2. Select **Add a policy**.
3. Build an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) using the [DLP Profile](/cloudflare-one/traffic-controls/http-policies/#dlp-profile) selector. For example, the following policy blocks users from uploading sensitive data to any location other than an approved corporate application. It combines three conditions: the request content matches a DLP profile, the HTTP method is `POST`, and the destination is not an approved application:

   | Selector    | Operator | Value                                                     | Logic | Action |
   | ----------- | -------- | --------------------------------------------------------- | ----- | ------ |
   | DLP Profile | in       | _Social Security, Insurance, Tax, and Identifier Numbers_ | And   | Block  |
   | HTTP Method | in       | _POST_                                                    | And   |        |
   | Application | not in   | _Workday_                                                 |       |        |

4. Select **Create policy**.

Gateway now applies the configured action when traffic matches the complete HTTP policy.

### DLP with the Forward action <Badge text="Beta" variant="caution" />

When you use [Forward](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) to invoke a Worker, Gateway scans the original request body before delivering it to the Worker. A DLP Block decision can interrupt an already-running Worker.

Gateway scans the final response after the Worker. Modified request bytes are not scanned again before reaching the origin.

Worker context does not include DLP match details. DLP matches cannot select a Forward policy.

## 3. Test DLP policy

You can test your DLP policy on any device connected to your [Zero Trust organization](/cloudflare-one/). To perform a basic test:

1. Go to [dlptest.com](http://dlptest.com/http-post/).
2. Enter a text message or upload a file containing the sensitive data.
3. Select **Submit** to send the request.

The request will be allowed or blocked according to your DLP policies. If the data matches a DLP policy, you will see the request in your [DLP logs](#4-view-dlp-logs).

Different sites will send requests in different ways. For example, some sites will split a file upload into multiple requests. Therefore, even if the policy works on `dlptest.com`, it is not guaranteed to work the same way on another site or application.

If the request is not blocked as expected, use [Test scan](/cloudflare-one/data-loss-prevention/test-scan/) to confirm that the profile detects your sample content on its own. A profile that matches in a test scan but not in traffic points to the policy, the traffic path, or TLS decryption.

## 4. View DLP logs

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Logs** > **HTTP request logs**.
2. Select **Filter**.
3. Choose an item under one of the following filters:
   - **DLP Profiles** shows the requests which matched a specific DLP profile.
   - **Policy** shows the requests which matched a specific DLP policy.

You can expand an individual row to view details about the request. By default, logs show that a match occurred but do not include the actual matched content. To see the data that triggered the DLP policy, [configure logging options](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/).

### Report false positives

If DLP flags a request that does not actually contain sensitive data (a false positive), you can report it to Cloudflare:

1. Select the log you want to report.
2. Select **Report DLP false positive** under **DLP details**.
3. The information to be sent to Cloudflare will appear. To confirm your report, select **Send report**.

Cloudflare will not respond directly to your report, but reporting false positives helps us improve our products. If you require technical assistance, reach out to [support](https://dash.cloudflare.com/?to=/:account/support).

# Logging options

Data Loss Prevention allows you to capture, store, and view the data that triggered a specific DLP policy for use as forensic evidence. DLP offers three logging approaches, each suited to different needs:

| Approach                                                                    | What it captures                                            | Encryption                      | Availability |
| --------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------- | ------------ |
| [Payload logging](#log-the-payload-of-matched-rules)                        | Redacted match + 75 bytes of surrounding context            | Encrypted with your public key  | All plans    |
| [AI prompt logging](#log-generative-ai-prompt-content)                      | Generative AI prompt topic, user prompt, and model response | Encrypted with your public key  | All plans    |
| [Logpush forensic copies](#send-dlp-forensic-copies-to-logpush-destination) | Complete HTTP request (headers + body)                      | Encrypted in transit only (TLS) | Enterprise   |

Users on all plans can log the [payload](#log-the-payload-of-matched-rules) or [generative AI prompt content](#log-generative-ai-prompt-content) of matched HTTP requests in their Cloudflare logs. Additionally, Enterprise users can [configure a Logpush job](#send-dlp-forensic-copies-to-logpush-destination) to send copies of entire matched HTTP requests to storage destinations.

The data that triggers a DLP policy is stored in the body of the HTTP request — the part that carries content such as file uploads, form submissions, and chat messages. This body is referred to as the payload. Payload logging is especially useful when diagnosing the behavior of DLP policies. Since the values that triggered a rule may contain sensitive data, they are encrypted with a customer-provided public key so that only you can examine them later. The stored data will include a redacted version of the match, plus 75 bytes of additional context on both sides of the match.

## Set a DLP payload encryption public key

Before you begin logging DLP payloads, you will need to [set a DLP payload encryption public key](/cloudflare-one/data-loss-prevention/dlp-settings/#payload-encryption-key). DLP uses public-key encryption so that matched sensitive data is readable only by you — Cloudflare does not have access to your private key and cannot decrypt your logs.

You can also [configure payload log masking](/cloudflare-one/data-loss-prevention/dlp-settings/#payload-log-masking) to control how DLP redacts sensitive data in logs.

## Log the payload of matched rules

DLP can log the payload of matched HTTP requests in your Cloudflare logs. Use payload logging to verify what content triggered a DLP detection — for example, to confirm whether a match was a real finding or a false positive.

### Turn on payload logging for a DLP policy

You can enable payload logging for any Allow or Block HTTP policy that uses the [_DLP Profile_](/cloudflare-one/traffic-controls/http-policies/#dlp-profile) selector — the filter condition that matches traffic against your DLP detection profiles.

1. Go to **Traffic policies** > **Firewall policies** > **HTTP**.
2. Edit an existing Allow or Block DLP policy, or [create a new policy](/cloudflare-one/data-loss-prevention/dlp-policies/#2-create-a-dlp-policy).
3. In the policy builder, scroll down to **Configure policy settings** and turn on **Log the payload of matched rules**.
4. Select **Save**.

Data Loss Prevention will now store a portion of the payload for HTTP requests that match this policy.

### View payload logs

To view DLP payload logs:

1. Go to **Insights** > **Logs** > **HTTP request logs**.
2. Go to the DLP log you are interested in reviewing and expand the row.
3. Select **Decrypt payload log**.
4. Enter your private key and select **Decrypt**.

You will see the [ID of the matched DLP Profile](/api/resources/zero_trust/subresources/dlp/subresources/profiles/methods/list/) followed by the decrypted payload.

:::note
Cloudflare does not store the key or the decrypted payload.
:::

### Report false and true positives to AI context analysis

When you have [AI context analysis](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#ai-context-analysis) turned on for a DLP profile, you can improve detection accuracy over time by reporting false and true positives. A false positive is a match that DLP flagged incorrectly (the content was not actually sensitive). A true positive confirms that DLP correctly identified sensitive data. These reports train the AI model to adjust its confidence threshold.

To report a DLP match payload as a false or true positive:

1. [Find and decrypt](#view-payload-logs) the payload log you want to report.
2. In **Log details**, choose a detected context match.
3. In **Context**, select the redacted match data.
4. In **Match details**, choose whether you want to report the match as a false positive or a true positive.

Based on your report, DLP's machine learning will adjust its confidence in future matches for the associated profile.

### Data privacy

- All Cloudflare logs are encrypted at rest (encrypted while stored on disk). Encrypting the payload content adds a second layer of encryption for the matched values that triggered a DLP rule.
- Cloudflare cannot decrypt encrypted payloads, since this operation requires your private key. Cloudflare staff will never ask for the private key.
- By default, DLP uses Full Mask to redact alphanumeric characters in the matched pattern, replacing them with `*` while preserving the format. For example, `123-45-6789` becomes `***-**-****`. You can [configure the masking level](#configure-payload-log-masking) to show partial or full matches if your incident response workflow requires more context.
  - You can define sensitive data with [Exact Data Match (EDM)](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/#exact-data-match-datasets). EDM match logs will redact your defined strings.

## Log generative AI prompt content

DLP can detect and log the prompt topic sent to an AI tool.

### Turn on AI prompt content logging for a DLP policy

You can enable AI prompt content logging for any Allow or Block HTTP policy that uses the [_Application_](/cloudflare-one/traffic-controls/http-policies/#application) selector with a supported [Application Granular Controls](/cloudflare-one/traffic-controls/http-policies/#granular-controls) application. This means your policy must target a specific AI application (such as ChatGPT) that Gateway can inspect at a granular level.

1. Go to **Traffic policies** > **Firewall policies** > **HTTP**.
2. Edit an existing Allow or Block DLP policy, or [create a new policy](/cloudflare-one/data-loss-prevention/dlp-policies/#2-create-a-dlp-policy).
3. In the policy builder, scroll down to **Configure policy settings** and turn on **Capture generative AI prompt content in logs**.
4. Select **Save**.

Data Loss Prevention will now store the user prompt and AI model response for requests that match this policy.

### View prompt logs

To view generative AI prompt log details:

1. Go to **Insights** > **Logs** > **HTTP request logs**.
2. Go to the DLP log you are interested in reviewing and expand the row.
3. Select **Decrypt payload log**.
4. Enter your private key and select **Decrypt**.
5. In **Summary** > **GenAI prompt captured**, select **View prompt**.

Gateway logs will provide a summary of the conversation, including the topic and AI model used, and the user prompt and AI model's raw response if available. A text prompt must be present for DLP to capture the prompt.

## Send DLP forensic copies to Logpush destination

:::note[Availability]
Only available on Enterprise plans.
:::

Unlike payload logging (which stores encrypted excerpts of matched content), forensic copies send the complete, unaltered HTTP request — including all headers and the full body — to an external storage destination.

Gateway allows you to send copies of entire HTTP requests matched in HTTP Allow and Block policies to storage destinations configured in [Logpush](/logs/logpush/) (Cloudflare's log delivery service), including third-party destinations. Forensic copies include unaltered payloads and headers which may include sensitive data. Logpush logs are encrypted in transit only, such as when sent as TLS traffic. Once the data reaches your storage destination, it is stored according to that destination's encryption policies — not encrypted by Cloudflare.

To set up the DLP Forensic Copy Logpush job:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** >**Logs**, and select **Manage Logpush**.
2. In Logpush, select **Create a Logpush job**.
3. Choose a [Logpush destination](/logs/logpush/logpush-job/enable-destinations/).
4. In **Configure logpush job**, choose the _DLP forensic copies_ dataset. Select **Create Logpush job**.
5. Return to **Zero Trust** and go to **Traffic policies** > **Firewall policies** > **HTTP**.
6. Edit an existing Allow or Block policy, or [create a new policy](/cloudflare-one/data-loss-prevention/dlp-policies/#2-create-a-dlp-policy). Your policy does not need to include a DLP profile — any Gateway HTTP policy can send forensic copies.
7. In the policy builder, scroll down to **Configure policy settings** and turn on **Send DLP forensic copies to storage**.
8. Select a storage destination. Gateway will list any configured Logpush jobs or integrations that can receive HTTP requests.
9. Select **Save policy**.

DLP will now send a copy of HTTP requests that match this policy to your Logpush destination.

Logpush supports up to four DLP Forensic Copy Logpush jobs per account. By default, Gateway will send all matched HTTP requests to your configured DLP Forensic Copy jobs. To send specific policy matches to specific jobs, configure [Log filters](/logs/logpush/logpush-job/filters/). If the request contains an archive file, DLP will only send up to 100 MB of uncompressed content to your configured storage.

# Profile settings

Profile settings control detection behavior for an individual DLP profile. You configure these settings when you [build a custom profile](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile) or edit an existing [predefined](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/) or custom profile.

Profile settings are distinct from [DLP settings](/cloudflare-one/data-loss-prevention/dlp-settings/), which are account-level settings that apply across all profiles and policies.

## Edit profile settings

To edit profile settings for an existing predefined or custom DLP profile:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Profiles**.
2. Choose a profile, then select **Edit**.
3. In **Settings**, configure the [settings](#available-settings) for your profile.
4. Select **Save profile**.

## Available settings

The following advanced detection settings are available for predefined and custom DLP profiles.

### Match count

Match count sets a minimum threshold for the number of detections required to trigger an action. DLP does not block or log content until the detection count reaches this threshold.

For example, if you set a match count of `10`, DLP takes action when it finds 10 or more matches in a single file or HTTP body. Matches do not have to be unique — the same credit card number appearing 10 times counts as 10 matches.

### Optical Character Recognition (OCR)

:::note[Deprecation notice]
Profile-level OCR settings will be deprecated in a future release. We recommend configuring OCR in [DLP settings](/cloudflare-one/data-loss-prevention/dlp-settings/#optical-character-recognition-ocr) instead.
:::

Optical Character Recognition (OCR) extracts and analyzes text within image files. When enabled, DLP can detect sensitive data within images that users upload or download.

OCR supports scanning `.jpg`/`.jpeg` and `.png` files between 4 KB and 1 MB in size. Text is encoded in UTF-8 format, including support for non-Latin characters.

For more information, refer to [DLP settings](/cloudflare-one/data-loss-prevention/dlp-settings/#optical-character-recognition-ocr).

### AI context analysis {/* ai-context-analysis */}

:::note[Deprecation notice]
Profile-level AI context analysis settings will be deprecated in a future release. We recommend configuring AI context analysis in [DLP settings](/cloudflare-one/data-loss-prevention/dlp-settings/#ai-context-analysis) instead.
:::

:::note
AI context analysis only supports Gateway HTTP and HTTPS traffic.
:::

AI context analysis uses a machine learning model to evaluate the surrounding context of a detection and adjust its confidence level. The model examines nearby text to determine whether a pattern match is likely to be genuine sensitive data or a false positive.

For example, a 16-digit number that matches a credit card pattern may receive a lower confidence score if it appears in a context where credit card numbers are unlikely (such as a product SKU list). Conversely, the same number appearing near terms like "payment" or "billing" would receive a higher confidence score. DLP logs matches that meet or exceed your configured [confidence threshold](#confidence-thresholds).

For full documentation on AI context analysis, refer to [DLP settings](/cloudflare-one/data-loss-prevention/dlp-settings/#ai-context-analysis).

### Confidence thresholds

Confidence thresholds indicate how confident Cloudflare DLP is in a detection. DLP determines the confidence level by inspecting the content for proximity keywords — related terms that appear near the detected data. For example, the word "SSN" appearing near a 9-digit number increases confidence that the number is a Social Security number.

When you set a confidence threshold on a profile, DLP only triggers on detections at that level or higher:

- **Low** (default) — Uses regular expressions with few proximity keywords. This is the most inclusive setting and has the highest tolerance for false positives.
- **Medium** — Applies additional validation to filter out low-confidence detections. This setting has a medium tolerance for false positives.
- **High** — Triggers only on detections classified as high confidence.

Confidence threshold is set on the DLP profile. Not all detection entries support confidence thresholds — when you select a threshold in the dashboard, entries that support confidence scoring display their current level. Entries without a displayed confidence level either do not support this feature or use detection methods (such as exact match) where confidence scoring does not apply.

To change the confidence threshold of a DLP profile:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Profiles**.
2. Select the profile, then select **Edit**.
3. In **Settings** > **Confidence threshold**, choose a new confidence threshold from the dropdown menu.
4. Select **Save profile**.

## Gateway detections

For inline detections in Gateway, you can log lower-confidence matches while blocking only high-confidence detections. This approach requires two HTTP policies with different DLP profiles:

1. A Low or Medium confidence profile with an Allow action — logs the detection without blocking.
2. A High confidence profile with a Block action — blocks the request.

For example:

| Selector    | Operator | Value                          | Action |
| ----------- | -------- | ------------------------------ | ------ |
| DLP Profile | in       | _Low Confidence Detections_    | Allow  |

| Selector    | Operator | Value                          | Action |
| ----------- | -------- | ------------------------------ | ------ |
| DLP Profile | in       | _Medium Confidence Detections_ | Allow  |

| Selector    | Operator | Value                          | Action |
| ----------- | -------- | ------------------------------ | ------ |
| DLP Profile | in       | _High Confidence Detections_   | Block  |

# DLP profiles

import { Render } from "~/components";

A DLP profile defines what sensitive data Cloudflare detects in your traffic. A profile combines one or more of the following building blocks:

- **[Detection entries](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/)** — reusable detection logic that identifies sensitive content, such as patterns, datasets, document fingerprints, predefined detections, and AI prompt topics.
- **Data classes** — reusable classification rules that combine detection entries and other signals into a single rule.
- **Labels** — sensitivity levels and data tags that describe matched content.

Data classes and labels are part of [Data Classification](/cloudflare-one/data-loss-prevention/data-classification/). Cloudflare DLP offers three types of profiles:

- **[Predefined profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/)** — Cloudflare-managed profiles for common sensitive data types such as credit card numbers, national identifiers, and AI prompts.
- **Custom profiles** — profiles you [build](#build-a-custom-profile) from detection entries, data classes, and labels, specific to your data, organization, and risk tolerance.
- **[Integration profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/integration-profiles/)** — profiles populated with data classifications from a third-party platform, such as Microsoft Purview sensitivity labels (requires Cloudflare CASB).

To decide which data types to focus on, use [Passive Detection](/cloudflare-one/data-loss-prevention/passive-detection/) to explore detections in sampled Gateway traffic before creating a DLP policy.

## Configure a predefined profile

<Render
	file="data-loss-prevention/predefined-profile"
	product="cloudflare-one"
/>

You can now use this profile in a [DLP policy](/cloudflare-one/data-loss-prevention/dlp-policies/#2-create-a-dlp-policy), [CASB integration](/cloudflare-one/cloud-and-saas-findings/casb-dlp/), [AI Gateway DLP policy](/ai-gateway/features/dlp/set-up-dlp/), or [Email Security outbound DLP policy](/cloudflare-one/email-security/policies/outbound/outbound-dlp/).

## Build a custom profile

<Render file="data-loss-prevention/custom-profile" product="cloudflare-one" />

Before you apply a custom profile to production traffic, use [Test scan](/cloudflare-one/data-loss-prevention/test-scan/) to confirm that it detects the content you expect.

You can now use this profile in a [DLP policy](/cloudflare-one/data-loss-prevention/dlp-policies/#2-create-a-dlp-policy), [CASB integration](/cloudflare-one/cloud-and-saas-findings/casb-dlp/), [AI Gateway DLP policy](/ai-gateway/features/dlp/set-up-dlp/), or [Email Security outbound DLP policy](/cloudflare-one/email-security/policies/outbound/outbound-dlp/).

# Integration profiles

import { Render } from "~/components";

:::note

Integration profiles require [Cloudflare CASB](/cloudflare-one/integrations/cloud-and-saas/).
:::

Integration profiles let you use data classifications from a third-party platform (such as Microsoft Purview sensitivity labels) directly in Cloudflare DLP. Instead of recreating classification rules yourself, Cloudflare retrieves them from the third-party platform and populates them as [detection entries](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/) in a DLP profile. You can then enable the entries you want and create a DLP policy to allow or block matching data.

Detection entries in integration profiles are managed by the third-party platform. You cannot manually add, edit, or delete these entries within Cloudflare DLP.

## Microsoft Purview Information Protection (MIP) sensitivity labels

Microsoft provides [Purview Information Protection sensitivity labels](https://learn.microsoft.com/en-us/purview/sensitivity-labels) to classify and protect sensitive data.

<Render file="dlp/mip-nested-labels-limitation" product="cloudflare-one" />

### Setup

To add MIP sensitivity labels to a DLP profile, integrate your Microsoft account with [Cloudflare CASB](/cloudflare-one/integrations/cloud-and-saas/microsoft-365/). A new integration profile will appear under **Zero Trust** > **Data loss prevention** > **Profiles**. The profile is named **MIP Sensitivity Labels** followed by the name of the CASB integration.

MIP sensitivity labels can also be added to a [custom DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/#build-a-custom-profile) as an existing entry.

### Syncing

Allow 24 hours for label additions and edits in your Microsoft account to propagate to Cloudflare DLP. Deletions in your Microsoft account will not delete entries in your Cloudflare DLP profile.

# Predefined profiles

import { Render } from "~/components";

Cloudflare Zero Trust provides predefined DLP profiles for common types of sensitive data, such as credit card numbers, national identifiers, and credentials. Each predefined profile groups related [detection entries](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/) which can be turned on or off individually.

To use a predefined profile, [enable the detection entries you want](/cloudflare-one/data-loss-prevention/dlp-profiles/#configure-a-predefined-profile) and add the profile to a [DLP policy](/cloudflare-one/data-loss-prevention/dlp-policies/). You can tune accuracy with [confidence thresholds](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#confidence-thresholds) and other [profile settings](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/).

Most profiles match when any enabled detection entry matches. Some add validation checks to improve accuracy, and a few use profile-specific matching logic to reduce false positives — for example, the [PII Record](#personally-identifiable-information-pii-record) profile requires multiple entries in close proximity.

## AI Prompt

DLP provides AI prompt protection profiles that detect sensitive prompts submitted to generative AI tools such as ChatGPT, Google Gemini, Perplexity, and Claude. Each profile evaluates prompts for **Content** (sensitive data within the prompt) and **Intent** (what the user wants the model to do):

- **AI Prompt: AI Security** — Prompts that attempt to circumvent AI security policies or request malicious code.
- **AI Prompt: Customer** — Prompts that contain customer names, projects, business activities, or confidential customer contexts.
- **AI Prompt: Financial Information** — Prompts that contain financial numbers or confidential business data.
- **AI Prompt: PII** — Prompts that contain or request personal information such as names, SSNs, or email addresses.
- **AI Prompt: Technical** — Prompts that contain source code, code snippets, proprietary algorithms, or credentials such as API keys and passwords.

For the full list of underlying topics, refer to [AI prompt topics](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/#ai-prompt-topics).

## Credentials and Secrets

The following secrets are validated with regex.

- Amazon Web Services (AWS) keys
- Azure API keys
- Google Cloud Platform keys
- SSH keys

The following Cloudflare API credentials are validated algorithmically using a checksum. Only credentials generated after [Cloudflare's token format update](/fundamentals/api/get-started/token-formats/) will be matched by these entries.

| Detection entry | Format |
| --- | --- |
| Cloudflare User API Key | `cfk_` followed by 40 alphanumeric characters and an 8-character hex checksum |
| Cloudflare User API Token | `cfut_` followed by 40 alphanumeric characters and an 8-character hex checksum |
| Cloudflare Account Owned API Token | `cfat_` followed by 40 alphanumeric characters and an 8-character hex checksum |

## Financial Information

<Render
	file="profile-all-plans"
	product="cloudflare-one/data-loss-prevention"
/>

Credit card numbers begin with a six or eight-digit Issuer Identification Number (IIN) and are followed by up to 23 additional digits. Card verification values (CVVs) are not validated.


In the table below, entries use one of three validation methods. [Luhn's algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is a checksum formula used to verify credit card numbers. Entries validated "with checksum" use an arithmetic check specific to that number format. Entries validated "with regex" match a known text pattern without performing a mathematical check.

| Detection entry                  | Notes                                                                             |
| -------------------------------- | --------------------------------------------------------------------------------- |
| American Express Card Number     | Validated using [Luhn's algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm). |
| American Express Text            | Text matching `amex` or `american express`.                                       |
| Diners Club Card Number          | Validated using Luhn's algorithm.                                                 |
| CVV Card Number (labeled)        | Validated with regex.                                                             |
| Mastercard Card Number           | Validated using Luhn's algorithm.                                                 |
| Mastercard Text                  | Text matching `mastercard`.                                                       |
| Union Pay Card Number            | Validated using Luhn's algorithm.                                                 |
| Union Pay Text                   | Text matching `union pay`.                                                        |
| Visa Card Number                 | Validated using Luhn's algorithm.                                                 |
| Visa Text                        | Text matching `visa`.                                                             |
| United States ABA Routing Number | Validated algorithmically with checksum.                                          |
| IBAN                             | Validated with checksum.                                                          |

## Health Information

The following diagnosis and medication names are checked for surrounding ASCII characters to prevent false positives.

- FDA active ingredients
- FDA drug names
- ICD-10 FY2023 short descriptions
- ICD-11 short descriptions

## HTTP Archive (HAR) files

The **Unsanitized HAR** predefined profile detects HTTP Archive (HAR) files in traffic that have not been processed by Cloudflare's HAR sanitizer. HAR files frequently contain sensitive data such as session cookies, authorization headers, and other credentials.

| Detection entry       | Notes                                                                                                                           |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Unsanitized HAR file  | Detects HAR files that do not carry a Cloudflare sanitized marker. Files processed by the Cloudflare HAR sanitizer and unmodified since will not match this entry. |

You can use this profile in a Gateway HTTP policy to block HAR file uploads or redirect users to `https://har-sanitizer.pages.dev/` to sanitize the file before uploading. For more information, refer to [common DLP policies](/cloudflare-one/data-loss-prevention/dlp-policies/common-policies/).

## Personally Identifiable Information (PII) Record

The **Personally Identifiable Information (PII) Record** predefined profile is designed to detect records that contain multiple types of personal data. Unlike most predefined and custom DLP profiles, this profile matches only when at least three unique detection entries are found in close proximity.

This behavior helps reduce false positives from isolated matches.

The profile includes the following detection entries:

- Australia Passport Number
- American Express Card Number
- Diners Club Card Number
- US Driver's License Number
- Email Address
- Full Name
- US Mailing Address
- Mastercard Card Number
- US Individual Tax Identification Number (ITIN)
- US Passport Number
- US Phone Number
- Union Pay Card Number
- United States SSN Numeric Detection
- Visa Card Number

## Social Security, Insurance, Tax, and Identifier Numbers

<Render
	file="profile-all-plans"
	product="cloudflare-one/data-loss-prevention"
/>

The following national identifier detections are validated algorithmically when possible.

| Detection entry                                      | Notes                                                                                                                                                                                                                               |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| United States SSN Numeric Detection                  | Matched values must include commonly used separators. For example, `000-00-0000` matches but `000000000` does not. Unlike credit card numbers, Social Security numbers have no built-in checksum, so DLP validates the format only. |
| Social Security Number Text                          | Text matching `ssn` or `social security`.                                                                                                                                                                                           |
| Australia Tax File Number                            | Validated with checksum.                                                                                                                                                                                                            |
| Canada Social Insurance Number                       | Validated using Luhn's algorithm.                                                                                                                                                                                                   |
| France Social Security Number                        | Validated with regex.                                                                                                                                                                                                               |
| Hong Kong Identity Card (HKIC) Number                | Validated with checksum.                                                                                                                                                                                                            |
| Indonesia Identity Card Number                       | Validated with regex.                                                                                                                                                                                                               |
| Malaysian National Identity Card Number              | Validated with regex.                                                                                                                                                                                                               |
| Philippines Unified Multi-Purpose ID (UMID) Number   | Validated with regex.                                                                                                                                                                                                               |
| Singapore National Registration Identity Card Number | Validated with checksum.                                                                                                                                                                                                            |
| Taiwan National Identification Number                | Validated with checksum.                                                                                                                                                                                                            |
| Thai Identity Card Number                            | Validated with checksum.                                                                                                                                                                                                            |
| United Kingdom NHS Number                            | Validated with checksum.                                                                                                                                                                                                            |
| United Kingdom National Insurance Number             | Validated with regex.                                                                                                                                                                                                               |

## Source Code

The **Source Code** profile detects source code in common programming languages.

<Render
	file="data-loss-prevention/programming-language-list"
	product="cloudflare-one"
/>

# Test scan

import { Steps } from "~/components";

:::note[Availability]
Available as an add-on to Zero Trust Enterprise plans.

Users on Zero Trust Free and Pay-as-you-go plans can run test scans against the [Financial Information](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#financial-information) and [Social Security, Insurance, Tax, and Identifier Numbers](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#social-security-insurance-tax-and-identifier-numbers) predefined profiles.
:::

Use Test scan to check how Cloudflare Data Loss Prevention (DLP) evaluates sample content before you apply a profile to production traffic. You can paste text, upload a file, or upload an HTTP Archive (HAR) file, then review which profiles and detection entries match.

Antivirus scans run as if the content were live traffic. When the scanner detects an image, Optical Character Recognition (OCR) runs even if no profile has OCR turned on, and the extracted text appears in the results.

**Content is scanned in real time and never stored.**

Content goes directly to the DLP scanner. Gateway policies are not evaluated, no traffic passes through Gateway, and no Gateway activity logs are created.

## When to use Test scan

Use this tool to:

- Check whether a new or updated [detection entry](/cloudflare-one/data-loss-prevention/detection-entries/configure-detection-entries/) matches the content you expect.
- Compare sample content against one or more [DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/) before using them in production.
- Investigate false positives and missed detections by reviewing confidence levels, match context, and proximity keywords.
- Confirm how DLP identifies a file and review its antivirus and OCR results.

## Run a test scan

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Test scan**.

2. Under **Input method**, select the type of content you want to test:

   - **Text**: Enter or paste content into **Content to scan**.
   - **File**: Select or drag a file into the upload area. Files can be up to 10 MB. To test several files at once, upload a `.zip` archive. Each file inside the archive is scanned and reported separately.
   - **HAR**: Select or drag a `.har` file into the upload area. Up to 50 bodies are scanned per file. Request and response bodies count toward the same limit.

3. Choose which profiles to test. Turn on **Scan against all profiles**, or select one or more profiles from the list.

4. Select **Scan**.
</Steps>

## Review scan results

Depending on the input and scan result, the results can include the following sections:

| Section               | What it shows                                                                                                                                        |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Summary**           | The number of profiles matched and files scanned, and the overall antivirus status.                                                                  |
| **File metadata**     | The detected file name, size, MIME type, extension, and file categories.                                                                             |
| **Antivirus results** | Whether each file is clean, suspicious, infected, or was not scanned, along with its SHA-256 hash. Infected files include available malware details. |
| **OCR results**       | The text that OCR extracted from each image.                                                                                                         |
| **Profile matches**   | The profiles, detection entries, data classes, data tags, and sensitivity levels that matched.                                                       |
| **Match contexts**    | The matched content, confidence level, and proximity keywords that increased or decreased confidence.                                                |
| **JSON**              | The complete scanner response, which you can download for further analysis.                                                                          |

To save the complete response as `dlp-scan-results.json`, open **JSON** and select **Download**. The file can contain sample payloads and match context. Store and share it as sensitive data.

## Example

A Gateway policy that uses a custom profile is not blocking a spreadsheet that contains credit card numbers. Use a test scan to find out whether the profile or the policy is responsible.

<Steps>
1. Run a test scan with the same file and select only the custom profile. This tests the profile on its own, with Gateway removed from the path.

2. If the scan returns matches, the profile detects this sample under Test scan settings. This does not confirm that Gateway inspects the traffic the same way, so check the Gateway policy conditions, the traffic path, and TLS decryption.

3. If the scan returns no matches, work through the profile itself. A profile matches when its [detection entries](/cloudflare-one/data-loss-prevention/detection-entries/) match, and each entry defines a single pattern or dataset, such as a credit card number pattern, a custom regular expression, or an exact match dataset. Confirm that the profile contains the entry you expect and that the entry is turned on.

4. Compare the entry against the values in the file. Some entries apply [additional validation](/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/) beyond pattern matching. A test value that has the expected format may still fail validation. Use a valid test value for the entry.

5. Check the profile's [match count](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#match-count). If it requires more matches than your sample contains, the profile does not trigger. Add more sample data or lower the match count.

6. Check the profile's [confidence threshold](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#confidence-thresholds). Test scan does not show detections below the threshold in **Match contexts**. To check whether the threshold prevented a match, temporarily lower it and run the scan again. If a match appears, review the match context. For example, adding relevant proximity keywords such as "payment" or "billing" may increase confidence. You can also lower the profile's confidence threshold if appropriate.
</Steps>

## Troubleshoot scan results

### Expected content does not match

Confirm that you selected the intended profile and that the profile contains the expected detection entries. A detection may not appear when the content does not meet the entry's matching requirements, when the profile [match count](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#match-count) is greater than the number of matches in the sample, or when the detection is below the profile's [confidence threshold](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#confidence-thresholds).

### Test scan matches content that should not match

Raise the profile's [confidence threshold](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#confidence-thresholds) so that DLP only triggers on higher confidence detections, or increase the [match count](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#match-count) so that a single incidental value does not trigger the profile. For custom entries, narrow the regular expression or dataset.

[AI context analysis](/cloudflare-one/data-loss-prevention/dlp-settings/#ai-context-analysis) can also reduce false positives in production, but it only supports Gateway HTTP and HTTPS traffic.

If a profile behaves correctly here but blocks legitimate traffic in production, the policy is likely too broad. Refer to [Troubleshoot DLP](/cloudflare-one/data-loss-prevention/troubleshoot-dlp/).

### OCR extracted text but no entries matched

OCR runs on images, but DLP only matches the extracted text against profiles that have OCR turned on. If **OCR results** shows the text you expect and no entry triggered, OCR is turned off for the profile you selected.

To apply OCR to every profile, turn on [OCR in DLP settings](/cloudflare-one/data-loss-prevention/dlp-settings/#optical-character-recognition-ocr). You can also turn on OCR for an [individual profile](/cloudflare-one/data-loss-prevention/dlp-profiles/advanced-settings/#optical-character-recognition-ocr), though profile-level OCR is deprecated.

### Test scan matches but Gateway does not

If the scan detects the content but your Gateway policy does not, the selected profile can detect the sample under Test scan settings. This does not confirm that Gateway can decrypt or inspect the traffic in the same way. Check the Gateway policy, traffic path, TLS decryption, and supported file types. For more information, refer to [Troubleshoot DLP](/cloudflare-one/data-loss-prevention/troubleshoot-dlp/).

### A scan returns an error

For file uploads, confirm that the file is 10 MB or smaller. For HAR files, confirm that the file contains valid HAR data. If the scanner is temporarily unavailable, wait and run the scan again.

## Limitations

- **AI prompt profiles are not supported.**
- Changes to your DLP configuration can take up to two minutes to take effect in a test scan. After you update a profile, detection entry, or data class, wait before testing.
- Profile detection is validated, but Gateway policy conditions and actions are not. To verify that a complete policy allows, blocks, or logs traffic as expected, [test the DLP policy with Gateway traffic](/cloudflare-one/data-loss-prevention/dlp-policies/#3-test-dlp-policy).

:::note
Fair use rate limits apply. This tool is intended for validating configuration, not for bulk or automated scanning.
:::

# Email security

import { Description, Render } from "~/components";

:::caution[Important]
Refer to [Area 1](/email-security/) if you are looking for the Area 1 documentation.
:::

:::note
If you have not yet purchased Email security, you can try Email security with Retro Scan. Refer to [Retro Scan](/cloudflare-one/email-security/retro-scan/) to learn more.
:::

<Description>Protect your email inbox with Email security.</Description>

Cloudflare Email Security uses AI, threat intelligence, and security rules to analyze every incoming email, protecting your organization from phishing, malware, [Business Email Compromise](https://www.cloudflare.com/en-gb/learning/email-security/business-email-compromise-bec/) (where attackers impersonate executives or authority figures to commit fraud), vendor email fraud, and spam.

It integrates with your existing email provider (such as Outlook or Gmail) and can be deployed via [API](/cloudflare-one/email-security/setup/post-delivery-deployment/api/), [BCC](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/gmail-bcc-setup/)/[Journaling](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/journaling-setup/m365-journaling/), or [MX/Inline](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/).

When you complete the [setup process](/cloudflare-one/email-security/setup/), the Cloudflare dashboard will display the Email security overview page.

The Email security overview provides you with:

- **Quick actions**, where you can:
  - View [submissions](/cloudflare-one/email-security/submissions/)
  - Manage detection settings: manage [allow policies](/cloudflare-one/email-security/policies/inbound/allow-policies/), [blocked senders](/cloudflare-one/email-security/policies/inbound/blocked-senders/), [trusted domains](/cloudflare-one/email-security/policies/inbound/trusted-domains/), [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) and [additional detections](/cloudflare-one/email-security/settings/detection-settings/additional-detections/).
  - [Run screens](/cloudflare-one/email-security/investigation/search-email/#screen-criteria): Search, filter, reclassify, and bulk-move emails
- **Recommendations**: Suggested next steps to improve your configuration. For example, submitting misclassified emails for reclassification, creating policies, or protecting users at risk of [impersonation](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/).
- **Email security metrics**: Activity from the last seven days.
- **Recently modified policies**: A list of recently changed policies.
- **Education and resources**: Links to [implementation guides](/cloudflare-one/implementation-guides/), [Email security changelogs](/cloudflare-one/changelog/email-security/), and [API documentation](https://developers.cloudflare.com/api/resources/email_security/subresources/investigate/methods/get/)

To access the Email security overview:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Go to **Email security** > **Overview**.

---

## Troubleshooting

For help resolving common issues with Email Security, refer to [Troubleshoot Email Security](/cloudflare-one/email-security/troubleshooting/).

# PhishGuard

PhishGuard is a team of analysts that routinely inspects your email environment and responds to threats that come through your email inbox.

While Email security uses advanced technologies to protect your email inbox, PhishGuard offers an additional human component to protect your email environment against impersonation events, suspicious items, false negatives/false positives, and any new event that automated intelligent systems may miss due to a lack of context (for example, a compromised account activity).

PhishGuard only works on a post-delivery environment (only emails that have already landed in your email inbox are reviewed). As a result, PhishGuard analysts may [submit a message for review](/cloudflare-one/email-security/submissions/#submit-messages-for-review) or [auto-move](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) based on their findings.

:::caution
Auto-moves are mandatory for PhishGuard customers.
:::

PhishGuard coordinates with the email detections team, allowing you to directly request immediate detection for specific items and implement custom detections unique to your needs. An example of this is requesting to block all PayPal traffic if you do not use PayPal for invoicing. This capability allows you to take ownership over the rules governing your email environment through PhishGuard's human intervention.

Additionally, PhishGuard analysts:

- Use real-time threat data to identify malicious activity. Email-based threats are responded to rapidly, and immediately reported and documented.
- Review every [user](/cloudflare-one/email-security/investigation/search-email/#user-submissions) and [team](/cloudflare-one/email-security/investigation/search-email/#team-submissions) submission so your security team can focus on more critical activities.
- Help you detect and mitigate threats faster, reducing the time attacks have access to your network. This also helps reducing business impact, because it prevents data breaches, financial loss, and reputational damage.

To use PhishGuard:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **PhishGuard**.

The dashboard will display the following metrics:

- ROI Calculator
- Insider threat defense
- Email threat hunting
- Actions
- API Status
- Managed email security operations
- Reports

## ROI Calculator

Use the ROI Calculator to compare triage durations and hourly rates to calculate PhishGuard's return on investment.

The ROI Calculator displays:

- Total aggregated saved number in USD dollars.
- Triage duration: The amount of time in minutes spent triaging the message.
- Hourly rate.

## Insider threat defense

An [insider threat](https://www.cloudflare.com/en-gb/learning/access-management/what-is-an-insider-threat/) is a risk to an organization's security stemming from someone associated with the organization. PhishGuard looks for threat actor groups.

Insider threat defense on the dashboard displays **Insider leads** and **Insider reports generated**. **Insider leads** displays the number of emails identified as potential insider threat email. **Insider reports generated** displays the number of reports created based on insider leads.

## Email threat hunting

PhishGuard reviews suspicious and highly malicious activity in your email environment.

On the Cloudflare One dashboard, email threat hunting displays previously unknown phishing attacks.

Email threat hunting also gives you information on **Threat leads generated** and **Total reposts generated**.

## Actions

**Actions** allows you to review the most common actions taken by the PhishGuard team, such as escalations, threat hunts, and moves.

## API Status

API Status allows you to monitor and configure the current status of API message auto-moves and directory integrations.

Select **Message moves** to [configure auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/). Select **Directory integration** to [configure directories](/cloudflare-one/email-security/settings/domains-and-directories/directories/).

## Managed email security operations

Managed email security operations allows you to review the results of phish submissions reviewed by the PhishGuard team.

It displays the following:

- Total [phish submissions](/cloudflare-one/email-security/settings/submission-settings/)
- Tracked incidents
- Median time to resolve
- Resolved track incidents

## Reports

Under Reports, you can review reports of threats discovered and resolved by the PhishGuard team.

If you select the three dots, you can:

- **View report details**: Report Details gives you the following information about each report:
  - **Overview**: An Overview of the report. This includes date and time of the report, type of attack performed, and more.
  - **Target and victimology**: Company targeted.
  - **Details**: Displays information such as delivery disposition, current disposition, ES Alert ID, Message-ID, Timestamp, Subject, and Attempted Fraudulent Amount.
  - **Indicators of compromise (IOC)**: [Indicators of compromise (IOC)](https://www.cloudflare.com/en-gb/learning/security/what-are-indicators-of-compromise/) are information about a specific security breach that can help security teams determine if an attack has taken place.
- Preview email.
- [Move email](/cloudflare-one/email-security/settings/detection-settings/auto-moves/).

# Retro Scan

Use Retro Scan to check whether your current email security provider has missed any threats. Cloudflare scans up to 14 days of emails in your Microsoft 365 mailbox and generates a report of malicious messages. Once the scan is complete, you will receive an email notification with a link to the report.

:::note
Retro Scan is only available for Microsoft 365 accounts.
:::

To start a free scan:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Overview**.
3. Select **Start a free scan** > **Generate report**.
4. Enable your [Microsoft integration](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration). Once you have enabled your Microsoft integration, you will be redirected to a page where you will add your domains and specify your current email security system.
5. Generate Retro Scan report:
   - **Connect domains**: Select at least one domain from your integration, then select **Continue**.
   - **Select current solution**: Select the email security tool you are currently using, then select **Continue**.
   - **Review details**: Confirm the domain and current solution you selected, then select **Continue**. You will receive an email notification once the report is ready.
6. When you receive the notification email, select the link to view the full report.
7. On the Cloudflare dashboard, select **View report**.

The dashboard will display **Overview** and **Details** pages.

### Overview

The **Overview** page shows a summary of the scan results across your selected domains, including:

- [Disposition evaluation](/cloudflare-one/email-security/monitoring/#disposition-evaluation), the verdict assigned to each scanned message (for example: malicious, suspicious, or spam)
- Malicious threat types
- Malicious targets, the top recipients targeted by malicious messages
- Malicious threat origins

### Details

The **Details** page lists up to 1,000 emails that were assigned a disposition during the scan. Select any email to review [details](/cloudflare-one/email-security/investigation/search-email/#details) about the message.

# Troubleshoot Email security

import { Render } from "~/components";

<Render file="troubleshooting/email-security" product="cloudflare-one" />

---

## How to contact Support

If you cannot resolve the issue, [open a support case](/support/contacting-cloudflare-support/). Please provide the **Message ID** or **Alert ID** for the affected emails, which you can find in the **Investigation** section of the dashboard.

# Investigation

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Search email

With Email security, you can use different screen criteria to search through your email, reclassify and move a certain volume of messages, find similar emails, and export messages.

## Screen criteria

Email security allows you to use popular, regular, and advanced screening criteria to search through your inbox. Advanced screening will give you the most in-depth investigation of your inbox.

To screen through your email traffic:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Investigation**, then **Run new screen**.
4. Choose between **Popular**, **Regular**, and **Advanced** screen methods. Refer to the explanation below to learn what each method does.

The results will be displayed on a table. The table allows you to review and take action on the messages that match your chosen screening criteria.

### Popular screen

A popular screen allows you to view messages based on common pre-defined criteria.

To use a popular screen criteria:

1. Under **Method**, select **Popular screens**.
2. Select one of the following criteria:
   - **Moved emails**: View emails automatically or manually moved within the last seven days.
   - **Reclassified emails**: Emails that had their disposition reclassified within the last seven days.
   - **Malicious emails**: Emails assigned the malicious disposition within the last seven days.
   - **Spoof emails**: Emails assigned the spoof disposition within the last seven days.
   - **Suspicious emails**: Emails assigned the suspicious disposition within the last seven days.
   - **Spam emails**: Emails assigned to the spam disposition within the last seven days.
3. Select **Run screen**.

To modify your screening criteria, under **Active screen criteria**, select **Modify**.

### Regular screen

A regular screen allows you to investigate your inbox by inserting a term to screen across all criteria.

To use a regular screen criteria:

1. Under **Method**, select **Regular screen**.
2. Select a **Date range**.
3. Enter a keyword.
4. Select **Run screen**.

To include all emails as part of the search, enable **Include all mail**.

To modify your screening criteria, under **Active screen criteria**, select **Modify**.

To reset your screening criteria, select **Reset**.

### Advanced screen

The advanced screen criteria gives you the option to narrow message results based on specific criteria. The advanced screen has several options (such as keywords, subject keywords, sender domain, and more) to scan your inbox.

To use advanced screen criteria:

1. Under **Method**, select **Advanced screen**.
2. (Required) Select a date range.
3. (Optional) Fill in the other fields. All fields, except for Subject, must be filled with one value only.
4. Select **Run screen**.

To include all emails as part of the search, enable **Include all mail**.

To modify your screening criteria, under **Active screen criteria**, select **Modify**.

To reset your screening criteria, select **Reset**.

## Move messages

Moving messages allows you to move messages to a specific folder. You can move up to 1,000 messages at a time.

To move messages:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security**, and select **Investigation**.
2. On the Investigation page, select all the messages you want to move.
3. Select the **Action** dropdown, then select **Move**.
4. Select among one of the following folders:
   - **Inbox**: Move messages to the primary email folder.
   - **Junk email**: Move messages to the junk or spam folder.
   - **Trash**: Move messages to the trash or deleted items email folder.
   - **Soft delete (user recoverable)**: Move messages to the user's Deleted Items folder. This option is for Microsoft 365 only.
   - **Hard delete (admin recoverable)**: Delete messages from a user's inbox.
5. Select **Save**.

To move messages in bulk, select **Select all messages** > **Action** > **Move**.

## Find similar emails

Each detection has an Email Detection Fingerprint (EDF) hash that Email security sends to the Search API to retrieve similar detections.

To find similar detection results:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security**, and select **Investigation**.
2. On the Investigation page, under **Your matching messages**, search for the **Similar emails** column.
3. Select the number of similar emails. Selecting the number will show you a list of similar emails.

## Export messages

With Email security, you can export messages to a CSV file.

To export messages:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security**, and select **Investigation**.
2. On the Investigation page, under **Your matching messages**, select **Export to CSV**.
3. Select **Export messages** on the pop-up message. You can export up to 500 messages from the dashboard. To export up to 1,000 matching messages, use the [API](/api/resources/email_security/subresources/investigate/methods/get/).

To export messages in bulk, select **Select all messages** > **Export to CSV**.

## Email status

Email security allows you to review the status and actions of each email.

To view status and actions for each email:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security**, and select **Investigation**.
2. On the Investigation page, select the three dots.
3. Selecting the three dots will show you the following options:

- If the email is quarantined:
  - **View details**: Refer to [Email details](#email-details) to learn more.
  - **View similar emails**: Find similar emails based on the `value_edf_hash` (Electronic Detection Fingerprint hash).
  - **Release**: Email security will no longer quarantine your chosen messages.
  - **Submit for review**: Choose the dispositions of your messages if they are incorrect. Refer to [Reclassify messages](#reclassify-messages) to learn more.

- If the email is not quarantined:
  - **View details**.
  - **View similar emails**.
  - **View submission detail**.
  - **[Move](/cloudflare-one/email-security/settings/detection-settings/auto-moves/)** (only available if you authorized moves).
  - **[Submit for review](#reclassify-messages)**.

## Email details

Email security shows you the following email detail information:

- Details
- Action log
- Raw message
- Mail trace

### Details

Email security displays the following details:

1. **Threat type**: Threat type of the email, for example, [credential harvester](/cloudflare-one/email-security/reference/how-es-detects-phish/), and [IP-based spam](/cloudflare-one/email-security/reference/how-es-detects-phish/).
2. **Validation**: Email validation methods [SPF](https://www.cloudflare.com/learning/dns/dns-records/dns-spf-record/), [DKIM](https://www.cloudflare.com/learning/dns/dns-records/dns-dkim-record/), [DMARC](https://www.cloudflare.com/learning/dns/dns-records/dns-dmarc-record/). The dashboard will display Pass if SPF, DKIM and DMARC checks have passed.
3. **Sender details**: Information include:
   - IP address
   - Registered domain
   - Autonomous sys number: This number identifies your [autonomous system (AS)](https://www.cloudflare.com/en-gb/learning/network-layer/what-is-an-autonomous-system/).
   - Autonomous sys name: This name identifies your autonomous system (AS).
   - Country
4. **Links identified**: A list of malicious links identified by Email security. Refer to [Open links](#open-links) to open links in Security Center, Browser Isolation or an external tool of your choice.
5. **Attachments**: If an email has an attachment, the Cloudflare dashboard will display the filename, and the disposition assigned. You can open attachments in [Browser Isolation](/cloudflare-one/remote-browser-isolation/). Only PDF files are currently supported.
6. **Reasons for disposition**: Description of why the email was deemed as malicious, suspicious, or spam. The dashboard also displays [Cloudy summaries](/cloudflare-one/email-security/investigation/search-email/#cloudy-summaries).

#### Cloudy summaries

The Cloudflare dashboard uses [Cloudy](/fundamentals/reference/cloudy-ai-agent/) to explain why an email was classified as unwanted. 

Cloudy analyzes the underlying detection code and generates a description of the specific detection logic that led to an email final disposition. Each summary provides a rating option that allows you to provide feedback to the Email security team. Cloudy summaries are only available for emails with a final [disposition](/cloudflare-one/email-security/reference/dispositions-and-attributes/#available-values). 

**View all signatures** allows you to view all the detections that triggered on the email, including detections that did not determine the final disposition.

#### Open links

You can open links in Security Center or [Browser Isolation](/cloudflare-one/remote-browser-isolation/), or copy and paste the link so you can investigate content in external tools.

When you select a link in a suspicious email, you risk exposing your device and your company's network to malware, ransomware, and credential harvesting.

Browser Isolation eliminates any risk of your device being compromised by opening all web content from unverified or suspicious sources in a safe, disposable remote browser session hosted by Cloudflare.

To open links in Security Center:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Email security** > **Investigation**.
2. Locate the message you want to open links for, select the three dots, then select **View details**.
3. Under **Details**, go to **Links identified**.
4. Locate the link you want to open, and select **Open in Security Center**.
5. You will be redirected to Investigate in the Cloudflare dashboard.
6. Select **Scan now**.
7. The dashboard will generate a report for your link.

To open links in Browser Isolation:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Email security** > **Investigation**.
2. Locate the message you want to open links for, select the three dots, then select **View details**.
3. Under **Details**, go to **Links identified**.
4. Locate the link you want to open, and select **Open in Browser Isolation**.
5. The link will open in a separate window where you will be able to browse the content securely.

Alternatively, you can directly [open links in Browser Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/#open-links-in-browser-isolation).

When you open a link from an email, Cloudflare will present you with a blue bar. This indicates that the page is isolated and that you are protected from any potential malicious content on that page.

:::note
If you purchased Gateway and [Browser Isolation](/cloudflare-one/remote-browser-isolation/), you can perform more actions when opening links.
:::

To open and investigate a link in an external tool:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Email security** > **Investigation**.
2. Locate the message you want to open links for, select the three dots, then select **View details**.
3. Under **Details**, go to **Links identified**.
4. Locate the link you want to open, and select **Copy URL**.
5. Paste the link in your external tool.

:::caution[Warning]
You may encounter a `400 Bad Request` error after turning Clientless Web Isolation on.
:::

If you encounter this error:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Settings** > **Resources**.
2. Select **Generate certificate**.
3. Choose the **Expiration** (5 years is recommended), then select **Generate certificate**. Your certificate is now generated, and the dashboard will display its Deployment Status as INACTIVE.
4. Select the three dots, and then select **Activate** to activate your certificate.
5. Select the three dots, and then select **Mark as in-use**.
6. Your certificate deployment status should display AVAILABLE IN-USE.

### Action log

Action log allows you to review post-delivery actions performed on your selected message. The action log displays:

- **Date**: Date when the post-delivery action was performed.
- **Activity**: The activity taken on an email. For example, moving the email to the trash folder, releasing a quarantined email, and more.

### Raw message

Raw message allows you to view the raw details of the message. You can also choose to download the email message. To download the message, select **Download .EML**.

### Mail trace

Mail trace allows you to track the path your selected message took from the sender to the recipient. Mail trace displays:

- **Date**: The date and time when the mail was tracked.
- **Type**: An email can be inbound (email sent to you from another email), or outbound (emails sent from your email address).
- **Activity**: The activity taken on an email. For example, moving the email to the trash folder, releasing a quarantined email, and more.

# Download a report

Email security allows you to download three types of reports:

- Disposition report
- Retro scan report
- Security report

## Download a disposition report

A disposition report shows you all the email messages based on the type of disposition you selected.

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Select **Monitoring** > **Download report**.
3. In **Report type**, select **Email disposition report**.
4. Under **Email disposition report**, select the **Date Range** (required), and the **Disposition**.
5. Select **Export to CSV**.

Refer to [Dispositions and attributes](/cloudflare-one/email-security/reference/dispositions-and-attributes/) to learn more.

## Download a retro scan report

Retro scan scans the last 14 days of your emails, and gives you a report on bulk, spam, spoof, suspicious and malicious emails.

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Select **Monitoring** > **Download report**.
3. In **Report type**, select **Retro Scan report**.
4. Select **View report** to view a report of your last 14 days of emails.

Refer to [Retro Scan](/cloudflare-one/email-security/retro-scan/) to learn more.

## Download a security report

A security report provides an overview of your email traffic. The report can be generated on the last 30, 60, 90 days, or a timeframe of your choice.

The reports contains:

- An executive summary: A summary of the threats detected in your organization's email traffic in the last 30 days.
- Threat detection: Review metrics regarding dispositions, policy detection, and impersonation attempts.
- Submissions: Review the metrics of emails your security team or users have requested to reclassify.

To download a security report:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Select **Monitoring** > **Download report**.
3. In **Report type**, select **Security report** and the **Date range**.
4. Select **Generate report**.
5. Your security report is being generated. You will receive an email with the security report attached once it is ready.

# Monitoring

import { GlossaryTooltip, Render } from "~/components";

Once you have chosen a domain to scan, Email security allows you to monitor the traffic scanned from your email inboxes.

:::note
With Email security, you can enable logs to send detection data to an endpoint of your choice. Refer to [Enable Email security logs](/cloudflare-one/insights/logs/logpush/email-security-logs/) for more information.
:::

To monitor your inbox:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Under **Email security**, select **Monitoring**.

The dashboard will display the following metrics:

- Email activity
- [Disposition evaluation](/cloudflare-one/email-security/reference/dispositions-and-attributes/)
- Detection details
- [Impersonations](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/)
- [Phish submissions](/cloudflare-one/email-security/settings/submission-settings/)
- [Auto-move events](/cloudflare-one/email-security/settings/detection-settings/auto-moves/)
- [Detection settings metrics](/cloudflare-one/email-security/settings/detection-settings/)

## Email activity

Email activity aggregates statistics about emails scanned and dispositions assigned (the number of email flagged due to a detection) within a given timeframe.

To view the live number of email scanned and dispositions scanned, enable **Live mode**.

## Disposition evaluation

Email traffic that flows through Email security is given a final disposition, which represents Email security's evaluation of that specific message.

Disposition evaluation displays the following dispositions:

<Render
	file="email-security/dispositions-and-attributes"
	product="cloudflare-one"
/>

## Detection details

Detection details displays information about:

- **Malicious** disposition:
  - **Email threat types**: Top malicious threat types, and their number relative to the total amount of malicious threats received.
  - **Targeted users**: Top number of emails targeted, and their number relative to the total amount of malicious targets.
  - **Malicious links**: A graph displaying the total number of malicious links and their distribution throughout the month.
  - **Malicious attachments**: Number of malicious attachments, and the top types of malicious files received.
- **Suspicious** disposition:
  - **Suspicious threat types**: Top suspicious threat types, and their number relative to the total amount of threats received.
  - **Suspicious targets**: Top number of emails targeted, and their number relative to the total amount of malicious targets.
  - **Suspicious links**: A graph displaying the total number of suspicious links and their distribution throughout the month.
- **Spoof** disposition:
  - **Spoof users (impersonated names)**: Top number of impersonated names, and their number relative to the total number of detection received.
  - **Spoof targets**: Top number of targeted emails.
  - **Sender v. envelope mismatch**: This field indicates the number of mismatches between the email address the message was sent from, and the email address the message was _actually_ sent from.

## Impersonations

Impersonations are a form of phishing attack where the actor pretends to be someone else to steal sensitive information.

**Impersonations** displays the number of targeted users, and a chart describing the total number of impersonation attempts.

- To view all targeted users, select **View all targeted users**.
- To view all impersonation emails, select **View all impersonation emails**.
- To view impersonated users, select **View impersonated users**.

Refer to [Trusted domains](/cloudflare-one/email-security/policies/inbound/trusted-domains/) to add a trusted domain, and [Impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) to add a user to the impersonation registry.

## Phish submissions

Phishing is a type of attack that involves stealing sensitive information with the aim of using and selling the information.

A phish submission happens when a user or an administrator reports a phishing attack. Refer to [Phish submissions](/cloudflare-one/email-security/settings/submission-settings/) to learn how to submit a phish.

Phish submissions displays the following information:

- **All submissions**: The total number of phish submissions.
- **User submissions**: The number of phish submissions reported by your users.
- **Admin submissions**: The number of phish submissions reported by an administrator.

Select **Review submissions** to review a filtered list of phish submissions reported by your team.

## Auto-move events

Auto-move events are emails moved to different inboxes based on the disposition Email security assigned.

This panel shows you the total number of auto-moves and the source folder from which these retractions are originating from.

Refer to [Auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) to configure auto-move events.

## Detection settings metrics

Detection settings metric displays information about:

- **Allowed traffic**: Traffic that Email security will exempt emails that match certain patterns from normal detection scanning. Allowed traffic shows metrics on emails that were allowed to go through user inboxes.
- **Blocked traffic**: Traffic that Email security automatically blocks from senders. Blocked traffic shows metrics on emails that were blocked from user inboxes.
- **Domain age**: The number of days since domain registration.

Select **Configure** to configure policy and rules for [allowed traffic](/cloudflare-one/email-security/policies/inbound/allow-policies/), [blocked traffic](/cloudflare-one/email-security/policies/inbound/blocked-senders/) and [domain age](/cloudflare-one/email-security/settings/detection-settings/additional-detections/).

# Policies

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Allow policies

import { Example, Details } from "~/components";

Email security allows you to configure allow policies. An allow policy exempts messages that match certain patterns from normal detection scanning.

## How allow policies work

Allow policies are crucial for legitimate messages that may otherwise be blocked due to, for example, an incorrect setup.

<Example title="Example of allow policy">

An example of allow policy is a phishing simulation product. You want to configure a phishing simulation product as **Accept sender** so Email security does not scan the messages (or crawl links) in these simulated messages.

</Example>

Allow policies can be configured to match messages based on specific criteria such as individual email addresses, IP address ranges, or domains. This flexibility allows you to exempt legitimate messages from specific sources, even if those sources have low spam reputation or send bulk messages from their own servers.

Allow policies are used to mitigate false positives. When an email has been marked as malicious or suspicious, but you still want to receive that email, you configure that email as part of an allow policy.

### Accept sender

Allow policies in Email security give you the option to choose **Accept sender**.

Accept sender creates exceptions for messages that would otherwise be marked as spam, bulk, or spoof. However, Email security will continue to scan the message for maliciousness.

It is recommended to choose this option, as it is the safest option to protect your email inbox from malicious or suspicious activities.

<Example title="Example of a use case where marketing emails that are legitimate have been blocked">

When a marketing email does not follow the correct template, it may be marked as malicious or spam. It may not be possible to change the template. However, in this scenario, the marketing email is legitimate.

To make sure that users still receive the marketing email, you will have to select **Accept sender** and add the marketing domain in **Rule Type** > **Domains**.

**Accept sender** and **Domains** combined exempt marketing emails that may not follow the correct template.

</Example>

<Details header="Regular expressions and emails to add as Accept sender">

Below you can find a list of known services you can add when configuring an Accept sender. We recommend you use [RegExr Validation](https://regexr.com/) to validate your regular expressions.

- Google

  `drive-shares-noreply@google.com`

  `.*@docs\.google\.com`

  `.*@docos\.bounces\.google\.com`

  `.*@calendar-server\.bounces\.google\.com`

  `.*@alerts\.bounces\.google\.com`

  `calendar-notification@google.com`

  `.*\+bnc.*@<gsuited-company-domain>`

  `noreply-cloud@google.com`

  `<groupname>@<gsuite-company-domain>`

  `.*@doclist\.bounces\.google\.com`

- DocuSign

  `.*@docusign\.net`

- Twitter - Mentions/Retweets

  `notify@twitter.com`

- GitHub (mentions and notifications)

  `noreply@(github|git)\.<github-enterprise-hosting-domain>`

  `notifications@github.com`

- Apache Foundations (Developers)

  `.*@.*\.apache\.org`
  `jira@apache.org`

- Atlassian

  `jira@<company-hosted-jira-domain>`

  `jira@<team-name>.atlassian.net`

  `confluence@<company-hosted-jira-domain>`

  `confluence@<team-name>.atlassian.net`

- Intercom

  `notifications@intercom-mail.com`

  `notifications@mail.intercom.io`

- SharePoint

  `no-reply@sharepointonline.com`

- Box and Dropbox

  `.*@dropbox\.com`
  `noreply@box.com`

- Salesforce

  `.*@chatter\.salesforce\.com`

  `.*@.*\.(apex|bnc)\.salesforce\.com`

  `.*@.*\.bnc(\.sandbox)?\.salesforce\.com`

- Webex - Invites/Mentions

  `messenger@webex.com`

- Bulk mailers

  `.*@.*mailchimp\.com`

  `.*@mandrillapp\.com`

  `.*mailspike\.org`

- LinkedIn

  `invitations@linkedin.com`

- FBWork

  `.*@fbworkmail\.com`

- Asana

  `.*@mail\.asana\.com`

- EchoSign

  `.*@mail\.echosign\.com`

- HelloSign

  `noreply@(email|mail)\.hellosign\.com`

- Podio

  `noreply@podio.com`

- Quip

  `noreply.*@quip\.com`

- Zeplin

  `no-reply@zeplin.io`

- DataHug

  `notifications@datahug.com`

- Paperless

  `.*@paperlesspost\.com`

- NetSuite

  `.*@.*\.na\d\.netsuite\.com`

- FS-ISAC

  `cyberintel@lists.fsisac.com`

- Expensify

  `replies\+[0-9]+@expensify\.com`

- KnowBe4

  `.*@[a-z]+\.knowbe4\.com`

  `147\.160\.167\.([1-5][0-9]|6[0-2]|[1-9])`

- FreshDesk

  `.*@.*\.freshdesk\.com`

- Webroot

  `167.89.85.54`
  `49.72.237.117`

- Wombat Egress IPs

  **Training Platform**

  `107.20.210.250`
  `52.1.14.157`

- Phishing Assessment

  `107.23.16.222`
  `54.173.83.138`

</Details >

## Configure allow policies

To configure allow policies:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Policies & rules**, then go to **Inbound** > **Allow policies**.
4. On the **Allow policies** page, select **Add a policy**.
5. On the **Add an allow policy** page, enter the policy information:
   - **Input method**: Choose between **Manual input**, and **Uploading an allow policy**:
     - **Manual input**:
       - **Action**: Select one of the following to choose how Email security will handle messages that match your criteria:
         - **Trust sender**: Messages will bypass all detections and link following.
         - **Exempt recipient**: Message to this recipient will bypass all detections.
         - **Accept sender**: Messages from this sender will be exempted from Spam, Spoof, and Bulk dispositions. Refer to [Allow policy configuration use cases](#use-case-1) for use case examples on how to configure allow policies for accept sender.
     - **Rule type**: Specify the scope of your policy. Choose one of the following:
       - **Email addresses**: Must be a valid email. Enter an email address whose emails are going to be exempted.
       - **IP addresses**: This is the IP address of the email server. Any email address sent from this email server is going to be allowed. The IP address can only be IPv4. IPv6 and CIDR are invalid entries.
       - **Domains**: Must be a valid domain.
     - **Regular expressions**: Must be valid Java expressions. Regular expressions are matched with fields related to the sender email address (envelope from, header from, reply-to), the originating IP address, and the server name for the email. For example, you can enter `.*@domain\.com` to exempt any email address that ends with `domain.com`.
     - **(Recommended) Sender verification**: This option enforces DMARC, SPF, or DKIM authentication. If you choose to enable this option, Email security will only honor policies that pass authentication.
       - **Notes**: Provide additional information about your allow policy.
     - **Uploading an allow policy**: Upload a file no larger than 150 KB. The file can only contain `Values`, `Rule Type`, `Is Regex`, `Sender Verification`, `Trusted Sender`, `Exempt Recipient`, `Acceptable Sender`, and `Notes` fields. The first row must be a header row. Refer to [CSV uploads](#csv-uploads) for an example file.
6. Select **Save**.

<Details header="Allow policy configuration use cases">

The following use cases show how you could configure allow policies for accept sender.

### Use case 1

<Example title="Company receives emails from third-party providers not used internally. These emails are sent from the service provider, and Email security gives these emails an incorrect disposition.">
    This use case can affect companies such as Shopify, PayPal, and Docusign.

    To solve this:

    1. Create a [team submission](/cloudflare-one/email-security/submissions/team-submissions/).
    2. Inform your Cloudflare contact about the escalation.
    3. Do not set up allow policies or blocked senders. In this use case, configuring allow policies will create a security gap. Setting up blocked senders will block legitimate emails from providers such as Shopify, PayPal, and Docusign.

</Example>

### Use case 2

<Example title="Company receives emails via third-party providers that are used internally. These emails are sent from the company's custom domain, but Email security marks these emails as bulk, spam, or spoof.">

This use case can cause the emails you want to receive to follow the auto-moves rules you set up. This use case affects emails from internal tools (such as Salesforce, Atlassian, and Figma) that are given an incorrect disposition.

To solve this, when you add an allow policy in the Cloudflare One dashboard:

1. Choose **Accept sender**.
2. Verify that **Sender verification (recommended)** is turned on.

</Example>

### Use case 3

<Example title="Company receives emails via third-party providers that are used internally. These emails are sent from the company's custom domain, but Email security marks these emails as bulk, spam, or spoof. The custom email domain does not support DMARC, SPF, or DKIM, and would fail Sender Verification.">

This use case impacts the emails from internal tools (such as Salesforce, Atlassian, and Figma) that are given an incorrect disposition.

To solve this, when you add an allow policy in the Cloudflare One dashboard:

1. Choose **Accept sender** based on the static IP you own.
2. Ensure that **Sender verification (recommended)** is turned off.

:::caution
Do not use email addresses or email domains for this policy as they can be easily spoofed without **Sender Verification (Recommended)** enabled.
:::

</Example>

</Details>

### CSV uploads

You can upload a file no larger than 150 KB. The file can only contain `Values`, `Rule Type`, `Is Regex`, `Sender Verification`, `Trusted Sender`, `Exempt Recipient`, `Acceptable Sender`, and `Notes` fields. The first row must be a header row.

An example file would look like this:

```txt
Values, Rule Type, Is Regex, Sender Verification, Trusted Sender, Exempt Recipient, Acceptable Sender, Notes
whale@notaphish.com, EMAIL, false, true, true, false, true, not a phish
```

## Export allow policies

To export all allow policies:

1. On the **Allow policies** page, select **Value(s)**. Selecting **Value(s)** will select all allow policies.
2. Select **Export to CSV**.

To export specific allow policies:

1. On the **Allow policies** page, select the allow policies you want to export.
2. Select **Export to CSV**.

## Edit allow policy

To edit an allow policy:

1. On the **Allow policies** page, select the allow policy you want to edit.
2. Select the three dots > **Edit**.
3. Edit the allow policy.
4. Select **Save**.

## Delete allow policy

To delete an allow policy:

1. On the **Allow policies** page, select the allow policy you want to delete.
2. Select the three dots > **Delete**.
3. On the pop-up message, select **Delete**.

To delete multiple allow policies at once:

1. On the **Allow policies** page, select the allow policies you want to delete.
2. Select **Action**.
3. Select **Delete**.

# Blocked content

Blocked content policies are only available on Enterprise plans and Enterprise + [PhishGuard](/cloudflare-one/email-security/phishguard/) plans.

Email security allows you to configure blocked content policies. A blocked content policy marks all messages containing specific words or phrases with a malicious [disposition](/cloudflare-one/email-security/reference/dispositions-and-attributes/).

## How blocked content works

Blocked content policies use regular expressions to define content patterns that Email security scans for in inbound email messages. You can block content in the subject, body, or both. When a message matches a blocked content policy, Email security automatically assigns it a malicious disposition.

This is useful when your organization needs to block emails containing specific terms associated with known threats, scams, or unwanted content, regardless of who sent them.

## Create a blocked content policy

To create a blocked content policy:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Policies & rules**, then go to **Inbound** > **Blocked content**.
4. Select **Create a policy**.
5. In **Step 1 - Name your policy**, enter a policy name and, optionally, a description in **Note**.
6. In **Step 2 - Build the pattern**, configure the content to block:
   - **Scope**: Choose where to look for the content. Select one of the following:
     - **Subject only**: Match the pattern in the email subject line only.
     - **Body only**: Match the pattern in the email body only.
     - **Subject and body**: Match the pattern in both the subject and body.
   - **Pattern**: Enter a regular expression that defines the content to block. For example, entering `pay` will match content containing that pattern.
   - **Exact word match**: Select this option to only match the exact word. For example, `pay` would match "pay" or "PAY", but not "payment" or "payable". Case is insensitive.
7. In **Step 3 - Validate your policy**, review and test your policy:
   - **Review potential matches**: Displays a list of words that your pattern could match.
   - **Test specific content against your regular expression**: Enter a sample text and select **Test policy** to verify whether your policy catches the expected content.
8. Select **Create policy**.

### Allowed regex modifiers

When building your pattern, you can use the following regex modifiers:

| Regex modifier | Description         |
| -------------- | ------------------- |
| `.`            | Any character       |
| `*`            | Zero or more        |
| `+`            | One or more         |
| `?`            | Optional            |
| `\d`           | Digit               |
| `\w`           | Word character      |
| `\s`           | Whitespace          |
| `\b`           | Word boundary       |
| `[abc]`        | Character class     |
| `[^abc]`       | Negated class       |
| `[a-z]`        | Range               |
| `{n,m}`        | Quantifier (max 50) |
| `\\|`          | Or                  |
| `^`            | Start               |
| `$`            | End                 |

## Edit a blocked content policy

To edit a blocked content policy:

1. On the **Blocked content** page, select the policy you want to edit.
2. Select the three dots > **Edit**.
3. Edit the blocked content policy.
4. Select **Save**.

## Delete a blocked content policy

To delete a blocked content policy:

1. On the **Blocked content** page, select the policy you want to delete.
2. Select the three dots > **Delete**.
3. On the pop-up message, select **Delete**.

To delete multiple blocked content policies at once:

1. On the **Blocked content** page, select the policies you want to delete.
2. Select **Action**.
3. Select **Delete**.

# Blocked senders

Email security marks all messages from these senders with a malicious [disposition](/cloudflare-one/email-security/reference/dispositions-and-attributes/).

## How blocked senders work

Blocked senders ensures messages from any sender is automatically marked as malicious, preventing them from reaching users' inbox.

Sometimes, the same email, IP address or domain always sends malicious emails to the company. In this case, you can add an email address, IP address or domain as a blocked sender. You can choose to enter a regular expression by turning **Regular expression** on.

## Configure blocked senders

To configure blocked senders:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Policies & rules**, then go to **Inbound** > **Blocked senders**.
4. On the **Blocked senders** page, select **Add a sender**.
5. Select the **Input method**: Choose between **Manual input**, and **Upload blocked sender list**:
   - **Manual input**:
     - **Sender type**:
       - **Email addresses**: Must be a valid email.
       - **IP addresses**: Can only be IPv4. IPv6 and CIDR are invalid entries.
       - **Domains**: Must be a valid domain.
       - **Regular expressions**: Must be valid Java expressions. Regular expressions are matched with fields related to the sender email address (envelope from, header from, reply-to), the originating IP address, and the server name for the email. For example, you can enter `.*@domain\.com` to exempt any email address that ends with `domain.com`.
     - **Notes**: Provide additional information about the blocked sender policy.
   - **Upload blocked sender list**: Upload a file that follows the [CSV upload format](#csv-uploads).
6. Select **Save**.

### CSV uploads

You can upload a file no larger than 150 KB. The file can only contain `Blocked Sender`, `Blocked Sender Type`, `Is Regex`, and `Notes` fields. The first row must be a header row.

An example file would look like this:

```txt
Blocked Sender, Blocked Sender Type, Is Regex, Notes
john.smith@gmail.com, EMAIL, false, John Smith
example.com, DOMAIN, false, Melanie Turner
```

## Export blocked senders

To export all blocked senders:

1. On the **Blocked senders** page, select **Sender**. Selecting **Sender** will select all blocked senders.
2. Select **Export to CSV**.

To export specific blocked senders:

1. On the **Blocked senders** page, select **Value(s)**. Select the blocked senders you want to export.
2. Select **Export to CSV**.

## Edit a blocked sender

To edit a blocked sender:

1. On the **Blocked senders** page, select the blocked sender you want to edit.
2. Select the three dots > **Edit**.
3. Edit the blocked sender.
4. Select **Save**.

## Delete a blocked sender

To delete a blocked sender:

1. On the **Blocked senders** page, select the blocked sender you want to delete.
2. Select the three dots > **Delete**.
3. On the pop up message, select **Delete**.

To delete multiple blocked senders at once:

1. On the **Blocked senders** page, select the senders you want to delete.
2. Select **Action**
3. Select **Delete**.

# Inbound

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Partner domain TLS

To add additional TLS (Transport Layer Security) requirements for emails coming from certain domains, you can enforce higher levels of SSL/TLS inspection. If TLS is required, mail without TLS from the specified domain will be dropped.

:::note
To enforce TLS across all emails, you will need to enforce TLS requirements when you are onboarding your domain. To only enforce TLS for specific emails, go to **Policies & rules** > **Inbound** > **Partner domain TLS**, then select **Add a domain**.
:::

To set up a partner domain:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) and select **Email security**.
2. Select **Policies & rules**, then go to **Inbound** > **Partner domain TLS**.
3. Select **Add a domain**.
4. Enter a valid domain name. You can also exclude subdomains by selecting **Add exclude**.
5. (Optional) Add an optional note to describe your rule(s).
6. Select **Save**.

To edit a partner domain, select the three dots > **Edit**.

To delete a partner domain, select the three dots > **Delete**.

# Trusted domains

Email security allows you to exempt known partner and internal domains from typical detection scanning. Adding trusted domains helps to reduce false positives on malicious, suspicious, and spoof [dispositions](/cloudflare-one/email-security/reference/dispositions-and-attributes/). Email security only checks the date when the domain is created.

## How trusted domains work

Trusted domains are not for the email message itself, but for entire domains.

By default, Email security automatically detects lookalike domains. Lookalike domains can be something like this: `thisisdomain.com` and `thisisadomain.com`. Both domains almost look identical.

If an email is received from a domain that looks like a configured domain, this will trigger a detection. Trusted domain is configured to ignore this detection.

In [Additional detections](/cloudflare-one/email-security/settings/detection-settings/additional-detections/), you can configure malicious domain and suspicious [domain age](/cloudflare-one/email-security/settings/detection-settings/additional-detections/#configure-domain-age).

Malicious domain age means that someone may create a domain today, similar to a target, and start sending emails with that domain. This is usually how many phish campaigns start. In this case, the domain is usually marked as Malicious. Malicious domain age is usually set to 7 days.

Suspicious domain age means that after 7 days (this number corresponds to the Malicious domain age), a domain may not be malicious, but it can still be suspicious. Email security will mark these domains as Suspicious. It is recommended to configure the **Suspicious domain age** between 30 and 45 days.

To view whether a domain is malicious or suspicious:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Investigation**.
2. Run a screen. For example, select **Run screen** for **Malicious emails**, then select **Run screen**.
3. Under **Your matching messages**, if any message displays **Domain Age** under **Threat types**, that means that the domain age is too low, and therefore the disposition assigned is Malicious. If the domain is legitimate, you can add it as a trusted domain:

- Go to **Policies & rules** > **Inbound** > **Trusted domains**.
- Under **Domain Info**, add the domain, and select **New Domain**. This will mark the domain whose age is low as a trusted domain.

## Configure trusted domains

To configure a trusted domain:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Policies & rules**, then go to **Inbound** > **Trusted domains**.
4. On the **Trusted domains** page, select **Add a domain**.
5. Select the **Input method**: Choose between **Manual input**, and **Upload trusted domain list**:
   - **Manual input**:
     - **Domain info**: Enter a valid domain name.
     - **Domain type**: Select one or both options:
       - **Proximity domain**: Domains with similar spelling to your existing domain.
       - **Recent domain**: Domains created recently.
     * **Notes**: Provide additional information about the trusted domain list.
   - **Upload trusted domain list**: You can upload a file no larger than 150 KB of multiple trusted domains. The file can only contain `Domain`, `Proximity`, `New`, `Is Regex`, and `Notes` fields. The first row must be a header row. Refer to [CSV uploads](#csv-uploads) for an example file.
6. Select **Save**.

### CSV uploads

You can upload a file no larger than 150 KB of multiple trusted domains. The file can only contain `Domain`, `Proximity`, `New`, `Is Regex`, and `Notes` fields. The first row must be a header row.

An example file would look like this:

```txt
Domain, Proximity, New, Is Regex, Notes
mydomain.com, true, true, false, First Person
testdomain.com, false, true, false, New Hire
```

## Export trusted domains

To export all trusted domains:

1. On the **Trusted domains** page, select **Domain**. Selecting **Domain** will select all trusted domains.
2. Select **Export to CSV**.

To export specific trusted domains:

1. On the **Trusted domains** page, select the trusted domains you want to export.
2. Select **Export to CSV**.

## Edit trusted domains

To edit a trusted domain:

1. On the **Trusted domains** page, select the trusted domains you want to edit.
2. Select the three dots > Edit.
3. Edit the trusted domain.
4. Select **Save**.

## Delete trusted domains

To delete trusted domains:

1. On the **Trusted domains** page, select the trusted domain you want to delete.
2. Select the three dots > **Delete**.
3. On the pop up message, select **Delete**.

To delete multiple trusted domains at once:

1. On the **Trusted domains** page, select the trusted domains you want to delete.
2. Select **Action**.
3. Select **Delete**.

# Outbound

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Outbound Data Loss Prevention (DLP)

:::note[Compatibility]
Outbound DLP is only compatible with Microsoft 365. You need to have Microsoft E3 or E5 license to enable Outbound DLP.
:::

Outbound Data Loss Prevention ensures the protection of sensitive information in outbound emails with [Cloudflare Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/). Outbound Data Loss Prevention integrates with your inbox, and it proactively monitors your email to prevent unauthorized data leaks.

To enable Outbound DLP:

1. [Create an outbound policy](/cloudflare-one/email-security/policies/outbound/outbound-dlp/#1-create-an-outbound-policy).
2. [Set up DLP Assist add-in](/cloudflare-one/email-security/policies/outbound/outbound-dlp/#2-dlp-assist-add-in).

## 1. Create an outbound policy

An outbound policy allows you to control outbound email flow.

To create an outbound DLP policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Email security**.
2. Select **Policies & rules**, then go to **Outbound** > **Outbound DLP**.
3. Select **Add a policy**.
4. Name your policy.
5. Build an expression to match specific email traffic. For example, you can create a policy that blocks outbound emails containing identifying numbers:

   | Selector            | Operator | Value                                                     | Logic | Action |
   | ------------------- | -------- | --------------------------------------------------------- | ----- | ------ |
   | Recipient email     | not in   | `example.com`                                             | And   | Block  |
   | Matched DLP profile | in       | _Social Security, Insurance, Tax, and Identifier Numbers_ |       |        |

6. (Optional) Choose whether to use the default block message or a custom message.
7. Select **Create policy**.

After creating your policy, you can modify or reorder your policies in **Email security** > **Outbound DLP**.

### Selectors

| Selector            | Description                                                                                                       |
| ------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Recipient email     | The intended recipient of an outbound email.                                                                      |
| Email sender        | The user in your organization sending an email.                                                                   |
| Matched DLP profile | The [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/) that content of an email matches upon scan. |

## 2. DLP Assist add-in

The Data Loss Prevention (DLP) Assist add-in allows Microsoft 365 users to deploy a DLP solution for free using Cloudflare's Email security. DLP Assist add-in protects your data egress from Outlook web and desktop client.

To set up DLP Assist add-in:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Email security**.
2. Select **Policies & rules**, then go to **Outbound** > **Outbound DLP**.
3. Select **View Microsoft add-in instructions** > Select **Download add-in**. This downloads a `.xml` file necessary to install the add-in on the client side.
4. Set up the add-in in Microsoft 365:
   - Log in to the [Microsoft admin panel](https://security.microsoft.com/homepage) and go to **Microsoft 365 Admin Center** > **Settings** > **Integrated Apps**.
   - Choose **Upload custom apps** and select **Office Add-in** for the application type.
   - Select **Upload manifest file (.xml) from device**.
   - Upload the Cloudflare add-in file you downloaded in step three. Then, verify and complete the wizard. It can take up to 24 hours for an add-in to propagate.

The add-in works by inserting headers into the [EML](https://en.wikipedia.org/wiki/EML) on the client side before the message is sent out.

To block, encrypt, or send approval, you can configure rules within Microsoft Purview DLP:

1. Go to [Microsoft Purview](https://purview.microsoft.com/datalossprevention/overview?tid=11648e1c-3d60-40e2-bf07-f8d481e48e2d).
2. Select **Policies** > **Create policy**.
3. Do not choose any templates or custom policy. Select **Next**.
4. Choose a name and description for the policy: You can choose any name. However, this guide will use `Cloudflare Assist Block`.
5. Select **Next** on **Admin Units**:
   - Choose to only apply to **Exchange Email**.
   - Choose **Create or customize advanced DLP Rules**.
6. Select **Create rule**:
   - Create a policy name.
   - Add the following conditions:
     - **Header contains words or phrases**: `Key: cf_outbound_dlp with Value: BLOCK`
     - Select **AND**.
     - **Content is shared from Microsoft 365**: Select **with people from outside my organization**.
7. Under **Actions**, the admin can choose what to do with the message. You can use the **Restrict access or encrypt the content in Microsoft 365 locations** to block the message or encrypt it.
8. Under **User notifications**, turn on notifications. Admins can also edit the message if they want to. You can also configure if the admin wants to receive a notification under **Incident reports** > **Use this severity level in admin alerts and reports**.
9. Select **Save**.
10. Select **Turn the Policy On Immediately**.

:::note
The Cloudflare add-in can take up to 24 hours to propagate after install.
:::

### Limitations

Outbound DLP presents its limitations:

- Outbound DLP only protects user-managed inboxes.
- Outbound DLP offers the most consistent experience on Outlook Web App and Outlook desktop, due to limitations imposed by Microsoft.

| Platform                             | Status                                                   |
| ------------------------------------ | -------------------------------------------------------- |
| Web client                           | Stable                                                   |
| New Outlook desktop client - Windows | Stable                                                   |
| Desktop client - macOS               | Can cause scanning to be delayed due to Apple limitation |
| Old Outlook desktop client           | Does not work due to Microsoft limitation                |
| Mobile client - iOS                  | Unstable due to Apple limitation                         |
| Mobile client - Android              | Unstable due to Microsoft limitation                     |

# Dispositions and attributes

import { GlossaryTooltip, Render } from "~/components";

Email security uses a variety of factors to determine whether a given email message, domain, URL, or packet is part of a <GlossaryTooltip term="phishing">phishing</GlossaryTooltip> campaign. These small pattern assessments are dynamic in nature and — in many cases — no single pattern will determine the final verdict.

:::note[Detection vs. disposition]
Detection is the process Email security does to identify what threat an email may contain. An email can have multiple detections, but they will only have one and final disposition. The detections an email have will determine the disposition of the email.
:::

## Dispositions

Any traffic that flows through Email security is given a final disposition, which represents our evaluation of that specific message. Each message will receive only one disposition header, so your organization can take clear and specific actions on different message types.

You can use disposition values when [setting up auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/).

### Available values

The following disposition values follow an order of maliciousness:

<Render
	file="email-security/dispositions-and-attributes"
	product="cloudflare-one"
/>

### Header structure

When Email security adds a disposition header to an email message, that header matches the following format:

```txt
X-CFEmailSecurity-Disposition: [Value]
```

Note that emails with a disposition of `SPAM` will be tagged with `UCE` (unsolicited commercial emails) in their headers:

```txt
X-CFEmailSecurity-Disposition: UCE
```

## Attributes

Traffic that flows through Email security can also receive one or more Attributes, which indicate that a specific condition has been met.

### Available values

| Attribute                               | Notes                                                                                                                                                                                                                         |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CUSTOM_BLOCK_LIST`                     | This message matches a value you have defined in your custom block list.                                                                                                                                                      |
| `NEW_DOMAIN_SENDER=<REGISTRATION_DATE>` | Alerts to mail from a newly registered domain. Formatted as yyyy-MM-dd HH:mm:ss ZZZ.                                                                                                                                          |
| `NEW_DOMAIN_LINK=<REGISTRATION_DATE>`   | Alerts to mail with links pointing out to a newly registered domain. Formatted as yyyy-MM-dd HH:mm:ss ZZZ.                                                                                                                    |
| `ENCRYPTED`                             | Email message is encrypted.                                                                                                                                                                                                   |
| `EXECUTABLE`                            | Email message contains an executable file.                                                                                                                                                                                    |
| `BEC`                                   | Indicates that an email address was contained in your [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) list. Associated with `MALICIOUS` or `SPOOF` dispositions. |

### Header structure

When Email security adds a disposition header to an email message, that header matches the following format:

```txt
X-CFEmailSecurity-Attribute: [Value]
X-CFEmailSecurity-Attribute: [Value2]
```

# How Email security detects phish

import { GlossaryTooltip, Render } from "~/components";

<Render file="email-security/detect-phish" product="cloudflare-one" />

# Reference

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Regional processing

Email security uses Cloudflare's [Data Localization Suite (DLS)](/data-localization/) to allow you to control where your emails are processed. You do not need Data Localization Suite with Email security to choose the different locations.

:::note
Regional processing is only available for customers who deploy Email security via MX/Inline or BCC/Journaling.
:::

Currently, you can process emails in the following regions:

- US
- Germany
- India
- Australia

# Settings

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Additional detections

Email security allows you to configure the following additional detections:

- Domain age
- Blank email detection
- [Automated Clearing House (ACH)](https://en.wikipedia.org/wiki/Automated_clearing_house) change from free email detection
- HTML attachment email detection

To configure additional detections:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Settings** > **Additional detections** > **View**.
4. On the **Detection settings** page, select **Edit**.

## Configure domain age

The domain age is the time since the domain has been registered.

Because of the domain age detection, [trusted domains](/cloudflare-one/email-security/policies/inbound/trusted-domains/) can be used to create an exception to the age detection.

To configure a domain age:

1. On the **Edit additional detections** page:
   - Select **Malicious domain age**: Controls the threshold for a malicious disposition. Maximum of 100 days. It is recommended to set the **Malicious domain age** to 7 days.
   - Select **Suspicious domain age**: Controls the threshold for a suspicious disposition. Maximum of 100 days. It is recommended to set the **Suspicious domain age** between 30 and 45 days.
2. Select **Save**.

## Configure blank email detection

Blank email detection detects emails with blank bodies and assigns a default disposition. You can choose between **Malicious** and **Suspicious** as dispositions.

To enable blank email detection:

1. On the **Edit additional detections** page, enable **Blank email detection**.
2. Choose between **Malicious** and **Suspicious**.
3. Select **Save**.

## Configure ACH change from free email detection

[Automated Clearing House (ACH)](https://en.wikipedia.org/wiki/Automated_clearing_house) is a banking term related to direct deposits. ACH change from free email detection detects payroll inquiries or change requests from free email domains and assigns a default disposition. You can choose between **Malicious** and **Suspicious** as dispositions.

To enable ACH change from free email detection:

1. On the **Edit additional detections** page, enable **ACH change from free email detection**.
2. Choose between **Malicious** and **Suspicious**.
3. Select **Save**.

## Configure HTML attachment email detection

HTML attachment email detection detects HTM and HTML attachments in emails and assigns a default disposition.

To enable HTML attachment email detection:

1. On the **Edit additional detections** page, enable **HTML attachment email detection**.
2. Choose between **Malicious** and **Suspicious**.
3. Select **Save**.

# Auto-move events

Auto-moves allow you to automatically move emails out of your inbox based on a [disposition](/cloudflare-one/email-security/reference/dispositions-and-attributes/) that Email security assigns to each message (for example, malicious, spam, or spoof).

Use auto-moves to enforce email security policy without relying on end users to identify and act on threats themselves. After you configure auto-moves, Email security handles flagged messages according to the action you choose for each disposition.

To configure auto-move events:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Settings** > **Auto-moves** > **View**.
4. Select **Configure**.
5. For each disposition (malicious, spam, bulk, suspicious, spoof), choose what happens to matching emails:
   - **Soft delete - user recoverable**: Moves the message to the user's **Recoverable Items - Deleted** folder. The user can still find and restore the message. This option is only available for Microsoft 365 customers. Refer to [Microsoft 365 Exchange data deletion](https://learn.microsoft.com/en-us/compliance/assurance/assurance-exchange-online-data-deletion) for more information.
   - **Hard delete - admin recoverable**: Removes the message from the user's inbox entirely. Only an administrator can recover it.
   - **Move to trash**: Moves the message to the user's trash or deleted items folder. This option is only available for Google Workspace users.
   - **Move to junk**: Moves the message to the user's junk or spam folder.
   - **No action**: Leaves the message where it is. Email security still records the disposition, but does not move the message.
6. Select **Save**.

# Detection settings best practices

This guide describes how to configure detection settings to mitigate impersonation risks while ensuring legitimate delivery.

Once you configure the [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) to mitigate spoof detections, you can add emails in the impersonation registry as secondary email. Refer to [Edit users](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/#edit-users) to learn how to add a secondary email address.

For impersonation events that are caused by systems, Cloudflare recommends that you configure an [allow policy](/cloudflare-one/email-security/policies/inbound/allow-policies/) to mitigate delivery disruptions.

To maintain a higher security posture, allow policies should be defined with the narrowest possible scope. Start with specific expressions or email addresses that will target the actual sender or system. If the system is sending from a variety of addresses, you can create an expression that is wider while keeping the expression specific. In some situations, it is better to have multiple specific entries than a more generic policy that allows a whole domain.

## Policy selection criteria

When you configure an [allow policy](/cloudflare-one/email-security/policies/inbound/allow-policies/), you can choose how Email security handles messages that match your criteria.

Allow policies are suitable for services that may spoof people's names.

Use **Accept sender** with **Sender verification (recommended)** turned on for systematic traffic. For example, a file shared through Google Drive will create a notification using the name of the user that is sharing the document. However, the underlying email address used will be a Google system address.

Use **Trusted Sender** for emails that do not require phishing inspections. This will exempt messages from any phishing analysis, including links analysis.

Example use cases:

- Temporary rules (to avoid over-detection)
- Phishing simulations
- Applications that send one time links for verification

## Best practices for configuration

- Prioritize static IPs: Use known and owned, static IP addresses for relay servers. Avoid [ephemeral IP addresses](https://docs.cloud.google.com/vpc/docs/ip-addresses#ephemeral_and_static_ip_addresses) as their transient nature can lead to policy degradation.
- Enforce Sender Verification: Always have **Sender Verification (Recommended)** enabled in the Cloudflare dashboard. It validates the originating system's email authentication records (namely [SPF](https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-spf-record/), [DKIM](https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-dkim-record/), and [DMARC](https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-dmarc-record/)) against the domain to ensure authenticity.
- Handle unsanctioned traffic: Unsanctioned traffic is traffic which has not been approved within an organization. This is also known as [Shadow IT](https://www.cloudflare.com/en-gb/learning/access-management/what-is-shadow-it/). If an unsanctioned system generates spam or spoofed content, [configure a text add-on](/cloudflare-one/email-security/settings/detection-settings/configure-text-add-ons/) to append a tag to the subject line and automatically [move](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) the message to the junk folder.

# Configure link actions

You can configure how Email security handles links in emails.

:::note
You can only configure link actions if you deploy Email security via [MX/Inline](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment/).
:::

To configure link actions:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Settings** > **Link actions** > **View**.

You can configure **Link actions settings**, or **URL rewrite ignore patterns**.

## Link actions settings

To configure link actions, select **Configure**.

The dashboard will display **Open links evaluated as suspicious in a remote browser (Recommended)**. This option is turned on by default. Email security will also allow you to select message dispositions to open all the links for dispositioned emails in a remote browser.

Select one or more disposition, then select **Save**.

If **Open links evaluated as suspicious in a remote browser (Recommended)** is turned off, you can select **URL defang** or **No action** on each disposition. Select **Save** once you have completed the configuration.

When opening links, Email security will not allow you to:

- [Copy (from remote to client)](/cloudflare-one/remote-browser-isolation/isolation-policies/)
- [Paste (from client to remote)](/cloudflare-one/remote-browser-isolation/isolation-policies/)
- Use [keyboard](/cloudflare-one/remote-browser-isolation/isolation-policies/)
- [Print](/cloudflare-one/remote-browser-isolation/isolation-policies/)
- [Download files](/cloudflare-one/remote-browser-isolation/isolation-policies/)
- [Uploads files](/cloudflare-one/remote-browser-isolation/isolation-policies/)

## Add patterns for URLs

You can add patterns for URLs that should be rewritten.

1. Under **URL rewrite ignore patterns**, select **Add a pattern**.
2. Enter a valid IP, URL, or regular expression. You can enter up to 512 characters.
3. Select **Save**.

To edit a pattern, go to the pattern you want to edit, select the three dots, then **Edit**. Once you have finished modifying the URL pattern, select **Save**.

To delete a pattern, go to the pattern you want to delete, select the three dots, then **Delete**.

# Configure text add-ons

You can create custom labels to be used as the subject or body prefix for emails with specific dispositions.

:::note
You can only configure text add-ons if you deploy Email security via [MX/Inline](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment/).
:::

:::caution
If you currently do not have text add-ons enabled, configuring text add-ons will add a banner to the subject line. As a result, the subject line and the email body will be reduced.
:::

## Subject prefix

To configure a subject prefix:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Settings** > **Text add-ons** > **View**.
4. Select **Configure** > **Subject prefix**.
5. Populate each disposition with a subject prefix, and turn on the **Status** to enable the subject prefix for a specific disposition.

### Advanced settings

In **Advanced settings**, you can configure **Add "labels" variable**. This option allows you to add a dynamic value for a label that lists dispositions and allows for additional text.

To turn on **Add "labels" variable**:

1. Go to **Advanced settings** > **Add "labels" variable**.
2. Choose between:
   - **Use default**.
   - **Use custom "labels" variable**: Enter the custom label in the text box.

Once you have configured the subject prefix, select **Save**.

## Body prefix

A body prefix is a custom label added to the top of the email body for emails with specific dispositions.

Populate each disposition with a body prefix, and turn on the **Status** to enable the body prefix for a specific disposition.

### Advanced settings

In Advanced settings, you can configure **Add "labels" or "threat types" variable**. This option allows you to add a dynamic value for labels that lists dispositions, or threats that lists the threat types behind an assigned disposition.

To turn on **Add "labels" or "threat types" variable**:

1. Go to **Advanced settings**:
2. Choose between:
   - **Add "labels" variable**: This option allows you to add a dynamic value that for a label that lists dispositions and allows for additional text. Choose between:
     - **Use default**.
     - **Use custom "labels" variable**: Enter the custom label in the text box.

Once you have configured the body prefix, select **Save**.

### Add threat types variable

This option allows you to include a dynamic value for '%THREATS' that lists the threat types behind an assigned disposition. It can include additional, HTML-formatted text.

The dashboard will display **Default** or **Custom** (to use "labels" or "threat types" variable), depending on how you configured the [advanced settings](/cloudflare-one/email-security/settings/detection-settings/configure-text-add-ons/#advanced-settings-1).

# Impersonation registry

The impersonation registry contains combinations of emails of users who are likely to be impersonated. If there is an email that is on the impersonation registry not listed as an alternative email address, that email will be reported as potential [business email compromise (BEC)](https://www.cloudflare.com/en-gb/learning/email-security/business-email-compromise-bec/).

:::note

The impersonation registry should contain a list of users who are likely to be impersonated. Email security applies enhanced security to variations of registered email addresses for additional [Business Email Compromise (BEC)](https://www.cloudflare.com/en-gb/learning/email-security/business-email-compromise-bec/) protection.

For easier tracking, the Email security team recommends syncing and structuring VIPs in groups, and avoid doing manual inputs of users.
:::

To add a user to the impersonation registry:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Settings** > **Impersonation registry** > **View**.
4. Select **Add a user**.
5. Select **Input method**: Choose between **Manual input**, **Upload manual list**, and **Select from existing directories**:
   - **Manual input**: Enter the following information:
     - **User info**: enter a valid **Display name**.
     - **User email**: Enter one of the following:
       - **Email address**: Enter all known email addresses, separated by a comma.
       - **Regular expressions**: Must be valid Java expressions.
   - **Upload manual list**: You can upload a file no larger than 150 KB containing all variables of potential emails. The file must contain `Display_Name` and `Email`, and the first row must be the header row. Refer to [CSV uploads](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/#csv-uploads) for an example file.
   - **Select from existing directories**:
     - **Select directory**: Select your directory.
     - **Add users or groups**: Choose the users or groups you want to register.
6. Select **Save**.

### CSV uploads

You can upload a file no larger than 150 KB containing all variables of potential emails. The file must contain `Display_Name` and `Email`, and the first row must be the header row.

An example file would look like this:

```txt
Display Name, Email
Star Phish, star@nophish.com
Phish Ee, phishee@nophish.com
```

## Edit users

:::note

Administrators can edit the names and emails of users who belong to the Email security directory. Administrators from other integrated directories cannot edit the name and the primary emails of users.

:::

To edit users from the Email security directory:

1. Select the user you want to edit.
2. Select the three dots > **Edit**.
3. Enter the **Display name**, **Email** and **Secondary email**.
4. Select **Save**.

To edit users from other integrations:

1. Select the user you want to edit.
2. Select the three dots > **Edit**.
3. Enter the **Secondary email**.
4. Select **Save**.

## Remove users

:::note

Administrators can remove users who belong to the Email security directory from the **Impersonation registry**. Users who come from an integrated directory cannot be removed from the **Impersonation registry** directly.

To remove a user from an integrated directory:

1. Select **Directories** on the sidebar.
2. Select the directory where your user is allocated.
3. Select the **Users** tab.
4. Search for the user you want to remove.
5. Select the three dots > **Remove from registry**.

:::

To remove a user from the impersonation registry:

1. Select the user you want to remove.
2. Select the three dots > **Remove from registry**.
3. Read the pop-up message, then select **Remove user**.

To remove multiple users at once from the impersonation registry:

1. Select all the users you want to remove.
2. Select **Action** > **Remove from registry**.
3. Read the pop-up message, then select **Remove users**.

# Detection settings

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Domains

import { GlossaryTooltip, Render } from "~/components";

<Render file="email-security/domain-information" product="cloudflare-one" />

# Domains & directories

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Directories

Directories are folders to store user data. Email security allows you to manage directories from the Cloudflare dashboard.

To add a directory:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Select **Settings** > **Domains & directories**.
3. Select **View** on the **Directories** card.
4. On the **Domains & directories** page, select **Add a directory** > **Connect an integration**.
5. Select either **Google Workspace CASB + EMAIL** or **Microsoft CASB + EMAIL**.
6. Refer to [Enable Gmail BCC integration](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#enable-gmail-bcc-integration) if you choose **Google Workspace CASB + EMAIL**. Refer to [Enable Microsoft integration](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration) if you choose **Microsoft CASB + EMAIL**.

To sync a directory:

1. Locate the directory you want to sync.
2. Select the three dots, then select **Sync now**.

:::note
The **Auto sync** option is on by default. It is recommended to keep this option on at all times to ensure directories are always synchronized.
:::

# Manage Email security directories

You can manage your Email security directory by editing and deleting added users.

:::note[Registered users]
The Email security directory contains registered users only. A registered user is a user added to the [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/).
:::

To modify or delete users in the Email security directory:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security** > **Settings**.
2. Select **View** on the **Directories** card.
3. Select **Email security Directory**.

## Add a user

To manually add a user to the Email security directory:

1. On the sidebar, go to **Settings** > **Impersonation registry** > **View**.
2. Select **Add a user**:

- Choose **Manual input** as the **Input method**.
- Under **User info**, enter the **Display name**.
- Under **User email**, enter the **Email addresses**.

3. Select **Save**.

To view users you manually added:

1. Go to **Email security** > **Settings** > **Directories** > **View**.
2. Select **Email security Directory**.
3. Any manually added user will be displayed under the table as **REGISTERED**.

## Edit a user

To edit a user in the Email security directory:

1. Select the user you want to edit.
2. Select the three dots > **Edit**.
3. Enter a user name and/or email.
4. Select **Save**.

## Delete a user

To delete a user from the Email security directory:

1. Select the user you want to delete.
2. Select the three dots > **Delete**.
3. Read the pop-up message, and then select **Delete user**.

To delete multiple users from the registry at once:

1. Select the users you want to delete.
2. Select the **Action** dropdown list > **Delete**.

# Manage integrated directories

To manage an integrated directory:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Directories**.
4. Under **Directory name**, select your directory.
5. You will be redirected to a page where you can manage [Groups](/cloudflare-one/email-security/settings/domains-and-directories/directories/manage-integrated-directories/manage-groups-directory/) or [Users](/cloudflare-one/email-security/settings/domains-and-directories/directories/manage-integrated-directories/manage-users-directory/) directories.

# Manage groups in your directory

Email security allows you to view and manage your groups directory and their [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/). When a group is added to the registry, all members are registered by default.

To manage a group directory:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security** > **Settings**.
2. Select **View** on the **Directories** card.
3. Locate your directory, select the three dots > **View details**.
4. Select **Groups**.

## Add groups to registry

Email security allows you to add group names to the registry.

To add a single group to the registry:

1. Select the group name you want to add.
2. Select the three dots > **Add to registry**.

To add multiple groups to the registry at once:

1. Select the group names you want to add to the registry.
2. Select the **Action** dropdown list.
3. Select **Add to registry**.

## Remove groups from registry

Email security allows you to remove group names from the registry.

To remove a single group from the registry:

1. Select the group name you want to remove.
2. Select the three dots > **Remove from registry**.

To remove multiple groups from the registry at once:

1. Select the group names you want to remove from registry.
2. Select the **Action** dropdown list.
3. Select **Remove from registry**.

## Filter impersonation registry

You can filter the list of group names by registered and unregistered.

A group name is registered when it is part of the [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/). A group name is unregistered when they are not part of the impersonation registry.

To filter the list:

1. Select **Show filters** > **Impersonation registry**.
2. Select one of the following:
   - **All**: To view registered and unregistered groups.
   - **Registered**: To view registered groups.
   - **Unregistered**: To view unregistered groups.
3. Select **Apply filters**.

# Manage users in your directory

Email security allows you to view and manage the [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) status of your users directory.

To manage users directory:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security** > **Settings**.
2. Select **View** on the **Directories** card.
3. Locate your directory, select the three dots > **View details**.
4. Select **Users**.

## Add users to registry

To add a single user to the registry:

1. Select the name you want to add.
2. Select the three dots > **Add to registry**.

To add multiple users to the registry at once:

1. Select the names you want to add to the registry.
2. Select the **Action** dropdown list.
3. Select **Add to registry**.

## Remove users from registry

Email security allows you to remove users from the registry.

To remove a single user from the registry:

1. Select the name you want to remove.
2. Select the three dots > **Remove from registry**.

To remove multiple users from the registry at once:

1. Select the names you want to remove from the registry.
2. Select the **Action** dropdown list.
3. Select **Remove from registry**.

## Edit a user

To edit a user:

1. Under **Display name**, locate the user you want to edit.
2. Select the three dots > **Edit**.
3. Edit the user, then select **Save**.

## Filter a user

You can filter the list of users by registered and unregistered.

A user is registered when they are added to the [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/). A user is unregistered when they are not part of the impersonation registry.

To filter the impersonation registry:

1. Select **Show filters** > **Impersonation registry**.
2. Choose one of the following:
   - **All**: To view registered and unregistered users.
   - **Registered**: To view registered users.
   - **Unregistered**: To view unregistered users.
3. Select **Apply filters**.

To filter users:

1. Select **Show filters** > **Users**.
2. Choose one of the following:
   - **All**: To view users in groups and not in groups.
   - **Users in groups**: To view users in groups.
   - **Users not in groups**: To view users not in groups.
3. Select **Apply filters**.

# Submission settings

import { GlossaryTooltip, Render } from "~/components";

As part of your continuous email security posture, administrators and security analysts need to submit missed <GlossaryTooltip term="phishing">phishing</GlossaryTooltip> samples to Email security, so Cloudflare can process them and take necessary action.

Submitting missed phish samples to Cloudflare is of paramount importance and necessary for continuous protection. Submitting missed phish samples helps Cloudflare improve our machine learning (ML) models, and alerts us of new attack vectors before they become prevalent.

There are three routes you can use to report an email as a phish:

- Via Investigation, by [reclassifying an email](/cloudflare-one/email-security/settings/submission-settings/#reclassify-an-email).
- Via [PhishNet 365](/cloudflare-one/email-security/settings/submission-settings/phishnet-365/).
- Via [Submission addresses](/cloudflare-one/email-security/settings/submission-settings/submission-addresses/).

## Reclassify an email

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Investigation**.
3. On the **Investigation** page, under **Your matching messages**, select the message you want to reclassify. Select the three dots, then select **Submit for review**. By selecting **Submit for review**, you are requesting a new disposition for the message.
4. Select the new disposition, then select **Save**.

When you report an email as phish, this email will be displayed under [User submissions](/cloudflare-one/email-security/submissions/user-submissions/).

# PhishNet Microsoft 365

import { GlossaryTooltip } from "~/components";

PhishNet is an add-in button that helps users to submit directly to Email security <GlossaryTooltip term="phishing">phish</GlossaryTooltip> samples missed by Email security's detection.

To set up PhishNet Microsoft 365:

1. Get the customized manifest URL from [Cloudflare One](https://one.dash.cloudflare.com/?to=/:account/email-security/settings/email-policy/phish-submission?tab=phish-submission).
2. Log in to the [Microsoft admin panel](https://admin.microsoft.com/).
3. Go to **Microsoft 365 admin center** > **Settings** > **Integrated Apps**.
4. Select **Upload custom apps**.
5. Choose **Provide link to manifest file** and paste the URL you copied from the Cloudflare One dashboard.
6. Verify and complete the wizard.

# PhishNet for Google Workspace

To set up PhishNet with Google Workspace you need admin access to your Google Workspace account.

## Set up PhishNet for Google Workspace

1. Log in to [Google Workspace Marketplace apps](https://workspace.google.com/marketplace/app/cloudflare_phishnet/11369379045) using this direct link and an administrator account.
2. Select **Admin install** to install Cloudflare PhishNet. Read the warning, and select **Continue**.
3. You will be redirected to the **Allow data access** page, where you can choose to install Cloudflare PhishNet for **Everyone at your organization**, or **Certain groups or organizational units**. If you choose the latter option, you will have to select the users in the next step.
4. After choosing the groups you want to install PhishNet for, agree with Google's terms of service, and select **Finish**.
5. Cloudflare PhishNet has been installed. Select **DONE**.

You have now successfully installed Cloudflare PhishNet.

## Submit phish with PhishNet

1. In your Gmail web client, open the message you would like to flag as either spam or phish.
2. Select the PhishNet logo on the side panel.
3. Under **Select Submission Type**, select **Spam** or **Phish**.
4. Select **Submit Report**.

# Submission addresses

To view the destination addresses of user and team submissions:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Settings** > **Submission addresses** > **View**.

The dashboard will display **User submission addresses** and **Team submission addresses**.

# Before you begin

import { Markdown } from "~/components";

Before you start the onboarding process, you will have to:

1. Choose a deployment path: Email security provides two deployment modes, [post-delivery](/cloudflare-one/email-security/setup/post-delivery-deployment/) for API and BCC/Journaling and [pre-delivery](/cloudflare-one/email-security/setup/pre-delivery-deployment/) for MX/Inline. 
2. Learn about dispositions, impersonation registry, and submissions.
3. Know the steps to configure your email environment correctly.

The following table compares features available across API, BCC/Journaling and MX/Inline:

| Feature             	| Microsoft 365                                       	| Google Workspace                  	| Others (On-prem/Cloud)                                   	|
|---------------------	|-----------------------------------------------------	|-----------------------------------	|----------------------------------------------------------	|
| Deployment type     	| API and MX                                          	| BCC and MX                        	| MX only                                                  	|
| API integration     	| Microsoft Graph API                                 	| BCC only                          	| None                                                     	|
| BCC/Journaling      	| Uses a Journal Rule in the Microsoft Purview portal 	| Uses BCC rules                    	| Uses journaling                                          	|
| Inline/MX Mode      	| MX records point to Cloudflare                      	| MX records point to Cloudflare    	| MX records point to Cloudflare                           	|
| Message remediation 	| Auto-moves through Read/Write API                   	| Auto-moves through Read/Write API 	| Messages can be blocked, quarantined, or modified inline 	|

Note that:

- All email providers support MX/Inline deployment.
- Microsoft 365 or Google Workspace users who integrate Email security via API, BCC/Journaling can modify emails primarily through deletion or  post-delivery [move](/cloudflare-one/email-security/settings/detection-settings/auto-moves/).
- Microsoft 365 or Google Workspace users who integrate Email security via MX/Inline can modify emails via post-delivery [move](/cloudflare-one/email-security/settings/detection-settings/auto-moves/), [link actions](/cloudflare-one/email-security/settings/detection-settings/configure-link-actions/) and [text add-ons](/cloudflare-one/email-security/settings/detection-settings/configure-text-add-ons/).

## 1. Choose a deployment

### Post-delivery deployment

When you choose post-delivery deployment, Cloudflare scans emails **after** they reach a users' inbox.

If you are a Microsoft 365 user, this is done via [Microsoft's Graph API](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/) or [journaling](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/journaling-setup/m365-journaling/).

If you are a [Google Workspace](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/gmail-bcc-setup/) or [Microsoft Exchange](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/bcc-microsoft-exchange/) user, this is done via BCC.

#### Why you should consider post-delivery deployment

Post-delivery deployment is time-efficient, because it does not involve MX changes. Post-delivery deployment does not disrupt mail flow. Post-delivery deployment allows you to enable [auto-move events](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) to hard or soft delete messages, and synchronize your [directory](/cloudflare-one/email-security/settings/domains-and-directories/directories/) when you use Microsoft Graph API or Google Workspace.

:::note
When you choose post-delivery deployment:

- The threat is removed **after** the message has been delivered to the inbox.
- It requires API scopes, or BCC/Journaling rule configuration.
- Auto-move is only available in BCC/Journaling if you associate an integration.
  :::

### Pre-delivery deployment

When you choose pre-delivery deployment, Cloudflare scans emails **before** they reach a users' inbox. The MX record points to Cloudflare.

#### Why you should consider pre-delivery deployment

Pre-delivery deployment provides you with the highest level of protection. It enforces [text add-ons](/cloudflare-one/email-security/settings/detection-settings/configure-text-add-ons/) or link rewrite at delivery.

Pre-delivery blocks threats in transit, and it adds banners or texts before the user views the email.

:::note
When you choose pre-delivery deployment:

- You must edit MX records or create a connector.
- You can enable auto-move events only after you associate an integration.
- Cloudflare [egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/) are allowed on downstream mail servers.
  :::

## 2. Understand dispositions

Dispositions allow you to configure policies and tune reporting. For example, you can configure a policy to move suspicious emails to your junk folder.

Refer to [Dispositions](/cloudflare-one/email-security/reference/dispositions-and-attributes/#dispositions) to learn more about dispositions.

## 3. Set up the impersonation registry

Most [business email compromise (BEC)](https://www.cloudflare.com/en-gb/learning/email-security/business-email-compromise-bec/) targets executives or finance roles. You must add addresses of roles who are likely to be impersonated. Refer to [Impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) to learn how to add a user to the impersonation registry.

Roles you may want to include in the impersonation registry are:

- C-suites
- Finance roles
- HR
- IT help-desk
- Legal

You should review your impersonation registry on a quarterly basis as roles change.

## 4. Submit messages

A submission is a change to an email's disposition **after** initial scanning. It is Cloudflare's built-in feedback loop for correcting false positives/negatives **and** training the detection models to get smarter over time. Refer to [Submit messages for review](/cloudflare-one/email-security/submissions/#submit-messages-for-review) to learn how to reclassify a message.

### Who can reclassify messages

[Security teams](/cloudflare-one/email-security/submissions/team-submissions/) and [end users](/cloudflare-one/email-security/submissions/user-submissions/) can perform a submission.

### Why you should submit messages

Submissions are critical because:

- **They help improve model accuracy**: Every validated submissions teaches Cloudflare's machine learning to recognise new lures, language, infrastructure, and benign patterns.
- **They reduce alert fatigue**: Correcting Suspicious or Spam emails that users actually want tailors detections to your organization, cutting noise in the dashboard.
- **They close the remediation loop**: When a disposition is upgraded to Malicious, Cloudflare auto-moves those emails out of every inbox (Graph API or Google Workspace API integrations).
- **They can help you log activity taken on any submission**: Each submission displays a submission ID, details about original, requested and final dispositions, and more. Refer to [Submit messages for review](/cloudflare-one/email-security/submissions/#submit-messages-for-review) to learn more about submissions.

To make the most of submissions:

1. Review submissions on a weekly basis.
2. Ensure you have an integration associated with any MX/Inline deployment. When you associate an integration, you will not need to upload the EMLs every time; Cloudflare can use APIs to receive a copy of your email messages.
3. Investigate any increase in [user submissions](/cloudflare-one/email-security/investigation/search-email/#user-submissions) (users may have found a phish that bypassed filters) and confirm that analyst-final dispositions align with your policies.

A correct use of submissions ensures that Email security delivers a stronger protection with less manual tuning.

## 5. Configuration checklist

Follow the below checklist to ensure your email environment is set up correctly:

| Step                                                                                                                                                                                                                                                                                                     | Post-delivery | Pre-delivery  |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------- |
| Authorize integration ([Graph API](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration) or [Google Workspace](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/))    | Required[^1]  | Required [^2] |
| Associate an integration with an MX/Inline domain                                                                                                                                                                                                                                                        |               | Required      |
| Add/verify domains                                                                                                                                                                                                                                                                                       | Required      | Required      |
| [Update MX records/connector](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/), then allow Cloudflare [egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/) on downstream mail server                                          |               | Required      |
| Populate [impersonation registry](/cloudflare-one/email-security/settings/detection-settings/impersonation-registry/) and [allow](/cloudflare-one/email-security/policies/inbound/allow-policies/)/[block](/cloudflare-one/email-security/policies/inbound/blocked-senders/) lists | Required      | Required      |
| Configure [partner domain TLS](/cloudflare-one/email-security/setup/pre-delivery-deployment/partner-domain-tls/) and admin quarantine                                                                                                                                                                    |               | Required      |
| Configure [text add-ons](/cloudflare-one/email-security/settings/detection-settings/configure-text-add-ons/) and [link actions](/cloudflare-one/email-security/settings/detection-settings/configure-link-actions/)                                                                                      |               | Required      |
| Send a test email and verify it appears in **Monitoring** > [**Email activity**](/cloudflare-one/email-security/monitoring/#email-activity) with expected disposition                                                                                                                                    | Required      | Required      |

[^1]: Associating an integration with BCC/Journaling is required for post-delivery but not for pre-delivery.

[^2]: Still used for directory/auto‑move insight if desired as well as authorizing free API CASB.

Now that you know which deployment path to choose, you can begin your onboarding process.

# Manage domains

Once you have deployed your domain, Email security allows you to add, filter and edit domains. You can also choose to stop a domain from being scanned.

## Add domains

To protect a new domain:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com) > Email security.
2. Select **Settings**, go to **Domains** and select **View**.
3. Select **Add a domain**.

## Filter domains

To filter your domains:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Go to **Settings** > **Domain management** > **Domains**, then select **View**.
3. Select **Show filters** > **Configured method**. Choose among the following filters: - **MS Graph API**: To view domains connected via MS Graph API. - **BCC/Journaling**: To view domains connected via BCC/Journaling. - **MX/Inline**: To view domains connected via MX/Inline. - **Retro Scan**: To view domains scanned by Retro Scan.
4. Select **Apply filters**.

## Edit domains

To edit your domains:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Go to **Settings** > **Domain management** > **Domains**, then select **View**.
3. On the **Domains** page, locate your domain, select the three dots > **Edit**.
4. If you did not manually add your domain, you will only be able to edit **Hops**. If you manually added your domain, you will be able to edit **Domain name** and **Hops**.
5. Select **Save**.

## Prevent Cloudflare from scanning a domain

To stop scanning domains:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Go to **Settings** > **Domain management** > **Domains**, then select **View**.
3. On the **Domains** page, locate your domain, select the three dots > **Stop scanning**.
4. Select **Stop scanning** again to stop Cloudflare from scanning your domain.

# Post-delivery deployment

import { DirectoryListing } from "~/components"

<DirectoryListing />

# API deployment

When you choose an API deployment, email messages only reach Email security after they have already reached a user's inbox.

Then, through an integration with your email provider, Email security can [auto-move messages](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) based on your organization's policies.

![With API deployment, messages travel through Email security's email filter after reaching your users.](~/assets/email-security/M365_API_Deployment_Graph.png)

## Benefits

When you choose API deployment, you get the following benefits:

- Easy protection for complex email architectures, without requiring any change to mailflow operations.
- Agentless deployment for Microsoft 365.

## Limitations

However, API deployment also has the following disadvantages:

- Email security is dependent on Microsoft's Graph API, and outages will increase the message dwell time in the inbox.
- Your email provider may throttle API requests from Email security.
- Email security requires read and write access to mailboxes.
- Requires API support from your email provider (does not typically support on-premise providers).
- Detection rates may be lower if multiple solutions exist.
- Messages cannot be modified or quarantined.

# Set up with Microsoft 365

import { Render } from "~/components";

This guide will instruct you through setting up Microsoft 365 with Email security via the Cloudflare dashboard.

## Prerequisites

To use Email security, you will need to have:

<Render
	file="email-security/onboarding-prerequisites"
	product="cloudflare-one"
/>

## Enable Email security via the dashboard

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) and select **Email security**..
2. Select **Overview**. Select one of the following options depending on your use case:

- If you have not purchased Email security, select **Contact sales**.
- If you have not associated any integration:
  - Select **Set up**.
  - Choose **MS Graph API** > **Authorize**.
  - Refer to [Enable Microsoft integration](#enable-microsoft-integration) to continue the onboarding process.
- If you have associated an integration, but have not connected a domain:
  - Select **Connect a domain**.
  - Choose **MS Graph API**. Refer to [Connect your domains](#connect-your-domains) to connect your domain(s).

### Enable Microsoft integration

To enable Microsoft integration:

1. **Configure policy**: Choose how [CASB](/cloudflare-one/integrations/cloud-and-saas/) interacts with your data. Select **Read-only mode** or **Read-Write mode**. It is recommended that you choose **Read-Write mode**.
2. **Name integration**: Add your integration name, then select **Continue**.
3. **Authorize integration**:
   - Select **Authorize**. Selecting **Authorize** will take you to the Microsoft Sign in page where you will have to enter your email address.
   - Once you enter your email address, select **Next**.
   - After selecting **Next**, the system will show a dialog box with a list of requested permissions. Select **Accept** to authorize Email security. Upon authorization, you will be redirected to a page where you can review details and enroll integration.
4. **Review details**: Review your integration details, then:
   - Select **Complete Email security set up** where you will be able to connect your domains and configure auto-moves.
   - Select **Continue to Email security**.

Continue with [Connect your domains](#connect-your-domains) for the next steps.

### Connect your domains

On the **Set up Email security** page, you will be able to connect your Microsoft domains. To connect your domains:

1. **Connect domains**: Select at least one domain. Then, select **Continue**.
2. (Optional) **Modify default scanning**: You can configure which folder Email security can scan.
3. (Optional - select **Skip for now** to skip this step) **Redirect messages**: Refer to [Auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) to learn what auto-moves are, and how to configure auto-moves.
4. **Review details**: Review your connected domains, then select **Go to Domains**.

Your domains are now connected successfully.

### Connect new domains

To connect new domains:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Select **Settings** > **Domain management** > **Domains**, then select **View**.
3. Select **Add a domain**.
4. Select a method for connecting your mail environment to Email security:
   - If you select **MS Graph API**, refer to [Enable Microsoft integration](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration).
   - If you select BCC/Journaling, choose how to connect your domains:
     - If you select **Integrate with MS**, refer to [Enable Microsoft integration](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration).
     - If you select **Integrate with Google**, refer to [Connect your domains](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/connect-domains/).
     - If you select **Manual add**, refer to [Enter domain manually](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/journaling-setup/manual-add/#enter-domain-manually).

## Prevent Cloudflare from scanning a domain

If you want to prevent Cloudflare from scanning a domain:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Go to **Settings** > **Domain management** > **Domains**, then select **View**.
3. On the **Domain management** page, select the domain you do not want to be scanned.
4. Select the three dots > **Stop scanning**.

## View an integration

To view the integration for each connected domain:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Go to **Settings** > **Domain management** > **Domains**, then select **View**.
3. Select a domain.
4. Select the three dots > **View integration**.

Once you have set up Email security to scan through your inbox, Email security will display detailed information about your inbox. Refer to [Monitor your inbox](/cloudflare-one/email-security/monitoring/) to learn more.

<Render
	file="email-security/post-verification-setup"
	product="cloudflare-one"
/>

## Next steps

<Render file="email-security/enable-logs" product="cloudflare-one" />

# BCC/Journaling

BCC/Journaling deployment is a post-delivery type of deployment. Cloudflare analyzes emails after they reach the user's inbox. Every time you receive an email, your email provider will send a blind copy to Cloudflare for an analysis.

- Choose [BCC](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/gmail-bcc-setup/) if your email provider is Gmail.
- Choose [Journaling](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/journaling-setup/m365-journaling/) if your email provider is Microsoft 365.

# Microsoft Exchange BCC setup

import { GlossaryTooltip, Render } from "~/components";

For customers using Microsoft Exchange, setting up Email security via BCC is quick and easy. You need to configure an inbound rule to send emails to Email security via BCC for processing and detection of potential <GlossaryTooltip term="phishing">phishing</GlossaryTooltip> attacks. The following email flow shows how this works:

![Email flow when setting up a phishing assessment risk for Microsoft Exchange with Email security.](~/assets/email-security/Microsoft_Exchange_365.png)

:::note[Auto-moves for Microsoft Exchange customers]
Microsoft Exchange customers can auto-move if your email service is on-premise and you are using Microsoft Exchange online.
:::

## Configure Inbound Rule

1. Access Exchange's **Management Console**, and go to **Organization Configuration** > **Hub Transport**.

   ![Access Hub transport](~/assets/images/email-security/deployment/api-setup/exchange/step1.png)

2. On the **Actions** pane, select **New Transport Rule**.

3. Give the transport rule a name and a description and select **Next**.

   ![Give transport rule a name and description](~/assets/images/email-security/deployment/api-setup/exchange/step3.png)

4. In the **Condition** configuration panel, select the option **from users that are inside or outside the organization** option. In the dropdown that opens, select **Outside the organization**.

   ![Select scope of transport rule](~/assets/images/email-security/deployment/api-setup/exchange/step4.png)

5. Still in the same **Condition** configuration panel, add a second condition to the transport rule. Select **sent to users that are inside or outside the organization, or partners**. Keep the default value of **Inside the organization**.

   ![Select where to send emails](~/assets/images/email-security/deployment/api-setup/exchange/step5.png)

6. Select **Next**.

7. In the **Action** configuration panel, select **Blind carbon copy (Bcc) the message to addresses**. Edit the **addresses** variable to add the addresses you want to copy as BCC.

   ![Select BCC and edit email addresses](~/assets/images/email-security/deployment/api-setup/exchange/step7.png)

8. In **Specify Recipient**, select the **down arrow** next to the **Add** button > **External E-Mail Address**.

   ![Select external e-mail address](~/assets/images/email-security/deployment/api-setup/exchange/step8.png)

9. Enter the BCC address provided by Email security. This address is specific to your account.

   ![Enter the BCC address provided by Email security](~/assets/images/email-security/deployment/api-setup/exchange/step9.png)

10. Select **OK** > **OK** to return to the main configuration page of the transport rule.

11. At the main configuration page of the transport rule, select **Next** to continue to the Exception configuration panel.

12. You do not need to configure an exception rule. Select **Next**.

    ![You do not need to configure an exception rule](~/assets/images/email-security/deployment/api-setup/exchange/step12.png)

13. In **Create Rule**, select the **New** button.

    ![Select the new button](~/assets/images/email-security/deployment/api-setup/exchange/step13.png)

14. Select **Finish** to close the transport rule configuration panel. This will return you to the Exchange Management Console.

    ![Select finish](~/assets/images/email-security/deployment/api-setup/exchange/step14.png)

:::note

If you have multiple rules, you may need to change the order of the BCC rule and move it to the right location in your rule sequence. This is needed so you can send BCC messages to Email security. Usually, the Email security BCC rule will be at the top of the ruleset. The configured conditions of the Email security BCC rule will only trigger for inbound messages.
:::

## Email processing and reports

In BCC mode, all emails are put through automated phishing detections by Email security. Emails that trigger phishing detections are logged for reporting via product portal, email and Slack. Emails that do not trigger any detections are deleted.

## Next steps

<Render file="email-security/enable-logs" product="cloudflare-one" />

# BCC setup

import { DirectoryListing } from "~/components"


<DirectoryListing />

# Add BCC rules

This page will show you how to add BCC rules in the Google Admin Console.

BCC stands for Blind Carbon Copy. A BCC rule is a Google Workspace feature that allows you to create a secure copy of all selected outbound and inbound emails. When you allow Email security to receive a copy of your emails, Cloudflare can perform post-delivery analysis to protect your email inbox.

To add BCC rules:

1. Log in to the [Google Admin Console](https://admin.google.com/).
2. On the sidebar, go to **Apps** > **Google Workspace** > **Gmail** > **Compliance**.
3. Go to **Content Compliance** > Select **Edit**.
4. Add a **Content Compliance** filter, and name it `Email security - BCC`.
5. In **Email messages to affect**, select **Inbound**.
6. Select the recipients you want to send emails to Email security via BCC. Under **Add expressions that describe the content you want to search for in each message**:
   - Select **If ANY of the following match the message**.
   - Select **Add** to configure the expression.
     - Select **Advanced content match**.
     - In **Location**, select **Headers + Body**.
     - In **Match type**, select **Matches regex**.
     - In **Regexp**, input `.*`. You can customize the regex as needed and test within the admin page or on sites like [Regexr](https://regexr.com/).
     - Select **SAVE**.
7. In **If the above expressions match, do the following**:
   - Select **Modify message**.
     - Ensure that **Envelope recipient** > **Change envelope recipient** is unselected, so that emails will not be dropped as an unintended consequence. You will select this option at a later stage.
     - Go to **Also deliver to**, select **Add more recipients** > **ADD** > Choose **Advanced**:
       - Under **Envelope recipient**, select **Change envelope recipient** > **Replace recipient** > Enter the service address. This is the service address you copied and pasted in step 5 when [connecting your domains](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/connect-domains/).
         If you did not copy and paste the service address: - In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security**. - Go to **Settings** and locate your domain under **Your domains**. - Select the three dots > **View domain** > **Service address**. Copy and paste the service address.
       - Under **Spam and delivery options**, ensure **Suppress bounces from this recipient** is not enabled.
       - Under **Headers**, select **Add X-Gm-Spam and X-Gm-Phishy headers**.
       - Select **SAVE**.
8. In **Account types to affect**, select **Users** and **Groups**.
9. Select **SAVE**.

To verify that BCC rules have been configured successfully:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security** > **Settings**.
2. Select **Domains** > **View**.
3. Locate your domain. Under Status, the dashboard should display **Active**. This means that the BCC rules have been configured successfully, and your mail flow is being detected.

# Connect your domains

import { GlossaryTooltip, Render } from "~/components";

To connect your domains, you will need to [enable your Gmail BCC integration](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#enable-gmail-bcc-integration). Once you have enabled your Gmail BCC integration, the Cloudflare dashboard will redirect you to the **Set up Email security** page.

On the **Set up Email security** page:

1. **Connect domains**: Select at least one domain. Then, select **Continue**.
2. (**Optional**) **Add manual domains**: Select **Add domain name** to manually enter additional domains. Then, select **Continue**.
3. (**Optional**) **Adjust hop count**: Enter the number of <GlossaryTooltip term="Hops">hops</GlossaryTooltip>. Then, select **Continue**. Configuring the hop count will determine where you want Cloudflare to sit in the email processing chain.
4. (**Optional**, select **Skip for now** to skip this step) **Move messages**: Refer to [Auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) to configure auto-moves. Then, select **Continue**.
5. **Select your processing location**: Configure where you want Cloudflare to process your email. **Global** will be the default option. If you choose **Global**, `<account tag>@CF-emailsecurity.com` will be your regional service address. Once you have chosen your processing location, select **Continue**. Refer to [Regional processing](/cloudflare-one/email-security/reference/regional-processing/) to learn more.
6. **Review details**: Review your connected domains and service addresses. Then, select **Go to domains.**

Your domains are now added successfully.

On the **Domains** page, select the three dots > **View integration**. The dashboard will display your [domain information](/cloudflare-one/email-security/settings/domains-and-directories/domains/).

Under **Source**, the dashboard will display **Google integration**, along with the **Integration name**.

## Add additional domains

To add additional domains:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security** > **Settings**.
2. Select **Connect an integration** > **BCC/Journaling** > **Integrate with Google** > **Authorize**.
3. **Connect domains**: Select the domains you want to add, then select **Next**.
4. (Optional) Select **Add manual domains**: Enter additional domains manually, then select **Next**.
5. (Optional) Select **Adjust hop count**: Enter the number of <GlossaryTooltip term="Hops">hops</GlossaryTooltip>.
6. **Review details**: Review your selected domains, then use the following email to configure the service address with your third-party email provider:
   ```txt
   <account tag>@CF-emailsecurity.com
   ```
7. Select **Save**.

<Render
	file="email-security/post-verification-setup"
	product="cloudflare-one"
/>

# Enable auto-moves

If you do not have an integration:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security**.
2. Go to **Settings** > **Domain management** > **Domains** > select **View**.
3. Locate your domain, select the three dots > Select **Associate an integration**.
4. Select **Connect an integration**. You will then be redirected to the **Add an integration** page.
5. Select **Google Workspace CASB+EMAIL** > **Select Integration**.
6. Once you select an integration, you can [enable Gmail BCC integration](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#enable-gmail-bcc-integration).

# Enable Gmail BCC integration

import { Render } from "~/components";

This guide describes the process for enabling Email security with Google Workspace. It requires setting up a [service account](https://docs.cloud.google.com/iam/docs/service-account-overview) and a JSON key in Google Cloud Platform (GCP), followed by configuring domain-wide delegation in the Google Workspace Admin Console to authorize the integration.

## Prerequisites

To use Email security, you will need to have:

<Render
	file="email-security/onboarding-prerequisites"
	product="cloudflare-one"
/>

## Enable Gmail BCC integration:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Overview**. Select one of the following options:

- If you have not purchased Email security, select **Contact sales**.
- If you have not associated any integration:
  - Select **Set up**, then choose **BCC/Journaling**.
  - Select **Integrate with Google** > **Authorize**.
  - Name your integration, then select **Next**.
  - Go to [step 1](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#1-create-a-service-account-in-your-gcp-project) to continue the process of associating an integration.
- If you have associated an integration, but have not connected a domain:
  - Select **Connect a domain**.
  - Choose **BCC/Journaling** > **Integrate with Google**.
  - Refer to [Connect your domains](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/connect-domains/) to connect your domain(s).

### 1. Create a Service Account in your GCP Project

1. Once you have named your integration, select **Next**.
2. On the [Google Cloud Console](https://console.cloud.google.com/welcome/new), go to the sidebar, select **APIs & Services**, then select **Credentials**.
3. Select **CREATE CREDENTIALS** > **Service account**. Refer to [Service accounts overview](https://docs.cloud.google.com/iam/docs/service-account-overview) to learn more about service accounts.
4. Fill in the details to create a service account:
   - **Service account name**: Enter `Cloudflare Google Integration`.
   - **Service account ID**: Enter `cloudflare-google-integration`.
   - **Service account description**: Enter `Cloudflare Google Integration`.
   - Select **CREATE AND CONTINUE**.

### 2. Create a JSON Key for your Service Account

On the [Google Cloud Console](https://console.cloud.google.com/welcome/new):

1. On the sidebar, select **IAM & Admin** > **Service Accounts**.
2. Locate your email, select the three dots, then select **Manage keys**.
3. Select **Add key** > **Create new key**.
4. Select **JSON** > Select **CREATE**. This downloads a `.json` file which you will use when [uploading a JSON key](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#3-upload-json-key).

:::note
If service account key creation is disabled, follow [Google's troubleshooting steps](https://docs.cloud.google.com/iam/docs/troubleshoot-org-policies#sa-key-creation-policy).
:::

### 3. Upload JSON Key

On the [Cloudflare One dashboard](https://one.dash.cloudflare.com/), upload the `.json` file downloaded on step 3.

### 4. Enable Necessary Google Workspace APIs in GCP

Enable the following APIs on the Google Cloud Console:

- [Google Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com?project=winter-surf-439414-h1)
- [Google Drive API](https://console.cloud.google.com/apis/library/drive.googleapis.com?project=winter-surf-439414-h1)
- [Google Admin SDK API](https://console.cloud.google.com/apis/library/admin.googleapis.com?project=winter-surf-439414-h1)
- [Gmail API](https://console.cloud.google.com/apis/library/gmail.googleapis.com?project=winter-surf-439414-h1)
- [Google Service Usage API](https://console.cloud.google.com/apis/library/serviceusage.googleapis.com?project=winter-surf-439414-h1)

### 5. Log in to Google Workspace Admin Console

Log in to Google Workspace Admin Console: Enter your password and log in to the Google Workspace Admin Console.

### 6. Create a Domain-Wide Delegation API Client

1. Copy the **Client ID** and **Scopes** displayed on the Cloudflare One dashboard.
2. On Google Admin, go to **Security** > **Access and data control** > **API controls**.
3. Select **MANAGE DOMAIN WIDE DELEGATION** > **Add new**.
4. Use the Client ID and copy the scopes to create a new API client. Refer to [Delegate domain-wide authority to your service account](https://cloud.google.com/chronicle/docs/soar/marketplace-integrations/google-alert-center?_gl=1*skktsb*_ga*MTMxODg5NDExMy4xNzI5NjA1MzYy*_ga_WH2QY8WWF5*MTcyOTc3MDg2Ny40LjEuMTcyOTc3MDg5OC4yOS4wLjA.#delegate_domain-wide_authority_to_your_service_account). Then, select **Next**.

### 7. Confirm Workspace Administrator Email

Enter the email associated with the Google Workspace Administrator account. Your email must match the email associated with your Google Workspace account, or else your integration will not work.

### 8. Create integration

1. Select **Create integration**.
2. Once you created your integration, you will be redirected to the **Review details** page, where you will be able to review **Integration details**.
3. Review your details, then select **Complete Email security set up** > **Continue to Email security**.

## Verify integration

To verify that the integration has been successful:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations**.
2. Under **Your integrations**, locate your integration, and ensure that the integration displays **CASB+EMAIL** under **Type**.

:::note
If you do not reach the step to complete the Email security set up:

1. Go to **Integrations** > **Cloud & SaaS Integrations** > **Integrations**.
2. Delete the integration, if present. Locate your integration, select **Configure**, then select **Delete**.
3. Follow the steps from the beginning to [enable Gmail BCC integration](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#enable-gmail-bcc-integration).
   :::

## Next steps

Now that you have created an integration:

- [Connect your domains](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/connect-domains/) for Email security to start scanning your inbox.
- [Enable logs](/cloudflare-one/insights/logs/logpush/email-security-logs/) to send detection data to an endpoint of your choice.

# Overview

For customers using Gmail as their email provider, setting up Email security is quick and easy.

You will need to [create an integration](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/), [add BCC rules](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/add-bcc-rules/), and [connect your domain(s)](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/connect-domains/). You can choose to [add additional domains](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/connect-domains/#add-additional-domains) at a later stage.

Once you set up Google integration, Email security will receive a copy of your email messages. You will need a Google integration to enable [auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/).

The following email flow shows how this works:

![Gmail BCC deployment flow](~/assets/email-security/Gmail_Deployment_BCC.png)

# Gmail BCC setup

import { DirectoryListing } from "~/components"


<DirectoryListing />

# Journaling setup

import { DirectoryListing } from "~/components"


<DirectoryListing />

# Microsoft 365 journaling setup

import { GlossaryTooltip, Render } from "~/components";

Microsoft 365 journaling is a post-delivery setup method that ensures a copy of every incoming and outgoing email is forwarded to Cloudflare for analysis. When you create a [journal rule](https://learn.microsoft.com/en-us/exchange/security-and-compliance/journaling/journaling#journal-rules) in the Microsoft Purview compliance portal, Cloudflare can scan messages that have already landed in your inbox.

The following diagram shows how this works:

![Email flow when setting up Microsoft 365 with Email security.](~/assets/email-security/M365Deployment_Journaling.png)

To enable Microsoft 365 journaling deployment:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Select **Overview**. If you have not purchased Email security, select **Contact Sales**. Otherwise, select **Set up** > **BCC/Journaling**.
3. Select **Integrate with MS** > **Authorize**.
4. Continue with [Integrate with Microsoft 365](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/journaling-setup/m365-journaling/#1-integrate-with-microsoft-365) to connect your Microsoft integration.

## 1. Integrate with Microsoft 365

To integrate with Microsoft 365:

1. **Name integration**: Add your integration name, then select **Continue**.
2. **Authorize integration**:
   - Select **Authorize**. Selecting **Authorize** will take you to the **Microsoft Sign in** page where you will have to enter your email address.
   - Once you enter your email address, select **Next**.
   - After selecting **Next**, the dashboard will show you a dialog box with a list of requested permissions. Select **Accept to authorize Email security**. Upon authorization, you will be redirected to a page where you can review details and enroll the integration.
3. **Review details**: Review your integration details, then:
   - Select **Complete Email security set up** where you will be able to connect your domains and configure auto-moves.
   - Select **Continue to Email security**.

Continue with [Connect your domains](#connect-your-domains) for the next steps.

### Connect your domains

On the **Set up Email security** page:

1. **Connect domains**: Select at least one domain. Then, select **Continue**.
2. (**Optional**) **Add manual domains**: Select **Add domain name** to manually enter additional domains. Then, select **Continue**.
3. (**Optional**) **Adjust hop count**: Enter the number of <GlossaryTooltip term="Hops">hops</GlossaryTooltip>. Then, select **Continue**.
4. (**Optional**, select **Skip for now** to skip this step) **Move messages**: Refer to [Auto-moves](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) to configure auto-moves. Then, select **Continue**.
5. **Select your processing location**: Configure where you want Cloudflare to [process your email](/cloudflare-one/email-security/reference/regional-processing/). **Global** will be the default option. If you choose **Global**, `<account tag>@CF-emailsecurity.com` will be your regional service address. Once you have chosen your processing location, select **Continue**.
6. **Review details**: Review your connected domains and service addresses. Then, select **Go to domains.**

Your domains are now added successfully.

To view your connected domains:

1. Go to **Settings**.
2. Locate your domain, select the three dots > **View domain**. Selecting **View domain** will display information about your domain.

## 2. Configure journal rule

1. Log in to the [Microsoft Purview compliance portal](https://compliance.microsoft.com/homepage).

2. On the sidebar, go to **Settings** (the gear icon) > **Data Lifecycle Management** > **Exchange (legacy)**.

3. In **Send undeliverable journal reports to** enter the email address of a valid user account. Note that you cannot use a team or group address. Select **Save** once you entered the email address.

4. On the sidebar, go to **Solutions** > **Data Lifecycle Management** > **Exchange (legacy)**.

5. Select **Journal rules**.

6. Select **New rule** to configure a journaling rule, and configure it as follows:
   - **Send journal reports to**: This is the address you copied and pasted in step 5 of [Connect your domains](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/journaling-setup/m365-journaling/#connect-your-domains).
   - **Journal rule name**: `Journal Messages to Email security`
   - **Journal messages sent or received from**: _Everyone_
   - **Type of message to journal**: _External messages only_

7. Select **Next**.

8. Verify the information is correct, and select **Submit** > **Done**.

Once saved, the rule is automatically active. However, it may take a few minutes for the configuration to propagate and start pushing messages to Email security. After it propagates, you can [monitor your inbox](/cloudflare-one/email-security/monitoring/) in the Cloudflare dashboard to check the number of messages processed. This number will grow as journaled messages are sent to Email security from your Exchange server.

<Render
	file="email-security/post-verification-setup"
	product="cloudflare-one"
/>

## Verify successful addition

To verift that your domain has been added successfully and that your emails are being scanned:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), select **Email security**.
2. Go to **Settings** > **Domain management** > **Domains**, then select **View**.
3. Under **Your domains**, locate your domain, and verify that **Status** is set to **Active**. The **Configured method** should be **BCC/Journaling**.


## Next steps

<Render file="email-security/enable-logs" product="cloudflare-one" />

# Manually add domains

import { GlossaryTooltip, Render } from "~/components";

This page will teach you how to manually add domains via BCC/Journaling on the Cloudflare dashboard.

This setup is ideal if your email provider is not Microsoft 365 or Google Workspace, or you do not want to directly integrate your account. Beware that manually add does not support [auto-move](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) or [directory synchronization](/cloudflare-one/email-security/settings/domains-and-directories/directories/).

## Prerequisites

To use Email security, you will need to have:

<Render
	file="email-security/onboarding-prerequisites"
	product="cloudflare-one"
/>


## Manually add domains

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Select **Overview**. If you have not purchased Email security, select **Contact Sales**. Otherwise, select **Set up** > **BCC/Journaling**.
3. Select **Manual add**.

## Users with domains on Cloudflare

On the **Set up Email security** page:

1. **Connect domains**: Select at least one domain. Then, select **Continue**.
2. (**Optional**) **Add manual domains**: Manually enter additional domains. Then, select **Continue**.
3. (**Optional**) **Adjust hop count**: Enter the number of <GlossaryTooltip term="Hops">hops</GlossaryTooltip>, and then select **Continue**.
4. **Select your processing location**: Configure where you want Cloudflare to process your email. **Global** will be the default option. If you choose **Global**, `<account tag>@CF-emailsecurity.com` will be your regional service address. Once you have chosen your processing location, select **Continue**.
5. **Review details**: Review your connected domains and regional service address. Then, select **Go to domains.**

## Users who do not have domains with Cloudflare

If you do not have domains with Cloudflare, the Cloudflare dashboard will display two options:

- Add a domain to Cloudflare.
- Enter domain manually.

### Add a domain to Cloudflare

Selecting **Add a domain to Cloudflare** will redirect you to a new page where you will connect your domain to Cloudflare. Once you have entered an existing domain, select **Continue**.

### Enter domain manually

On the **Set up Email security** page:

1. **Connect domains**: Select at least one domain. Then, select **Continue**.
2. (**Optional**) **Add manual domains**: Manually enter additional domains. Then, select **Continue**.
3. (**Optional**) **Adjust hop count**: Enter the number of <GlossaryTooltip term="Hops">hops</GlossaryTooltip>, and then select **Continue**.
4. **Configure service address with your third party email provider**: Copy and paste the service address into your third-party email provider to allow BCC/Journaling: `<account tag>@CF-emailsecurity.com`.
5. **Review details**: Review your connected domains. Then, select **Go to domains.**

## Enable auto-moves

To enable auto-move events, you will have to associate an integration.

To associate an integration:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Email security**.
2. Go to **Settings** > **Domain management** > **Domains** > Select **View**.
3. On the **Domain management** page, locate your domain, select the three dots, then select **Associate an integration**.
4. Select **Connect an integration**. Follow the steps to [enable the Microsoft 365 integration](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration).
5. Select the three dots, then select **Associate an integration**. Select the integration, then select **Associate**.

Now that your domain has an associated integration, enable [auto-move events](/cloudflare-one/email-security/settings/detection-settings/auto-moves/) on your domain.

# Egress IPs

When Email Security processes inbound messages through an [MX/Inline deployment](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment/), it re-delivers the messages to your mailbox from its own IP addresses, known as egress IPs (the source addresses Cloudflare sends outbound mail from). Your existing email provider (such as Microsoft 365 or Google Workspace) needs to be configured to accept connections from these addresses, otherwise it will reject the messages as coming from an unauthorized sender.

Add all of the following addresses to your mail provider's IP allowlist.

:::caution[Additional information for Microsoft 365]

Microsoft 365 does not support IPv6 addresses or the following IPv4 ranges:

- `104.30.32.0/19` 
- `134.195.26.0/23`

If you use Microsoft 365, use the individual `/24` blocks (256 addresses each) listed in [Microsoft 365 `/24` addresses](#microsoft-365-24-addresses) instead.

:::

### IPv4

```txt
52.11.209.211
52.89.255.11
52.0.67.109
54.173.50.115
104.30.32.0/19
158.51.64.0/26
158.51.65.0/26
134.195.26.0/23
35.157.195.63
52.58.35.43
```

### IPv6

```txt
2405:8100:c400::/38
```

## Microsoft 365 `/24` addresses

Use these IPv4 addresses for Microsoft 365, instead of the `/19` and `/23` subnets:

```txt
104.30.32.0/24
104.30.33.0/24
104.30.34.0/24
104.30.35.0/24
104.30.36.0/24
104.30.37.0/24
104.30.38.0/24
104.30.39.0/24
104.30.40.0/24
104.30.41.0/24
104.30.42.0/24
104.30.43.0/24
104.30.44.0/24
104.30.45.0/24
104.30.46.0/24
104.30.47.0/24
104.30.48.0/24
104.30.49.0/24
104.30.50.0/24
104.30.51.0/24
104.30.52.0/24
104.30.53.0/24
104.30.54.0/24
104.30.55.0/24
104.30.56.0/24
104.30.57.0/24
104.30.58.0/24
104.30.59.0/24
104.30.60.0/24
104.30.61.0/24
104.30.62.0/24
104.30.63.0/24
134.195.26.0/24
134.195.27.0/24
```

# Pre-delivery deployment

import { DirectoryListing } from "~/components"



<DirectoryListing />

# Set up MX/Inline deployment

import { Render } from "~/components";

## Prerequisites

To use Email security, you will need to have:

<Render
	file="email-security/onboarding-prerequisites"
	product="cloudflare-one"
/>

## Initiate MX/Inline configuration

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security**.
3. Select **Overview**. Select one of the following options:

- If you have not purchased Email security, select **Contact sales**.
- If you have not associated any integration, [associate an integration](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/#associate-an-integration), then select **Set up**.
- If you have associated an integration, but have not connected a domain, select [**Connect a domain**](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/#connect-a-domain).

4. Select **MX/Inline**.
5. To start the MX/Inline configuration, you will need to have completed the prerequisite setup on your email provider's platform. Once you have completed this step, select **I confirm that I have completed all the necessary requirements**. Then, select **Start configuration**.

:::note
You can only onboard one domain at a time.
:::

## Associate an integration

MX/Inline does not require an integration for protection to be effective. However, it is a best practice to connect an integration.

To associate an integration:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations** > **Cloud & SaaS Integrations** > **Integrations**
2. Select **Connect an integration**.
3. Select an application: Choose between **Google Workspace CASB + EMAIL**, or **Microsoft CASB + EMAIL**.
   - Refer to [Enable Gmail BCC integration](/cloudflare-one/email-security/setup/post-delivery-deployment/bcc-journaling/bcc-setup/gmail-bcc-setup/enable-gmail-integration/#1-create-a-service-account-in-your-gcp-project) if you select **Google Workspace CASB + EMAIL**.
   - Refer to [Enable Microsoft integration](/cloudflare-one/email-security/setup/post-delivery-deployment/api/m365-api/#enable-microsoft-integration) if you select **Microsoft CASB + EMAIL**.
4. After you have associated an integration, go to **Email security** > **Set up**.
5. Follow the instructions to [connect a domain](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/#connect-a-domain).

## Connect a domain

If you have verified zones on Cloudflare, continue with the following steps:

1. **Connect a domain**: Select your domain. Then, select **Continue**.
2. **Select position**: This step allows you to choose where Email security fits into your mail flow and configure position settings:
   - **Select position**: Choose between:
     - **Sit first (hop count = 1)**: Email security is the first server that receives the email. There are no other email scanners or services between the Internet and Cloudflare.
     - **Sit in the middle (hop count > 1)**: Email security sits anywhere other than the first position. Other servers receive emails _before_ Email security. There are other email scanners or email services in between.
   - **Position settings**: Refine how Email security receives and forwards emails:
     - **Forwarding address**: This is your mail flow next hop after Email security. This value is auto-filled, but you can still change it.
     - **Outbound TLS**: Choose between:
       1. **Forward all messages over TLS** (recommended).
       2. **Forward all messages using opportunistic TLS**.
   - Select **Continue**.
3. (**Optional**, select **Skip for now** to skip this step) **Configure quarantine policy**: Select dispositions to automatically prevent certain types of incoming messages from reaching a recipient's inbox.
4. (Optional) **Update MX records**:
   - Email security can automatically update MX records for domains that proxy traffic through Cloudflare. Under **Your mail processing location**, select your mail processing location. You can refer to [Regional processing](/cloudflare-one/email-security/reference/regional-processing/) for more information.
   - You can also choose to allow Cloudflare to update MX records by selecting **I confirm that I allow Cloudflare to update to the new MX records**. When Email security updates MX records, we replace your original MX records with Email security MX records.
   - Select **Continue**.
5. **Review details**: Review your domain, then select **Go to domains**.

## Users who do not have domains with Cloudflare

If you do not have domains with Cloudflare, the dashboard will display two options:

- [Enter domain manually](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/#enter-domain-manually).
- [Add a domain to Cloudflare](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/#add-a-domain-to-cloudflare).

## Enter domain manually

1. **Add domains**: Manually enter domain names.
2. **Review all domains**: Review all your domains, then select **Continue**.
3. **Verify your domains**: It may take up to 24 hours for your domains to be verified. Select **Done**.
4. Once your domains have been verified, the dashboard will display a message like this: **You have verified domains ready to connect to Email security**. This means that you can now set up Email security via MX/Inline.
5. Select **Set up**, then select **MX/Inline**.
6. Follow the steps to [initiate MX/Inline configuration](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/#initiate-mxinline-configuration).

### Add a domain to Cloudflare

Selecting **Add a domain to Cloudflare** will redirect you to a new page where you will connect your domain to Cloudflare. Once you have entered an existing domain, select **Continue**.

Then, follow the steps to [Set up MX/Inline](/cloudflare-one/email-security/setup/pre-delivery-deployment/mx-inline-deployment-setup/).

<Render
	file="email-security/post-verification-setup"
	product="cloudflare-one"
/>

# MX/Inline deployment

With pre-delivery deployment, also known as Inline deployment, Email security evaluates email messages before they reach a user's inbox.

![Inline deployment diagram](~/assets/email-security/Email_security_Deployment_Inline.png)

Before you change your MX records, you will have to set up the [Time to Live (TTL)](/dns/manage-dns-records/reference/ttl/) on your DNS records. If you do not set up the TTL, the DNS propagation will take longer to happen.

Cloudflare recommends to decrease the TTL to five minutes (also known as [Auto](/dns/manage-dns-records/reference/ttl/#proxied-records)) 3 to 5 days prior to the planned MX record change. Reducing the TTL allows the DNS record to propagate ahead of time, so changes take effect rapidly. Once you have completed your onboarding process, you can choose to increase the TTL.

When you have configured your TTL, you can deploy Email security via MX/Inline. An MX record is a [DNS record](/dns/manage-dns-records/).

If your DNS records are hosted by Cloudflare (or any other provider, except for Google), you can [edit your DNS records](/dns/manage-dns-records/how-to/create-dns-records/#edit-dns-records) via the dashboard or the API to point your MX records to Cloudflare.

By changing your MX records, Email security will be positioned between your incoming emails and Microsoft 0365 or Gmail.

Email security becomes a hop in the [SMTP](https://www.cloudflare.com/en-gb/learning/email-security/what-is-smtp/) processing chain and physically interacts with incoming email messages. Based on your policies, various messages are blocked before reaching the inbox.

When you choose an inline deployment, you get the following benefits:

- Messages are processed and physically blocked before arriving in a user's mailbox.
- Your deployment is simpler, because any complex processing can happen downstream and without modification.
- Email security can modify delivered messages, adding subject or body mark-ups.
- Email security can offer high availability and adaptive message pooling.
- You can set up advanced handling downstream for non-quarantined messages with added X-headers.

# Partner domain TLS

To add additional TLS (Transport Layer Security) requirements for emails coming from certain domains, you can enforce higher levels of SSL/TLS inspection. If TLS is required, mail without TLS from the specified domain will be dropped.

:::note
To enforce TLS across all emails, you will need to enforce TLS requirements when you are onboarding your domain. To only enforce TLS for specific emails, you can do so by going to **Settings** > **Partner domain TLS** > **Add a domain**.
:::

To set up a partner domain:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) and select **Email security**.
2. Select **Settings** > **Partner domain TLS** > **View**.
3. Select **Add a domain**.
4. Enter a valid domain name. You can also exclude subdomains by selecting **Add exclude**.
5. (Optional) Add an optional note to describe your rule(s).
6. Select **Save**.

To edit a partner domain, select the three dots > **Edit**.

To delete a partner domain, select the three dots > **Delete**.

# Cisco - Email security as MX Record

import { Render } from "~/components";

![A schematic showing where Email security sits in the life cycle of an email received](~/assets/email-security/Cisco_to_Email_Security_MX_Inline.png)

In this tutorial, you will learn how to configure Cisco IronPort with Email security as MX record.

<Render
	file="email-security/deployment/mx-deployment-prereq"
	product="cloudflare-one"
/>

## 1. Add a Sender Group for Email security Email Protection IPs

To add a new Sender Group:

1. Go to **Mail Policies** > **HAT Overview**.

2. Select **Add Sender Group**.

3. Configure the new Sender Group as follows:
   - **Name**: `Email security`.
   - **Order**: Order above the existing **WHITELIST** sender group.
   - **Comment**: `Email security Email Protection egress IP Addresses`.
   - **Policy**: `TRUSTED` (by default, spam detection is disabled for this mail flow policy).
   - **SBRS**: Leave blank.
   - **DNS Lists**: Leave blank.
   - **Connecting Host DNS Verification**: Leave all options unchecked.

4. Select **Submit and Add Senders** and add the IP addresses mentioned in <a href="/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/" target="_blank">Egress IPs</a>

## 2. Configure Incoming Relays

You need to configure the Incoming Relays section to tell IronPort to ignore upstream hops, since all the connections are now coming from Email security. This step is needed so the IronPort can retrieve the original IPs to calculate IP reputation. IronPort also uses this information in the Anti-Spam (IPAS) scoring of messages.

1. To enable the Incoming Relays Feature, select **Network** > **Incoming Relays**.
2. Select **Enable** and commit your changes.
3. Now, you will have to add an Incoming Relay. Select **Network** > **Incoming Relays**.
4. Select **Add Relay** and give your relay a name.
5. Enter the IP address of the MTA, MX, or other machine that connects to the email gateway to relay incoming messages. You can use IPv4 or IPv6 addresses.
6. Specify the `Received:` header that will identify the IP address of the original external sender.
7. Commit your changes.

## 3. Disable SPF checks

Make sure you disable Sender Policy Framework (SPF) checks in IronPort. Because Email security is acting as the MX record, if you do not disable SPF checks, IronPort will block emails due to an SPF failure.

Refer to [Cisco's documentation](https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance/117973-faq-esa-00.html) for more information on how to disable SPF checks.

## 4. Set up MX/Inline

<Render
	file="email-security/deployment/set-up-mx-inline-step"
	product="cloudflare-one"
/>

# Cisco - Cisco as MX Record

import { GlossaryTooltip, Render } from "~/components";

![A schematic showing where Email security is in the life cycle of an email received](~/assets/email-security/Cisco_to_Cisco_MX_Inline.png)

In this tutorial, you will learn how to configure Email security with Cisco as MX record.

## 1. Add a Sender Group for Email security Email Protection IPs

To add a new Sender Group:

1. Go to **Mail Policies** > **HAT Overview**.

2. Select the **Add Sender Group** button.

3. Configure the new Sender Group as follows:
   - **Name**: `Email security`.
   - **Order**: Order above the existing **WHITELIST** sender group.
   - **Comment**: `Email security Email Protection egress IP Addresses`.
   - **Policy**: `TRUSTED` (by default, spam detection is disabled for this mail flow policy).
   - **SBRS**: Leave blank.
   - **DNS Lists**: Leave blank.
   - **Connecting Host DNS Verification**: Leave all options unchecked.

4. Select **Submit and Add Senders**, and add the IP addresses mentioned in <a href="/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/" target="_blank">Egress IPs</a>. If you need to process emails in the EU or India regions for compliance purposes, add those IP addresses as well.

## 2. Add <GlossaryTooltip term="SMTP">SMTP</GlossaryTooltip> route for the Email security Email Protection Hosts

To add a new SMTP Route:

1. Go to **Network** > **SMTP Routes**.

2. Select **Add Route**.

3. Configure the new SMTP Route as follows:
   - **Receiving Domain**: `a1s.mailstream`
   - In **Destination Hosts**, select **Add Row**, and add the Email security MX hosts. Refer to the [Geographic locations](#5-geographic-locations) table for more information on which MX hosts to use.

## 3. Create Incoming Content Filters

To manage the mail flow between Email security and Cisco ESA, you need two filters:

- One to direct all incoming messages to Email security.
- One to recognize messages coming back from Email security to route for normal delivery.

### Incoming Content Filter - To Email security

To create a new Content Filter:

1. Go to **Mail Policies** > **Incoming Content Filters**.

2. Select **Add Filter** to create a new filter.

3. Configure the new Incoming Content Filter as follows:
   - **Name**: `ESA_to_A1S`
   - **Description**: `Redirect messages to Email security for anti-phishing inspection`
   - **Order**: This will depend on your other filters.
   - **Condition**: No conditions.
   - **Actions**:
     - For **Action** select **Send to Alternate Destination Host**.
     - For **Mail Host** input `a1s.mailstream` (the SMTP route configured in step 2).

### Incoming Content Filter - From Email security

To create a new Content Filter:

1. Go to **Mail Policies** > **Incoming Content Filters**.

2. Select the **Add Filter** button to create a new filter.

3. Configure the new Incoming Content Filter as follows:
   - **Name**: `A1S_to_ESA`
   - **Description**: `Email security inspected messages for final delivery`
   - **Order**: This filter must come before the previously created filter.
   - **Conditions**: Add conditions of type **Remote IP/Hostname** with all the IP addresses mentioned in <a href="/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/" target="_blank">Egress IPs</a>. For example:

   | Order | Condition            | Rule                 |
   | ----- | -------------------- | -------------------- |
   | `1`   | `Remote IP/Hostname` | `Remote IP/Hostname` |
   | `2`   | `Remote IP/Hostname` | `52.89.255.11`       |
   | `3`   | `Remote IP/Hostname` | `52.0.67.109`        |
   | `4`   | `Remote IP/Hostname` | `54.173.50.115`      |
   | `5`   | `Remote IP/Hostname` | `104.30.32.0/19`     |
   | `6`   | `Remote IP/Hostname` | `158.51.64.0/26`     |
   | `7`   | `Remote IP/Hostname` | `158.51.65.0/26`     |
   - Ensure that the _Apply rule:_ dropdown is set to **If one or more conditions match**.
   - **Actions**: Select **Add Action**, and add the following:
     | Order | Action | Rule |
     | ----- | ----------------------------------------------- | ---------------- |
     | --1 | `Skip Remaining Content Filters (Final Action)` | `skip-filters()` |

## 4. Add the Incoming Content Filter to the Inbound Policy table

Assign the Incoming Content Filters created in [step 3](#3-create-incoming-content-filters) to your primary mail policy in the Incoming Mail Policy table. Then, commit your changes to activate the email redirection.

## 5. Geographic locations

<Render
	file="email-security/deployment/mx-geographic-locations"
	product="cloudflare-one"
/>

## 6. Set up MX/Inline

<Render
	file="email-security/deployment/set-up-mx-inline-step"
	product="cloudflare-one"
/>

# Google Workspace as MX Record

import { Render, GlossaryTooltip } from "~/components";

![A schematic showing where Email security is in the life cycle of an email received](~/assets/email-security/Email_Security_Gmail_MX_Inline.png)

In this tutorial, you will learn how to configure Google Workspace with Email security as MX record.

<Render
	file="email-security/deployment/mx-deployment-prereq"
	product="cloudflare-one"
/>

## Requirements

- Provisioned Email security account.
- Access to the Google administrator console ([Google administrator console](https://admin.google.com/) > **Apps** > **Google Workspace** > **Gmail**).
- Access to the domain nameserver hosting the MX records for the domains that will be processed by Email security.

## 1. Set up Inbound Email Configuration

Set up [Inbound Email Configuration](https://support.google.com/a/answer/60730?hl=en) with the following details:

- In **Gateway IPs**, select the **Add** link, and add the IPs mentioned in [Egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/).
- Select **Automatically detect external IP (recommended)**.
- Select **Require TLS for connections from the email gateways listed above**.
- Do not select **Reject all mail not from gateway IPs**. You will enable this option at a later time to ensure your mail flows.
- Select **SAVE**.

## 2. (Optional) Set up an email quarantine

[Set up an email quarantine](https://support.google.com/a/answer/6104172?hl=en#add-new-quarantine) with the following details:

- **Name**: Email security Malicious.
- **Description**: Email security Malicious.
- For the **Inbound denial consequence**, select **Drop message**.
- For the **Outbound denial consequence**, select **Drop message**.
- Select **SAVE**.

To access the newly created quarantine, select **GO TO ADMIN QUARANTINE** or access the quarantine directly by pointing your browser to https://email-quarantine.google.com/adminreview.

## 3. (Optional) Create a content compliance filter

Go to **Compliance**, and create a [content compliance filter](https://support.google.com/a/answer/1346934?hl=en#zippy=%2Cstep-go-to-gmail-compliance-settings-in-the-google-admin-console%2Cstep-enter-email-messages-to-affect) to send malicious messages to quarantine. Enter the following details:

- **Content compliance**: Add `Quarantine Email security Malicious`.
- **Email messages to affect**: Select **Inbound**.
- **Add expressions that describe the content you want to search for in each message**:
  - Select **Add** to add the condition.
  - In **Simple content match**, select **Advanced content match**.
  - In **Location**, select **Full headers**.
  - In **Match type**, select **Contains text**.
  - In **Content**, enter `X-CFEmailSecurity-Disposition: MALICIOUS`.
  - Select **SAVE** to save the condition.
- If the above expression match, do the following, select **Quarantine message** and the **Email security Malicious** quarantine that was created in the previous step.
- Select **SAVE**.

If you would like to quarantine the other dispositions, repeat the above steps and use the following strings for the other dispositions:

- `X-CFEmailSecurity-Disposition: BULK`
- `X-CFEmailSecurity-Disposition: SPOOF`
- `X-CFEmailSecurity-Disposition: UCE` (`UCE` is the equivalent of `SPAM`)

If desired, you can create a separate quarantine for each of the dispositions.

## 4. Set up MX/Inline

<Render
	file="email-security/deployment/set-up-mx-inline-step"
	product="cloudflare-one"
/>

## 5. (Recommended) Secure Google Workspace from MX records bypass

One method of a DNS attack is to search for old MX records and send <GlossaryTooltip term="phishing">phishing</GlossaryTooltip> emails directly to the mail server. To secure the email flow, you should enforce an email flow where inbound messages are accepted by Google Workspace only when they originate from Email security. This can be done by adding a connector to only allow email from Email security with TLS encryption. This step is optional but recommended.

:::caution[Important]
This step should not be performed until 72 hours after all domains in your Google Workspace have been onboarded to Email security, and Email security is their MX record. If a domain has not been onboarded or DNS is still propagating, you will impact production email flow for that domain.
:::

After 72 hours, the MX record DNS update will have sufficiently propagated across the Internet. It is now safe to secure your email flow. This will ensure that Google Workspace only accepts messages that are first received by Email security. This step is highly recommended to prevent threat actors from using cached MX entries to bypass Email security by injecting messages directly into Google Workspace.

1. Access the [Google Administrative Console](https://admin.google.com/), then select **Apps** > **Google Workspace** > **Gmail**.

2. Select **Spam, Phishing and Malware**.

3. Go to **Inbound gateway** and select **Edit Inbound gateway**.

4. Enable **Reject all mail not from gateway IPs** and select **Save**.

5. Select **Save** once more to commit and activate the configuration change in the Gmail advanced configuration console.

# Prerequisites

import { DirectoryListing } from "~/components"



<DirectoryListing />

# Microsoft 365 as MX Record

import { Render, Markdown, GlossaryTooltip } from "~/components";

![A schematic showing where Email security is in the life cycle of an email received](~/assets/email-security/Email_security_M365_MX_Inline.png)

In this tutorial, you will learn how to configure Microsoft 365 with Email security as its MX record.

<Render
	file="email-security/deployment/mx-deployment-prereq"
	product="cloudflare-one"
/>

## 1. Add Email security IP addresses to Allow List

1. Go to the [Anti-spam policies page](https://security.microsoft.com/antispam) > Select **Edit connection filter policy**.
2. In **Always allow messages from the following IP addresses or address range**, add IP addresses and CIDR blocks mentioned in the [Egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/) page.
3. Select **Save**.
4. Microsoft recommends disabling SPF Hard fail when an email solution is placed in front of it:
   - Return to the [Anti-spam option](https://security.microsoft.com/antispam).
   - Select **Default anti-spam policy**.
   - Select **[Edit spam threshold and properties](https://learn.microsoft.com/en-us/defender-office-365/anti-spam-bulk-complaint-level-bcl-about)** > **Mark as spam** > **SPF record: hard fail**, and ensure it is set to **Off**.
5. Select **Save**.

## 2. Configure Enhanced Filtering

### Create an inbound connector

1. [Set up a connector](https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/set-up-connectors-to-route-mail#1-set-up-a-connector-from-your-email-server-to-microsoft-365-or-office-365).
2. Select **Partner organization** under **Connection from**.
   - Provide a name for the connector:
     - **Name**: `Email security Inbound Connector`
     - **Description**: `Inbound connector for Enhanced Filtering`
3. In **Authenticating sent email**, select **By verifying that the IP address of the sending server matches one of the following IP addresses, which belongs to your partner organization.**
4. Enter all of the egress IPs in the [Egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/) page.
5. In **Security restrictions**, accept the default **Reject email messages if they aren't sent over TLS** setting.

### Enable enhanced filtering

Now that the inbound connector has been configured, you will need to enable the enhanced filtering configuration of the connector.

1. Go to the [Security admin console](https://security.microsoft.com/homepage), and [enable enhanced filtering](https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/enhanced-filtering-for-connectors#use-the-microsoft-defender-portal-to-configure-enhanced-filtering-for-connectors-on-an-inbound-connector).
2. Select **Automatically detect and skip the last IP address** and **Apply to entire organization**.
3. Select **Save**.

## 3. Configure anti-spam policies

To configure anti-spam policies:

1. Open the [Microsoft 365 Defender console](https://security.microsoft.com/).
2. Go to **Email & collaboration** > **Policies & rules**.
3. Select **Threat policies**.
4. Under **Policies**, select **Anti-spam**.
5. Select the **Anti-spam inbound policy (Default)** text (not the checkbox).
6. In **Actions**, scroll down and select **Edit actions**.
7. Set the following conditions and actions (you might need to scroll up or down to find them):

- **Spam**: _Move messages to Junk Email folder_.
- **High confidence spam**: _Quarantine message_.
  - **Select quarantine policy**: _AdminOnlyAccessPolicy_.
- **Phishing**: _Quarantine message_.
  - **Select quarantine policy**: _AdminOnlyAccessPolicy_.
- **High confidence phishing**: _Quarantine message_.
  - **Select quarantine policy**: _AdminOnlyAccessPolicy_.
- **Retain spam in quarantine for this many days**: Default is 15 days. Email security recommends 15-30 days.
  - Select the spam actions in the above step:

8. Select **Save**.

## 4. Create transport rules

To create the transport rules that will send emails with certain [dispositions](/cloudflare-one/email-security/reference/dispositions-and-attributes/#dispositions) to Email security:

1. Open the new [Exchange admin center](https://admin.exchange.microsoft.com/#/homepage).
2. Go to **Mail flow** > **Rules**.
3. Select **Add a Rule** > **Create a new rule**.
4. Set the following rule conditions:
   - **Name**: `Email security Deliver to Junk Email folder`.
   - **Apply this rule if**: _The message headers_ > _includes any of these words_.
     - **Enter text**: `X-CFEmailSecurity-Disposition` > **Save**.
     - **Enter words**: `BULK` > **Add** > **Save**.
   - **Apply this rule if**: Select **+** to add a second condition.
   - **And**: _The sender_ > _IP address is in any of these ranges or exactly matches_ > enter the egress IPs mentioned in <a href="/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/" target="_blank">Egress IPs</a>.
   - **Do the following** - _Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _5_.

5. Select **Next**.
6. You can use the default values on this screen. Select **Next**.
7. Review your settings and select **Finish** > **Done**.
8. Select the rule **Email security Deliver to Junk Email folder** you have just created, and **Enable**.
9. Select **Add a Rule** > **Create a new rule**.
10. Set the following rule conditions:
    - **Name**: `Email security Admin Managed Host Quarantine`.
    - **Apply this rule if**: _The message headers_ > _includes any of these words_.
      - **Enter text**: `X-CFEmailSecurity-Disposition` > **Save**.
      - **Enter words**: `MALICIOUS`, `UCE`, `SPOOF` > **Add** > **Save**.
    - **Apply this rule if**: Select **+** to add a second condition.
    - **And**: _The sender_ > _IP address is in any of these ranges or exactly matches_ > enter the egress IPs in the <a href="/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/" target="_blank">Egress IPs</a>.
    - **Do the following**: _Redirect the message to_ > _hosted quarantine_.

11. Select **Next**.
12. You can use the default values on this screen. Select **Next**.
13. Review your settings and select **Finish** > **Done**.
14. Select the rule you have just created, and select **Enable**.

## 5. Set up MX/Inline

<Render
	file="email-security/deployment/set-up-mx-inline-step"
	product="cloudflare-one"
/>

## 6. (Recommended) Secure Microsoft 365 from MX records bypass

One method of a DNS attack is to search for old MX records and send <GlossaryTooltip term="phishing">phishing</GlossaryTooltip> emails directly to the mail server. To secure the email flow, you should enforce an email flow where inbound messages are accepted by Microsoft 365 only when they originate from Email security. This can be done by adding a connector to only allow email from Email security with TLS encryption. This step is optional but recommended.

:::caution[Important]
This step should not be performed until 72 hours after all domains in your Microsoft 365 organization have been onboarded to Email security, and Email security is their MX record. If a domain has not been onboarded or DNS is still propagating, you will impact production email flow for that domain.
:::

#### Create Connector

1. Go to the new [Exchange admin center](https://admin.exchange.microsoft.com/#/homepage).

2. Go to **Mail flow** > **Connectors**.

3. Select **Add a connector**.

4. Go to **Connection from** > **Partner organization**.

5. Select **Next**.

6. Set the following options:
   - **Name** - `Secure M365 Inbound`
   - **Description** - `Only accept inbound email from Email security`

7. Select **Next**.

8. Make sure **By Verifying that the sender domain matches one of the following domains** is selected.

9. Enter `*` in the text field, and select **+**.

10. Select **Next**.

11. Make sure **Reject email messages if they aren't sent over TLS** is selected.

12. Still in the same screen, select **Reject email messages if they aren't sent from within this IP address range**, and enter all the egress IPs in the [Egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/) page.

13. Select **Next**.

14. Review your settings and select **Create connector**.

# 5 - Junk email folder and administrative quarantine

import { Render } from "~/components";

In this tutorial, you will learn to deliver `BULK` messages to the user's junk email folder, and `MALICIOUS`, `SPAM`, and `SPOOF` messages to the Administrative Quarantine (this requires an administrator to release the emails).

## Configure anti-spam policies

<Render
	file="email-security/deployment/m365-use-cases-antispam"
	product="cloudflare-one"
	params={{
		one: "_AdminOnlyAccessPolicy_",
		two: "_AdminOnlyAccessPolicy_",
		three: "_AdminOnlyAccessPolicy_",
		four: "step7-adminonly-case5.png",
	}}
/>

## Create transport rules

<Render
	file="email-security/deployment/m365-use-case-transport-rules"
	product="cloudflare-one"
	params={{
		one: "`Email security Deliver to Junk Email folder`",
		two: "BULK",
		three:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _5_",
		four: "step4-rules.png",
		five: "`Email security Admin Managed Host Quarantine`",
		six: " `MALICIOUS`, `UCE`, `SPOOF`",
		seven: "_Redirect the message to_ > _hosted quarantine_",
		eight: "step10-hosted-quarantine-case5.png",
	}}
/>

# 4 - User managed quarantine and administrative quarantine

import { Render } from "~/components";

In this tutorial, you will learn to deliver `SPAM` and `SPOOF` messages to the user managed quarantine, and `MALICIOUS` messages to the administrative quarantine (this requires an administrator to release the emails).

## Create quarantine policies

<Render
	file="email-security/deployment/m365-use-case-2-4-create-quarantine-policy"
	product="cloudflare-one"
/>

## Configure quarantine notifications

<Render
	file="email-security/deployment/m365-use-case-configure-quarantine-notifications"
	product="cloudflare-one"
/>

## Configure anti-spam policies

To configure anti-spam policies:

1. Open the [Microsoft 365 Defender console](https://security.microsoft.com/)

2. Go to **Email & collaboration** > **Policies & rules**.

3. Select **Threat policies**.

4. Under **Policies**, select **Anti-spam**.

5. Select the **Anti-spam inbound policy (Default)** text (not the checkbox).

6. In the **Actions** section, scroll down and select **Edit actions**.

7. Set the following conditions and actions (you might need to scroll up or down to find them):
   - **Spam**: _Quarantine message_.
     - **Select quarantine policy**: _UserNotifyUserRelease_.
   - **High confidence spam**: _Quarantine message_.
     - **Select quarantine policy**: _UserNotifyAdminRelease_.
   - **Phishing**: _Quarantine message_.
     - **Select quarantine policy**: _UserNotifyAdminRelease_.
   - **High confidence phishing**: _Quarantine message_.
     - **Select quarantine policy**: _UserNotifyAdminRelease_.
   - **Retain spam in quarantine for this many days**: Default is 15 days. Email security recommends 15-30 days.

8. Select **Save**.

## Create transport rules

<Render
	file="email-security/deployment/m365-use-case-transport-rules"
	product="cloudflare-one"
	params={{
		one: "`Email security User Quarantine Message`",
		two: "`UCE`, `SPOOF`",
		three:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _5_",
		four: "step4-rules-case4.png",
		five: "`Email security User Quarantine Message Admin Release`",
		six: "`MALICIOUS`",
		seven:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _9_",
		eight: "step10-admin-release-case4.png",
	}}
/>

# Use cases

import { DirectoryListing } from "~/components"


<DirectoryListing />

# 1 - Junk email and Email security Admin Quarantine

import { Render } from "~/components";

In this tutorial, you will learn how to deliver emails to the Microsoft 365 junk email folder and the Admin Quarantine in Email security.

## Create quarantine policies

<Render
	file="email-security/deployment/m365-use-case-1-3-create-quarantine-policy"
	product="cloudflare-one"
/>

## Configure quarantine notifications

<Render
	file="email-security/deployment/m365-use-case-configure-quarantine-notifications"
	product="cloudflare-one"
/>

## Configure anti-spam policies

<Render
	file="email-security/deployment/m365-use-cases-antispam"
	product="cloudflare-one"
	params={{
		one: "_UserNotifyAdminRelease_",
		two: "_UserNotifyAdminRelease_",
		three: "_UserNotifyAdminRelease_",
	}}
/>

## Create transport rules

To create the transport rules that will send emails with certain dispositions to Email security:

1. Open the new [Exchange admin center](https://admin.exchange.microsoft.com/#/homepage).

2. Go to **Mail flow** > **Rules**.

3. Select **Add a Rule** > **Create a new rule**.

4. Set the following rule conditions:
   - **Name**: `Email security Deliver to Junk Email folder`.
   - **Apply this rule if**: _The message headers_ > _includes any of these words_.
     - **Enter text**: `X-CFEmailSecurity-Disposition` > **Save**.
     - **Enter words**: `BULK` > **Add** > **Save**.
   - **Apply this rule if**: Select **+** to add a second condition.
   - **And**: _The sender_ > _IP address is in any of these ranges or exactly matches_ > enter the egress IPs in the [Egress IPs](/cloudflare-one/email-security/setup/pre-delivery-deployment/egress-ips/) page.
   - **Do the following** - _Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _5_.

5. Select **Next**.

6. You can use the default values on this screen. Select **Next**.

7. Review your settings and select **Finish** > **Done**.

8. Select the rule `Email security Deliver to Junk Email folder` you have just created, and select **Enable**.

# 3 - Junk email and administrative quarantine

import { Render } from "~/components";

In this tutorial, you will learn how to deliver `BULK` messages to the users's junk email folder, and `MALICIOUS`, `SPAM`, and `SPOOF` messages to the administrative quarantine (this requires an administrator to release the emails).

## Create quarantine policies

<Render
	file="email-security/deployment/m365-use-case-1-3-create-quarantine-policy"
	product="cloudflare-one"
/>

## Configure quarantine notifications

<Render
	file="email-security/deployment/m365-use-case-configure-quarantine-notifications"
	product="cloudflare-one"
/>

## Configure anti-spam policies

<Render
	file="email-security/deployment/m365-use-cases-antispam"
	product="cloudflare-one"
	params={{
		one: "_UserNotifyAdminRelease_",
		two: "_UserNotifyAdminRelease_",
		three: "_UserNotifyAdminRelease_",
	}}
/>

## Create transport rules

<Render
	file="email-security/deployment/m365-use-case-transport-rules"
	product="cloudflare-one"
	params={{
		one: "`Email security Deliver to Junk Email folder`",
		two: "BULK",
		three:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _5_",
		four: "step4-rules.png",
		five: "`Email security User Quarantine Message`",
		six: "`MALICIOUS`, `UCE`, `SPOOF`",
		seven:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _9_",
		eight: "step10-user-quarantine.png",
	}}
/>

# 2 - Junk email and user managed quarantine

import { Render } from "~/components";

In this tutorial, you will learn how to deliver `BULK` messages to the user's junk folder, and `SPAM` and `SPOOF` messages to the user managed quarantine.

## Create quarantine policies

<Render
	file="email-security/deployment/m365-use-case-2-4-create-quarantine-policy"
	product="cloudflare-one"
/>

## Configure quarantine notifications

<Render
	file="email-security/deployment/m365-use-case-configure-quarantine-notifications"
	product="cloudflare-one"
/>

## Configure anti-spam policies

<Render
	file="email-security/deployment/m365-use-cases-antispam"
	product="cloudflare-one"
	params={{
		one: "_UserNotifyUserRelease_",
		two: "_UserNotifyAdminRelease_",
		three: "_UserNotifyAdminRelease_",
	}}
/>

## Create transport rules

<Render
	file="email-security/deployment/m365-use-case-transport-rules"
	product="cloudflare-one"
	params={{
		one: "`Email security Deliver to Junk Email folder`",
		two: "BULK",
		three:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _5_",
		four: "step4-rules.png",
		five: "`Email security User Quarantine Message`",
		six: "`UCE`, `SPOOF`",
		seven:
			"_Modify the message properties_ > _Set the Spam Confidence Level (SCL)_ > _9_",
		eight: "step10-user-quarantine.png",
	}}
/>

# Submissions

Submitting messages allows you to choose the disposition of your messages if the disposition is incorrect. This helps improve Email security's detection accuracy and ensures proper handling of email threats.

## Submit messages for review

To submit a message for review:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Email security** and select **Investigation**.
2. On the **Investigation** page, under **Your matching messages**, select the message you want to reclassify.
3. Select the three dots, then select **Submit for review**.
4. Under **New disposition**, select among the following:
   - **Malicious**: Traffic invoked multiple phishing verdict triggers, met thresholds for bad behavior, and is associated with active campaigns.
   - **Spoof**: Traffic associated with phishing campaigns that is either non-compliant with your email authentication policies (SPF, DKIM, DMARC) or has mismatching Envelope From and `Header From` values.
   - **Spam**: Traffic associated with non-malicious, commercial campaigns.
   - **Bulk**: Traffic associated with [Graymail](https://en.wikipedia.org/wiki/Graymail_%28email%29), that falls in between the definitions of `SPAM` and `SUSPICIOUS`. For example, a marketing email that intentionally obscures its unsubscribe link.
   - **Clean**: Traffic not associated with any phishing campaigns.
5. Select **Save**.

To submit messages in bulk, select **Select all messages** > **Action** > **Request submissions**.

To release messages in bulk, select **Select all messages** > **Action** > **Release**.

## Upload EML files

Email security classifies certain emails as "Clean". If you disagree with the disposition, you can upload an EML file and reclassify the email.

On the **Investigation** page:

1. Go to the email marked as **Clean**.
2. Select the three dots > **Submit for review**.
3. Upload the EML file.
4. Select a new disposition.
5. Select **Save**.

## View submissions

Once you have submitted your messages, you can access those on **Submissions**.

To view submissions:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Submissions**.
3. Choose from the following submission types:
   - [**Team submissions**](/cloudflare-one/email-security/submissions/team-submissions/): View emails your security team submitted for submissions.
   - [**User submissions**](/cloudflare-one/email-security/submissions/user-submissions/): View emails your users submitted for submissions.
   - [**Invalid submissions**](/cloudflare-one/email-security/submissions/invalid-submissions/): View submissions that could not be processed.

# Invalid submissions

A submission is invalid when:

- A submission has no EML file attached.
- A submission has been made with an incorrect file extension.
- A submission was made to the wrong team or user alias.

To ensure your submission is valid:

- Ensure your submission has a file attached with a `.eml` file extension.
- Ensure you configure the domain you are submitting emails for.
- Ensure policies are configured correctly.

To view invalid submissions:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Submissions**.
3. Select **Invalid submissions**.

You can search by submission ID or submitted email.

You can filter based on **Date Range** and **Submitted by** (which will list emails that made the invalid submissions). Once you have configured your desired filters, select **Apply filters**.

## Enable notifications

To enable Invalid submission email notifications:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Settings**.
3. Go to **Invalid submission emails** and turn on **Invalid submission email notifications**.

# Team submissions

Team submissions are the emails your security team submitted for submission. All team submissions receive a human review by Cloudflare.

## View team submissions

To view team submissions:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Submissions**.
3. Select **Team submissions**.

## Filter team submissions

Select among the following filters:

- **Date Range**: You can select a date range from the last 7, last 30, and last 90 days.
- **Original disposition**: Select among the [available values](/cloudflare-one/email-security/reference/dispositions-and-attributes/#available-values).
- **Submitted as**: Select among the [available values](/cloudflare-one/email-security/reference/dispositions-and-attributes/#available-values).
- **Final disposition**: Select among the [available values](/cloudflare-one/email-security/reference/dispositions-and-attributes/#available-values).
- **Escalation**: Filter by team submissions that have been escalated or not. Select among `Yes`, `No`, or `All`.

Once you have selected all the filters, select **Apply filters**.

The dashboard will populate the table with the list of emails your security team submitted for submission, including a **Submission ID**, and the **Email subject**.

## View submission details

To gain more details on a specific submission:

1. Go to the submission you want to have more details for.
2. Select the three dots > select among **View more**, **View email message** and **View similar details**.

# User submissions

User submissions are the emails your users submitted for submission. User submissions help enhance our detection model, but can be escalated for human review.

Any email that is reported as [phish](/cloudflare-one/email-security/settings/submission-settings/#reclassify-an-email) will be displayed under **User submissions**.

:::note
[PhishGuard](/cloudflare-one/email-security/phishguard/) customers can have submissions analyzed when submitting at either user or team level. Any non-PhishGuard customer can still have submissions analyzed by submitting at team level.
:::

## View user submissions

To view user submissions:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Select **Email security** > **Submissions**.
3. Select **User submissions**.

## Filter user submissions

Select among the following filters:

- **Date Range**: Select a date range from the last 7, last 30, and last 90 days.
- **Original disposition**: Select among the [available values](/cloudflare-one/email-security/reference/dispositions-and-attributes/#available-values).
- **Submitted as**: Select among the [available values](/cloudflare-one/email-security/reference/dispositions-and-attributes/#available-values).

Once you have selected all the filters, select **Apply filters**.

The dashboard will populate the table with the list of emails your users submitted for submission, including a **Submission ID**, and the **Email subject**.

## View submission details

To gain more details on a specific submission:

1. Go to the submission you want to have more details for.
2. Select the three dots > select among **View more**, **View email message**, **View similar details**, and **Escalate**.

## Escalate a submission

To escalate a submission:

1. Go to the submission you want to escalate.
2. Select the three dots > select **Escalate**.
3. The dashboard will display a message to authorize escalation. Select **Escalate**.

# Identity FAQ

[❮ Back to FAQ](/cloudflare-one/faq/)

## Can Access work with multiple identity providers at the same time?

Yes. Your team can simultaneously use multiple providers, reducing friction when working with partners or contractors. Get started by adding your preferred identity providers as login methods in Zero Trust. Then, when securing a new application behind Access, you'll be able to choose which providers you want your users to log in with to reach that application.

## What if the identity provider my team uses is not listed?

You can add your preferred identity providers to Cloudflare Access even if you do not see them listed in Zero Trust, as long as these providers support SAML 2.0 or [OpenID Connect (OIDC)](/cloudflare-one/integrations/identity-providers/generic-oidc/).

## How do end users log out of an application protected by Access?

Access provides a URL that will end a user's current session.

To force log out of an Access application, go to:

`<your-application-domain>/cdn-cgi/access/logout`

To log out of an App Launcher session, go to:

`<your-team-name>.cloudflareaccess.com/cdn-cgi/access/logout`

For more information, refer to our [session management page](/cloudflare-one/access-controls/access-settings/session-management/#log-out-as-a-user).

# Tunnels FAQ

import { Render } from "~/components";

[❮ Back to FAQ](/cloudflare-one/faq/)

## ​Can I create a Tunnel for an apex domain?

Yes. With [Named Tunnels](https://blog.cloudflare.com/argo-tunnels-that-live-forever/) you can create a CNAME at the apex that points to the named tunnel.

## ​Does Cloudflare Tunnel support Websockets?

Yes. Cloudflare Tunnel has full support for Websockets.

## ​Does Cloudflare Tunnel support gRPC?

Yes. <Render file="tunnel/grpc-support" product="cloudflare-one" />

## How can Tunnel be used with Partial DNS (CNAME Setup)?

Cloudflare offers two modes of setup: [Full Setup](/dns/zone-setups/full-setup/), in which the domain uses Cloudflare DNS nameservers, and [Partial Setup](/dns/zone-setups/partial-setup/) (also known as CNAME setup) in which the domain uses non-Cloudflare DNS servers.

The best experience with Cloudflare Tunnel is using Full Setup because Cloudflare manages DNS for the domain and can automatically configure DNS records for newly started Tunnels.

You can still use Tunnel with Partial Setup. You will need to create a new DNS record with your current DNS provider for each new hostname connected through Cloudflare Tunnel. The DNS record should be of type CNAME or ALIAS if it is on the root of the domain. The name of the record should be the subdomain it corresponds to (e.g. `example.com` or `tunnel.example.com`) and the value of the record should be `subdomain.domain.tld.cdn.cloudflare.net`. (e.g. `example.com.cdn.cloudflare.net` or `tunnel.example.com.cdn.cloudflare.net`)

For a complete walkthrough of using Access with a partial CNAME setup, refer to [Publish a self-hosted application to the Internet](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/#partial-cname-setup).

## How can origin servers be secured when using Tunnel?

Tunnel can expose web applications to the Internet that sit behind a NAT or firewall. Thus, you can keep your web server otherwise completely locked down. To double check that your origin web server is not responding to requests outside Cloudflare while Tunnel is running you can run netcat in the command line:

```sh
netcat -zv [your-server's-ip-address] 80
netcat -zv [your-server's-ip-address] 443
```

If your server is still responding on those ports, you will see:

```txt
[ip-address] 80 (http) open
```

If your server is correctly locked down, you will see:

```txt
[ip-address] 443 (https): Connection refused
```

## Large-file and streaming traffic through Tunnel

It depends on how you route the traffic.

Public hostname routes make applications available on the Internet through Cloudflare's reverse proxy. On Free, Pro, and Business plans, the [service-specific terms](https://www.cloudflare.com/service-specific-terms-application-services/#content-delivery-network-free-pro-or-business) require you to use a specific paid service to serve video and other large files.

Refer to [Delivering videos with Cloudflare](/fundamentals/reference/policies-compliances/delivering-videos-with-cloudflare/) for available options, such as [Stream](/stream/).

Private network routes do not publish applications to the Internet, and this restriction does not apply. Users and networks can reach these routes through the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), [Cloudflare Mesh](/mesh/), or [Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/).

For more information, refer to [Private networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/).

## What records are created for routing to a Named Tunnel's hostname?

Named Tunnels can be routed via DNS records, in which case we use CNAME records to point to the `<UUID>.cfargotunnel.com`; Or as Load Balancing endpoints, which also point to `<UUID>.cfargotunnel.com`.

## Does Cloudflare Tunnel send visitor IPs to my origin?

No. When using Cloudflare Tunnel, all requests to the origin are made internally between `cloudflared` and the origin.

To log external visitor IPs, you will need to [configure an alternative method](/support/troubleshooting/restoring-visitor-ips/restoring-original-visitor-ips/).

## Why does the name 'warp' and 'argo' appear in some legacy materials?

Cloudflare Tunnel was previously named Warp during the beta phase. As Warp was added to the Argo product family, we changed the name to Argo Tunnel to match. Once we no longer required users to purchase Argo to create Tunnels, we renamed Argo Tunnel to Cloudflare Tunnel.

## Is it possible to restore a deleted tunnel?

No. You cannot undo a tunnel deletion. If the tunnel was locally-managed, its [`config.yaml` file](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/#configuration-file) will still be present and you can create a new tunnel with the same configuration. If the tunnel was remotely-managed, both the tunnel and its configuration are permanently deleted.

## How do I contact support?

Before contacting the Cloudflare support team:

1. Take note of any specific error messages and/or problematic behaviors.

2. Make sure that `cloudflared` is updated to the [latest version](https://github.com/cloudflare/cloudflared).

3. Gather any relevant error/access logs from your server.

4. If needed set [`--loglevel`](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#loglevel) to `debug`, so the Cloudflare support team can get more info from the `cloudflared.log` file.

5. Include your [Cloudflare Tunnel diagnostic logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/diag-logs/) (`cloudflared-diag-YYYY-MM-DDThh-mm-ss.zip`).

# Devices FAQ

[❮ Back to FAQ](/cloudflare-one/faq/)

## Why does my Windows device appear to switch from Wi-Fi to Ethernet when I enable the Cloudflare One Client?

As the Cloudflare One Client has replaced WinDivert with WinTun architecture, all Windows machines using WinTun will show as being connected using a virtual adapter. Windows, by default, shows virtual adapter connections with a wired Ethernet connection icon, even if the device is connected over wireless. This is by design and should have no impact on connectivity.

## Why is my device not connecting to a closer Cloudflare data center?

As our [Network Map](https://www.cloudflare.com/en-gb/network/) shows, we have locations all over the globe. However, in the Advanced Connection stats of our application, you may notice that the data center (colo) you are connecting to isn't necessarily the one physically closest to your location. This can be due to a number of reasons:

* Sometimes your nearest colo may be undergoing maintenance or having problems. Check the [Cloudflare Status page](https://www.cloudflarestatus.com/) for system status.
* Your Internet provider may choose to route traffic along an alternate path for reasons such as cost savings, reliability, or other infrastructure concerns.

## Why is my public IP address sometimes visible?

The Cloudflare One Client is meant to ensure all your traffic is kept private between you and the origin (the site you are connecting to), but not from the origin itself. In a number of cases, if the origin site you are communicating with can't determine who you are and where you're from, they can't serve locale relevant content to you.
Sites inside Cloudflare network are able to see this information. If a site is showing you your IP address, chances are they are in our network. Most sites outside our network (orange clouded sites) however are unable to see this information and instead see the nearest egress colo to their server. We are working to see if in the future we can't find a way to more easily share this information with a limited number of gray clouded sites where it is relevant to both parties.

## Why has my throughput dropped while using the Cloudflare One Client?

The Cloudflare One Client is in part powered by 1.1.1.1. When visiting sites or going to a new location on the Internet, you should see blazing fast DNS lookups. However, the Cloudflare One Client is built to trade some throughput for enhanced privacy, because it encrypts all traffic both to and from your device. While this isn't noticeable at most mobile speeds, on desktop systems in countries where high speed broadband is available, you may notice a drop. We think the tradeoff is worth it though and continue to work on improving performance all over the system.

## Why is my device not connecting to a public Wi-Fi?

The Wi-Fi network may have a captive portal that is blocking the Cloudflare One Client from establishing a secure connection. In order to access the portal, and therefore the Internet, you will need to temporarily turn off the Cloudflare One Client. After you login to the captive portal through your browser, you can turn the Cloudflare One Client back on to access corporate resources.

For more information, refer to [Captive portal detection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/captive-portals/).

## Why is my device not connecting to the Internet?

A third-party service or ISP may be blocking WARP, or Zero Trust settings may be misconfigured. For a list of common issues and steps to resolve, refer to our [troubleshooting guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/).

## Why is my device not connecting to the corporate Wi-Fi?

An [OS firewall rule](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#system-firewall) on the device may be blocking the EAP/Radius server that allows users to join the Wi-Fi network. If your corporate Wi-Fi uses a Radius server for network authentication, add the Radius server to your [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) Exclude list.

## Why is my device not connecting to my private network?

If your private network is [exposed via Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/):

* Verify that the Cloudflare One Client is [properly configured](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/#device-configuration) on the device.
* Verify that the user is allowed through by your Access and Gateway policies.
* Verify that the [local LAN settings](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/#router-configuration) for the device do not overlap with the CIDR range of your private network.

When contacting Cloudflare support, ensure that you include [Cloudflare One Client debug logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) for your device. These logs will help Cloudflare support understand the overall architecture of your machine and networks.

# General

[❮ Back to FAQ](/cloudflare-one/faq/)

## What is the difference between Cloudflare Gateway and 1.1.1.1?

1.1.1.1 does not block any DNS query. When a browser requests for example.com, 1.1.1.1 simply looks up the answer either in cache or by performing a full recursive DNS query.

Cloudflare Gateway's DNS resolver introduces security into this flow. Instead of allowing all DNS queries, Gateway first checks the hostname being queried against the intelligence Cloudflare has about threats on the Internet. If that query matches a known threat, or is requesting a blocked domain configured by an administrator as part of a Gateway policy, Gateway stops it before the site could load for the user - and potentially execute code or phish that team member.

## Is multi-factor authentication supported?

Access supports two methods of enforcing MFA:

- **Independent MFA** — Access prompts users for a second factor directly, without relying on your identity provider. You can configure MFA requirements per organization, application, or policy. For more information, refer to [Enforce independent MFA](/cloudflare-one/access-controls/policies/mfa-requirements/#independent-mfa).
- **Identity provider-based MFA** — Access respects the [MFA policies](/cloudflare-one/access-controls/policies/mfa-requirements/#identity-provider-based-mfa) set in your identity provider. For example, if your users are logging into an Access protected app through Okta, Okta would enforce an MFA check before sending the valid authentication confirmation back to Cloudflare Access.

## Which browsers are supported?

These browsers are supported:

- Internet Explorer 11
- Edge (current release, last release)
- Firefox (current release, last release)
- Chrome (current release, last release)
- Safari (current release, last release)

## What languages does the Cloudflare dashboard support?

The Cloudflare dashboard is available in the following languages:

- Deutsch (German)
- English
- Español (Spanish)
- Français (French)
- Italiano (Italian)
- 日本語 (Japanese)
- 한국어 (Korean)
- Português (Portuguese)
- 简体中文 (Mandarin Chinese, Simplified)
- 繁體中文 (Mandarin Chinese, Traditional)

To change your dashboard language, refer to [Profile settings](/fundamentals/user-profiles/customize-account/#language).

## What data localization services are supported?

Cloudflare Zero Trust can be used with the Data Localization Suite to ensure that traffic is only inspected in the regions you choose. For more information refer to [Use Zero Trust with Data Localization Suite](/data-localization/how-to/zero-trust/).

# Getting started with Cloudflare Zero Trust FAQ

[❮ Back to FAQ](/cloudflare-one/faq/)

## How do I sign up for Cloudflare Zero Trust?

You can sign up today on the [Cloudflare dashboard](https://dash.cloudflare.com/). Go to **Zero Trust**, choose a team name and a payment plan, and start protecting your network in just a few minutes.

## What is a team domain/team name?

Your team domain is a unique subdomain assigned to your Cloudflare account, for example, `<your-team-name>.cloudflareaccess.com`. [Setting up a team domain](/cloudflare-one/setup/#2-create-a-zero-trust-organization) is an essential step in your Zero Trust configuration. This is where your users will find the apps you have secured behind Cloudflare Zero Trust — displayed in the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) — and will be able to make login requests to them. The customizable portion of your team domain is called **team name**. You can view your team name and team domain in the [Cloudflare dashboard](https://dash.cloudflare.com/) under **Zero Trust** > **Settings**.

| team name        | team domain                             |
| ---------------- | --------------------------------------- |
| `your-team-name` | `<your-team-name>.cloudflareaccess.com` |

You can change your team name at any time, unless you have the Cloudflare dashboard SSO feature enabled on your account. If Cloudflare dashboard SSO is enabled, you must [turn off SSO](/fundamentals/manage-members/dashboard-sso/#change-your-zero-trust-team-name) before changing your team name.

When you change your team name, the old name becomes available for other accounts to claim. However, if you delete your entire Zero Trust organization, any team name it used is permanently reserved and cannot be reused by any account — including your own.

:::caution[Warning]

If you change your team name, you need to update your organization's identity providers (IdPs) and the Cloudflare One Client to reflect the new team name in order to avoid any mismatch errors.
:::

### How do I transfer a team name to another account?

If you want to move a team name from one Cloudflare account to another (for example, migrating from a personal account to a company account), you can do so as long as the source Zero Trust organization still exists:

1. In the source account, go to **Settings** and change the team name to a temporary value (for example, `mycompany-old`).
2. In the destination account, go to **Settings** and set the team name to the desired value.

:::caution

Do not delete the Zero Trust organization on the source account before changing the team name. If the organization is deleted, the team name is permanently locked and no account will be able to claim it.

:::

### Why is my old team name is still showing up on the Login page and App Launcher?

After changing your team name, you will need to check your Block page, Login page, and App Launcher settings to make sure the new team name is reflected.

To verify that your team name change is successfully rendering on the Block page, Login page and App Launcher:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Custom pages** > **Team name and domain**.
2. Find the **Account Gateway block page** and **Access login page** sections, then select **Manage** next to the page you would like to review first.
3. Review that the value in **Your Organization's name** matches your new team name.
4. If the desired name is not already displayed, change the value to your desired team name and select **Save**.
5. Check both pages (**Account Gateway block page** and **Access login page** to set **Your Organization's name** as your desired team name.

The App Launcher will display the same team name set on the Access login page, so you do not need to update the **Your Organization's name** field in the App Launcher page.

## How do I change my subscription plan?

To make changes to your subscription, visit the Billing section under **Zero Trust** > **Settings** in the [Cloudflare dashboard](https://dash.cloudflare.com/). You can change or cancel your subscription at any time. Just remember - if you downgrade your plan during a billing cycle, your downgraded pricing will apply in the next billing cycle. If you upgrade during a billing cycle, you will be billed for the upgraded plan at the moment you select it.

## How are active seats measured?

Cloudflare Zero Trust subscriptions consist of seats that users in your account consume. When users authenticate to an application or enroll their agent into the Cloudflare One Client, they count against one of your active seats. Seats can be added, removed, or revoked at **Settings** > **Cloudflare One plan**. If all seats are currently consumed, you must first remove users before decreasing your purchased seat count.

### Removing users

User seats can be removed for Access and Gateway at **Team & Resources** > **Users** > **Your users**. Removing a user will have consequences both on Access and on Gateway:

- **Access**: All active sessions for that user will be invalidated. A user will be able to log back into an application unless you create an [Access policy](/cloudflare-one/access-controls/policies/) to block future logins from that user.

- **Gateway**: All active devices for that user will be logged out of your Zero Trust organization, which stops all filtering and routing via the Cloudflare One Client. A user will be able to re-enroll their device unless you create a [device enrollment policy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) to block them.

:::caution

The Remove action will remove a user's seat, but it will not permanently revoke their ability to authenticate. To permanently disable a user's ability to authenticate, you must modify the policies that allow them to reach a given application or enroll a device in the Cloudflare One Client.

:::

### Revoking users

The Revoke action will terminate active sessions and log out active devices, but will not remove the user's consumption of an active seat.

## How do I know if my network is protected behind Cloudflare Zero Trust?

You can visit the [Zero Trust help page](https://help.one.cloudflare.com/). This page will give you an overview of your network details, as well as an overview of the categories that are being blocked and/or allowed.

# FAQ

import { LinkButton, Render } from "~/components";

Review answers to the most commonly asked questions on Cloudflare Zero Trust, as well as a troubleshooting section to help you solve common issues and errors you may come across.

If you cannot find the answer you are looking for, go to our [community page](https://community.cloudflare.com/) and post your question there.

---

## Getting started with Cloudflare Zero Trust

For extra guidance on experiencing Cloudflare Zero Trust for the first time.

<LinkButton variant="primary" href="/cloudflare-one/faq/getting-started-faq/">
	Getting started ❯
</LinkButton>

## General

For general questions on Cloudflare Zero Trust and how it works.

<LinkButton variant="primary" href="/cloudflare-one/faq/general-faq/">
	General ❯
</LinkButton>

## Identity

For questions on identity providers and accessing applications behind Cloudflare Zero Trust.

<LinkButton variant="primary" href="/cloudflare-one/faq/authentication-faq/">
	Identity ❯
</LinkButton>

## Policies

For questions on how policies work, and how to create and test them.

<LinkButton variant="primary" href="/cloudflare-one/faq/policies-faq/">
	Policies ❯
</LinkButton>

## Devices

For questions on device connectivity and the Cloudflare One Client.

<LinkButton variant="primary" href="/cloudflare-one/faq/devices-faq/">
	Devices ❯
</LinkButton>

## Tunnels

For questions on connecting applications with Tunnels.

<LinkButton
	variant="primary"
	href="/cloudflare-one/faq/cloudflare-tunnels-faq/"
>
	Tunnels ❯
</LinkButton>

# Policies FAQ

[❮ Back to FAQ](/cloudflare-one/faq/)

## What is the order of policy enforcement?

Gateway and Access policies generally trigger from top to bottom based on their position in the policy table in the UI. Exceptions include Bypass and Service Auth policies, which Access evaluates first. Similarly, for Gateway HTTP policies, Do Not Inspect and Isolate policies take precedence over all Allow or Block policies. To learn more about order of enforcement, refer to our documentation for [Access policies](/cloudflare-one/access-controls/policies/#order-of-execution) and [Gateway policies](/cloudflare-one/traffic-controls/order-of-enforcement/).

## **How can I bypass the L7 firewall for a website?**

Cloudflare Gateway uses the hostname in the HTTP `CONNECT` header to identify the destination of the request. Administrators who wish to bypass a site must create a [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policy in order to prevent HTTP inspection from occurring on both encrypted and plaintext traffic.

Bypassing the L7 firewall results in no HTTP traffic inspection, and logging is disabled for that HTTP session.

## Can I secure applications with a second-level subdomain URL?

Yes. Ensure that your SSL certificates cover the first- and second-level subdomain. Most certificates only cover the first-level subdomain and not the second. This is true for most Cloudflare certificates. To cover a second-level subdomain with a CF certificate, create an [advanced certificate](/ssl/edge-certificates/advanced-certificate-manager/manage-certificates/).

Wildcard-based policies in Cloudflare Access only cover the level where they are applied. Add the wildcard policy to the left-most subdomain to be covered.

## How do isolation policies work together with HTTP policies?

Isolation policies, like all HTTP policies, are evaluated in stages. When a user makes a request which evaluates an Isolation policy, the request will be rerouted to an isolated browser and re-evaluated for HTTP policies. This makes it possible for an isolated browser to remotely render a block page, or have malicious content within the isolated browser blocked by HTTP policies.

## Why is API or CLI traffic not isolated?

Isolation policies are applied to requests that include `Accept: text/html*`. This allows Browser Isolation policies to co-exist with API and command line requests.

## Can Access enforce policies on a specific nonstandard port?

No. Cloudflare Access cannot enforce a policy that would contain a port appended to the URL. However, you can use Cloudflare Tunnel to point traffic to non-standard ports. For example, if Jira is available at port `8443` on your origin, you can proxy traffic to that port via Cloudflare Tunnel.

## Why can I still reach domains blocked by a Gateway policy?

If the domain is blocked by a DNS, network, or HTTP policy, it may be because:

- **Your policy is still being updated.** After you edit or create a policy, Cloudflare updates the new setting across all of our data centers around the world. It takes about 60 seconds for the change to propagate.

If the domain is only blocked by a DNS policy, it may be because:

- **Your device is using another DNS resolver.** If you have other DNS resolvers in your DNS settings, your device could be using IP addresses for resolvers that are not part of Gateway. As a result, the domain you are trying to block is still accessible from your device. Make sure to remove all other IP addresses from your DNS settings and only include Gateway's DNS resolver IP addresses.

- **Your policy is not assigned to a DNS location.** If your policy is not assigned to a DNS location and you send a DNS query from that location, Gateway will not apply that policy. Assign a policy to a DNS location to make sure the desired policy is applied when you send a DNS query from that location.

- **Your DoH endpoint is not a Gateway DNS location**. Browsers can be configured to use any DoH endpoint. If you chose to configure DoH directly in your browser, make sure that the DoH endpoint is a Gateway DNS location.

If the domain is only blocked by a network policy, it may be because:

- **Your browser is reusing an existing connection**. Network policies only apply when a connection is opened. If a browser is connected to a domain to be blocked by a network policy, Gateway will not block requests until the connection is closed. To block the domain, close any related tabs or restart your browser.

## When does Access return a Forbidden status page versus a login page?

Access returns a Forbidden page with status codes `401`/`403` when it determines there is no way a user can pass a [policy](/cloudflare-one/access-controls/policies/). If Cloudflare can make a full policy determination that a user will not be able to log in, Access will return a Forbidden page instead of a [login page](/cloudflare-one/reusable-components/custom-pages/access-login-page/).

For example, your application has a policy that requires a user to be in a [specific geolocation](/cloudflare-one/access-controls/policies/#allow) to log in.

As admin, you could define this geolocation policy by using [Include](/cloudflare-one/access-controls/policies/#include) rules, meaning the user could log in to the application from Country A or Country B.

Or you could define this geolocation policy using a [Require](/cloudflare-one/access-controls/policies/#require) rule, meaning the user must be in Country A to log in.

If a user from country C attempts to access the application, in both the Include and Require scenarios, the user will receive the Forbidden page. This is because Country C was not defined in either scenario. Therefore, Cloudflare has determined that this user cannot meet policy requirements and will receive the Forbidden status page.

# Implementation guides

import { CardGrid, LinkTitleCard, Render } from "~/components";

Implementation guides cover deployment steps and best practices for specific Cloudflare One use cases.

<CardGrid>

<LinkTitleCard
	title="Secure web traffic from threats"
	href="/learning-paths/secure-internet-traffic/concepts/"
	icon="seti:lock"
>
	Inspect and filter all Internet-bound traffic from your users to block
	threats, enforce acceptable use policies, and prevent data loss.
</LinkTitleCard>

<LinkTitleCard
	title="Replace your VPN"
	href="/learning-paths/replace-vpn/concepts/"
	icon="seti:db"
>
	Give users secure, auditable network and application access.
</LinkTitleCard>

<LinkTitleCard
	title="Secure private apps without a client"
	href="/learning-paths/clientless-access/concepts/"
	icon="laptop"
>
	Provide browser-based access to internal web applications, SSH servers, and
	RDP sessions without installing software on user devices.
</LinkTitleCard>

<LinkTitleCard
	title="Secure your email with Email security"
	href="/learning-paths/secure-your-email/concepts/"
	icon="email"
>
	Use Cloudflare's Email security to protect your Microsoft 365 email inbox from
	phishing and malware attacks.
</LinkTitleCard>

<LinkTitleCard
	title="Holistic AI security with Cloudflare One"
	href="/learning-paths/holistic-ai-security/concepts/"
	icon="magnifier"
>
	Monitor and secure generative AI usage within your organization.
</LinkTitleCard>

</CardGrid>

# Analytics overview

import { Render } from "~/components";

The Cloudflare One Analytics overview provides a dashboard that reports on how Cloudflare One is protecting your organization and networks. Use this page to monitor usage and potential security concerns within your organization.

To view the Analytics overview, log in to [Cloudflare One](https://one.dash.cloudflare.com) and go to **Overview**.

The Analytics overview includes reports and insights across the following products and categories:

- [Global status](#global-status) of your Zero Trust Organization
- [Access](#access)
- Gateway
  - [HTTP traffic](#proxy-traffic)
  - [Network traffic](#gateway-network-requests)
  - [DNS traffic](#dns-traffic)
  - [Firewall policies](#gateway-insights)

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Global status

In **Global status**, you can view a report on your organization's Cloudflare One adoption that contains the following metrics:

- Access apps configured
- Gateway HTTP policies
- Gateway network policies
- Gateway DNS policies
- SaaS integrations
- Data Loss Prevention (DLP) profiles

You can also view a report on your [seat usage](/cloudflare-one/team-and-resources/users/seat-management/) across your Zero Trust Organization that contains the following metrics. A seat is a billable unit consumed when a user authenticates to your Zero Trust organization.

- Total seats
- Used seats
- Unused seats

## Access

In **Access**, you can view a report on your [Cloudflare Access](/cloudflare-one/access-controls/) configuration that contains:

**Metrics:**

- Total access attempts
- Granted access
- Denied (policy violation)
- Active logins over time
- Top applications with most logins

**Filters:**

- Access data by country

## Gateway

### Proxy traffic

In **Proxy traffic**, you can view a report on your [Cloudflare Gateway](/cloudflare-one/traffic-controls/) HTTP traffic that contains:

**Metrics:**

- Total requests over time
- Allowed requests
- Blocked requests
- Isolated requests (served through [Browser Isolation](/cloudflare-one/remote-browser-isolation/))
- Do not inspect requests
- Top bandwidth consumers (GB)
- Top denied users

**Filters:**

- Gateway HTTP traffic data by country

### Gateway (network requests)

In **Gateway (network requests)**, you can view a report on your Gateway network traffic that contains:

**Metrics:**

- Total sessions
- Authenticated sessions
- Blocked sessions
- Allowed sessions
- Override sessions
- Top bandwidth consumers in GB
- Top denied users

**Filters:**

- Gateway network traffic data by country

### DNS traffic

In **DNS traffic**, you can view a report on your Gateway DNS traffic that contains:

**Metrics:**

- Total DNS queries
- Allowed DNS queries
- Blocked DNS queries
- Override DNS queries
- Safe Search DNS queries
- Restricted DNS queries
- Other DNS queries

**Filters:**

- Gateway DNS traffic by query type
- Gateway DNS traffic by country

### Gateway insights

In **Gateway insights**, you can view a report on your Gateway firewall policies that contains the following metrics:

- Top domain blocking policies
- Most user queries
- Top devices
- Top countries

### CASB metrics

In **CASB**, you can review instances of security issues — such as misconfigurations, unauthorized user activity, and shadow IT — found in your SaaS integrations by [Cloudflare CASB](/cloudflare-one/cloud-and-saas-findings/).

- Integrations by number of findings
- [DLP](/cloudflare-one/data-loss-prevention/) findings by profile name

# Insights

import { DirectoryListing, Render } from "~/components";

Cloudflare One offers observability tools to monitor and troubleshoot your environment:

- [Analytics Overview](/cloudflare-one/insights/analytics-overview/) to monitor overall Cloudflare One usage.
- [Analytics Dashboards](/cloudflare-one/insights/analytics/) to review organizational traffic trends and policy insights.
- [Logs](/cloudflare-one/insights/logs/) for event-level investigation.
- [Digital Experience Monitoring (DEX)](/cloudflare-one/insights/dex/) for device, network, and application performance.

## Troubleshooting workflow example

A user reports they cannot reach an internal application behind [Cloudflare Access](/cloudflare-one/). To address the issue:

1. Check the [Analytics overview dashboard](/cloudflare-one/insights/analytics-overview/) to review if other users are experiencing similar issues.
2. Review [Logs](/cloudflare-one/insights/logs/) to examine the user's authentication attempts and blocked requests.
3. Use [DEX](/cloudflare-one/insights/dex/) to evaluate the user's device health and network performance.

## How to use these tools together

### Onboarding

After onboarding your devices and users, use these tools to confirm everything is set up correctly and to monitor your organization's activity.

1. Start with [Logs](/cloudflare-one/insights/logs/) to validate initial configuration and confirm that authentication is successful.
2. Use [Analytics Overview](/cloudflare-one/insights/analytics-overview/) to confirm expected patterns and policy activity.

If your device is experiencing connectivity issues, Cloudflare recommends starting with [troubleshooting WARP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/troubleshooting-guide/) as WARP misconfiguration is the most common cause of connectivity issues.

### Daily monitoring

1. Use [Analytics Dashboards](/cloudflare-one/insights/analytics/) to understand trends and for visualizations of your log data.

    Administrators typically start with Analytics Dashboards because they offer:

    - A high-level view of activity across your products, like Access, or security use cases, such as AI and shadow IT.
    - Visibility into trends, provided through time-series graphs, to track the evolution of key metrics (such as [DNS queries](/cloudflare-one/insights/analytics/gateway/#dns-query-analytics), [network sessions](/cloudflare-one/insights/analytics/gateway/#network-session-analytics), [HTTP requests](/cloudflare-one/insights/analytics/gateway/#http-request-analytics), and [CASB posture/content findings](/cloudflare-one/insights/analytics/data-analytics/)) over time.

2. Use [Logs](/cloudflare-one/insights/logs/) as needed for event-level verification.

    Use Logs when you need to:

    - Investigate a specific event; for example, a user's [failed authentication attempt](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/) when trying to log in to an application.
    - Validate identity or device details; for example, confirming which user made the request, how they authenticated, and whether their device met required [posture conditions](/cloudflare-one/insights/logs/dashboard-logs/posture-logs/).
    - Confirm policy matches; for example, verifying which [specific rule](/cloudflare-one/access-controls/policies/#rule-types) allowed, blocked, or challenged a user's request and why it was applied.

### User-reported issues

Users may report problems like slow or failing connections to internal apps.

1. Start with [Analytics Dashboards](/cloudflare-one/insights/analytics/) to review whether the issue impacts others.
2. Check [Logs](/cloudflare-one/insights/logs/) for failed authentication attempts, blocked requests, or unexpected policy matches.
3. Use [DEX](/cloudflare-one/insights/dex/) to diagnose device- or network-level causes with [synthetic tests](/cloudflare-one/insights/dex/tests/) and [device monitoring](/cloudflare-one/insights/dex/monitoring/).

# Access event analytics

import { Render } from "~/components";

Access event analytics allows you to review login attempts to the applications you protect behind [Access](/cloudflare-one/access-controls/policies/). Access event analytics are powered by [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/).

To view Access event analytics:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Go to **Dashboards**.
3. Select **Access event analytics**.

Access Event Analytics aggregates authentication activity based on your [Access policies](/cloudflare-one/access-controls/policies/policy-management/).

<Render file="analytics/access-analytics-comparison" product="cloudflare-one" />

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Available insights

The Access event analytics dashboard includes a time-series chart of authentication events, allowing you to identify spikes in login activity over a selected period.

- Events are displayed on the vertical axis.
- Time (in your local timezone) is shown along the horizontal axis.

The Access event analytics dashboard also shows data on your usage patterns with metrics including:

- Top used applications
- Top users
- Top IP addresses
- Top identities
- Top countries
- Top application types

These insights help you detect anomalies, and optimize policy rules.

# AI security

import { Render } from "~/components";

The AI security report dashboard summarizes your organization's AI usage and potential security risks.

To view the AI security report dashboard:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Go to **Dashboards**.
3. Select **AI security report**.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Prerequisites

To populate the AI security report dashboard, you must have:

* [Cloudflare Gateway](/cloudflare-one/traffic-controls/) enabled to inspect outbound HTTP and DNS traffic.
* User traffic to SaaS AI applications (for example, ChatGPT or Gemini) sent through Cloudflare Gateway.
* [Model Context Protocol (MCP) servers](/cloudflare-one/access-controls/ai-controls/) behind [Cloudflare Access](/cloudflare-one/access-controls/) policies.

## Available insights

The AI security report dashboard includes the following panels and metrics:

* [Top 5 visited AI applications by user count](#top-5-visited-ai-applications-by-user-count)
* [Statuses applied to AI applications by application count](#statuses-applied-to-ai-applications-by-application-count)
* [Data uploaded to Artificial Intelligence applications by status](#data-uploaded-to-artificial-intelligence-applications-by-status)
* [MCP servers behind Access over time](#mcp-servers-behind-access-over-time)
* [Access login events to MCP servers](#access-login-events-to-mcp-servers)

### Top 5 visited AI applications by user count

Displays the most accessed AI tools in your organization and the number of users visiting each application in a time-series graph.  
Each bar represents user activity for a specific AI application (for example, ChatGPT or Gemini) over time.

Use this chart to monitor adoption trends and detect new or unauthorized AI tools being accessed.

### Statuses applied to AI applications by application count

Reports the total number of AI applications identified and their review statuses.  
Statuses include:

* Unreviewed — Applications not yet evaluated by administrators.  
* In Review — Applications currently under review for approval.  
* Unapproved — Applications that are restricted or blocked.  
* Approved — Applications explicitly permitted for organizational use.

### Data uploaded to Artificial Intelligence applications by status

Reports the amount of data transferred to AI tools, broken down by review status (Unreviewed, In Review, Unapproved, Approved).  
Use this report to understand whether sensitive data is being sent to unapproved or unreviewed AI applications.

### MCP servers behind Access over time

Displays the number of Model Context Protocol (MCP) servers protected by [Cloudflare Access](/cloudflare-one/access-controls/) policies over time. Use this panel to verify that newly deployed MCP servers are protected.

### Access login events to MCP servers

Reports the number of login events to MCP servers protected by Access policies. Use this panel to identify unusual login patterns, such as spikes in access from unexpected users.

# Application Access Report

import { Render } from "~/components";

The Application Access Report provides a high-level summary of [Access](/cloudflare-one/access-controls/policies/) usage across your organization. This dashboard helps administrators monitor authentication patterns, identity provider usage, and Access configuration metrics. If Access is not configured in your account, the dashboard appears empty.

The Application Access Report is powered by [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/).

To view the Application Access Report dashboard:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Go to **Dashboards**.
3. Select **Application Access Report**.

<Render file="analytics/access-analytics-comparison" product="cloudflare-one" />

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Prerequisites

To populate the Application Access Report dashboard, you must have:

* At least one [Access application](/cloudflare-one/access-controls/applications/) configured in your account.
* Users authenticating to these applications through Cloudflare Access.

## Available insights

The Application Access Report dashboard includes the following panels and metrics:

* [Summary of Access activity](#summary-of-access-activity)
* [Access events](#access-events)
* [Access decisions by event count](#access-decisions-by-event-count)
* [Access applications by event count](#access-applications-by-event-count)
* [Access events by type](#access-events-by-type)
* [Top counts of event details](#top-counts-of-event-details)
* [Access admin metrics](#access-admin-metrics)

### Summary of Access activity

The Summary of Access activity section shows a time series of Access login events over a selected period and a summary of login events. You can filter a time period in the upper right corner of the dashboard.

### Access events

Shows a time series of Access login events over a selected period. Each bar represents the number of login events in the x-axis time interval.
You can use this graph to review user authentication activity and detect unusual login spikes.

### Access decisions by event count

Displays the total number of Access decisions made, grouped by outcome (for example, **Granted** or **Denied**).  

### Access applications by event count

Shows a breakdown of authentication events by application type (for example, **Self-hosted**, **SaaS**, **Private network**, **Infrastructure** or **MCP Portal**).  
Use this view to determine which application types users most frequently access.

### Access events by type

Categorizes authentication events by method, such as **SSO** or **Login** (direct credential-based authentication).  
This panel helps administrators understand how users are authenticating across applications and identity providers.

### Top counts of event details

Lists the most common Access event attributes, including:

* Application name — Displays the top accessed applications.
* Identity provider — Shows which identity providers (IdPs) were most used.
* Users — Lists top users by number of login events.
* Countries — Displays top countries where users logged in.
* IP addresses — Lists the top source IPs associated with login events.

These insights help administrators identify usage patterns and trends.

### Access admin metrics

Provides a summary of Access configurations made by admin in your organization, including:

* Applications configured — Total number of Access-protected applications, broken down by type (for example, Self-hosted, SaaS, RDP, SSH, Private network, and [Cloudflare Dashboard SSO](/fundamentals/manage-members/dashboard-sso/)).
* Policies configured — Total number of Access policies, grouped by [policy action](/cloudflare-one/access-controls/policies/#actions) (for example, Allow, Block, Bypass, or Service Auth).

This section helps administrators audit their Access setup and verify that expected resources and policies are in place.

# Data security analytics

import { Render } from "~/components";

The Data security analytics dashboard reports security issues and sensitive data found within your SaaS applications, cloud environments, and HTTP traffic. It combines Cloud Access Security Broker (CASB) findings with activity from Data Loss Prevention (DLP) policies. If neither DLP policies nor CASB integrations are configured in your account, the dashboard appears empty.

To view the Data security analytics dashboard:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Go to **Dashboards**.
3. Select **Data security analytics**.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Prerequisites

To populate this dashboard with partial data, you need at least one of the following:

- At least one HTTP policy that references a [DLP profile](/cloudflare-one/data-loss-prevention/dlp-policies/).
- At least one SaaS integration enrolled in [CASB](/cloudflare-one/integrations/cloud-and-saas/).
- At least one Cloud integration enrolled in [CASB](/cloudflare-one/integrations/cloud-and-saas/).
- At least one SaaS or Cloud integration enrolled in [CASB](/cloudflare-one/integrations/cloud-and-saas/) and a DLP profile applied to it.

To learn which sensitive data types appear in your Gateway traffic before creating a DLP policy, use [Passive Detection](/cloudflare-one/data-loss-prevention/passive-detection/). It shows sampled detections separately from the policy activity on this dashboard.

## Available insights

The dashboard includes the following panels and metrics:

- [SaaS and Cloud findings by count](/cloudflare-one/insights/analytics/data-analytics/#saas-and-cloud-findings-by-count)
- [Posture findings by Severity](/cloudflare-one/insights/analytics/data-analytics/#posture-findings-by-severity)
- [DLP matches in HTTP requests over time](/cloudflare-one/insights/analytics/data-analytics/#dlp-matches-in-http-requests-over-time)
- Top integrations by posture findings
- Top integrations by content findings
- Top cloud resources by findings
- Top users by DLP policies triggered

### SaaS and Cloud findings by count

The SaaS and Cloud findings by count chart shows a time series view of Posture and Content findings. [Posture findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#posture-findings) are configuration and access issues detected by CASB, such as misconfigurations, unauthorized user activity, and other data security issues. [Content findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#content-findings) are instances of potential data exposure as identified by [DLP](/cloudflare-one/data-loss-prevention/).

Each bar represents the total number of findings detected within a given time interval. You can use this view to observe patterns or spikes in findings over time. Hover over any bar to view the exact count of Posture and Content findings for that period.

To review findings in detail, log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Cloud & SaaS findings** > **Posture Findings** or **Content Findings**.

### Posture findings by Severity

The Posture findings by severity chart displays the distribution of CASB findings based on their [severity levels](/cloudflare-one/cloud-and-saas-findings/manage-findings/#severity-levels). Each segment of the circle represents the number of posture issues classified as `Critical`, `High`, `Medium`, or `Low`.

To review findings in detail, log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Cloud & SaaS findings** > **Posture Findings**.

### DLP matches in HTTP requests over time

The DLP matches in HTTP requests over time chart displays when [DLP policies](/cloudflare-one/data-loss-prevention/dlp-policies/) were triggered by users over a specified period of time.

Unlike the SaaS and Cloud findings by count chart, which shows CASB findings from data at rest (files already stored in your connected SaaS applications), the DLP matches in HTTP requests over time chart shows DLP detections in HTTP traffic — data actively moving through your network.

To review DLP detections in detail, log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Insights** > **Logs** > **HTTP request logs**. Use the **DLP profiles** or **DLP match data** filters to view HTTP requests that triggered a DLP policy.

# Gateway analytics (DNS, HTTP, network sessions)

import { Render } from "~/components";

Gateway analytics include three separate dashboards:

- HTTP request analytics.
- DNS query analytics.
- Network policy analytics.

To review Gateway analytics:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Go to **Dashboards**.
3. Select your desired dashboard.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## HTTP request analytics

Your [Gateway HTTP policies](/cloudflare-one/traffic-controls/http-policies/) power the HTTP request analytics dashboard. If you are not using Gateway HTTP policies, the dashboard will appear empty.

The HTTP request analytics dashboard helps you identify trends in how your HTTP policies apply over time. By visualizing allowed, [isolated](/cloudflare-one/remote-browser-isolation/) (rendered in a remote browser), and [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) (bypassing TLS decryption) requests, the dashboard provides insights into traffic behavior and policy trends, making it easier to spot anomalies or shifts in usage patterns.

To review a detailed description of an HTTP request and its associated policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Select **Logs**.
3. Select **HTTP request logs**.
4. Use the **Policy** filter to view HTTP requests that triggered a policy or other filters to narrow down your results.

### Provided analytics

- HTTP Requests over time
  - Time series view of HTTP requests
- Top Actions
- Top Countries
- Top Blocked Users
- Top Bandwidth Consumers
- Top Devices
- Top Source IPs

## DNS query analytics

Your [Gateway DNS policies](/cloudflare-one/traffic-controls/dns-policies/) power the DNS query analytics dashboard. If you are not using Gateway DNS policies, the dashboard will appear empty.

The DNS query analytics dashboard helps you identify trends in how your DNS policies apply over time. By visualizing allowed, blocked, and overridden (DNS response replaced by a policy-defined address) queries, the dashboard provides insights into traffic behavior and policy trends, making it easier to spot anomalies or shifts in usage patterns.

To review a detailed description of a DNS query and its associated policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Select **Logs**.
3. Select **DNS query logs**.
4. Use the **Policy** filter to view DNS queries that triggered a policy or other filters to narrow down your results.

### Provided analytics

- DNS Queries over time
  - Time series view of DNS queries
- Top Actions
- Top Countries
- Top Blocked Users
- Top Allowed Users
- Top Blocked Devices

## Network policy analytics

Your [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) power the Network policy analytics dashboard. If you are not using Gateway network policies, the dashboard will appear empty.

The Network policy analytics dashboard helps you identify trends in how your Gateway network policies apply over time. By visualizing allowed, blocked, and overridden (traffic rerouted by a policy-defined rule) sessions, the dashboard provides insights into traffic behavior and policy trends, making it easier to spot anomalies or shifts in usage patterns.

To review a detailed description of a network session and its associated policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Select **Logs**.
3. Select **Network logs**.
4. Use the **Policy** filter to view network sessions that triggered a policy or other filters to narrow down your results.

### Provided analytics

- Network Sessions over time
  - Time series view of network sessions
- Top Actions
- Top Countries
- Top Blocked Users
- Top Bandwidth Consumers
- Top Devices
- Top Source IPs

## GraphQL queries

You can use the [GraphQL Analytics API](/analytics/graphql-api/) to query your Gateway Analytics data. Available [datasets](/analytics/graphql-api/features/data-sets/) for Gateway include:

| Dataset                                                   | Description                                                                                                                              |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `gatewayL4DownstreamSessionsAdaptiveGroups`               | Metrics for Gateway network sessions from user devices to the Cloudflare global network.                                                 |
| `gatewayL4UpstreamSessionsAdaptiveGroups`                 | Metrics for Gateway network sessions from the Cloudflare global network to user devices.                                                 |
| `gatewayL4SessionsAdaptiveGroups`                         | Metrics for Gateway network sessions with adaptive sampling.                                                                             |
| `gatewayL7RequestsAdaptiveGroups`                         | Metrics for Gateway HTTP requests with adaptive sampling.                                                                                |
| `gatewayResolverQueriesAdaptiveGroups`                    | Metrics for Gateway DNS queries with adaptive sampling.                                                                                  |
| `gatewayResolverByRuleExecutionPerformanceAdaptiveGroups` | Time to execute Gateway DNS policies on the Cloudflare global network.                                                                   |
| `gatewayResolverByCustomResolverGroups`                   | Metrics for Gateway DNS queries resolved using custom resolvers.                                                                         |
| `gatewayResolverByCategoryAdaptiveGroups`                 | Metrics for Gateway DNS queries sorted by [domain category](/cloudflare-one/traffic-controls/domain-categories/) with adaptive sampling. |

To explore the schema, you can use a GraphQL client such as [GraphiQL](https://github.com/graphql/graphiql/tree/main/packages/graphiql#readme) or [Altair](https://altairgraphql.dev/).

1. [Create an API token](/analytics/graphql-api/getting-started/authentication/api-token-auth/) with the following permissions:

   | Type    | Item              | Permission |
   | ------- | ----------------- | ---------- |
   | Account | Account Analytics | Read       |

2. In your GraphQL client, [add your API token](/analytics/graphql-api/getting-started/authentication/graphql-client-headers/) as an Authorization header.
3. Compose a query to access your Gateway Analytics datasets. For example, you can query the `gatewayResolverQueriesAdaptiveGroups` dataset to return the adaptive groups of DNS queries resolved by Gateway:

   ```graphql graphql-api-explorer
   query GatewaySampleQuery($accountTag: string!, $start: Time) {
   	viewer {
   		accounts(filter: { accountTag: $accountTag }) {
   			gatewayResolverQueriesAdaptiveGroups(
   				filter: { datetime_gt: $start }
   				limit: 10
   			) {
   				count
   				dimensions {
   					queryNameReversed
   					resolverDecision
   				}
   			}
   		}
   	}
   }
   ```

For more information, refer to [Compose a query in GraphiQL](/analytics/graphql-api/getting-started/compose-graphql-query/).

# Dashboards

import { DirectoryListing, Render } from "~/components";

The [Cloudflare dashboard](https://dash.cloudflare.com/) provides a catalog of saved analytics views under **Zero Trust** > **Insights** for reporting and investigation. Analytics Dashboards provide visualizations of your log data.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

<DirectoryListing />

# Network session analytics

import { Render } from "~/components";

The Network session analytics dashboard provides visibility into your Cloudflare One traffic patterns. This dashboard helps you understand how traffic flows through your network, including on-ramps (how traffic enters Cloudflare, such as the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), [proxy endpoints (PAC files)](/cloudflare-one/traffic-controls/proxy-endpoints/), [Browser Isolation](/cloudflare-one/remote-browser-isolation/), [Cloudflare Mesh](/mesh/), [Workers VPC](/workers-vpc/), or Cloudflare Tunnel) and off-ramps (how traffic exits Cloudflare, such as the public Internet, a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), or a Mesh node).

The dashboard is based on the [Zero Trust network sessions Logpush dataset](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/). For definitions on any field, refer to the dataset schema documentation.

To review Network session analytics:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Dashboards**.
2. Select **Network session analytics**.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Use cases

The Network session analytics dashboard helps you:

- **Understand traffic patterns**: Visualize how traffic flows through your network infrastructure.
- **Monitor bandwidth usage**: Track upload, download, and total bytes transferred across your network.
- **Identify connection issues**: Analyze connection close reasons to troubleshoot network problems.
- **Track user and device activity**: Monitor unique users and devices accessing your network.

## Provided analytics

### Summary metrics

- **Session count**: Total number of network sessions. Each session represents an individual TCP, UDP, ICMP, or ICMPv6 flow that passes through Gateway.
- **Bytes total**: Total bytes transferred (upload + download)
- **Unique users**: Number of distinct users

### Traffic by location

- **World map**: Geographic visualization of network traffic by the Cloudflare data center where traffic entered the network (ingress) and where it exited (egress)
- **Location list**: Top Cloudflare data center locations by ingress and egress session count with accompanying graph
- **Change**: Shows the total change across ingress and egress for each location

### Top analytics

- **Top protocols**: Most used network protocols (TCP, UDP, ICMP, ICMPv6)
- **Top connection close reasons**: Common reasons for session termination:
  - Client closed
  - Origin closed
  - Client idle timeout
  - Client error
  - Unknown
  - Client TLS error
  - Origin unreachable
  - Too many new sessions for user
  - Origin TLS error
  - Origin unroutable

For the full list of reasons for session termination, refer to [ConnectionCloseReason](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/#connectionclosereason).

### Troubleshoot session limit errors

Session limit close reasons identify the type and scope of a limit. Reasons containing `ACTIVE_SESSIONS` indicate too many concurrent sessions. Reasons containing `NEW_SESSIONS` indicate that sessions are being created too quickly. `FOR_ACCOUNT` reasons aggregate sessions for the account on the Cloudflare server enforcing the limit and can affect multiple users connected to that server. `FOR_USER` reasons apply to one user.

Use [Zero Trust Network Session Logs](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/), not Gateway activity logs, to investigate these errors. Filter by `ConnectionCloseReason`, then correlate `SessionStartTime` and `SessionID` with fields such as `Email`, `UserID`, `DeviceID`, `SourceIP`, `OriginIP`, `OriginPort`, `Protocol`, and `ConnectionReuse`.

Reduce automatic retries and connection churn in the affected application. Reuse connections when the application and protocol support it. If expected sustained traffic continues to produce these errors, contact your account team or [Cloudflare Support](/cloudflare-one/troubleshooting/contact-support/) for review.

## Related resources

- [Zero Trust network sessions Logpush dataset](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/): View detailed logs for individual network sessions.
- [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/): Configure policies that apply to network traffic.

# Shadow IT SaaS analytics

import { Render } from "~/components";

Shadow IT SaaS analytics provides visibility into the SaaS applications your users are visiting. The dashboard aggregates data from Gateway HTTP traffic to track application usage across your organization. This information allows you to create identity and device-driven Cloudflare One policies to secure your users and data.

To access Shadow IT SaaS analytics:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Go to **Dashboards**.
3. Select **Shadow IT: SaaS analytics**.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## Prerequisites

To allow Cloudflare to discover shadow IT in your traffic, you must set up [HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/).

## Use Shadow IT SaaS analytics

### 1. Review applications

The first step in using the Shadow IT SaaS analytics dashboard is to review applications in the [Application Library](/cloudflare-one/team-and-resources/app-library/). The App Library synchronizes application review statuses with approval statuses from the Shadow IT Discovery SaaS analytics dashboard.

<Render file="app-library-review-apps" product="cloudflare-one" />

### 2. Monitor usage

Review the Shadow IT SaaS analytics dashboard for application usage. Filter the view based on:

    | Field              | Description                                                                                                                  |
    | ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
    | Application        | SaaS application's name and logo.                                                                                            |
    | Application type   | [Application type](/cloudflare-one/traffic-controls/application-app-types/#app-types) assigned by Cloudflare One.             |
    | Application status | Application's approval status.                                                                                               |
    | Hostname           | Hostname of the requested SaaS application.                                                                                |
    | Country            | Country code associated with the user's source IP address.                                                                 |

To manage application statuses in bulk, select **Set Application Statuses** to review applications your users commonly visit and update their approval statuses.

### 3. Create policies

After marking applications, you can create [HTTP policies](/cloudflare-one/traffic-controls/http-policies/) based on application review status. For example, you can create policies that:

- Launch all **Unreviewed** and **In review** applications in an [isolated browser](/cloudflare-one/traffic-controls/http-policies/common-policies/#1-isolate-unreviewed-or-in-review-applications).
- [Block access](/cloudflare-one/traffic-controls/http-policies/common-policies/#2-block-unapproved-applications) to all **Unapproved** applications.
- Limit file upload capabilities for specific application statuses.

To create an HTTP status policy directly from Shadow IT Discovery:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights**.
2. Select **Dashboards** > **Shadow IT: SaaS analytics**.
3. Select **Set application statuses**.
4. Select **Manage HTTP status policies**, then choose an application status and select **Create policy**.

## Available insights

The Shadow IT SaaS analytics dashboard includes several insights to help you monitor and manage SaaS application usage.

- **Number of applications by status**: A breakdown of how many applications have been categorized into each [approval status](#1-review-applications). The list of applications is available in the [App Library](/cloudflare-one/team-and-resources/app-library/).
- **Data uploaded per application status**: A time-series graph showing the amount of data uploaded to applications in the given status.
- **Data downloaded per application status**: A time-series graph showing the amount of data downloaded from applications in the given status.
- **User count per application status**: A time-series graph showing the number of unique users who have interacted with at least one application in a given status. A single user can appear in multiple status categories if they access applications with different statuses. For example, a user who accesses both an **Approved** application and an **Unapproved** application will be counted in both status categories.
- **Top-N metrics**: A collection of metrics providing insights into top applications, users, devices, and countries.

### Understanding user counts

The user count chart displays unique users in two ways:

- **Time-series bars**: Show unique users per time interval (for example, per hour or per day). The same user can appear in multiple time intervals if they were active during those periods.
- **Legend totals**: Show unique users across the entire selected time range, deduplicated. Each user is counted only once per status, regardless of how many time intervals they appeared in.

For example, if User A accesses an Approved application every hour for three hours, they will appear in each hourly bar but will only be counted once in the legend total.

# DEX MCP server

The MCP server [(Model Context Protocol)](https://cloudflare.com/learning/ai/what-is-model-context-protocol-mcp/) for Digital Experience Monitoring (DEX) is an AI tool that allows customers to ask a question like, "Show me the connectivity and performance metrics for the device used by carly&#8204;@acme.com", and receive an answer that contains data from the DEX API.

Any Cloudflare One customer using a Free, Pay-as-you-go, or Enterprise account can access the DEX MCP server.

There are two primary options for connecting to the DEX MCP server:

- [In Cloudflare's AI Playground](#cloudflare-ai-playground)
- [With your preferred AI assistant](#ai-assistant)

## Cloudflare AI Playground

Cloudflare's AI Playground allows you to quickly try out the DEX MCP server.

You can test the DEX MCP server in less than one minute by visiting the AI Playground's website.

1. Copy the URL for the DEX MCP server: `https://dex.mcp.cloudflare.com/mcp`.
2. Open [playground.ai.cloudflare.com](https://playground.ai.cloudflare.com) in a browser.
3. Find the section in the left sidebar titled **MCP Servers**.
4. Paste the URL for the DEX MCP server into the URL input box and select **Connect**.
5. Authenticate your Cloudflare account, and then start asking questions about your DEX data.

:::note
You need to ask specific and explicit questions to get a response. For example, first you need to provide the following instruction: "Set XYZ as the active account". Then, you can ask a specific question: "Fetch the DEX test results for the user bob@&#8204;acme.com over the past 24 hours".
:::

## AI Assistant

You can get a more flexible and robust experience by configuring the DEX MCP server with your preferred AI assistant (for example, Claude, Gemini, or ChatGPT).

If you have any issues during the configuration process, you can ask your AI assistant for help with configuring an MCP server via URL.

### Claude Code

You need a Claude Pro account or higher subscription to configure an MCP server.

1. Install [Claude Code](https://docs.anthropic.com/en/docs/claude-code/setup).
2. Add the DEX MCP server:

   ```sh
   claude mcp add --transport http cloudflare-dex-analysis https://dex.mcp.cloudflare.com/mcp
   ```

3. Run `claude mcp list` to verify the connection.
4. Authenticate your Cloudflare account and authorize the DEX MCP server.
5. Ask Claude "Are you connected to the DEX MCP server?" to test the connection.

### Gemini CLI

All tiers of Google AI Free, Pro, and Ultra offer an MCP server integration via the Gemini CLI.

You will need to use a CLI of your choice and npm or homebrew to install and access the Gemini CLI.

{/* prettier-ignore-start */}

1. Visit the GitHub page for the [Gemini CLI](https://github.com/google-gemini/gemini-cli) and follow the installation instructions.
2. Navigate to the `settings.json` file for your Gemini CLI install and open it in a text editor of your choice.

   <details>
	 <summary>File path for the `settings.json` file</summary>
   - Windows: `%USERPROFILE%\.gemini\settings.json`
   - Mac and Linux: `~/.gemini/settings.json`
   </details>

3. Copy the JSON configuration for the DEX MCP server and paste it into **settings.json**. Save the file.

   ```json
   {
     "mcpServers": {
       "cloudflare-dex-analysis": {
         "httpUrl": "https://dex.mcp.cloudflare.com/mcp"
       }
     }
   }
   ```

4. Run Gemini in your CLI of choice.
5. If everything is working as expected, the Gemini CLI will show the following message:<br/>
   `Using: 1 MCP server (ctrl+t to view)`
6. Authenticate the email associated with your Cloudflare account in the Gemini CLI.
7. You can start asking the Gemini CLI questions about DEX. As a simple test, you can ask "Are you connected to the DEX MCP server".

{/* prettier-ignore-end */}

### ChatGPT

You need a ChatGPT Pro or Business account to configure an MCP server. ChatGPT Free and Plus do not support MCP servers.

1. Download the [ChatGPT desktop app](https://chatgpt.com/features/desktop).
2. Open the ChatGPT desktop app, and log in or set up an account.
3. Open the **Settings** menu and select **Connectors**.
4. Select the option to create a new Connector.
5. Provide a **Name** (like `DEX MCP`), **Description** (optional), and **MCP Server URL** for the Connector. The DEX MCP Server URL is: `https://dex.mcp.cloudflare.com/mcp`.
6. Create the new Connector.
7. Before you ask ChatGPT a question about DEX, select the **+** (plus) button next to the ChatGPT prompt box.
8. Select **Use Connectors** > **Add Sources**, then select the DEX MCP as a source.
9. You can start asking ChatGPT questions about DEX. As a simple test, you can ask "Are you connected to the DEX MCP server".

# Digital experience

import { DirectoryListing, Render } from "~/components";

<Render file="dex/intro" product="cloudflare-one" />

Enrolled devices automatically send device-state telemetry to DEX. Synthetic tests are optional checks that administrators create to monitor specific public or private endpoints.

DEX is compatible with Cloudflare's [Customer Metadata Boundary](/data-localization/metadata-boundary/) (CMB) for the EU (European Union). When CMB is configured for the EU, customer logs are stored exclusively in the EU region.

<Render file="analytics/observability-tools-note" product="cloudflare-one" />

## When a user reports a problem

If a user notifies that “the connection is not working” or “performance is slow,” DEX allows you to:

- Use [device monitoring](/cloudflare-one/insights/dex/monitoring/) to check device health and endpoint connectivity.
- Optionally, test network health and application responsiveness with [synthetic tests](/cloudflare-one/insights/dex/tests/) that run periodically from user devices.
- Identify whether problems originate from the device (such as [issues with the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/troubleshooting-guide/)), the network, or Cloudflare.

## Troubleshooting other Cloudflare One features

Use DEX to troubleshoot other Cloudflare One features:

- Test connectivity to a [SaaS application secured with Access](/cloudflare-one/access-controls/applications/http-apps/saas-apps/).
- Verify that a website routed through [Gateway](/cloudflare-one/traffic-controls/) is reachable from user devices.
- Confirm that users can successfully reach internal resources after configuring a [Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).

### Get started

To start using DEX for device, network, and application monitoring:

1. [Create a Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization).
2. [Install the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) and sign in to register your device to the organization.
3. (Optional) Create [tests](/cloudflare-one/insights/dex/tests/) to verify device connectivity to applications and networks.
4. [Monitor](/cloudflare-one/insights/dex/monitoring/) device and network health across your fleet using real-time and historical metrics.
5. Use [diagnostics](/cloudflare-one/insights/dex/diagnostics/) to run speed tests and collect remote captures from user devices.
6. Set up [notifications](/cloudflare-one/insights/dex/notifications/) to get alerts when degraded connectivity or application performance is detected.

### Troubleshooting

For help resolving common issues with Digital Experience Monitoring, refer to [Troubleshoot Digital Experience Monitoring](/cloudflare-one/insights/dex/troubleshooting/).

### Directory

Review all available documentation for DEX capabilities.

<DirectoryListing />

# IP visibility

import { Render, Details } from "~/components";

<Details header="Feature availability">

| System   | Availability | Minimum client version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2025.1.861.0         |
| macOS    | ✅           | 2025.1.861.0         |
| Linux    | ✅           | 2025.1.861.0         |
| iOS      | ❌           |                      |
| Android  | ❌           |                      |
| ChromeOS | ❌           |                      |

</Details>

DEX's IP visibility gives administrators insight into three different IP types per device:

1. **Device**: The private IP address of an end-user device.
2. **ISP**: The public IP that the ISP assigns when it routes the end-user device's traffic.
3. **Gateway**: The router's private IP (the router the end device is connected to.)

:::note

The ISP IP is only visible to users with the [Zero Trust PII role](/cloudflare-one/roles-permissions/#cloudflare-zero-trust-pii).

:::

DEX's IP visibility supports both IPv6 and IPv4 addresses.

IP information helps IT administrators troubleshoot network issues and identify device locations. Common uses include:

- Identifying which access point or network segment a user is connected to
- Verifying that network access control (NAC) policies are applied correctly
- Diagnosing firewall restrictions on specific VLANs (virtual local area networks)
- Troubleshooting Layer 2 (data link layer) and DHCP (Dynamic Host Configuration Protocol) issues
- Indirectly determining user identity and device location

## View a device's IP information

To view IP information for a user device:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Team & Resources** > **Devices** > **Your devices**.
2. Select a device, then select **View details**.
3. Go to **IP details**.
4. Review the IP details for your selected device's most recent session.

## View a device's IP history

DEX's IP visibility allows you to review an event log of a device's IP history for the last seven days. To view a device's IP history:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Team & Resources** > **Devices** > **Your devices**.
2. Select a device > **View details** > go to **IP details**.
3. Select **View all ISPs**.

## Troubleshoot with IP visibility

While IP visibility allows you to inspect a device's IP information, use [DEX's live analytics](/cloudflare-one/insights/dex/monitoring/#available-metrics) to review which Cloudflare data center the device is connected to. When traffic leaves a Cloudflare One Client-connected end-user device, it will hit a [Cloudflare data center](/support/troubleshooting/general-troubleshooting/gathering-information-for-troubleshooting-sites/#identify-the-cloudflare-data-center-serving-your-request).

To find which Cloudflare data center a device is connected to:

1. Follow the steps listed in [View IP information](#view-a-devices-ip-history) to find a device's IP information.
2. On the device page, select **Colocation & client** or find the **Client** table at the top of the page.
3. In the **Client** table, find **Colocation** to review which Cloudflare data center your selected device's outbound (egress) traffic is routed through.

# Device monitoring

import { Render } from "~/components";

Monitor performance and network status for your organization's [fleet](/cloudflare-one/insights/dex/monitoring/#fleet-status) (all devices with the Cloudflare One Client installed and connected to your Zero Trust organization) or individual [user devices](/cloudflare-one/insights/dex/monitoring/#device-monitoring).

Network and device performance data helps IT administrators troubleshoot performance issues, investigate network connectivity problems, and monitor device health.

## Device overview

A fleet is a collection of user devices. All devices in a fleet have the Cloudflare One Client installed and are connected to a [Cloudflare Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization).

To view fleet status:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Review the information under **Live analytics**.

### View metrics

The **Device overview** tab shows real-time and historical connectivity metrics for all devices in your organization.

To view analytics on a per-device level, go to [Device monitoring](/cloudflare-one/insights/dex/monitoring/#device-monitoring).

### Available metrics

- **Devices connected by colo**: Number of devices connected to a given [Cloudflare data center](https://www.cloudflarestatus.com/).

- **Connectivity status**: Percentage of devices in a given Cloudflare One Client state.

  | Status       | Description                                                                                                                                                                                                                                                                                                                                                                               |
  | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | Connected    | The Cloudflare One Client has successfully established a connection to the Cloudflare global network.                                                                                                                                                                                                                                                                                                          |
  | Disconnected | The Cloudflare One Client has been intentionally or unintentionally disconnected from the Cloudflare global network.                                                                                                                                                                                                                                                                                           |
  | Paused       | A user or administrator has taken an explicit action to temporarily turn off WARP, for example by entering an [admin override code](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-admin-override-codes). Paused clients will [auto-connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect) after a timeout period. |
  | Connecting   | The Cloudflare One Client is pending connection, but is actively trying to establish a connection to the Cloudflare global network.                                                                                                                                                                                                                                                                            |

- **Mode**: [Client mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) deployed on the device.

- **Colo**: Percentage of devices connected to a given Cloudflare data center.

- **Platform**: Operating system of the device.

- **Major Version**: Cloudflare One Client version installed on the device.

- **Device Status Over Time**: Cloudflare One Client connection status over the selected time period.

- **Connection Methods Over Time**: Client mode used by the device over the selected time period.

## Device monitoring

Review network and device performance for a device enrolled in your fleet.

### View a device's performance

To view a device's network and device performance metrics:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Team & Resources** > **Devices** > **Your devices**.
2. Select a device > **View details**.
3. Select the **DEX** tab.
4. In **Device Monitoring**, scroll down to **Network performance** and **Device Performance**.

### Network and device performance metrics

#### Network performance metrics

- **Unique networks over time**: How many unique SSIDs (Wi-Fi network names) the device was connected to.

- **Network I/O**: How much data the device transferred (uploads and downloads) over the primary network interface.

#### Device performance metrics

- **Battery percentage and cycles**: Displays battery percentage and [battery cycles](https://support.apple.com/en-us/102888) over time. Use this metric to debug potential performance issues possibly related to battery health or power-saving measures that trigger at low-battery levels.

- **CPU usage**: CPU utilization over time. Use this metric to debug slow system performance due to high CPU usage.

- **Memory utilization**: Memory utilization over time. Use this metric to debug performance issues related to an overtaxed memory.

- **Disk I/O**: Displays number of disk read/write operations over time. Use this metric to debug performance errors due to heavy disk operations.

## Export DEX device state event logs

<p>The log data for all <a href="/logs/logpush/logpush-job/datasets/account/dex_device_state_events/">DEX device state events</a> can be exported to <a href="/r2/">R2</a>, a cloud bucket, or a SIEM via <a href="/cloudflare-one/insights/logs/logpush/">Logpush</a>.</p>

# Notifications

import { AvailableNotifications, Render } from "~/components";

<Render file="dex/notifications-intro" product="cloudflare-one" />

## Manage notifications

DEX notifications are configured on the [Cloudflare dashboard](https://dash.cloudflare.com/). For more information, refer to [Create a notification](/notifications/get-started/#create-an-alert).

## Available notifications

<AvailableNotifications product="DEX" />

## Alert logic

### Z-score

Cloudflare uses a z-score to detect unusual traffic spikes or drops. A [z-score](https://en.wikipedia.org/wiki/Standard_score) is the number of standard deviations the current value is from the mean. Cloudflare calculates the mean and standard deviation by comparing the current five minutes to the past four hours. This is measured every five minutes.

To trigger an alert, the z-score value must be above 3.5 or below -3.5, which indicates the current value is significantly different from the recent baseline.

### SLO

A service-level objective (SLO) measures the percentage of valid events that succeeded. It is defined as (good events / valid events) \* 100, where valid events are those that could be measured in a given time period. DEX notifications evaluate both a short window (five minutes) and a long window (one hour) and trigger an alert if availability falls below the SLO threshold in either window.

# Rules

import { Render } from "~/components";

DEX rules allow you to create and manage testing policies for targeted user groups within your [fleet](/cloudflare-one/insights/dex/tests/) (all devices with the Cloudflare One Client installed and connected to your Zero Trust organization). After creating a rule, you can use it to define the scope of a [test](/cloudflare-one/insights/dex/tests/) to specific groups such as departments (like finance or sales), devices, and/or users. You can apply and reuse rules on your desired tests.

Use DEX rules to scope a test to a specific group within your fleet for more precise problem detection and resolution.

## Create a rule

To create a rule:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Select the **Rules** tab.
3. Select **Add a rule**.
4. Give your rule a name and build your desired expressions.
5. Select **Create rule** to finalize your rule.

### Selectors

Selectors are required categories in a DEX rule expression that define a group within a fleet. The selector(s) you have defined in a rule will determine which group a test will impact.

Review the available selectors and their scope in the following list.

| Selector                     | Description                                                                                                                       |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **User email**               | For specifying [user emails](/cloudflare-one/traffic-controls/identity-selectors/#user-email).                                    |
| **User group emails**        | For specifying [group emails](/cloudflare-one/traffic-controls/identity-selectors/#user-group-email).                             |
| **User group IDs**           | For specifying [group IDs](/cloudflare-one/traffic-controls/identity-selectors/#user-group-ids).                                  |
| **User group names**         | For specifying a [group name](/cloudflare-one/traffic-controls/identity-selectors/#user-group-names).                             |
| **Operating systems**        | For specifying operating systems.                                                                                                 |
| **Operating system version** | For specifying an operating system version (use Operator `in`) or versions (use Operator `is`).                                   |
| **Managed network**          | For specifying users accessing the network from the office (managed network) compared to those accessing remotely.                |
| **SAML attributes**          | For specifying a value from the [SAML Attribute Assertion](/cloudflare-one/traffic-controls/identity-selectors/#saml-attributes). |
| **Colos**                    | For specifying a Cloudflare data center (colocation) that users are connected to.                                                          |

## Add a rule to a test

After you have created a rule, you can add it to a test. If you do not add a rule to a test, the test will run on your entire device fleet.

To add a rule to a test:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Select the **Tests** tab.
3. Choose an existing test and select **Edit**, or select **Add a test** to make a new test.
4. Under **Select DEX rules**, select the rule you would like to apply.
5. Select **Save test** for an existing rule or **Add rule** for the new test.

:::note

<Render file="warp/client-notification-lag" product="cloudflare-one" />
:::

To view which tests a rule is being applied to:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Select the **Rules** tab.
3. Choose a rule and select **Edit**.
4. Select the **DEX tests** tab and review the list of tests that include your selected rule.

## Create a test using a rule

You can create a new test from the [DEX test dashboard as described above](/cloudflare-one/insights/dex/rules/#add-a-rule-to-a-test) or directly from the DEX rules dashboard.

To create a new test using a rule from DEX rules:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Select the **Rules** tab.
3. Select a rule and select **Edit**.
4. Select the **DEX tests** tab.
5. You will be able to review all the tests that currently include this rule. To create a new test, select **Create a test using this rule**.
6. Enter all required information, making sure that the box next to your rule name is checked.
7. Select **Add test**.

## Related resources

- [DEX HTTP test](/cloudflare-one/insights/dex/tests/http/) - Assess the accessibility of a web application.
- [DEX Traceroute test](/cloudflare-one/insights/dex/tests/traceroute/) - Measure the network path of an IP packet from an end-user device to a server.

# Troubleshoot Digital Experience Monitoring

import { Render } from "~/components";

<Render file="troubleshooting/dex" product="cloudflare-one" />

---

## How to contact Support

If you cannot resolve the issue, [open a support case](/support/contacting-cloudflare-support/). Please provide a [remote capture](/cloudflare-one/insights/dex/diagnostics/client-packet-capture/) from the Zero Trust dashboard for the affected device.

# Client packet capture

import { Details, Render } from "~/components";

<Render file="dex/pcaps-run-availability" product="cloudflare-one" />

Remote captures allow administrators to collect packet captures (PCAPs) and Cloudflare One Client diagnostic logs directly from end user devices. A packet capture is a recording of network traffic at the packet level. This data can be used to troubleshoot network problems, investigate security incidents, and identify performance bottlenecks.

## Start a remote capture

<Render file="dex/pcaps-run" product="cloudflare-one" />

## Check remote capture status

<Render file="dex/pcaps-check" product="cloudflare-one" />

## Download remote captures

<Render file="dex/pcaps-download" product="cloudflare-one" />

### Device PCAP contents

The downloaded PCAP folder contains three files:

- `capture-default.pcap`: Packet captures for the primary network interface.
- `capture-tunnel.pcap`: Packet captures for traffic inside of the WARP tunnel.
- `results.json`: Reports successful and failed packet captures.

You can analyze `.pcap` files using Wireshark or another third-party packet capture tool.

### Diagnostic log files

Refer to [Cloudflare One Client diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/#warp-diag-logs) for a description of each file.

## Diagnostics analyzer (beta)

<Render file="dex/pcaps-view-warp-diag" product="cloudflare-one" />

## Limitations

- Packet captures are subject to the following limits:

  | Limit Type  | Maximum Value |
  | ----------- | ------------- |
  | Time limit  | 600 seconds   |
  | File size   | 50 MB         |
  | Packet size | 1500 bytes    |

- Cloudflare One Client diagnostic logs have no file size limit, but files larger than 100 MB cannot be uploaded to Cloudflare and must be shared directly with the admin.

- Windows devices do not support concurrent remote captures. If you start a remote capture while another is in progress, the second capture will fail immediately.

- PCAPs will fail on Windows if you have another third-party packet capture tool (such as, Packet Monitor `pktmon`) running.

- On Windows, packet captures may fail on devices configured with a non-English language due to limitations with the underlying `PktMon` tool.

# Diagnostics

import { DirectoryListing } from "~/components";

Diagnostics tools allow administrators to remotely investigate device connectivity and network performance issues. Use remote captures to collect packet captures and diagnostic logs from end-user devices, or run speed tests to measure network throughput and latency from the Cloudflare One client.

To access diagnostics, go to the [Cloudflare One dashboard](https://dash.cloudflare.com/one) and select **Insights** > **Digital experience** > **Diagnostics**.

<DirectoryListing />

# Speed test

import { Details } from "~/components";

Speed tests allow administrators to remotely measure network performance from end-user devices running the [Cloudflare One client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/). Each test runs from the client to Cloudflare's network edge and reports metrics for internet speed, latency, and network quality.

Speed tests help IT teams:

- Objectively measure network performance with the Cloudflare One client turned on.
- Identify performance bottlenecks affecting specific users, devices, or locations.
- Respond to user reports of slow connectivity with concrete data.

<Details header="Feature compatibility">

Feature availability

- All Cloudflare One plans

Supported client modes

- Traffic and DNS mode
- Traffic only mode

Supported operating systems:

| System   | Support |
| -------- | ------- |
| Windows  | ✅      |
| macOS    | ✅      |
| Linux    | ✅      |
| iOS      | ❌      |
| Android  | ❌      |
| ChromeOS | ❌      |

</Details>

To run a speed test from a device:

1. In [Zero Trust](https://dash.cloudflare.com/one), go to **Insights** > **Digital experience** > **Diagnostics**.
2. Select **Run diagnostics**.
3. Search for a device by user email, device name, or device ID.
4. Select the device, then select **Device speed test**.

The test runs in the background on the selected device. Results appear in the diagnostics view once the test completes.

## Speed test metrics

Each speed test reports the following metrics:

### Internet speed

| Metric              | Description                                                                                        |
| ------------------- | -------------------------------------------------------------------------------------------------- |
| Download throughput | The rate at which data is received by the device from Cloudflare's network edge, measured in Mbps. |
| Upload throughput   | The rate at which data is sent from the device to Cloudflare's network edge, measured in Mbps.     |

### Latency

| Metric           | Description                                                                                                                                 |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Download latency | The round-trip time measured during an active download, reflecting latency under load.                                                      |
| Upload latency   | The round-trip time measured during an active upload, reflecting latency under load.                                                        |
| Unloaded latency | The baseline round-trip time measured when no significant data transfer is occurring. This reflects the inherent latency of the connection. |
| Jitter           | The variation in latency over time. High jitter can cause inconsistent performance in real-time applications.                               |

### Network quality score

Network quality scores estimate the end-user experience for common application types based on the measured speed and latency values.

| Score           | Description                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------- |
| Video streaming | Rates the connection quality for video streaming applications based on throughput and latency.          |
| Video streaming | Estimates the connection quality for video streaming applications based on throughput and latency.      |
| Web chat / RTC  | Estimates the connection quality for real-time communication applications such as video calls and VoIP. |

# HTTP test

import { Details, Render } from "~/components";

<Details header="Feature availability">
| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/)    | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul> <li> Traffic and DNS mode </li> <li> Traffic only mode </li> </ul> | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2023.3.381           |
| macOS    | ✅           | 2023.3.381           |
| Linux    | ✅           | 2023.3.398           |
| iOS      | ❌           |                      |
| Android  | ✅           | 1.0                  |
| ChromeOS | ✅           | 1.0                  |

</Details>

<Render file="dex/http-test-intro" product="cloudflare-one" />

HTTP tests run periodically from devices that have the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) installed and turned on. You can use them to verify that an internal application is reachable after a configuration change or to monitor a SaaS application for outages that affect your organization.

## Create a test

<Render file="dex/http-test-create-steps" product="cloudflare-one" />

## Test results

<Render file="dex/http-test-results-metrics" product="cloudflare-one" />

Use these metrics together to identify where in the connection a problem occurs. For example, a high DNS response time with a normal server response time points to a DNS resolution issue rather than a problem with the target server.

## Export DEX application test logs

<Render
	file="dex/logpush-export-application-tests"
	product="cloudflare-one"
/>

## Related resources

- [DEX rules](/cloudflare-one/insights/dex/rules/) - Define which users or groups a test applies to, using selectors such as user email, user group, operating system, or managed network.

# Synthetic tests

import { DirectoryListing, Render } from "~/components";

<Render file="dex/tests-intro" product="cloudflare-one" />

Cloudflare One Client devices automatically send device-state telemetry to DEX. Synthetic tests are separate, optional checks that administrators create for specific public or private endpoints.

DEX tests will only run when the Cloudflare One Client is turned on, whereas [fleet status](/cloudflare-one/insights/dex/monitoring/#fleet-status) metrics are always available.

To control which users or groups run a test, use [DEX rules](/cloudflare-one/insights/dex/rules/).

<DirectoryListing />

## Export DEX application test logs

<Render file="dex/logpush-export-application-tests" product="cloudflare-one" />

# Traceroute test

import { Details, Render } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/)    | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul> <li> Traffic and DNS mode </li> <li> Traffic only mode </li> </ul> | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2023.5.587           |
| macOS    | ✅           | 2023.5.589           |
| Linux    | ❌           |                      |
| iOS      | ❌           |                      |
| Android  | ✅           | 1.0                  |
| ChromeOS | ✅           | 1.0                  |

</Details>

A traceroute test measures the network path of an IP packet from an end-user device to a server. The packet passes through a series of intermediate routers — each called a "hop" — and the test records the response time and packet loss at each one. You can use the results to troubleshoot network issues by identifying which hop along the path is causing increased latency or dropped packets.

## Create a test

To set up a traceroute test for an application:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Select the **Tests** tab.
3. Select **Add a Test**.
4. Fill in the following fields:
   - **Name**: Enter any name for the test.
   - **Target**: Enter the IP address of the server you want to test (for example, `192.0.2.0`). You can test either a public-facing endpoint or a private endpoint you have connected to Cloudflare.
   - **Source device profiles**: (Optional) Select the [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) that you want to run the test on. A device profile defines Cloudflare One Client settings for a specific set of devices in your organization. If no profiles are selected, the test will run on all supported devices connected to your Zero Trust organization.
   - **Test type**: Select _Traceroute_.
   - **Test frequency**: Specify how often the test will run. Input a minute value between 5 and 60.
5. Select **Add test**.

Next, [view the results](/cloudflare-one/insights/dex/tests/view-results/) of your test.

## Test results

A traceroute test measures the following data:

| Data            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Network path    | IP address, average response time, and packet loss for each hop (router) between the device and the target. This is the core traceroute data — it maps the route your traffic takes.                                                                                                                                                                                                                                                                       |
| Round trip time | Time, in milliseconds, between sending out a packet and receiving a response from the target. This is the end-to-end latency measurement.                                                                                                                                                                                                                                                                                                                  |
| Number of hops  | Number of routers encountered between the device and the target.                                                                                                                                                                                                                                                                                                                                                                                            |
| Packet loss     | Percentage of IP packets that failed to receive a response.                                                                                                                                                                                                                                                                                                                                                                                                 |
| Availability    | Percentage of tests where at least one packet reached the destination. A value below 100% means the destination was completely unreachable during some test runs.                                                                                                                                                                                                                                                                                           |
| Last seen ISP   | The Internet Service Provider that is managing the connection from the device to Cloudflare. (Only available on macOS and Windows.) <br/> <br/> DEX looks up the IP address of the ISP in a geolocation database and returns the corresponding [ASO (Autonomous System Organization) and ASN (Autonomous System Number)](https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/). If the ASO and ASN are `Unknown`, it means this information is unavailable in the geolocation data provider. |

## Export DEX application test logs

<Render
	file="dex/logpush-export-application-tests"
	product="cloudflare-one"
/>

## Related resources

- [DEX rules](/cloudflare-one/insights/dex/rules/) - Define which users or groups a test applies to, using selectors such as user email, user group, operating system, or managed network.

# View test results

import { Render } from "~/components";

Use the results of a Digital Experience Monitoring (DEX) test to monitor availability and performance for a specific application. DEX stores test results for 7 days on all plans, according to the [log retention policy](/cloudflare-one/insights/logs/#log-retention).

## Prerequisites

- At least one [test](/cloudflare-one/insights/dex/tests/) has been created under **DEX** > **Tests**.
- Admins must have at least the [Cloudflare Zero Trust Reporting role](/cloudflare-one/roles-permissions/#zero-trust-roles).

## View results for all devices

To view an overview of test results for all devices:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Digital experience**.
2. Select the **Tests** tab.
3. Select a test to view detailed results.

## View results for an individual device

To view analytics on a per-device level:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Team & Resources** > **Devices** > **Your devices**.
2. Select the device you want to view, and then select **View details**.
3. Select the **Tests** tab.
4. Select a test to view detailed results.

## Export DEX application test logs

<Render
	file="dex/logpush-export-application-tests"
	product="cloudflare-one"
/>

## Related resources

- [DEX HTTP test](/cloudflare-one/insights/dex/tests/http/) - Send a `GET` request from enrolled devices to a web application and measure response times.
- [DEX Traceroute test](/cloudflare-one/insights/dex/tests/traceroute/) - Map the network route between a device and a server, showing each hop along the path.
- [DEX rules](/cloudflare-one/insights/dex/rules/) - Define which users or groups a test applies to, using selectors such as user email, user group, operating system, or managed network.

# Logs

import { DirectoryListing, Badge, Render } from "~/components";

Review detailed logs for your Zero Trust organization.

<DirectoryListing />

## Log retention

Cloudflare stores Zero Trust logs for different periods of time based on the service and plan type:

|                         | Free      | Standard  | Access    | Gateway   | Enterprise   |
| ----------------------- | --------- | --------- | --------- | --------- | ------------ |
| **Admin logs**          | 18 months | 18 months | 18 months | 18 months | 18 months    |
| **Access logs**         | 24 hours  | 30 days   | 30 days   | 24 hours  | 180 days     |
| **DNS logs**            | 24 hours  | 30 days   | 24 hours  | 30 days   | 180 days[^1] |
| **Network logs**        | 24 hours  | 30 days   | 24 hours  | 30 days   | 30 days      |
| **HTTP logs**           | 24 hours  | 30 days   | 24 hours  | 30 days   | 30 days      |
| **DEX logs**            | 7 days    | 7 days    | 7 days    | 7 days    | 7 days       |
| **Device posture logs** | 30 days   | 30 days   | 30 days   | 30 days   | 30 days      |

[^1]: Enterprise users on per query plans cannot store DNS logs via Cloudflare. You can still export logs via [Logpush](/cloudflare-one/insights/logs/logpush/). For more information, contact your account team.

## Log Explorer <Badge text="Beta" variant="caution" size="small" />

Log Explorer users can store Zero Trust logs directly within Cloudflare in an [R2 bucket](/r2/) and access them with the dashboard or API. Log Explorer supports the following Zero Trust datasets:

<Render file="log-explorer-account-datasets" product="logs" />

For more information, refer to [Log Explorer](/observability/logs/log-explorer/).

## Customer Metadata Boundary

You can use Cloudflare Zero Trust with the Data Localization Suite to restrict data storage to a specific geographic region. For more information, refer to [Customer Metadata Boundary](/data-localization/metadata-boundary/).

## Data privacy

For more information on how we use this data, refer to our [Privacy Policy](https://www.cloudflare.com/application/privacypolicy/).

# Access authentication logs

import { GlossaryTooltip, TabItem, Tabs, APIRequest } from "~/components";

Access authentication logs help you track who accessed your protected applications, when they accessed them, and whether they were allowed in. Use these logs to investigate suspicious login attempts, audit user activity, or troubleshoot access issues.

Cloudflare Access generates two types of audit logs:

- **[Authentication audit logs](#authentication-logs)** record each login attempt (successful or failed) by a user or service to an Access application.
- **[Per-request audit logs](#per-request-logs)** record individual HTTP requests that authenticated users make to protected [application paths](/cloudflare-one/access-controls/policies/app-paths/) and infrastructure targets.

## Authentication logs

Cloudflare Access logs an authentication event whenever a user or service attempts to log in to an application, whether the attempt succeeds or not.

[Identity-based authentication](#identity-based-authentication) refers to login attempts that were evaluated based on who the user is — for example, their email address, identity provider (IdP) group, SAML group, or OIDC claim.

[Non-identity authentication](#non-identity-authentication) refers to login attempts that were evaluated based on context rather than user identity — for example, IP address, device posture, country, valid certificate, or service token.

:::note
Authentication logs do not capture the user's actions during a self-hosted or SaaS application session. To audit individual requests made during a session, refer to [Per-request logs](#per-request-logs).
:::

### Identity-based authentication

#### View Access authentication logs

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

To view logs for identity-based authentication events:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Logs**.
2. Select **Access authentication logs**.

	:::note[Log viewer (beta)]
	Access authentication logs use an updated log viewer with enhanced filtering capabilities. To switch to the classic view, select **Return to old logs**.
	:::
3. (Optional) Filter the logs that display in the log viewer. You can filter logs by their timestamp and event details (such as the Access application, user email, policy decision, and more).

	:::tip
	Querying for fewer fields improves log loading performance.
	:::
4. Select an individual timestamp to investigate the event in more detail.

</TabItem> <TabItem label="API">

The [Access authentication logs](/api/resources/zero_trust/subresources/access/subresources/logs/subresources/access_requests/methods/list/) API endpoint provides a custom URL to export audit log events for your account.

<APIRequest
	path="/accounts/{account_id}/access/logs/access_requests"
	method="GET"
	parameters={{
		account_id: "$ACCOUNT_ID",
		limit: 25,
		direction: "desc",
		since: "2020-07-01T05:20:00Z",
		until: "2020-10-01T05:20:00Z",
	}}
/>

```json title="Response"
{
	"success": true,
	"errors": [],
	"messages": [],
	"result": [
		{
			"user_email": "michelle@example.com",
			"ip_address": "198.41.129.166",
			"app_uid": "df7e2w5f-02b7-4d9d-af26-8d1988fca630",
			"app_domain": "test.example.com/admin",
			"action": "login",
			"connection": "saml",
			"allowed": false,
			"created_at": "2014-01-01T05:20:00.12345Z",
			"ray_id": "187d944c61940c77"
		}
	]
}
```

</TabItem> </Tabs>

#### Explanation of the fields

Identity-based authentication logs contain the following fields:

##### Basic information

| Field            | Description                                                                                                              |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **App**          | Name of the Access application.                                                                                          |
| **User email**   | Email address of the authenticating user.                                                                                |
| **User ID**      | Unique identifier (UUID) of the authenticating user.                                                                     |
| **IP address**   | IP address of the authenticating user.                                                                                   |
| **App UID**      | Unique identifier (UUID) of the Access application.                                                                      |
| **App domain**   | URL of the Access application.                                                                                           |
| **App type**     | Specifies the type of Access application: self-hosted, browser SSH, browser VNC, browser RDP, SaaS, or infrastructure.   |
| **Event**        | Type of authentication event, such as a login attempt.                                                                   |
| **Connection**   | Identity provider used to authenticate (for example, `saml`, `onetimepin`, `google-apps`).                               |
| **Allow**        | Whether the authentication attempt was allowed (`true`) or denied (`false`).                                             |
| **Request time** | Timestamp of the authentication event.                                                                                   |
| **Ray ID**       | A unique identifier for every request through Cloudflare. Useful for tracing a specific request through Cloudflare logs. |
| **Country**      | Country associated with the user's IP address.                                                                           |

##### Infrastructure applications

Cloudflare Access logs the following information when the user authenticates to an [infrastructure application](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/):

| Field         | Description                                                                                                                                                                                                                            |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Hostname**  | Hostname of the infrastructure target.                                                                                                                                                                                                 |
| **Target ID** | UUID of the infrastructure target.                                                                                                                                                                                                     |
| **SSH user**  | The UNIX user, such as `root`, that the authenticating user specified when connecting to the infrastructure target.                                                                                                                    |
| **SSH logs**  | SSH commands that the user ran on the target. Requires configuring an [SSH encryption key](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#ssh-command-logs) before the session begins. |

### Non-identity authentication

To retrieve logs for non-identity authentication events, use the [GraphQL Analytics API](/analytics/graphql-api/tutorials/querying-access-login-events/). These logs are not available in the Cloudflare One dashboard.

## Per-request logs

Users who have authenticated through Access have access to authorized URL paths for the duration of their session. Cloudflare provides several ways to audit these requests.

### Using Cloudflare Logs

Enterprise customers have access to detailed logs of requests on their Cloudflare dashboard. Enterprise customers also have access to Cloudflare's Logpush service, which can be configured from the Cloudflare dashboard or API. For more information about Cloudflare HTTP and infrastructure logging, refer to [Cloudflare Logs](/logs/).

Once a member of your team authenticates to reach an HTTP resource behind Access, Cloudflare generates a <GlossaryTooltip term="JSON web token">JSON Web Token (JWT)</GlossaryTooltip> for that user that contains their SSO identity. Cloudflare signs this token using RS256 (RSA Signature with SHA-256), an asymmetric algorithm, and makes the public key available so that you can verify the token is authentic.

When a user requests a URL, Access appends the user identity from that token as a request header, which Cloudflare logs as the request passes through the network. Your team can collect these logs in your preferred third-party Security information and event management (SIEM) software or storage destination by using [Cloudflare Logpush](/cloudflare-one/insights/logs/logpush/). When enabled with the Access user identity field, the logs export to your systems as JSON similar to the example below.

```json
{
   "ClientIP": "198.51.100.206",
   "ClientRequestHost": "jira.widgetcorp.tech",
   "ClientRequestMethod": "GET",
   "ClientRequestURI": "/secure/Dashboard/jspa",
   "ClientRequestUserAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36",
   "EdgeEndTimestamp": "2019-11-10T09:51:07Z",
   "EdgeResponseBytes": 4600,
   "EdgeResponseStatus": 200,
   "EdgeStartTimestamp": "2019-11-10T09:51:07Z",
   "RayID": "5y1250bcjd621y99",
   "RequestHeaders":{"cf-access-user":"srhea"}
},
{
   "ClientIP": "198.51.100.206",
   "ClientRequestHost": "jira.widgetcorp.tech",
   "ClientRequestMethod": "GET",
   "ClientRequestURI": "/browse/EXP-12",
   "ClientRequestUserAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36",
   "EdgeEndTimestamp": "2019-11-10T09:51:27Z",
   "EdgeResponseBytes": 4570,
   "EdgeResponseStatus": 200,
   "EdgeStartTimestamp": "2019-11-10T09:51:27Z",
   "RayID": "yzrCqUhRd6DVz72a",
   "RequestHeaders":{"cf-access-user":"srhea"}
}
```

### Using the `cf-access-user` field

In addition to the HTTP request fields available in Cloudflare Enterprise logging, requests made to applications behind Access include the `cf-access-user` field, which contains the user identity string. This offers another tool for auditing user behavior. To add the `cf-access-user` field to your HTTP request logs, you must add it as a custom field. Refer to [Custom fields](/logs/logpush/logpush-job/custom-fields/) for instructions.

Keep in mind that Access does not log all interactions. Per-request audit logs can indicate that a specific user visited `domain.com/admin` and then `domain.com/admin/panel`, but the logs only capture interactions that result in a new HTTP request. Purely client-side interactions that do not generate server requests are not logged.

# Admin activity logs

Admin activity logs record configuration changes made by members of your Cloudflare account. These logs are useful for auditing who changed a policy or setting and investigating unexpected configuration changes. Use these logs to monitor when a member creates, updates, or deletes configurations in your [Zero Trust organization](/cloudflare-one/setup/#create-a-zero-trust-organization).

To view admin activity logs, log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Insights** > **Logs** > **Admin activity logs**.

## Explanation of the fields

| Field           | Description                                           | Example Value                                                                      |
| ----------------| ------------------------------------------------------| -----------------------------------------------------------------------------------|
| Email           | User who performed the action                         | josephli@cloudflare.com                                                            |
| Product         | Cloudflare product being modified                     | Tunnel                                                                             |
| Resource        | Specific resource type within the product             | Route                                                                              |
| Event           | Action performed (Create, Update, Delete)             | Create                                                                             |
| Date            | Timestamp of when the action occurred                 | April 30, 2026 • 12:19 AM                                                          |
| User IP Address | IP address of the user who made the change            | 2a09:bac6:6447:523::83:30                                                          |
| Interface       | How the change was initiated                          | API                                                                                |
| Audit record    | Unique identifier for the audit log entry             | caf1a547-17cc-484a-b4ce-5d3b32771a8f                                               |
| Old value       | Previous configuration state (empty for creates)      | {}                                                                                 |
| New value       | New configuration state after the change              | JSON object with fields like comment, network, tun_type, tunnel_id, virtual_network_id |

## Export admin activity logs

Enterprise users can export admin activity logs to a third-party storage destination or SIEM using [Logpush](/cloudflare-one/insights/logs/logpush/). For a list of all available fields, refer to [Audit Logs V2](/logs/logpush/logpush-job/datasets/account/audit_logs_v2/).

# Dashboard logs

import { DirectoryListing } from "~/components";

The following logs are available in the [Cloudflare One dashboard](https://one.dash.cloudflare.com/). Use these logs to review user activity, policy decisions, and connection details for your Zero Trust deployment.

<DirectoryListing button={true}/>

For additional datasets and long-term log storage, refer to [Logpush](/cloudflare-one/insights/logs/logpush/).

# Posture logs

Posture logs show the results of [device posture checks](/cloudflare-one/reusable-components/posture-checks/) — security requirements such as OS version, disk encryption, or endpoint protection status — reported by the Cloudflare One Client. Use these logs to identify which devices are passing or failing your organization's posture requirements and to troubleshoot individual check results.

To view device posture logs, log in to [Cloudflare One](https://one.dash.cloudflare.com/) and go to **Insights** > **Logs** > **Posture logs**. Logs will only display if you have configured [device posture checks](/cloudflare-one/reusable-components/posture-checks/) for your Zero Trust organization.

Enterprise users can generate more detailed logs with [Logpush](/cloudflare-one/insights/logs/logpush/).

## Explanation of the fields

### Device details

| Field             | Description                                                                              |
| ----------------- | ---------------------------------------------------------------------------------------- |
| **Name**          | Display name of the device as reported by the operating system (typically the hostname). |
| **ID**            | Device ID generated by the Cloudflare One Client.                                        |
| **Serial number** | Serial number of the device.                                                             |
| **Manufacturer**  | Manufacturer of the device (for example, Dell, Apple, Lenovo).                           |
| **Model**         | Model of the device (for example, MacBook Pro, ThinkPad X1).                             |

### User details

| Field               | Description                                                                                                                                                                                                                    |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Email**           | Email used to register the device with Zero Trust.                                                                                                                                                                             |
| **User ID**         | Unique identifier (UUID) of the user who registered the device.                                                                                                                                                    |
| **Registration ID** | UUID of the user's Cloudflare One Client registration. A unique registration ID is generated each time a device is registered. The same physical device may have multiple registration IDs if multiple users share the device. |

### Posture details

| Field               | Description                                                                                                                                                                                                                                                                        |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name**            | Name of the [device posture check](/cloudflare-one/reusable-components/posture-checks/).                                                                                                                                                                                           |
| **Type**            | Whether the check is a [Cloudflare One Client check](/cloudflare-one/reusable-components/posture-checks/client-checks/) (evaluated locally on the device) or a [service provider check](/cloudflare-one/integrations/service-providers/) (evaluated by a third-party integration). |
| **Rule ID**         | UUID of the device posture check.                                                                                                                                                                                                                                                  |
| **Conditions met**  | Whether the device passed or failed the posture check criteria. Evaluates to `true` if the **Received values** match the **Expected values**.                                                                                                                                      |
| **Expected values** | Values required to pass the device posture check. Compare with **Received values** to diagnose why a device failed.                                                                                                                                                                |
| **Received values** | Actual values detected on the device by the Cloudflare One Client or service provider.                                                                                                                                                                                             |

# SCIM provisioning logs

import { Render } from "~/components";

SCIM (System for Cross-domain Identity Management) activity logs allow administrators to audit how [SCIM provisioning](/cloudflare-one/team-and-resources/users/scim/) events in an identity provider (such as create, update, and delete) affect a user's identity and group membership in Zero Trust. You can compare your Zero Trust SCIM logs with your identity provider's SCIM logs to track how identity data is shared between the two services and pinpoint the source of any provisioning errors.

## View SCIM logs

For an overview of SCIM events across all users, log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Insights** > **Logs** > **SCIM provisioning logs**. This page lists the inbound SCIM requests that your identity providers have sent to Cloudflare. You can select an individual request to view more details about the SCIM operation.

To investigate how SCIM events impacted a specific user, go to their [User Registry identity](/cloudflare-one/team-and-resources/users/users/). View their last seen identity and group memberships, and track how their identity has changed over time.

<Render file="access/scim-requires-login" product="cloudflare-one" />

## Log fields

SCIM provisioning logs show the following information for each inbound SCIM request:

- **IdP name**: Name of the identity provider that sent the request
- **Timestamp**: Date and time of the request
- **Action**: HTTP request method (`POST`, `PUT`, `PATCH`, `DELETE`). `POST` indicates a resource was created, `PUT` indicates a full resource replacement, `PATCH` indicates a partial update, and `DELETE` indicates a resource was removed.
- **User email**: User who received the SCIM identity update
- **Group name**: Group that received the SCIM identity update
- **Resource type**: Whether the request modified a group or a user (`GROUP` or `USER`)
- **CF resource ID**: Persistent identifier for the user or group created by Cloudflare SCIM. Use this ID to look up the resource in Zero Trust.
- **IDP resource ID**: Identifier for the user or group provided by the identity provider. Use this ID to match the log entry with the corresponding record in your identity provider.
- **Outcome**: Whether the SCIM request was applied successfully (`SUCCESS` or `ERROR`)
- **Request body**: HTTP request body containing the data that was added, modified, or removed
- **JSON log**: SCIM request log in JSON format

# SSH command logs

SSH command logs record the commands that users run on infrastructure targets protected by [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/). Use these logs to audit user activity on your SSH servers and investigate specific sessions.

To view SSH command logs, log in to [Cloudflare One](https://one.dash.cloudflare.com/) and go to **Insights** > **Logs** > **SSH command logs**.

## Prerequisites

To generate SSH command logs, you must:

1. Set up [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) for your SSH servers.
2. [Enable SSH command logging](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#ssh-command-logs) by uploading an encryption public key. Cloudflare uses this key to encrypt your logs so that only you can read their contents.

## View SSH logs

SSH command logs displayed in the dashboard are encrypted using the public key you provided during setup. The logs are not readable in the dashboard — you must download and decrypt them locally. To view the contents of the logs:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Logs** > **SSH command logs**.
2. Filter the logs using the name of your SSH application.
3. Select the SSH session for which you want to export command logs.
4. In the side panel, scroll down to **SSH logs** and select **Download**.
5. Decrypt the log using the [SSH Logging CLI](https://github.com/cloudflare/ssh-log-cli/) and the private key that corresponds to the public key you uploaded.

## Log fields

| Field                       | Description                                                                                                                                                                                                                                                                                                                                                                                               |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Session ID**              | Unique identifier for the SSH session.                                                                                                                                                                                                                                                                                                                                                                    |
| **User email**              | Email address of the user who initiated the SSH session.                                                                                                                                                                                                                                                                                                                                                  |
| **Target ID**               | Identifier of the infrastructure target being accessed. Corresponds to the target you configured in Access for Infrastructure.                                                                                                                                                                                                                                                                            |
| **Client address**          | Source IP address of the SSH connection.                                                                                                                                                                                                                                                                                                                                                                  |
| **Server address**          | Destination IP address of the SSH server.                                                                                                                                                                                                                                                                                                                                                                 |
| **Session start datetime**  | Timestamp when the SSH session started.                                                                                                                                                                                                                                                                                                                                                                   |
| **Session finish datetime** | Timestamp when the SSH session ended.                                                                                                                                                                                                                                                                                                                                                                     |
| **Program type**            | Type of SSH program: `shell` (interactive terminal), `exec` (single command execution), `x11`, `direct-tcpip`, or `forwarded-tcpip`. Note that `x11`, `direct-tcpip`, and `forwarded-tcpip` correspond to SSH features that are [not currently supported](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#known-limitations) by Access for Infrastructure. |
| **Payload**                 | Captured request/response data in [asciicast v2](https://docs.asciinema.org/manual/asciicast/v2/) format, a structured terminal recording format. Includes commands for `exec` programs.                                                                                                                                                                                                                  |
| **Error**                   | SSH error message, if an error occurred during the session.                                                                                                                                                                                                                                                                                                                                               |

## Export SSH logs with Logpush

Enterprise users can export SSH command logs to external storage or analysis destinations using [Logpush](/cloudflare-one/insights/logs/logpush/). Unlike dashboard logs, Logpush payloads are not encrypted with a customer-provided public key — secure access to your storage destination accordingly.

For a list of all available fields, refer to [SSH Logs](/logs/logpush/logpush-job/datasets/account/ssh_logs/).

# Tunnel audit logs

[Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) creates outbound-only connections between your infrastructure and Cloudflare. Tunnel audit logs record when these connections start, stop, or register new DNS records.

Audit logs for Tunnel are available in the [account section of the Cloudflare dashboard](https://dash.cloudflare.com/?account=audit-log), which you can find by selecting your name or email in the upper right-hand corner of the dashboard. For general audit log features such as filtering and retention, refer to [Audit Logs](/fundamentals/account/account-security/audit-logs/). The following actions are logged:

| Action       | Description                                                                                                |
| ------------ | ---------------------------------------------------------------------------------------------------------- |
| Registered   | A tunnel connector (`cloudflared`) started and connected to Cloudflare's global network.                   |
| Unregistered | A tunnel connector disconnected from Cloudflare's global network.                                          |
| CNAME add    | A tunnel registered a new DNS record (CNAME or AAAA) to route traffic to an application behind the tunnel. |

# Gateway activity logs

import { Render, GlossaryTooltip, DirectoryListing } from "~/components";

Gateway activity logs record the DNS queries, Network packets, and HTTP requests inspected by Gateway. You can also download encrypted [SSH command logs](/cloudflare-one/insights/logs/dashboard-logs/ssh-command-logs/) for sessions proxied by Gateway.

Enterprise users can generate more detailed logs with [Logpush](/cloudflare-one/insights/logs/logpush/).

<DirectoryListing />

:::note[Private source IP substitution]

Gateway logs show the public IP address in the **Source IP** field. Private IP addresses are translated to public addresses via network address translation (NAT). To see the user's original private IP, refer to the **Source internal IP** field in the DNS, Network, or HTTP log details below.

:::

## View Gateway activity logs

To view Gateway activity logs:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Logs**.
2. Choose a type of Gateway log:
	- **DNS query logs**
	- **Network logs**
	- **HTTP request logs**
	:::note[Log viewer (beta)]
	Gateway logs use an updated log viewer with enhanced filtering capabilities. To switch to the classic view, select **Return to old logs**.
	:::
3. (Optional) Filter the logs that display in the log viewer. You can filter logs by their timestamp and event details (such as host, URL, user email, policy action, and more).

	:::tip
	Querying for fewer fields improves log loading performance.
	:::
4. Select an individual timestamp to investigate the event in more detail.

## Selective logging

By default, Gateway logs all events, including DNS queries and HTTP requests that are allowed and not a risk. You can choose to disable logging entirely or only log blocked requests.

To customize what Gateway logs:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.

2. Under **Traffic logging** > **Log traffic activity**, choose your preference for DNS, Network, and HTTP logs.

These settings only apply to logs displayed in Cloudflare One. Logpush data is unaffected.

## DNS logs

### Explanation of the fields

#### Basic information

| Field                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                      |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Query name**        | Name of the domain that was queried.                                                                                                                                                                                                                                                                                                                                                                                             |
| **Query ID**          | UUID of the query assigned by Cloudflare.                                                                                                                                                                                                                                                                                                                                                                                        |
| **Email**             | Email address of the user who registered the Cloudflare One Client where traffic originated from. If a non-identity on-ramp (such as a [proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/)) or machine-level authentication (such as a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/)) was used, this value will be `non_identity@<team-domain>.cloudflareaccess.com`. |
| **Action**            | The [Action](/cloudflare-one/traffic-controls/dns-policies/#actions) Gateway applied to the query (such as Allow or Block).                                                                                                                                                                                                                                                                                                      |
| **Time**              | Date and time of the DNS query.                                                                                                                                                                                                                                                                                                                                                                                                  |
| **Resolver decision** | The reason why Gateway applied a particular **Action** to the request. Refer to the [list of resolver decisions](#resolver-decisions).                                                                                                                                                                                                                                                                                           |
| **Resolved IPs**      | Resolved IP addresses in the response.                                                                                                                                                                                                                                                                                                                                                                                           |
| **CNAMEs**            | `CNAME` records in the query.                                                                                                                                                                                                                                                                                                                                                                                                    |

#### Configuration information

| Field                  | Description                                                                                                                  |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **DNS location**       | [User-configured location](/cloudflare-one/traffic-controls/resolver-endpoints/) from where the DNS query was made. |
| **Policy name**        | Name of the matched policy.                                                                                                  |
| **Policy ID**          | ID of the matched policy.                                                                                                    |
| **Policy description** | Description of the matched policy.                                                                                           |
| **DoH subdomain**      | <GlossaryTooltip term="DoH subdomain">DoH subdomain</GlossaryTooltip> of the DNS location.                                   |
| **Protocol**           | Protocol that was used to make the DNS query (such as `https`).                                                              |

#### Identities

| Field                  | Description                                                                                                                                                                                                        |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Email**              | Email address of the user who registered the Cloudflare One Client where traffic originated from.                                                                                                                            |
| **User ID**            | UUID of the user. Each unique email address in your organization will have a UUID associated with it.                                                                                                              |
| **Registration ID**    | UUID of the user's Cloudflare One Client registration. A unique registration ID is generated each time a device is registered for a particular email. The same physical device may have multiple registration IDs.           |
| **Device name**        | Display name of the device returned by the operating system to the Cloudflare One Client. Typically this is the hostname of a device. Not all devices will have a device name. Device names are not guaranteed to be unique. |
| **Device ID**          | UUID of the device connected with the Cloudflare One Client. Each physical device in your organization will have a UUID.                                                                                                     |
| **Last authenticated** | Date and time the user last authenticated their Zero Trust session.                                                                                                                                                |

#### DNS query details

| Field                                      | Description                                                                                           |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| **Query ID**                               | UUID of the query assigned by Cloudflare.                                                             |
| **Query type**                             | Type of [DNS query](https://en.wikipedia.org/wiki/List_of_DNS_record_types).                          |
| **Initial query domain categories**        | [Content categories](/cloudflare-one/traffic-controls/domain-categories/) that the domain belongs to. |
| **Matched categories**                     | Name of the Gateway policy category that match the domain.                                            |
| **Matched indicator feed names**           | Name of the indicator feeds that matched a Gateway policy.                                            |
| **Query indicator feed names**             | Name of the indicator feeds that a matched domain or IP belongs to.                                   |
| **Resolved continent IP geolocation**      | Continent code of the resolved IP address.                                                            |
| **Resolved country IP geolocation**        | Country code of the resolved IP address.                                                              |
| **DoT subdomain**                          | DoT subdomain of the DNS location.                                                                    |
| **Source IP**                              | Public source IP address of the DNS query.                                                            |
| **Source IP continent**                    | Continent code of the source IP address.                                                              |
| **Source IP country**                      | Country code of the source IP address.                                                                |
| **Source internal IP**                     | Private IP address assigned by the user's local network.                                              |
| **Application name**                       | Name of the application that matched the domain.                                                      |
| **Resolver IP**                            | Public IP address of the DNS resolver.                                                                |
| **Port**                                   | Port that was used to make the DNS query.                                                             |
| **Location ID**                            | ID of the DNS location where the query originated.                                                    |
| **Scheduling - Time zone**                 | Time zone of the DNS query source.                                                                    |
| **Scheduling - Time zone inferred method** | Method used to determine the DNS query source's time zone.                                            |

#### DNS response details

| Field                           | Description                                                                              |
| ------------------------------- | ---------------------------------------------------------------------------------------- |
| **Resolved CNAME categories**   | Content categories associated with the resolved `CNAME` records in the response.         |
| **Resolved IP categories**      | Content categories associated with the resolved IPs in the response.                     |
| **Resolved IPs**                | Resolved IPs in the response.                                                            |
| **Authoritative nameserver IP** | IP address of the authoritative nameserver answering the DNS query.                      |
| **EDE errors**                  | [Extended DNS error codes](https://www.rfc-editor.org/rfc/rfc8914.html) in the response. |

#### Custom resolver

| Field                      | Description                                                  |
| -------------------------- | ------------------------------------------------------------ |
| **Address**                | Address of your custom resolver.                             |
| **Policy**                 | Name of the matched resolver policy.                         |
| **Response**               | Status of the custom resolver response.                      |
| **Time (in milliseconds)** | Duration of time it took for the custom resolver to respond. |

### Resolver decisions

| Name                     | Value | Description                                                 |
| ------------------------ | ----- | ----------------------------------------------------------- |
| `blockedByCategory`      | `3`   | Domain or hostname matched a category in a Block policy.    |
| `allowedOnNoLocation`    | `4`   | Allowed because query did not match a Gateway DNS location. |
| `allowedOnNoPolicyMatch` | `5`   | Allowed because query did not match a policy.               |
| `blockedAlwaysCategory`  | `6`   | Domain or hostname is always blocked by Cloudflare.         |
| `overrideForSafeSearch`  | `7`   | Response was overridden by a Safe Search policy.            |
| `overrideApplied`        | `8`   | Response was overridden by an Override policy.              |
| `blockedRule`            | `9`   | IP address in the response matched a Block policy.          |
| `allowedRule`            | `10`  | IP address in the response matched an Allow policy.         |

## Network logs

:::caution[Failed connection logs]
Gateway only logs TCP connections that were successfully established. If a connection is not complete (such as a TCP SYN with no SYN ACK), Gateway does not record it in network logs.

To log failed connections, use [network session logs](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/). These logs are available for Enterprise users via [Logpush](/cloudflare-one/insights/logs/logpush/) or [GraphQL](/cloudflare-one/insights/analytics/gateway/#graphql-queries).
:::

### Explanation of the fields

#### Basic information

| Field                  | Description                                                                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Source IP**          | IP address of the user sending the packet.                                                                                                        |
| **Source Internal IP** | Private IP address assigned by the user's local network.                                                                                          |
| **Destination IP**     | IP address of the packet's target.                                                                                                                |
| **Action**             | The Gateway [Action](/cloudflare-one/traffic-controls/dns-policies/#actions) taken based on the first rule that matched (such as Allow or Block). |
| **Session ID**         | ID of the unique session.                                                                                                                         |
| **Time**               | Date and time of the session.                                                                                                                     |

#### Matched policies

| Field                  | Description                                                                                                                  |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **DNS location**       | [User-configured location](/cloudflare-one/traffic-controls/resolver-endpoints/) from where the DNS query was made. |
| **Policy name**        | Name of the matched policy.                                                                                                  |
| **Policy ID**          | ID of the policy enforcing the decision Gateway made.                                                                        |
| **Policy description** | Description of the matched policy.                                                                                           |

#### Identities

| Field                  | Description                                                                           |
| ---------------------- | ------------------------------------------------------------------------------------- |
| **Email**              | Email address of the user sending the packet. This is generated by the Cloudflare One Client.   |
| **User ID**            | ID of the user sending the packet. This is generated by the Cloudflare One Client.              |
| **Registration ID**    | ID of the user's device registration. This is generated by the Cloudflare One Client.           |
| **Device name**        | Name of the device that sent the packet.                                              |
| **Device ID**          | ID of the physical device that sent the packet. This is generated by the Cloudflare One Client. |
| **Last authenticated** | Date and time the user last authenticated with Zero Trust.                            |

#### Network query details

| Field                        | Description                                                                                                                                                |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Source IP**                | IP address of the user sending the packet.                                                                                                                 |
| **Source port**              | Source port number for the packet.                                                                                                                         |
| **Source country**           | Country code for the packet source.                                                                                                                        |
| **Source IP continent**      | Continent code of the source IP address.                                                                                                                   |
| **Source IP country**        | Country code of the source IP address.                                                                                                                     |
| **Destination IP**           | IP address of the packet's target.                                                                                                                         |
| **Destination port**         | Destination port number for the packet.                                                                                                                    |
| **Destination IP continent** | Continent code of the IP address for the packet's destination.                                                                                             |
| **Destination IP country**   | Country code of the IP address for the packet's destination.                                                                                               |
| **Traffic Source**           | How the traffic reached Cloudflare, such as the Cloudflare One Client, a [Cloudflare Mesh](/mesh/) node, or a [Workers VPC](/workers-vpc/) binding.       |
| **Traffic Destination**      | How the traffic left Cloudflare, such as the Internet, a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), or a Mesh node.      |
| **Transport protocol**       | Protocol over which the packet was sent.                                                                                                                   |
| **Detected Protocol**        | The detected [network protocol](/cloudflare-one/traffic-controls/network-policies/protocol-detection/).                                                    |
| **SNI**                      | Host whose Server Name Indication (SNI) header Gateway will filter traffic against.                                                                        |
| **Virtual Network**          | [Virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) that the client is connected to. |
| **Category details**         | Category or categories associated with the packet.                                                                                                         |
| **Proxy endpoint**           | [PAC file proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/) Gateway forwarded traffic to, if applicable.                    |
| **Application ID**           | ID of the application that matched the domain.                                                                                                             |
| **Application name**         | Name of the application that matched the domain.                                                                                                           |

## HTTP logs

:::note

Gateway does not log HTTP bodies. The exception is error requests: when an HTTP request results in an error, Gateway logs the first 512 bytes of the request for 30 days for internal troubleshooting.

:::

### Explanation of the fields

#### Basic information

| Field                        | Description                                                                                                                                                                                                                                                                                                                              |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Host**                     | Hostname in the HTTP header for the HTTP request. Gateway will log the SNI in this field if it responded to the request with a Do Not Inspect action. If Gateway does not receive the SNI, this field will be empty.                                                                                                                     |
| **Email**                    | Email address of the user who made the HTTP request. This is generated by the Cloudflare One Client.                                                                                                                                                                                                                                               |
| **Action**                   | The Gateway [Action](/cloudflare-one/traffic-controls/dns-policies/#actions) taken based on the first rule that matched (such as Allow or Block).                                                                                                                                                                                        |
| **Request ID**               | Unique ID of the request.                                                                                                                                                                                                                                                                                                                |
| **Time**                     | Date and time of the HTTP request.                                                                                                                                                                                                                                                                                                       |
| **Source internal IP**       | Private IP address assigned by the user's local network.                                                                                                                                                                                                                                                                                 |
| **User agent**               | User agent header sent in the request by the originating device.                                                                                                                                                                                                                                                                         |
| **Policy details**           | Policy corresponding to the decision Gateway made based on the traffic criteria of the request.                                                                                                                                                                                                                                          |
| **DLP profiles**             | Name of the matched [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/).                                                                                                                                                                                                                                                   |
| **DLP profile entries**      | Name of the matched entry within the DLP profile.                                                                                                                                                                                                                                                                                        |
| **Uploaded/downloaded file** | Information about the file transferred in the request found by [enhanced file detection](#enhanced-file-detection). Details include: <ul><li>File name</li><li>File type</li><li>File size</li><li>File hash (for Allowed requests only)</li><li>Content type</li><li>Direction (Upload/Download)</li><li>Action (Block/Allow)</li></ul> |

#### Matched policies

| Field                     | Description                                                                                                                  |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **DNS location**          | [User-configured location](/cloudflare-one/traffic-controls/resolver-endpoints/) from where the DNS query was made. |
| **Policy name**           | Name of the matched policy.                                                                                                  |
| **Policy ID**             | ID of the matched policy.                                                                                                    |
| **Policy description**    | Description of the matched policy.                                                                                           |
| **Matched category ID**   | ID of the category matched in the policy.                                                                                    |
| **Matched category name** | Name of the category matched in the policy.                                                                                  |

#### Identities

| Field                  | Description                                                                                                                   |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Email**              | Email address of the user who made the HTTP request. This is generated by the Cloudflare One Client.                                    |
| **User ID**            | ID of the user who made the request. This is generated by the Cloudflare One Client.                                                    |
| **Registration ID**    | ID of the user's device registration. This is generated by the Cloudflare One Client.                                                   |
| **Device name**        | Name of the device that made the request.                                                                                     |
| **Device ID**          | ID of the physical device that made the request. This is generated by the Cloudflare One Client on the device that created the request. |
| **Last authenticated** | Date and time the user last authenticated with Zero Trust.                                                                    |

#### HTTP query details

| Field                        | Description                                                                                                                                                |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **HTTP Version**             | HTTP version of the origin that Gateway connected to on behalf of the user.                                                                                |
| **HTTP Method**              | HTTP method used for the request (such as `GET` or `POST`).                                                                                                |
| **HTTP Status Code**         | [HTTP status code](/support/troubleshooting/http-status-codes/) returned in the response.                                                                  |
| **URL**                      | Full URL of the HTTP request.                                                                                                                              |
| **Referer**                  | Referer request header containing the address of the page making the request.                                                                              |
| **Source IP**                | Public source IP address of the HTTP request.                                                                                                              |
| **Source Port**              | Port that was used to make the HTTP request.                                                                                                               |
| **Source IP continent**      | Continent code of the HTTP request.                                                                                                                        |
| **Source IP country**        | Country code of the HTTP request.                                                                                                                          |
| **Destination IP**           | Public IP address of the destination requested.                                                                                                            |
| **Destination Port**         | Port of the destination requested.                                                                                                                         |
| **Destination IP continent** | Continent code of the destination requested.                                                                                                               |
| **Destination IP country**   | Country code of the destination requested.                                                                                                                 |
| **Blocked file reason**      | Reason why the file was blocked if a file transfer occurred or was attempted.                                                                              |
| **Category details**         | Detailed information on the category the blocked file belongs to.                                                                                          |
| **Application ID**           | ID of the application that matched the domain.                                                                                                             |
| **Application name**         | Name of the application that matched the domain.                                                                                                           |
| **Categories**               | [Content categories](/cloudflare-one/traffic-controls/domain-categories/) that the domain belongs to.                                                      |
| **Proxy endpoint**           | [PAC file proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/) Gateway forwarded traffic to, if applicable.                    |
| **Virtual Network**          | [Virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) that the client is connected to. |
| **Sandbox scanned**          | Status of the [file quarantine](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/).                                                          |

#### File detection details

| Field            | Description                                        |
| ---------------- | -------------------------------------------------- |
| **Name**         | Name of the detected file.                         |
| **Type**         | File type of the detected file.                    |
| **Size**         | Size of the detected file.                         |
| **Hash**         | Hash of the detected file, generated by DLP.       |
| **Content type** | MIME type of the detected file.                    |
| **Direction**    | Upload or download direction of the detected file. |
| **Action**       | The Action Gateway applied to the request.         |

### Enhanced file detection

Enhanced file detection is an optional feature that extracts more file information from HTTP traffic. When turned on, Gateway reads file information from the HTTP body rather than the HTTP headers, providing greater accuracy and reliability. This feature may have a minor impact on performance for file-heavy organizations.

To turn on enhanced file detection:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Proxy and inspection settings**, turn on **Inspect HTTPS requests with TLS decryption**.
3. In **Policy settings**, turn on **Allow enhanced file detection**.

### Isolate requests

When a user creates an [isolation policy](/cloudflare-one/remote-browser-isolation/isolation-policies/), Gateway logs isolation-related requests in two stages:

1. **Initial request** — The request that triggers isolation is logged with an Isolate action. Because this request is not yet isolated, the `is_isolated` field returns `false`.
2. **Subsequent requests** — After Zero Trust returns the result to the user in an isolated browser, Gateway logs all subsequent requests in the isolated browser with the action (such as Allow or Block), and the `is_isolated` field returns `true`.

## Limitations

If a connection closes before Gateway inspects and filters the traffic, Gateway logs the event with an Unknown action.

Gateway activity logs are not available in the dashboard if you turn on the [Customer Metadata Boundary (CMB)](/data-localization/metadata-boundary/) within Cloudflare Data Localization Suite (DLS). CMB restricts where customer traffic metadata and logs are stored by region. Enterprise users with CMB turned on can still export logs via [Logpush](/cloudflare-one/insights/logs/logpush/). For more information, refer to [DLS product compatibility](/data-localization/compatibility/#zero-trust).

# Manage PII

Cloudflare Gateway gives you multiple ways to safely handle your employees' personally identifiable information (PII) in activity logs:

- **Redact PII** (default) — PII is stored in logs but hidden from view. Only the Super Administrator and users with the [Cloudflare Zero Trust PII role](/cloudflare-one/roles-permissions/#cloudflare-zero-trust-pii) can view redacted PII. The underlying data is preserved — redaction only controls who can see it.
- **[Exclude PII](#exclude-pii)** — PII is not stored in logs at all. No user, including the Super Administrator, can retrieve it.

Only the Super Administrator can assign roles and determine who has permission to view PII. To add or remove the Cloudflare Zero Trust PII role for a user in your organization, refer to [Roles](/fundamentals/manage-members/roles/).

## Types of PII

Cloudflare Gateway can log the following types of PII:

- Source IP
- User email
- User ID
- Device ID
- URL
- Referer
- User agent

## Exclude PII

When you exclude PII, Gateway logs activity without storing any employee PII. This differs from the default redaction behavior — excluded PII is not stored and cannot be retrieved by any role, including the Super Administrator.

:::caution
Excluding PII is irreversible for the period it is active. If you turn on this setting and later turn it off, logs captured while the setting was on will permanently lack PII data.
:::

Changes to this setting do not affect PII already stored in previous logs.

To turn on the setting to exclude PII:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic policies** > **Traffic settings**.
2. In **Traffic logging**, turn on **Exclude personally identifiable information (PII) from logs**.

# Email security logs

import { DashButton } from "~/components";

Email security allows you to configure Logpush to export two types of log data: detection logs (records of threats identified in email traffic) and user action logs (records of administrative actions taken via the API or the dashboard). Each log type requires separate configuration.

## Enable detection logs

Detection logs record each threat identified by Email security, including metadata such as the message sender, recipient, and detection verdict.

To enable detection logs, refer to [Enable destinations](/logs/logpush/logpush-job/enable-destinations/). When configuring the Logpush job, select **Email security alerts** as the dataset.

## Enable user action logs

User action logs record all administrative actions taken via the [API](/api/resources/email_security/) or the dashboard.

Before you can enable user action logs for Email security, you must have a Logpush job configured for your storage destination. Refer to [Enable destinations](/logs/logpush/logpush-job/enable-destinations/) to enable logs on destinations such as Cloudflare R2, HTTP, Amazon S3, and more.

Once you have configured your destination, you can set up user action logs:

1. In the Cloudflare dashboard, go to the **Logpush** page.

   <DashButton url="/?to=/:account/logs" />

2. Select your storage destination.
3. Select the three dots > **Edit**.
4. Under **Configure logpush job**:

- **Job name**: Enter the job name, if it is not already prepopulated.
- **If logs match** > Select **Filtered logs** to capture only Email security events:
  - **Field**: Choose `ResourceType` (the type of resource that was changed).
  - **Operator**: Choose `starts with`.
  - **Value**: Enter `email_security`.

5. Select **Submit**.

You can now view logs via the Cloudflare dashboard.

# IDS logs

You can use Logpush with [Cloudflare Network Firewall IDS](/cloudflare-network-firewall/about/ids/) (Intrusion Detection System) to export logs of detected threats. IDS monitors your network traffic for a wide range of known threat signatures, including attacks such as ransomware, data exfiltration, and network scanning.

## Set up Logpush for IDS

1. Consult the [Logpush Destination docs](/logs/logpush/logpush-job/api-configuration/#destination) to learn about what destinations Logpush supports. The documentation will also instruct you on how to correctly format the destination URL for Logpush.

2. Follow the [Manage Logpush with cURL](/logs/logpush/examples/example-logpush-curl/) tutorial to validate your Logpush destination and define a Logpush job.

## Notes on using Logpush with IDS

- Magic IDS is an account-scoped dataset. Unlike zone-specific datasets that apply to a single domain, account-scoped datasets use a different API endpoint. Replace the string `/zone/<ZONE_ID>` in the Cloudflare API URLs in the tutorial with `/account/<ACCOUNT_ID>`.

- Consult the [Magic IDS Detection fields doc](/logs/logpush/logpush-job/datasets/account/magic_ids_detections/) to know what fields you want configured for the job.

- When creating the Logpush job, the dataset field should equal `magic_ids_detections`.

- Timestamps default to `unixnano` format (nanoseconds since the Unix epoch, January 1, 1970). If your destination expects a different format (such as RFC 3339), refer to [Logpush Options](/logs/logpush/logpush-job/api-configuration/#options) for available timestamp formats. In the Logpush API configuration string, options are appended after the field list.

# Logpush integration

import { GlossaryTooltip, Render, DirectoryListing } from "~/components";

With Cloudflare's [Logpush](/logs/logpush/) service, you can configure the automatic export of Zero Trust logs — including DNS queries, HTTP requests, device posture checks, and other events generated by Cloudflare One services — to third-party storage destinations or to third-party <GlossaryTooltip term="SIEM">security information and event management (SIEM)</GlossaryTooltip> solutions. Once exported, your team can analyze and audit the data as needed.

Logpush is available to all customers. Dataset availability depends on the Cloudflare products on your account. For more information, refer to [Logpush availability](/logs/logpush/#availability). For usage rates and included monthly usage, refer to [Logpush pricing](/logs/logpush/pricing/).

## Export Zero Trust logs with Logpush

To configure Logpush for Zero Trust logs:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Logs**.
2. Select **Manage Logpush**.
3. In Logpush, select **Create a Logpush job**. A Logpush job defines which dataset to export and where to send it.
4. Choose a [Logpush destination](/logs/logpush/logpush-job/enable-destinations/).
5. Follow the service-specific instructions to configure and validate your destination.
6. Choose the [Zero Trust datasets](#zero-trust-datasets) to export.
7. Enter a **Job name**, any [filters](/logs/logpush/logpush-job/filters/) you would like to add to narrow which logs are included (for example, only logs from a specific user or action), and the data fields you want to include in the logs.
8. (Optional) In **Advanced settings**, choose the timestamp format you prefer and whether you want to turn on log sampling. Log sampling delivers a randomly-sampled subset of logs rather than every event, which can reduce storage volume for high-traffic datasets.
9. Select **Submit**.

The setup of your Logpush integration is now complete. Logpush will begin delivering logs in batches to your selected destination. You can configure multiple destinations and add additional fields to your logs by returning to the **Logpush** page.

For more information on supported destinations, refer to [Enable destinations](/logs/logpush/logpush-job/enable-destinations/).

## Zero Trust datasets

Logpush supports all [dashboard logs](/cloudflare-one/insights/logs/dashboard-logs/) as well as additional datasets not available in the Cloudflare One dashboard. Refer to [Logpush datasets](/logs/logpush/logpush-job/datasets/) for a list of all available fields.

| Dataset                                                                                                    | Description                                                                                                                                              |
| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Access Requests](/logs/logpush/logpush-job/datasets/account/access_requests/)                             | HTTP requests to sites protected by Cloudflare Access                                                                                                    |
| [Audit Logs](/logs/logpush/logpush-job/datasets/account/audit_logs/)                                       | Authentication events through Cloudflare Access                                                                                                          |
| [Browser Isolation User Actions](/logs/logpush/logpush-job/datasets/account/biso_user_actions/)            | Data transfer actions performed by a user in the remote browser, such as copy, paste, and download events                                                |
| [CASB Findings](/logs/logpush/logpush-job/datasets/account/casb_findings/)                                 | Security issues detected by Cloudflare's Cloud Access Security Broker (CASB)                                                                             |
| [Device Posture Results](/logs/logpush/logpush-job/datasets/account/device_posture_results/)               | Device posture status from the Cloudflare One Client                                                                                                     |
| [DEX Application Tests](/logs/logpush/logpush-job/datasets/account/dex_application_tests/)                 | Digital Experience Monitoring (DEX) automated connectivity check results from the Cloudflare One Client                                                   |
| [DEX Device State Events](/logs/logpush/logpush-job/datasets/account/dex_device_state_events/)             | Digital Experience Monitoring (DEX) device event data like connectivity, CPU usage, and Disk I/O from the Cloudflare One Client                          |
| [Gateway DNS](/logs/logpush/logpush-job/datasets/account/gateway_dns/)                                     | DNS queries inspected by Cloudflare Gateway                                                                                                              |
| [Gateway HTTP](/logs/logpush/logpush-job/datasets/account/gateway_http/)                                   | HTTP requests inspected by Cloudflare Gateway                                                                                                            |
| [Gateway Network](/logs/logpush/logpush-job/datasets/account/gateway_network/)                             | Network packets inspected by Cloudflare Gateway                                                                                                          |
| [MCP Portal Logs](/logs/logpush/logpush-job/datasets/account/mcp_portal_logs/)                             | Requests made through [MCP server portals](/cloudflare-one/access-controls/ai-controls/mcp-portals/)                                                     |
| [SSH Logs](/logs/logpush/logpush-job/datasets/account/ssh_logs/)                                           | SSH command logs for [Access for Infrastructure targets](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) |
| [WARP Config Changes](/logs/logpush/logpush-job/datasets/account/warp_config_changes/)                     | Event logs that Cloudflare generates whenever a device changes [profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) |
| [WARP Toggle Events](/logs/logpush/logpush-job/datasets/account/warp_toggle_changes/)                      | Event logs that Cloudflare generates whenever a device toggles the Cloudflare One Client on or off                                                       |
| [Zero Trust Network Session Logs](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/) | Network session logs for all traffic proxied through Cloudflare Gateway across all supported on-ramps                                                    |

## Verify regional map application

If you are using [Regional Services](/data-localization/regional-services/) with Cloudflare One, you can configure which subset of Cloudflare data centers decrypt and route your traffic. This allows you to accommodate regional restrictions like GDPR or meet compliance requirements that include geographic restrictions on data flows or processing.

<Render file="verify-regional-map" product="data-localization" />

## Parse DNS logs

Logpush logs the following fields for each DNS query:

- Query name
- Query type
- Query class
- Response TTL
- Response data

Logpush provides DNS response data in two formats. `ResourceRecords` contains the raw DNS response in [Base64-encoded binary format](https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.3), which is compact but requires decoding before it is human-readable. `ResourceRecordsJSON` contains the same data in JSON, with the record name, type, class, TTL, and response data already parsed. For example:

```json
{
	"ResourceRecords": [
		{
			"type": "5",
			"data": "d3d3LmV4YW1wbGUuY29tAAABAAUAAABleGFtcGxlLmNvbQ=="
		},
		{
			"type": "1",
			"data": "ZXhhbXBsZS5jb20AAAEAAQAAAQIDBAUGBwgJ"
		}
	],
	"ResourceRecordsJSON": "[{\"name\":\"www.example.com\",\"type\":\"CNAME\",\"class\":\"IN\",\"ttl\":300,\"rdata\":\"example.com.\"},{\"name\":\"example.com\",\"type\":\"A\",\"class\":\"IN\",\"ttl\":300,\"rdata\":\"203.0.113.0\"}]"
}
```

## Additional Logpush guides

<DirectoryListing />

# Network Firewall log filters

import { APIRequest } from "~/components";

You can apply [Log filters](/logs/logpush/logpush-job/filters/) to your Logpush job to export only specific Cloudflare Network Firewall events. The examples below show common filter configurations using the Logpush API. Each filter uses a JSON structure with `where` clauses containing `key` (the log field to filter on), `operator` (the comparison, such as `eq` for equals or `!eq` for not equals), and `value` (the value to match).

The filters in this guide use the following log fields:

- `MitigationSystem` — Identifies which Cloudflare system sampled the packet. For Network Firewall events, this value is `magic-firewall`.
- `RulesetID` — The unique identifier of the managed ruleset containing the rule that matched the packet, if any. An empty string indicates no managed ruleset matched.
- `Outcome` — The action that Cloudflare systems took on the packet (`pass` or `drop`).
- `Verdict` — The action that Cloudflare systems determined should be taken on the packet (`pass` or `drop`). For disabled rules, `Verdict` may differ from `Outcome` because the rule evaluated the packet but did not enforce its action.

## Filter by enabled or disabled rules

Use the filter examples below to filter your Cloudflare Network Firewall traffic to display events for enabled or disabled rules.

The example below [creates a Logpush job](/api/resources/logpush/subresources/jobs/methods/create/) that only displays fields relevant to Cloudflare Network Firewall, and the filter only displays events for disabled rules.

<APIRequest
	path="/accounts/{account_id}/logpush/jobs"
	method="POST"
	json={{
		destination_conf: "<DESTINATION_CONF>",
		output_options: {
			field_names: [
				"ColoName",
				"Datetime",
				"Direction",
				"IPDestinationAddress",
				"IPDestinationSubnet",
				"IPProtocol",
				"IPSourceAddress",
				"IPSourceSubnet",
				"Outcome",
				"RuleID",
				"RulesetID",
				"SampleInterval",
				"Verdict",
			],
		},
		filter:
			'{"where":{"or":[{"and":[{"key":"MitigationSystem","operator":"eq","value":"magic-firewall"},{"key":"RulesetID","operator":"!eq","value":""},{"key":"Outcome","operator":"eq","value":"pass"},{"key":"Verdict","operator":"eq","value":"drop"}]}]}}',
	}}
/>

The example below [creates a Logpush job](/api/resources/logpush/subresources/jobs/methods/create/) that only displays fields relevant to Cloudflare Network Firewall, and the filter only displays events for enabled rules.

<APIRequest
	path="/accounts/{account_id}/logpush/jobs"
	method="POST"
	json={{
		destination_conf: "<DESTINATION_CONF>",
		output_options: {
			field_names: [
				"ColoName",
				"Datetime",
				"Direction",
				"IPDestinationAddress",
				"IPDestinationSubnet",
				"IPProtocol",
				"IPSourceAddress",
				"IPSourceSubnet",
				"Outcome",
				"RuleID",
				"RulesetID",
				"SampleInterval",
				"Verdict",
			],
		},
		filter:
			'{"where":{"or":[{"and":[{"key":"MitigationSystem","operator":"eq","value":"magic-firewall"},{"key":"RulesetID","operator":"!eq","value":""},{"or":[{"key":"Outcome","operator":"eq","value":"drop"},{"key":"Verdict","operator":"eq","value":"pass"}]}]}]}}',
	}}
/>

## Filter by allowed or blocked traffic

Use the filter examples below to filter your Cloudflare Network Firewall traffic to display events for allowed or blocked traffic.

The example below [creates a Logpush job](/api/resources/logpush/subresources/jobs/methods/create/) that only displays fields relevant to Cloudflare Network Firewall, and the filter only displays events where no explicit action was taken — that is, a packet passed through the firewall without matching any rule. By default, Cloudflare Network Firewall permits unmatched traffic. This is identified by an empty `RulesetID`.

<APIRequest
	path="/accounts/{account_id}/logpush/jobs"
	method="POST"
	json={{
		destination_conf: "<DESTINATION_CONF>",
		output_options: {
			field_names: [
				"ColoName",
				"Datetime",
				"Direction",
				"IPDestinationAddress",
				"IPDestinationSubnet",
				"IPProtocol",
				"IPSourceAddress",
				"IPSourceSubnet",
				"Outcome",
				"RuleID",
				"RulesetID",
				"SampleInterval",
				"Verdict",
			],
		},
		filter:
			'{"where":{"and":[{"key":"MitigationSystem","operator":"eq","value":"magic-firewall"},{"key":"RulesetID","operator":"eq","value":""}]}}',
	}}
/>

The example below [creates a Logpush job](/api/resources/logpush/subresources/jobs/methods/create/) that only displays fields relevant to Cloudflare Network Firewall, and the filter only displays events where explicit action was taken. The example includes both enabled and disabled Cloudflare Network Firewall rules.

<APIRequest
	path="/accounts/{account_id}/logpush/jobs"
	method="POST"
	json={{
		destination_conf: "<DESTINATION_CONF>",
		output_options: {
			field_names: [
				"ColoName",
				"Datetime",
				"Direction",
				"IPDestinationAddress",
				"IPDestinationSubnet",
				"IPProtocol",
				"IPSourceAddress",
				"IPSourceSubnet",
				"Outcome",
				"RuleID",
				"RulesetID",
				"SampleInterval",
				"Verdict",
			],
		},
		filter:
			'{"where":{"and":[{"key":"MitigationSystem","operator":"eq","value":"magic-firewall"},{"key":"RulesetID","operator":"!eq","value":""}]}}',
	}}
/>

## Filter to only Network Firewall events

If your Logpush job includes events from multiple Cloudflare mitigation systems, use the filter below to include only Cloudflare Network Firewall events. The example below [creates a Logpush job](/api/resources/logpush/subresources/jobs/methods/create/) that filters on `MitigationSystem` to include only Network Firewall traffic.

<APIRequest
	path="/accounts/{account_id}/logpush/jobs"
	method="POST"
	json={{
		destination_conf: "<DESTINATION_CONF>",
		output_options: {
			field_names: [
				"ColoName",
				"Datetime",
				"Direction",
				"IPDestinationAddress",
				"IPDestinationSubnet",
				"IPProtocol",
				"IPSourceAddress",
				"IPSourceSubnet",
				"Outcome",
				"RuleID",
				"RulesetID",
				"SampleInterval",
				"Verdict",
			],
		},
		filter:
			'{"where":{"key":"MitigationSystem","operator":"eq","value":"magic-firewall"}}',
	}}
/>

# Buckets

import { GlossaryTooltip, TabItem, Tabs, DashButton } from "~/components";

Before you can begin a full <GlossaryTooltip term="data packet">packet</GlossaryTooltip> capture, you must configure a cloud storage bucket where Cloudflare can write the captured traffic data. Setting up a bucket is not required for sample packet captures, which complete immediately and can be downloaded directly from the API.

You can configure an Amazon S3 or Google Cloud Platform bucket to use as a target. You can also [use R2](#r2) as a target using the API.

## Set up a bucket

Learn how to set up a bucket for use with full packet captures.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare One](https://one.dash.cloudflare.com) dashboard, go to **Network visibility** > **Diagnostics**.
2. Select the **Buckets** tab > **Add a bucket**.
3. Select a bucket service and select **Next**.
4. Enter the information related to your bucket for your service provider.
5. When you are done, select **Next**.

The **Prove ownership** step of the **Bucket configuration** displays.

</TabItem> <TabItem label="API">

Before you can begin using a bucket, you must first enable destinations. Follow the destination setup steps for your provider, then return here to validate ownership.

Refer to the [Amazon S3](/logs/logpush/logpush-job/enable-destinations/aws-s3/#create-and-get-access-to-an-s3-bucket) or [Google Cloud Storage](/logs/logpush/logpush-job/enable-destinations/google-cloud-storage/#create-and-get-access-to-a-gcs-bucket) documentation and follow the steps for those specific services.

</TabItem> </Tabs>

Next, validate the bucket and confirm ownership.

## Validate a bucket

After the initial bucket setup, you need to confirm you have access to the bucket via an ownership challenge. This verification prevents Cloudflare from writing capture data to a bucket you do not control. After you validate your bucket, you can begin using it to collect full packet captures.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. From the **Prove ownership** step of the **Bucket configuration**, locate the **Ownership token** field.
2. Find the ownership challenge file that Cloudflare placed in your bucket, copy its contents, and enter them in the **Ownership token** field.
3. When you are done, select **Create**. The **Packet captures** page displays.

The **Buckets** tab displays a list of the buckets associated with your account. Refer to the **Status** column to see the status of your bucket configuration.

</TabItem> <TabItem label="API">

The `bucket` field should be the URI of the bucket. For Amazon S3, the `bucket` field is in the form `s3://<bucket-name>/<directory>?region=<bucket-region>`, and for Google Cloud Storage the form is `gs://<bucket-name>/<directory>`.

```bash title="Ownership challenge request example"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps/ownership \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "destination_conf": "'${bucket}'"
}'
```

The response has a `"filename"` parameter which contains the content of the `ownership-challenge` text. Find the file in your bucket and copy the contents of the file.

```json title="Ownership challenge response example"
{
	"result": {
		"id": "cc20c2d6c62e11ecbe646b173af3b6b9",
		"status": "pending",
		"submitted": "2022-04-22T18:54:13.397413Z",
		"validated": "",
		"destination_conf": "gs://bucket-test", // Ensure you use a bucket that you created and registered in the Cloudflare dashboard.
		"filename": "ownership-challenge-1234.txt"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

Validate the bucket by inserting the copied text in the `ownership_text` below:

```bash title="Bucket validation example"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps/ownership/validate \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "destination_conf": "'${bucket}'",
  "ownership_challenge": "'${ownership_text}'"
}'
```

```json title="Bucket validation response"
{
	"result": {
		"id": "cc20c2d6c62e11ecbe646b173af3b6b9",
		"status": "success",
		"submitted": "2022-04-22T18:54:13.397413Z",
		"validated": "2022-04-27T14:54:46.440548Z",
		"destination_conf": "gs://<bucket-name>", // Ensure you use a bucket that you created and registered in the Cloudflare dashboard
		"filename": "ownership-challenge-1234.txt"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

If the `status` shows `success`, the bucket is configured and ready to use.

</TabItem> </Tabs>

The bucket status displays one of the following options:

- **Success:** The bucket is fully verified and ready to use.
- **Pending:** The challenge response was initiated but is pending verification. Bucket verification can take five to ten minutes to finish processing.
- **Failed:** The bucket could not be validated. If this occurs, verify that Cloudflare has write access to your bucket and that you submitted the correct contents of the ownership challenge file.

## List configured buckets

View a list of all buckets configured on your account.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare One](https://one.dash.cloudflare.com) dashboard, go to ***Insights** > *Network visibility** > **Diagnostics**.
2. Select the **Buckets** tab.

The list of buckets associated with your account displays.

</TabItem> <TabItem label="API">

```bash title="Bucket list request example"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps/ownership \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>"
```

```json title="Bucket list response example"
{
	"result": [
		{
			"id": "9a993aa6c58711ec89d3037647342e63",
			"status": "success",
			"submitted": "2022-04-26T16:58:24.550762Z",
			"validated": "2022-04-26T17:01:18.426458Z",
			"destination_conf": "s3://test-bucket?region=us-east-1",
			"filename": "ownership-challenge-1234.txt"
		}
	],
	"success": true,
	"errors": [],
	"messages": []
}
```

</TabItem> </Tabs>

To learn how to collect packet captures, refer to [Collect packet captures](/cloudflare-network-firewall/packet-captures/collect-pcaps/).

## R2

You can also use [Cloudflare R2](/r2/) as a storage destination for packet captures. R2 bucket configuration is available through the API only.

:::note
When you validate an R2 bucket, exclude the `access-key-id` and `secret-access-key` parameters from the `destination_conf` URL. Only include them in the initial registration request.
:::

### Create bucket and API token

1. In the Cloudflare dashboard, go to the **R2** page.

	<DashButton url="/?to=/:account/r2/overview" />

2. Select **Create bucket**.
3. Give your bucket a name > **Create bucket**.
4. Go to the R2 Overview page, and select **Manage R2 API Tokens**.
5. Select **Create API Token**.
6. In **Permissions**, choose **Object Read & Write**. Make sure you also select **Apply to specific buckets only**, and select the bucket you have created for PCAPs from the drop-down menu.
7. Select **Create API Token**.
8. Make sure you copy the **Secret Access Key** and **Access Key ID** values, as you will need them for the next step.

### Create initial request

Create your initial request to R2:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps/ownership \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "destination_conf": "r2://<BUCKET_NAME>?account-id=<ACCOUNT_ID>&access-key-id=<R2_ACCESS_KEY_ID>&secret-access-key=<R2_SECRET_ACCESS_KEY>"
}'
```

The [response](/api/resources/magic_transit/subresources/pcaps/subresources/ownership/methods/create/) has a `"filename"` parameter with the name of a file that Cloudflare wrote to your R2 bucket. You need to download it for the next step. Example:

```json
{
	"errors": [],
	"messages": [],
	"result": {
		"destination_conf": "<YOUR_R2_BUCKET>",
		"filename": "ownership-challenge-9883874ecac311ec8475433579a6bf5f.txt",
		"id": "9883874ecac311ec8475433579a6bf5f",
		"status": "success",
		"submitted": "2020-01-01T08:00:00Z",
		"validated": "2020-01-01T08:00:00Z"
	},
	"success": true
}
```

### Validate bucket ownership

Refer to the [Validate a bucket](#validate-a-bucket) API instructions for more details on the entire process to [validate your R2 bucket](/api/resources/magic_transit/subresources/pcaps/subresources/ownership/methods/validate/). When specifying the R2 destination for this validation, exclude the secret and access keys from the URL.

# Diagnostics

import { DirectoryListing } from "~/components"

Packet captures allow you to record raw network traffic data passing through Cloudflare's network so you can inspect it offline in tools like Wireshark. This is useful for diagnosing connectivity issues, verifying firewall rules, or investigating unexpected traffic patterns.

Cloudflare supports two types of packet captures: full and sample. Full packet captures are the default behavior.

:::note
The maximum packet capture runtime is 24 hours for sample and full packet captures.
:::

## Sample packet captures

Sample packet captures collect historical data on network traffic that has already passed through Cloudflare's network. They will not collect any new traffic sent to Cloudflare's network after the packet capture has started. All sample packet captures will complete immediately after they are started because they query historical traffic data.

Sample packet captures can be viewed in the Cloudflare dashboard. They only include the first 160 bytes of each packet, which is useful for capturing packet headers but will not provide detailed packet data. The sample data is collected across all Cloudflare's data centers to build a PCAP file. This allows you to get a global picture of traffic across all data centers.

You should use full packet captures if you need to collect data on packets that pass through your network less frequently.

## Full packet captures

Full packet captures actively monitor Cloudflare's network for packets that match the selected filters, and capture the complete packet data, including the payload. The matching packet data is saved to a cloud storage bucket that is owned and configured by you. You must [configure a bucket](/cloudflare-one/insights/network-visibility/diagnostics/buckets/) before starting a full packet capture.

Full packet captures will collect new traffic sent to Cloudflare's network after the packet capture has started, and include the full packet data. This type of capture cannot be viewed in the Cloudflare dashboard. You can download them from a cloud storage bucket and analyze them in Wireshark or another packet capture tool.

Refer to the articles in this section to learn how to use packet captures.

<DirectoryListing />

# Packet captures

import {
	Details,
	GlossaryTooltip,
	TabItem,
	Tabs,
	DashButton,
} from "~/components";

Packet captures record network traffic flowing through Cloudflare's network so you can analyze individual <GlossaryTooltip term="data packet">packets</GlossaryTooltip> for troubleshooting or security investigations. The output is contained within one or more files in PCAP format, which you can open in tools like [Wireshark](https://www.wireshark.org/).

There are two capture types:

- **Sample** captures query historical traffic data that has already passed through Cloudflare's network. They complete immediately and can be downloaded directly from the API, or from the Cloudflare dashboard.
- **Full** captures actively monitor for new traffic matching your filters and write the complete packet data to a cloud storage bucket you own. Before starting a full capture, you must first [configure a bucket](/cloudflare-one/insights/network-visibility/diagnostics/buckets/).

:::note

Packet captures are available for Cloudflare Advanced Network Firewall users. For access, contact your account team.

:::

## Send a packet capture request

Currently, when a packet capture is requested, packets flowing through Cloudflare's global network via the Magic Transit system are captured. The default API field for this is `"system": "magic-transit"`, both for the request and response.

:::note[Note]

For help determining which data center to select for a packet capture, go to [https://cloudflare.com/cdn-cgi/trace](https://cloudflare.com/cdn-cgi/trace) and refer to the `colo` field. Note some data centers can be regional such as `ORD` while other names may be more specific like `ord02`. Either of these names can be used for this same field.

:::

### Packet capture limits

**Sample and full**

- `time_limit`: The minimum value is `1` second and maximum value is `300` seconds.
- `packet_limit`: The minimum value is `1` packet and maximum value is `10000` packets.

**Full**

- `byte_limit`: The minimum value is `1` byte and maximum value is `1000000000` bytes (1 GB).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the Cloudflare dashboard, go to the **Network health** page.

   <DashButton url="/?to=/:account/networking-insights/health" />

2. Go to the **Diagnostics** tab.
3. In **Network packet captures**, select **Start a capture**.
4. Choose the type of capture you want to perform, and select **Next**.
5. Fill out the required fields to begin the capture and then select **Start**.

The **Network packet captures** page displays a list of captures.

</TabItem> <TabItem label="API">

The PCAPs API needs both `system` and `type` to be specified to start a capture. A PCAP's `system` is the product or logical subsystem where packets are captured, and a PCAP's `type` is how the captured packets are built into a PCAP file.

Currently, you can only send one collect request per minute for sample PCAPs, and you can only have one running or pending full PCAP at a time.

<Details header="Full PCAP">

For full PCAP requests, refer to the required parameters listed at [Create full PCAP requests](/api/resources/magic_transit/subresources/pcaps/methods/create/). Note that full packet captures require two more parameters than sample packets.

The full PCAP request endpoint also contains optional fields you can use to limit the amount of packets captured. Both full and sample packet requests contain an optional `filter_v1` parameter you can use to filter packets by IPv4 Source address, for example. For a full list of the filter options, refer to the [API reference](/api/resources/magic_transit/subresources/pcaps/methods/create/).

Leave `filter_v1` empty to collect all packets without any filtering.

```bash title="Full PCAP example request"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "filter_v1": {},
  "time_limit": 300,
  "packet_limit": 10000,
  "byte_limit": 100000000,
  "type": "full",
  "colo": "ORD",
  "system": "magic-transit",
  "destination_conf": "${BUCKET}"
}'
```

While the collection is in progress, the response returns the `status` field as `pending`. You must wait for the PCAP collection to complete before downloading the file. When the PCAP is ready to download, the status changes to `success`.

```json title="Full PCAP example response"
{
	"result": {
		"id": "7d7c88382f0b4d5daa9587aa45a1a877",
		"submitted": "2022-06-02T18:38:22.269047Z",
		"filter_v1": {},
		"time_limit": 300,
		"status": "pending",
		"type": "full",
		"system": "magic-transit",
		"packet_limit": 10000,
		"byte_limit": 100000000,
		"colo": "ORD",
		"destination_conf": "gs://<bucket-name>" // Ensure you use a bucket that you created and registered in the Cloudflare dashboard
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

</Details>

<Details header="Sample PCAP">

To create a sample PCAP request, send a JSON body with the required parameter listed at [Create sample PCAP request](/api/resources/magic_transit/subresources/pcaps/methods/create/).

:::note
The API uses `"type": "simple"` for sample captures. Use `simple` as the type value in your API requests.
:::

Leave `filter_v1` empty to collect all packets without any filtering.

```bash title="Sample PCAP example request"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "filter_v1": {
    "source_address": "1.2.3.4",
    "source_port": 123,
    "destination_address": "5.6.7.8",
    "destination_port": 80,
    "protocol": 6
  },
  "time_limit": 300,
  "packet_limit": 10000,
  "type": "simple",
  "system": "magic-transit"
}'
```

The response is a JSON body that contains the details of the job running to build the packet capture. The response contains a unique identifier for the packet capture request along with the details sent in the request.

```json title="Sample PCAP example response"
{
	"result": {
		"id": "6d1f0aac13cd40e3900d29f5dd0e8a2b",
		"submitted": "2021-12-20T17:29:20.641845Z",
		"filter_v1": {
			"source_address": "1.2.3.4",
			"source_port": 123,
			"destination_address": "5.6.7.8",
			"destination_port": 80,
			"protocol": 6
		},
		"time_limit": 60,
		"status": "pending",
		"packets_remaining": 0,
		"type": "simple",
		"system": "magic-transit"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

</Details>

</TabItem> </Tabs>

## Check packet capture status

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the Cloudflare dashboard, go to [Network health](https://dash.cloudflare.com/?to=/:account/networking-insights/health).
2. Go to the **Diagnostics** tab.
3. Locate your capture under **Network packet captures**.

</TabItem> <TabItem label="API">

To check the status of a running job, send a request to the endpoint and specify the PCAP identifier. The PCAP identifier is received in the response of a collect request as shown in the previous step.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps/{pcap_id} \
--header 'X-Auth-Email: <EMAIL>' \
--header 'X-Auth-Key: <API_KEY>'
```

The response will be similar to the one received when requesting a PCAP collection.

```json title="Sample PCAP example result"
{
	"result": {
		"id": "6d1f0aac13cd40e3900d29f5dd0e8a2b",
		"submitted": "2021-12-20T17:29:20.641845Z",
		"filter_v1": {
			"source_address": "1.2.3.4",
			"source_port": 123,
			"destination_address": "5.6.7.8",
			"destination_port": 80,
			"protocol": 6
		},
		"time_limit": 120,
		"status": "success",
		"packets_remaining": 0,
		"type": "simple",
		"system": "magic-transit"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

</TabItem> </Tabs>

The capture status displays one of the following options:

- **Complete** (API: `success`): The capture is done and ready for download.
- **In progress** (API: `pending`): Packets have been captured but the PCAP file is still being assembled.
- **Failure**: The capture failed. For full captures, verify that your bucket is correctly configured and that Cloudflare has write access to it. For sample captures, verify your filter configuration.

## Download packet captures

After your request finishes processing, you can download your packet captures.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare One](https://one.dash.cloudflare.com) dashboard, go to **Network visibility** > **Diagnostics**.
2. In **Packet captures**, select **Start a capture**.
3. Locate your packet capture you want to download, and select **Download**.

Packet captures are available to download when the **Status** displays **Success**.

</TabItem> <TabItem label="API">

Full captures can produce multiple PCAP files per capture because the capture can run across multiple machines at the data center. To merge these into a single file for analysis, refer to [Wireshark's mergecap documentation](https://www.wireshark.org/docs/man-pages/mergecap.html).

**Full PCAPs**

To obtain full PCAPs, download the files from the bucket specified in `destination_conf` after the PCAP's status is `success`. You may find multiple files named `pcap_<pcap_id>.pcap` per capture as captures can occur across multiple machines.

**Sample PCAPs**

Once the sample PCAP collection is complete, you can download the PCAP by specifying the PCAP identifier used earlier.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps/{pcap_id}/download \
--header 'X-Auth-Email: <EMAIL>' \
--header 'X-Auth-Key: <API_KEY>' \
--output download.pcap
```

</TabItem> </Tabs>

## List packet captures

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the Cloudflare dashboard, go to the **Network health** page.

   <DashButton url="/?to=/:account/networking-insights/health" />

2. Go to the **Diagnostics** tab.

The list of packet captures associated with your account displays under **Network packet captures**.

</TabItem> <TabItem label="API">

To view a list of sent requests, use the following command:

```bash title="List request example"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/pcaps \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>"
```

The response returns an array that includes up to 50 sent requests, which includes completed and ongoing requests.

```json title="List response example"
{
	"result": [
		{
			"id": "43adab5adeca4dab9c51f4b7f70f2ec3",
			"submitted": "2021-12-15T03:04:09.277394Z",
			"filter_v1": {},
			"time_limit": 120,
			"status": "success",
			"packets_remaining": 0,
			"type": "simple",
			"system": "magic-transit"
		}
	],
	"success": true,
	"errors": [],
	"messages": []
}
```

</TabItem> </Tabs>

# Integrations

import { DirectoryListing } from "~/components";

Integrate third-party applications and services with Cloudflare One.

<DirectoryListing />

# Anthropic

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Anthropic",
		integrationAccountType: "Anthropic account",
	}}
/>

This integration covers the following Anthropic products:

- Claude Console (organizations, workspaces/projects, users, invites)
- Anthropic API Platform (organization and project API keys)

:::note
To detect data loss prevention and posture findings, your organization must have Anthropic's Compliance API enabled. Contact your Anthropic representative to request access. When enabled, admin keys created in the Claude Console automatically include the `read:compliance_activities` scope.
:::

## Integration prerequisites

- An Anthropic [Enterprise or Platform organization](https://www.anthropic.com/pricing#team-&-enterprise)
- [Organization-level admin (or equivalent) privileges in Anthropic](https://support.anthropic.com/articles/10186004-api-console-roles-and-permissions) to view organization metadata and manage API keys

## Integration permissions

For the Anthropic integration to function, Cloudflare CASB requires authorization via **API keys**:

- `Admin API key (organization-level)`: Grants read-only access to organization/workspace metadata, members and invites, key metadata, and compliance activities used for findings.
- (Optional) `Project API key (project-level)`: Grants read-only access to project metadata and keys when you include project scopes in the scan.

These credentials follow the principle of least privilege so that only the minimum required access is granted.

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "Anthropic", slugRelativePath: "anthropic" }}
/>

### API key hygiene

Detect API keys that may be unused or overdue for rotation.

| Finding type              | FindingTypeID                          | Severity |
| ------------------------- | -------------------------------------- | -------- |
| Anthropic: Unused API key | `7e9bcc48-9548-4404-9bb2-83847150fc17` | Medium   |

### Access security

Flag organization access issues to help enforce best practices.

| Finding type                                                | FindingTypeID                          | Severity |
| ----------------------------------------------------------- | -------------------------------------- | -------- |
| Anthropic: High-privilege invite                            | `c4658dc8-3edf-4f14-920c-3843eabab095` | High     |
| Anthropic: Stale pending invite                             | `43d50e14-9b07-48bc-a060-111a629fe19a` | Low      |
| Anthropic: Claude Project visible across organization       | `019e4777-9f9a-7285-b46a-5590116a6c50` | Medium   |
| Anthropic: Claude Cowork enabled for role                   | `019e4777-9fa0-7b0e-9ce3-dfc3ac86c840` | High     |
| Anthropic: Claude Connector Always Allowed enabled for role | `019e4777-9fa3-7155-b92d-c9fa5979803a` | High     |
| Anthropic: Claude for Chrome enabled for role               | `019e4777-9fa5-736a-85c9-4cfa73e23a09` | High     |

### Data Loss Prevention (optional)

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

| Finding type                                                     | FindingTypeID                          | Severity |
| ---------------------------------------------------------------- | -------------------------------------- | -------- |
| Anthropic: Downloadable File with DLP Profile match              | `c6546fee-b2b0-4151-9546-6008f9430706` | High     |
| Anthropic: Claude Chat User Prompt with DLP Profile match        | `719e347a-2fef-40f0-bd70-52d85996dfd2` | High     |
| Anthropic: Claude Chat Assistant Response with DLP Profile match | `f2cd2eaf-b592-4bcc-8308-5975ac9a12bc` | High     |
| Anthropic: Claude Chat Uploaded File with DLP Profile match      | `c6afd6fa-6969-4214-afa8-a83a08cc611c` | High     |
| Anthropic: Claude Chat Generated File with DLP Profile match     | `15206512-4af6-466c-8f08-82a9151d4b79` | High     |
| Anthropic: Claude Project File with DLP Profile match            | `9e5d6c1d-66d7-4a5a-93f4-aa7971e4815a` | High     |
| Anthropic: Claude Project Document with DLP Profile match        | `40438417-a3f2-41ed-b370-51390e8c7594` | High     |
| Anthropic: Claude Chat Artifact with DLP Profile match           | `c3fc58b0-24f6-4cea-a99d-67555bceca01` | High     |
| Anthropic: Claude Project Instructions with DLP Profile match    | `e1d66413-79d6-46be-a841-a38a541d94a6` | High     |

# Atlassian Confluence

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Atlassian Confluence",
		integrationAccountType: "Atlassian Confluence Cloud account",
	}}
/>

:::note

At this time, the CASB integration for Confluence is only compatible with Confluence Cloud accounts. Support for Confluence Data Center will come at a future date.
:::

## Integration prerequisites

- A Confluence Cloud plan (Free, Standard, Premium, Enterprise)

- Access to a Confluence Cloud account with Site admin and/or Organization admin permissions

## Integration permissions

For the Confluence Cloud integration to function, Cloudflare CASB requires the following permissions via an OAuth 2.0 app:

- `read:confluence-space.summary`
- `read:confluence-props`
- `read:confluence-content.all`
- `read:confluence-content.summary`
- `read:confluence-content.permission`
- `read:confluence-user`
- `read:confluence-groups`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission, refer to the [Atlassian scopes documentation](https://developer.atlassian.com/cloud/confluence/scopes-for-oauth-2-3LO-and-forge-apps/).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{
		integrationName: "Atlassian Confluence",
		slugRelativePath: "atlassian-confluence",
	}}
/>

### Access security

Flag user and third-party app access issues, including account misuse, sharing security, and users not following best practices.

| Finding type                                                      | FindingTypeID                          | Severity |
| ----------------------------------------------------------------- | -------------------------------------- | -------- |
| Confluence: Unknown or anonymous user with edit access to content | `d5ad6f5e-3e7a-4409-a9dc-9707caca047e` | Critical |
| Confluence: Unknown or anonymous user with edit access to space   | `a531c40f-76f5-404e-9c9b-3b21a6da7b98` | High     |
| Confluence: Third-party app with edit access to space             | `aac0ac18-25ad-442a-9a24-01ecd85b0b2b` | Medium   |
| Confluence: Third-party app with edit access to content           | `8214431e-b708-49c9-b28b-3214f1b491d8` | Medium   |
| Confluence: Unknown or anonymous user with access                 | `a1d0d098-2602-4312-85a8-a62d3bc56aca` | Low      |
| Confluence: Third-party app with content access                   | `5ccf7326-386d-4afb-867a-fbf25978c33a` | Low      |

# Atlassian Jira

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Atlassian Jira",
		integrationAccountType: "Atlassian Jira Cloud account",
	}}
/>

:::note

At this time, the CASB integration for Jira is only compatible with Jira Cloud accounts. Support for Jira Data Center will come at a future date.
:::

## Integration prerequisites

- A Jira Cloud plan (Free, Standard, Premium, Enterprise)

- Access to a Jira Cloud account with Site admin and/or Organization admin permissions

## Integration permissions

For the Jira Cloud integration to function, Cloudflare CASB requires the following permissions via an OAuth 2.0 app:

- `read:jira-work`
- `read:jira-user`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission, refer to the [Atlassian scopes documentation](https://developer.atlassian.com/cloud/jira/platform/scopes-for-oauth-2-3LO-and-forge-apps/).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "Jira Cloud", slugRelativePath: "atlassian-jira" }}
/>

### Access security

Flag user and third-party app access issues, including account misuse and users not following best practices.

| Finding type                                | FindingTypeID                          | Severity |
| ------------------------------------------- | -------------------------------------- | -------- |
| Jira: Active user with unknown account type | `8dfd390d-911e-47bb-9ded-cb75fd91e793` | Low      |
| Jira: Active third-party app with access    | `01118135-a4ab-4b8f-887d-c814358da217` | Low      |
| Jira: Inactive third-party app with access  | `36f7de49-2938-4a54-b212-b4da74145a58` | Low      |
| Jira: Inactive user                         | `1e1a085c-1ef3-4199-bea5-ff52ccbd6d2d` | Low      |

### File security

Identify files that could be potentially problematic and worth deeper investigation.

| Finding type                              | FindingTypeID                          | Severity |
| ----------------------------------------- | -------------------------------------- | -------- |
| Jira: Issue attachment larger than 512 MB | `1e5473b7-588e-4954-b97d-a5a20b4f8c5a` | Medium   |

# Amazon Web Services (AWS) S3

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Amazon Web Services (AWS) S3",
		integrationAccountType: "AWS account",
	}}
/>

## Integration prerequisites

- An AWS account using AWS S3 (Simple Storage Service)
- For initial setup, access to the AWS account with permission to create a new IAM Role with the scopes listed below.

## Integration permissions

For the AWS S3 integration to function, Cloudflare CASB requires the following access scopes via an IAM Role with cross-account access:

- `s3:PutBucketNotification`
- `s3:GetObject`
- `s3:ListBucket`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission scope, refer to the [AWS S3 Permissions documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-with-s3-policy-actions.html).

## Compute account

You can connect an AWS compute account to your CASB integration to perform [Data Loss Prevention](/cloudflare-one/data-loss-prevention/) scans within your S3 bucket and avoid data egress. CASB will scan any objects that exist in the bucket at the time of configuration.

### Add a compute account

<Render file="casb/aws-compute-account" product="cloudflare-one" />

You can only connect one computer account to an integration. To remove a compute account, select **Manage compute accounts**.

### Configure compute account scanning

Once your AWS compute account has successfully connected to your CASB integration, you can configure where and how to scan for sensitive data:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Integrations** > **Cloud & SaaS integrations**.
2. Find and select your AWS integration.
3. Select **Create new configuration**.
4. In **Resources**, choose the buckets you want to scan. Select **Continue**.
5. Choose the file types, sampling percentage, and [DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/) to scan for.
6. (Optional) Configure additional settings, such as the limit of API calls over time for CASB to adhere to.
7. Select **Continue**.
8. Review the details of the scan, then select **Start scan**.

CASB will take up to an hour to begin scanning. To view the scan results, go to **Cloud & SaaS findings** > **Content Findings**.

To manage your resources, go to **Integrations** > **Cloud & SaaS integrations**, then find and select your AWS integration. From here, you can pause all or individual scans, add or remove resources, and change scan settings.

For more information, refer to [Content findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#content-findings).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "AWS S3", slugRelativePath: "aws-s3" }}
/>

### S3 Bucket security

Flag security issues in S3 Buckets, including overpermissioning, access policies, and user security best practices.

| Finding type                                             | FindingTypeID                          | Severity |
| -------------------------------------------------------- | -------------------------------------- | -------- |
| S3 Bucket ACL Allows Any Authenticated User to Write     | `09bc7d1f-e682-43bc-a4ce-e6e03b408244` | Critical |
| S3 Bucket ACL Allows Any Authenticated User to Write ACP | `9392a460-c566-4e0d-b06b-01d87dc84d7c` | Critical |
| S3 Bucket ACL Allows Public ACP Write                    | `5b792c7f-2546-4fcd-96dc-a58a53fea7e0` | Critical |
| S3 Bucket ACL Allows Public Write                        | `f50ae197-fa0a-4caa-be95-79aed91eed63` | Critical |
| S3 Bucket Policy Allows Any Authenticated User to Write  | `70fe0596-28bc-41dd-a2c3-1486fb0fb1dd` | Critical |
| S3 Bucket Policy Allows Public Write                     | `5e2aac4b-d8be-43dc-b324-84fdf63f760e` | Critical |
| S3 Bucket ACL Allows Any Authenticated User to Read      | `fda31c4d-24dc-43d4-8a84-a1a9e1df01a1` | High     |
| S3 Bucket ACL Allows Any Authenticated User to Read ACP  | `7232e46b-3539-4080-b905-022f1091556c` | High     |
| S3 Bucket ACL Allows Public ACP Read                     | `e324242c-5feb-41a3-8d91-f70611471fad` | High     |
| S3 Bucket ACL Allows Public Read                         | `f8c9f979-29f0-4ada-b09e-a149937a55d4` | High     |
| S3 Bucket Policy Allows Any Authenticated User to Read   | `c6b3a745-b535-45ea-b2c0-ba8a139ca634` | High     |
| S3 Bucket Policy Allows Public Read                      | `f3915412-eef9-47d9-8448-e04462de8ba2` | High     |
| S3 Bucket Without MFA Delete Enabled                     | `f108bd28-9870-453f-a439-01818e85bdc7` | High     |
| S3 Bucket Policy Allows Any Authenticated User to Delete | `4431eaeb-63e3-43c1-a4bc-029f09da66fd` | Medium   |
| S3 Bucket Policy Allows Any Authenticated User to List   | `319c9715-b86d-4215-bdfa-c5d9b3a5cc83` | Medium   |
| S3 Bucket Policy Allows Public Delete                    | `bbbeacbc-6692-4121-a785-d634da1e5c56` | Medium   |
| S3 Bucket Policy Allows Public List                      | `f7ae03e3-1303-4404-b6f5-a7f97e52105e` | Medium   |
| S3 Bucket Without Access Logging                         | `67d0995d-7b4a-40c5-a43f-7a98d20faac6` | Medium   |
| S3 Bucket Without AWS CloudTrail Logging                 | `89366ebe-ca0b-45fc-a9cb-135674e0a49b` | Medium   |
| S3 Bucket Without Lifecycle Policies                     | `2df6f1b8-e41c-4ab5-a466-992ce485a367` | Medium   |
| S3 Bucket Without Object-Level Logging                   | `9af2594c-3999-49c9-bd3d-2f4b091f99c0` | Medium   |
| S3 Bucket Without Replication Enabled                    | `cb61ef18-a498-456c-985c-78a45e19f4fe` | Medium   |
| S3 Bucket Without Versioning Enabled                     | `95e1284f-a514-4396-bf64-cd003818790c` | Medium   |

# Bitbucket Cloud

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Bitbucket Cloud",
		integrationAccountType: "Bitbucket Cloud Cloud account",
	}}
/>

:::note

Currently, the CASB integration for Bitbucket is only compatible with Bitbucket Cloud accounts. Support for Bitbucket Data Center will come at a future date.

:::

## Integration prerequisites

- A Bitbucket Cloud plan (Free, Standard, Premium, Enterprise)
- Access to a Bitbucket Cloud account with Site admin and/or Organization admin permissions

## Integration permissions

For the Bitbucket Cloud integration to function, Cloudflare CASB requires the following permission scopes via an OAuth 2.0 app:

- `account`
- `email`
- `issue`
- `pipeline`
- `project`
- `project:admin`
- `pullrequest`
- `repository`
- `repository:admin`
- `runner`
- `snippet`
- `webhook`
- `wiki`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission scope, refer to the [Atlassian scopes documentation](https://developer.atlassian.com/cloud/bitbucket/rest/intro/#oauth-2-0).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{
		integrationName: "Bitbucket Cloud",
		slugRelativePath: "bitbucket-cloud",
	}}
/>

### Repository security

Flag repository issues, including branch protection, access, and update frequency.

| Finding type                                                                                              | FindingTypeID                          | Severity |
| --------------------------------------------------------------------------------------------------------- | -------------------------------------- | -------- |
| Bitbucket Cloud: Repository is publicly accessible                                                        | `be273f0a-678e-49af-b9f8-8f3913acef97` | Critical |
| Bitbucket Cloud: Repository Default Branch Protection does not have PR Review Required                    | `6ad95c13-0d13-4595-bc76-bd86f4eba4b9` | High     |
| Bitbucket Cloud: Repository has no Default Branch Protection                                              | `324f2014-4d4b-4aa6-89a8-72a6c7da09d7` | Medium   |
| Bitbucket Cloud: Repository not updated in 12+ months                                                     | `a1bd3076-a68d-492e-9947-a01e15a4d1b3` | Medium   |
| Bitbucket Cloud: Repository Default Branch Protection does not disable direct pushes for all users/groups | `c60a7b00-1592-429a-8a32-d58101e4551f` | Medium   |
| Bitbucket Cloud: Repository Default Branch Protection does not have Stale PR Approvals Disabled           | `738c9839-5e1e-4048-85a3-7935ec4c647a` | Medium   |
| Bitbucket Cloud: Repository Default Branch Protection does not have Force Pushes Disabled                 | `4c52f441-0c24-4dbd-8f5e-0e5b829ee8e2` | Medium   |
| Bitbucket Cloud: Repository Default Branch Protection does not require passing builds to merge            | `afe4a27e-ee01-4ebe-914c-d480ac49a5c2` | Low      |
| Bitbucket Cloud: Repository Default Branch Protection allows branch deletion                              | `86411562-4b85-4677-b048-7887cc5b1567` | Low      |
| Bitbucket Cloud: Repository Default Branch Protection does not enforce merge checks                       | `64440d40-91de-4d13-9280-d5afa418ccf0` | Low      |
| Bitbucket Cloud: Key is older than 180 days                                                               | `0a135600-a109-434f-877c-1a6594dcd76d` | Low      |

# Box

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{ integrationName: "Box", integrationAccountType: "Box account" }}
/>

## Integration prerequisites

- A Box account on a Business plan (Business, Business Plus, Enterprise, Enterprise Plus)

- Access to a Box Business account with Admin permission

## Integration permissions

For the Box integration to function, Cloudflare CASB requires the following Box permissions via an OAuth 2.0 app:

- `Read all files and folders stored in Box`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about the permission, refer to the [Box Scopes documentation](https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/#read-all-files-and-folders).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "Box", slugRelativePath: "box" }}
/>

### File sharing

Identify files and folders that have been shared in a potentially insecure fashion.

<Render file="casb/shared-links" product="cloudflare-one" />

| Finding type                                             | FindingTypeID                          | Severity |
| -------------------------------------------------------- | -------------------------------------- | -------- |
| Box: File publicly accessible with edit access           | `fa0532dd-9d13-4c21-8227-62b8bd8be275` | Critical |
| Box: File publicly accessible with high download count   | `97c0845a-754b-4269-b548-85026867da64` | High     |
| Box: Folder publicly accessible with edit access         | `154eabed-19a7-4a07-9dfd-d08f5e839aed` | High     |
| Box: File shared company-wide with edit access           | `8df801de-327b-4d71-9f36-fc6f3e2c18da` | High     |
| Box: File publicly accessible with view access           | `ecca7eeb-3c04-46b2-a509-40393ada32ec` | High     |
| Box: Folder shared company-wide with high download count | `21bed8a9-b587-4a8b-b38f-8c9492b1d132` | Medium   |
| Box: File publicly accessible with high view count       | `540ab1db-5a9e-4968-b669-100e2b97fa85` | Medium   |
| Box: Folder that can be shared by anyone                 | `c56757c6-72e4-456c-8cb9-a5b0fd6ceb4a` | Medium   |
| Box: Folder shared company-wide with edit access         | `61082e41-3205-44a0-bb7e-34c02abd5137` | Medium   |
| Box: File shared company-wide with view access           | `5afdbe74-0311-4da8-a64e-6f25c3d4a2b7` | Medium   |
| Box: File shared company-wide with high download count   | `3cd0d8dd-d92b-4a46-b88f-076a17e11837` | Medium   |
| Box: Folder publicly accessible with view access         | `2e9d5774-3a22-4d45-9307-bb24207af3d7` | Medium   |
| Box: Folder shared company-wide with high view count     | `fd303606-a513-4bb5-9a87-b1c836f6e993` | Low      |
| Box: File larger than 2 GB                               | `ef889ceb-4cad-4d25-8845-d350a599825e` | Low      |
| Box: Folder with external email upload access            | `90f9b277-0846-4918-aac2-2e63fed576b5` | Low      |
| Box: Folder shared company-wide with view access         | `1bb68e90-9c1d-44ef-91a9-2ed4eb2eb5b2` | Low      |
| Box: File shared company-wide with high view count       | `22bf3a7b-1fd1-4eb6-b8f5-1b2e772b3484` | Low      |

### Data Loss Prevention (optional)

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

| Finding type                                                        | Severity | Description                                                                       |
| ------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------- |
| Box: File Publicly Accessible Read and Write with DLP Profile match | Critical | A Box file contains sensitive data that anyone on the Internet can read or write. |
| Box: File Publicly Accessible Read Only with DLP Profile match      | Critical | A Box file contains sensitive data that anyone on the Internet can read.          |
| Box: File Shared Company Wide Read and Write with DLP Profile match | Medium   | A Box file is shared with the entire company with read and write permissions.     |
| Box: File Shared Company Wide Read Only with DLP Profile match      | Medium   | A Box file is shared with the entire company with read permissions.               |

### User access

Flag user access issues, including account misuse and users not following best practices.

| Finding type                                             | FindingTypeID                          | Severity |
| -------------------------------------------------------- | -------------------------------------- | -------- |
| Box: Admin not required to use two-factor authentication | `40f33ef2-3eab-4855-b171-a71463f8fc96` | High     |
| Box: User not required to use two-factor authentication  | `a8f9e55a-cb7c-4e35-8dc0-fdf569919a97` | Medium   |
| Box: Inactive admin user                                 | `e6b82aa9-7d0d-4c85-a582-a377684ace47` | Medium   |
| Box: User with unconfirmed notification email            | `15b70c97-68f6-4ef0-afd1-891971162114` | Low      |
| Box: User with email alias configured                    | `085164ed-c555-40ed-9374-358a892e49ef` | Low      |
| Box: User allowed to collaborate with external users     | `01ed4b90-c470-4ea1-961a-7e64c2fec525` | Low      |
| Box: Inactive user                                       | `d709ccb3-9b9d-4a3c-a3af-a1def54c9a2e` | Low      |

### Account misconfigurations

Discover account and admin-level settings that have been configured in a potentially insecure way.

| Finding type        | Severity |
| ------------------- | -------- |
| Box: Active Webhook | Low      |

# Dropbox

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Dropbox",
		integrationAccountType: "Dropbox account",
	}}
/>

## Integration prerequisites

- A Dropbox Business plan (Standard, Advanced, Enterprise, or Education)
- Access to a Dropbox Business account with Team admin permissions

## Integration permissions

For the Dropbox integration to function, Cloudflare CASB requires the following Dropbox permissions via an OAuth 2.0 app:

- `account_info.read`
- `files.metadata.read`
- `files.content.read`
- `sharing.read`
- `team_info.read`
- `team_data.member`
- `team_data.governance.write`
- `team_data.governance.read`
- `files.team_metadata.read`
- `members.read`
- `groups.read`
- `sessions.list`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission, refer to the [Dropbox API Permissions documentation](https://developers.dropbox.com/oauth-guide#dropbox-api-permissions).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "Dropbox", slugRelativePath: "dropbox" }}
/>

### File and folder sharing

Identify files and folders that have been shared in a potentially insecure fashion.

| Finding type                                                           | FindingTypeID                          | Severity |
| ---------------------------------------------------------------------- | -------------------------------------- | -------- |
| Dropbox: File publicly accessible with edit access                     | `7fefad57-371b-4f27-b1f0-7d500c863bd0` | Critical |
| Dropbox: File shared company-wide with edit access                     | `265ed167-435c-4626-99ba-2fafd766c096` | High     |
| Dropbox: File publicly accessible with view access                     | `e8c057e4-d6ce-431b-9d03-d9aadff610d4` | High     |
| Dropbox: Shared link create policy set to default 'Public'             | `0afabc9a-3a98-4a67-941a-d1f0ce0cfbfe` | High     |
| Dropbox: File shared company-wide with view access                     | `02a14d67-27fa-4621-a280-1a25925d506f` | Medium   |
| Dropbox: Folder shared company-wide with edit access                   | `ac4da5b9-ddb0-4285-ba52-2ba4de43b530` | Medium   |
| Dropbox: Shared folder policy set to default 'Anyone'                  | `5d479ad5-d0f1-4c8f-b439-a39b399fe6c5` | Medium   |
| Dropbox: Group creation policy set to 'Admins and Members'             | `6f54b5eb-6867-490e-b823-08e91878eb40` | Medium   |
| Dropbox: Folder join policy set to 'Can join folders shared by Anyone' | `e5ffaecc-f61a-4019-a54f-2e5ac882d3f3` | Medium   |
| Dropbox: Folder member policy set to 'Can share folders with Anyone'   | `99d4a2af-12ec-43a1-9630-27ac4adf625c` | Medium   |
| Dropbox: Shared link create policy set to default 'Team-wide'          | `a3d02f04-4372-4ae3-99f9-e2caccee6e76` | Low      |

### Data Loss Prevention (optional)

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

| Finding type                                                   | Severity | Description                                                                           |
| -------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------- |
| File Publicly Accessible Read and Write with DLP Profile match | Critical | A Dropbox file contains sensitive data that anyone on the Internet can read or write. |
| File Publicly Accessible Read Only with DLP Profile match      | Critical | A Dropbox file contains sensitive data that anyone on the Internet can read.          |
| File Shared Company Wide Read and Write with DLP Profile match | Medium   | A Dropbox file is shared with the entire company with read and write permissions.     |
| File Shared Company Wide Read Only with DLP Profile match      | Medium   | A Dropbox file is shared with the entire company with read permissions.               |

### Suspicious applications

Detect when suspicious Dropbox applications are linked by members.

| Finding type                                     | FindingTypeID                          | Severity |
| ------------------------------------------------ | -------------------------------------- | -------- |
| Dropbox: Suspicious application linked by member | `8384c58c-1fc2-4caa-9836-c8ede7ca440d` | High     |

### User access and account misconfigurations

Flag user access issues, including users misusing accounts or not following best practices.

| Finding type                                         | FindingTypeID                          | Severity |
| ---------------------------------------------------- | -------------------------------------- | -------- |
| Dropbox: Admin user with unverified secondary email  | `cebb4104-1235-4049-a664-9fcd003ece71` | Medium   |
| Dropbox: Admin user with restricted directory access | `19378bb3-a3b7-4ee5-8ea7-39eec0a2ca7c` | Medium   |
| Dropbox: User with unverified email                  | `2b5804f7-4888-4872-a85a-a64805d10654` | Medium   |
| Dropbox: Invited user                                | `44d34aab-82fb-4a60-8e35-d7a75cfc789c` | Low      |
| Dropbox: Suspended user                              | `e356cfe6-97e6-4e30-9cb9-4a42a387844e` | Low      |
| Dropbox: User with secondary email configured        | `4bbb795a-cd34-41ba-865d-9bf9de61a592` | Low      |

# Google Cloud Platform (GCP) Cloud Storage

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Google Cloud Platform (GCP) Cloud Storage",
		integrationAccountType: "GCP account",
	}}
/>

## Integration prerequisites

- A GCP account using Cloud Storage.
- For initial setup, access to the GCP account with permission to create a new Service Account with the scopes listed below.

## Integration permissions

For the GCP Cloud Storage integration to function, Cloudflare CASB requires the following access scopes via a Service Account:

- `roles/viewer`
- `roles/storage.admin`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission scope, refer to the [GCP IAM roles for Cloud Storage documentation](https://cloud.google.com/storage/docs/access-control/iam-roles).

## Compute account

You can connect a GCP compute account to your CASB integration to perform [Data Loss Prevention](/cloudflare-one/data-loss-prevention/) scans within your Cloud Storage bucket and avoid data egress. CASB will scan any objects that exist in the bucket at the time of configuration.

### Add a compute account

To connect a compute account to your GCP integration:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Integrations** > **Cloud & SaaS integrations**.
2. Find and select your GCP integration.
3. Select **Open connection instructions**.
4. Follow the instructions provided to connect a new compute account.
5. Select **Refresh**.

You can only connect one compute account to an integration. To remove a compute account, select **Manage compute accounts**.

### Configure compute account scanning

Once your GCP compute account has successfully connected to your CASB integration, you can configure where and how to scan for sensitive data:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Integrations** > **Cloud & SaaS integrations**.
2. Find and select your GCP integration.
3. Select **Create new configuration**.
4. In **Resources**, choose the buckets you want to scan. Select **Continue**.
5. Choose the file types, sampling percentage, and [DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/) to scan for.
6. (Optional) Configure additional settings, such as the limit of API calls over time for CASB to adhere to.
7. Select **Continue**.
8. Review the details of the scan, then select **Start scan**.

CASB will take up to one hour to begin scanning. To view the scan results, go to **Cloud & SaaS findings** > **Content Findings**.

To manage your resources, go to **Cloud & SaaS findings** > **Integrations**, then find and select your GCP integration. From here, you can pause all or individual scans, add or remove resources, and change scan settings.

For more information, refer to [Content findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#content-findings).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{
		integrationName: "GCP Cloud Storage",
		slugRelativePath: "gcp-cloud-storage",
	}}
/>

### Cloud Storage Bucket security

Flag security issues in Cloud Storage Buckets, including overpermissioning, access policies, and user security best practices.

| Finding type                                                                     | FindingTypeID                          | Severity |
| -------------------------------------------------------------------------------- | -------------------------------------- | -------- |
| Google Cloud Platform: GCS Bucket Allows Public Write                            | `4583f5a9-a343-4e2f-a8b3-9237a911f337` | Critical |
| Google Cloud Platform: GCS Bucket IAM Policy Allows Public Access                | `032c1e88-0cff-47f6-8d75-046e0a7330de` | Critical |
| Google Cloud Platform: GCS Bucket Publicly Accessible                            | `cc028a95-46d4-4156-ac11-bc5713529824` | Critical |
| Google Cloud Platform: Public Access Prevention Enabled But Policy Grants Public | `cc02680e-9cc3-49d1-99d5-29d425bf142f` | Critical |
| Google Cloud Platform: GCS Bucket ACL Grants All Authenticated Users Access      | `e1a588af-0500-482e-b59d-fd2693ce7fc0` | Critical |
| Google Cloud Platform: GCS Bucket ACL Grants All Users Public Access             | `1904c004-8d4f-470e-9460-e77db23d6a86` | Critical |
| Google Cloud Platform: Public Access Prevention but ACL Grants allUsers          | `fcf2e27e-673f-4cd2-9b76-ec89c4c5872c` | Critical |
| Google Cloud Platform: GCS Bucket Versioning Disabled                            | `bd66e214-f205-4e00-bd68-121dad0a7988` | High     |
| Google Cloud Platform: GCS Bucket Without KMS Encryption                         | `0105d9c4-1a01-4b65-b33e-df6c55905147` | High     |
| Google Cloud Platform: GCS Uniform Bucket-Level Access Disabled                  | `6960b459-aa9e-4b41-84f6-26cdb75a1995` | High     |
| Google Cloud Platform: GCS Bucket IAM Policy Allows Public Read                  | `10420f34-8fdd-49cb-8d38-096a2de5824f` | High     |
| Google Cloud Platform: GCS Bucket Lacks Lifecycle Rules                          | `edcd5a8b-b128-404b-8207-23a80f669b65` | Medium   |
| Google Cloud Platform: GCS Bucket Logging Disabled                               | `d26f43c8-9406-481c-8c8b-1a7f05f3cc27` | Medium   |
| Google Cloud Platform: GCS Bucket Not Using 'Soft Delete'                        | `5542ed8e-77a6-43c1-8b9e-935e66009d34` | Medium   |
| Google Cloud Platform: GCS Bucket Retention Policy Disabled                      | `2d4a247c-8adb-4f2b-ae58-3568d633cb81` | Medium   |
| Google Cloud Platform: GCS Bucket IAM Policy Not Version 3                       | `ade2ede6-08c7-4962-b084-f6a29ee4a5b8` | Low      |
| Google Cloud Platform: GCS Bucket IAM Policy Using Legacy Roles                  | `11a592b9-4f51-4a1a-9925-a48a5ed01521` | Low      |

# GitHub

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "GitHub",
		integrationAccountType: "GitHub Organization",
	}}
/>

## Integration prerequisites

- A GitHub account with a Free, Pro, or Enterprise plan
- Membership to a GitHub Organization with Owner or GitHub App manager permissions

## Integration permissions

For the GitHub integration to function, Cloudflare CASB requires the following GitHub API permissions:

| Permission                  | Access      | Description                                                                                             |
| --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------- |
| Administration              | `Read-only` | View basic administrative information from the account.                                                 |
| Members                     | `Read-only` | View metadata on organization members                                                                   |
| Metadata                    | `Read-only` | View metadata surrounding an organization's assets, excluding sensitive private repository information. |
| Organization administration | `Read-only` | View information on organization settings                                                               |

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission, refer to the [GitHub App permissions reference](https://docs.github.com/en/rest/overview/permissions-required-for-github-apps).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "GitHub", slugRelativePath: "github" }}
/>

### Branches and merges

| Finding type                                                                           | FindingTypeID                          | Severity | Description                                                                                                              |
| -------------------------------------------------------------------------------------- | -------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| GitHub: Repository has no Default Branch Protection                                    | `5a0428fa-5c13-44b8-a028-9351c1d10a91` | Medium   | A repository's default branch does not have any branch protection rules enabled.                                         |
| GitHub: Repository Default Branch Protection does not have PR Review Required          | `edd3f193-af01-421d-9a50-cb1d147bf3a6` | Medium   | A repository's default branch does not have a **Require pull request reviews before merging** rule.                      |
| GitHub: Repository Default Branch Protection does not have Force Pushes Disabled       | `efc3e582-ef39-4316-b1f3-d4717ef30867` | Medium   | A repository's default branch has enabled **Allow force pushes**.                                                        |
| GitHub: Repository Default Branch Protection does not have Stale PR Approvals Disabled | `7dc170d7-b1ef-4138-95fb-403d16e7ed43` | Medium   | A repository's default branch does not have a **Dismiss stale pull request approvals when new commits are pushed** rule. |
| GitHub: Repository Default Branch Protection does not have Admin Restrictions          | `4e4aec5b-e763-41ac-9099-af874606959b` | Medium   | A repository's default branch does not have a **Do not allow bypassing the above settings** rule for administrators.     |
| GitHub: Repository Default Branch Protection does not have Status Checks               | `1eba1aeb-9827-4a03-9c47-8290bd3a83d5` | Medium   | A repository's default branch does not have a **Require status checks to pass before merging** rule.                     |
| GitHub: Organization repository has default WRITE permission                           | `fc074da0-1e1c-4982-8673-0852d70bf80c` | Medium   | A repository's default write protection settings were not changed.                                                       |
| GitHub: Repository not updated in 12+ months                                           | `68b6ef6d-7e00-4761-b3f1-fcf323dc9c26` | Medium   | No changes were made to a repository in at least a year.                                                                 |

Learn more about [GitHub branch protection rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/managing-a-branch-protection-rule).

### User accounts

| Finding type                                                 | FindingTypeID                          | Severity | Description                                                                                              |
| ------------------------------------------------------------ | -------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
| GitHub: Organization two-factor authentication disabled      | `47d01030-0ed8-496d-9484-f77899b21d59` | High     | An organization does not have its organization-wide two-factor authentication (2FA) requirement enabled. |
| GitHub: Organization user two-factor authentication disabled | `dfed92b2-a45e-46ed-a86b-8c7e3db01f3c` | High     | A member of the organization does not have two-factor authentication (2FA) enabled.                      |

# Cloud and SaaS integrations

import { DashButton, DirectoryListing, Steps, TabItem, Tabs } from "~/components";

You can integrate cloud environments and SaaS applications with [Cloudflare CASB](/cloudflare-one/cloud-and-saas-findings/). Once you have added an integration, you can [view and manage findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/).

You can also configure [CASB webhooks](/cloudflare-one/integrations/cloud-and-saas/webhooks/) to send posture finding instances to external systems.

The workflow pages for managing findings and webhooks appear first, followed by supported integrations and troubleshooting guides:

<DirectoryListing maxDepth={2} />

## Create an integration

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations** > **Cloud & SaaS integrations**.

   <DashButton url="/?to=/:account/integrations/cloud-and-saas/integrations" zeroTrust />

2. Select **Connect an integration** or **Add integration**.
3. Browse the available integrations and select the application you want to add.
4. Follow the integration instructions in the dashboard.
5. To run your first scan, select **Save integration**.

</Steps>

</TabItem> <TabItem label="Terraform (v5)">

Terraform supports only integrations that do not use OAuth. The `cloudflare_zero_trust_casb_integration` resource requires version 5.27.0 or later of the [Cloudflare Terraform provider](/terraform/). Version 4 is not supported.

<Steps>

1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
   - `Zero Trust Write`

2. Create an integration using the [`cloudflare_zero_trust_casb_integration`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_casb_integration) resource. The following example creates a Google Cloud Platform integration:

   ```tf
   resource "cloudflare_zero_trust_casb_integration" "google_cloud_platform" {
     account_id = var.cloudflare_account_id
     name       = "Google Cloud Platform"
     paused     = false

     google_cloud_platform = {
       google_cloud_platform_service_account = {
         service_account_key_json = file("service-account-key.json")
       }
     }
   }
   ```

</Steps>

</TabItem> </Tabs>

# OpenAI

import { Render } from "~/components";

The OpenAI integration detects a variety of data loss prevention, account misconfiguration, and user security risks in an integrated OpenAI account that could leave you and your organization vulnerable.

This integration covers the following OpenAI products:

- ChatGPT Enterprise (Workspaces)
- OpenAI Platform Projects (API keys)
- GPTs (custom GPTs)

:::note
Before you begin, ensure that OpenAI has enabled ChatGPT Enterprise Compliance API access for your organization. You will need a Project API key issued for your organization, your Organization ID, and your Workspace ID. These are available in your [OpenAI Project API Keys](https://platform.openai.com/settings/organization/projects).

If Compliance API access is not yet turned on for your organization, refer to [Enable Compliance API access](#enable-combliane-api-access).
:::

## Integration prerequisites

- An OpenAI organization with a ChatGPT Enterprise workspace
- Organization-level admin privileges to create and manage Admin API keys
- (Optional) A Project API key and the corresponding Project ID if you plan to include OpenAI Platform Projects in the scan scope

### Enable Compliance API access

Compliance API access is required to use the OpenAI CASB integration. To enable Compliance API access:

1. Contact `support@openai.com` to request access to the Compliance API for your organization and for the API key you will use with Cloudflare CASB. In your request, include:
   - The last four characters of the API key
   - The name of the API key
   - The name of the user who created the key
   - The requested scope (`read`, `write`, or both)
2. OpenAI will verify the key and grant the requested Compliance API scopes.
3. After the scopes are granted, [add the OpenAI integration to CASB](/cloudflare-one/integrations/cloud-and-saas/). When prompted, enter your Open AI Admin API key, Organization ID, and Workspace ID (available at `https://chatgpt.com/admin/settings`).

For more information, refer to the [OpenAI Help Center](https://help.openai.com/articles/9261474-compliance-api-for-enterprise-customers).

## Integration permissions

For the OpenAI integration to function, Cloudflare CASB requires the following authorization via API keys:

- `Admin API key (organization-level)`: Grants read-only access to organization/workspace metadata, GPTs, users, invites, and audit/compliance objects exposed by the ChatGPT Enterprise Compliance API.
- (Optional) `Project API key (project-level)`: Grants read-only access to OpenAI Platform project metadata and keys.

These credentials follow the principle of least privilege so that only the minimum required access is granted.

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "OpenAI", slugRelativePath: "openai" }}
/>

### Model and tool governance

Flag risky tool and capability settings on custom GPTs.

| Finding type                              | FindingTypeID                          | Severity | ChatGPT Enterprise required |
| ----------------------------------------- | -------------------------------------- | -------- | --------------------------- |
| OpenAI: GPT with Custom Actions enabled   | `6a0dfcff-bcbf-4dd3-8a6f-73c83ec9b564` | High     | ✅                          |
| OpenAI: GPT with Code Interpreter enabled | `203ba13f-3ba0-4d65-baf7-dd861de1ef70` | Medium   | ✅                          |
| OpenAI: GPT with web browsing enabled     | `786369c9-7c68-4922-a949-0c5eacacf8ab` | Medium   | ✅                          |

### Publishing and sharing

Identify GPTs that are externally visible beyond your organization.

| Finding type                                    | FindingTypeID                          | Severity | ChatGPT Enterprise required |
| ----------------------------------------------- | -------------------------------------- | -------- | --------------------------- |
| OpenAI: GPT publicly accessible via GPT Store   | `05c6b0f8-f5d9-4ee9-8a35-dc3a4d9884f8` | High     | ✅                          |
| OpenAI: GPT publicly accessible via public link | `10b14122-635b-431b-8485-a44eb897b005` | High     | ✅                          |

### API key hygiene

Detect API keys that may be stale, unused, or overdue for rotation.

| Finding type                        | FindingTypeID                          | Severity | ChatGPT Enterprise required |
| ----------------------------------- | -------------------------------------- | -------- | --------------------------- |
| OpenAI: Admin API key not rotated   | `1c2a1187-158e-470b-8605-311f88b93bbd` | High     | ❌                          |
| OpenAI: Project API key not rotated | `b0bb2471-2caf-4f18-90dd-15be50c72cdb` | High     | ❌                          |
| OpenAI: Unused admin API key        | `655d1808-6a95-4c72-a10f-51caa0730e43` | Medium   | ❌                          |
| OpenAI: Unused project API key      | `5fab5510-bdbe-40b2-917b-06ad330655a6` | Medium   | ❌                          |

### Access security

Flag user/invite issues to help enforce best practices.

| Finding type                  | FindingTypeID                          | Severity | ChatGPT Enterprise required |
| ----------------------------- | -------------------------------------- | -------- | --------------------------- |
| OpenAI: High-privilege invite | `b51e540f-30da-41ac-aed5-e6c83f9b18ed` | High     | ❌                          |
| OpenAI: Inactive user         | `490c53da-038f-4e51-9622-8c1319497728` | Medium   | ❌                          |
| OpenAI: Stale pending invite  | `af33ae0e-b87e-4a27-8b29-9bb21fc322f1` | Low      | ❌                          |

### Data Loss Prevention (optional)

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

| Finding type                                                | FindingTypeID                          | Severity | ChatGPT Enterprise required |
| ----------------------------------------------------------- | -------------------------------------- | -------- | --------------------------- |
| OpenAI: File in ChatGPT Conversation with DLP Profile match | `a269f21a-ab67-4bf5-b60c-73894ff44ac5` | High     | ✅                          |
| OpenAI: File in ChatGPT GPT with DLP Profile match          | `852ad64e-31d7-4cd5-bc92-d9b75024291b` | High     | ✅                          |
| OpenAI: File in ChatGPT Project with DLP Profile match      | `3fc07859-999e-4061-a08f-2e643e38439b` | High     | ❌                          |

# Salesforce (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Salesforce (FedRAMP)",
	}}
/>

<Render
	file="casb/salesforce-integration"
	product="cloudflare-one"
	params={{
		integrationName: "Salesforce (FedRAMP)",
		environmentName: "FedRAMP-compliant Salesforce environment",
		slugifiedName: "salesforce-fedramp",
	}}
/>

# Salesforce

import { Render } from "~/components";

<Render
	file="casb/salesforce-integration"
	product="cloudflare-one"
	params={{
		integrationName: "Salesforce",
		environmentName: "Salesforce environment",
		slugifiedName: "salesforce",
	}}
/>

# ServiceNow (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "ServiceNow (FedRAMP)",
	}}
/>

<Render
	file="casb/servicenow-integration"
	product="cloudflare-one"
	params={{
		integrationName: "ServiceNow (FedRAMP)",
		environmentName: "ServiceNow (FedRAMP) instance",
		slugifiedName: "servicenow-fedramp",
	}}
/>

# ServiceNow

import { Render } from "~/components";

<Render
	file="casb/servicenow-integration"
	product="cloudflare-one"
	params={{
		integrationName: "ServiceNow",
		environmentName: "ServiceNow instance",
		slugifiedName: "servicenow",
	}}
/>

# Slack

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Slack",
		integrationAccountType: "Slack Workspace",
	}}
/>

## Integration prerequisites

- A Slack user account
- Membership in a Slack Workspace (Free, Pro, Business+, or Enterprise Grid)
- If you are not the Workspace Owner and the `Require App Approval` setting is enabled for the Workspace, [request permission](https://slack.com/help/articles/202035138-Add-apps-to-your-Slack-workspace) to install apps.

## Integration permissions

For the Slack integration to function, Cloudflare CASB requires the following Slack API permissions:

- `channels:read`
- `files:read`
- `groups:read`
- `users:read`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission, refer to the [Slack Permission scopes reference](https://api.slack.com/scopes).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "Slack", slugRelativePath: "slack" }}
/>

### User account settings

| Finding type                                        | FindingTypeID                          | Severity | Description                                                                                            |
| --------------------------------------------------- | -------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| Slack: User with two-factor authentication disabled | `d1cc8596-d22c-435c-9f94-3ba068f019cd` | Critical | A user in the Slack Workspace does not have two-factor authentication (2FA) enabled for their account. |
| Slack: User with unverified email                   | `9fa4ae7c-07f0-453a-b232-e734b0f8877c` | High     | A user in the Slack Workspace has not verified the email they use to sign in.                          |

### Channel sharing

| Finding type                     | FindingTypeID                          | Severity | Description                                                                                       |
| -------------------------------- | -------------------------------------- | -------- | ------------------------------------------------------------------------------------------------- |
| Slack: Channel shared externally | `d298ba64-f013-4e28-b68a-63f758380355` | High     | A channel in the Slack Workspace has been shared with users who are not members of the Workspace. |

### File sharing

| Finding type                                     | FindingTypeID                          | Severity | Description                                                                   |
| ------------------------------------------------ | -------------------------------------- | -------- | ----------------------------------------------------------------------------- |
| Slack: File publicly accessible with view access | `9d96d3a2-696b-4802-98aa-c6c8572e806e` | Medium   | An external link has been created for a file uploaded to the Slack Workspace. |
| Slack: File larger than 2 GB                     | `c16d64a8-9f78-4f24-99ff-de7fcdc6871b` | Low      | A file ≥ 2 GB has been uploaded to the Slack Workspace.                       |

# Webhooks

import { APIRequest, Steps, TabItem, Tabs } from "~/components";

:::note[Availability]
Requires Cloudflare CASB.

To send a live finding instance to a webhook, you must be able to view posture finding instance details in Cloudflare One.
:::

Use CASB webhooks to send posture finding instances from Cloudflare One to external systems such as chat platforms, ticketing systems, SIEMs, SOAR tools, and custom automation services.

After you configure a webhook destination, you can test delivery from the **Webhooks** page and send posture finding instances directly from the finding details workflow.

## Prerequisites

- You have access to Cloudflare One.
- You have a public HTTPS endpoint that can receive `POST` requests.
- You have any authentication values required by your destination, such as a bearer token, Basic auth credentials, static headers, or an HMAC signing secret.

## Create a webhook

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Integrations** > **Webhooks**.
2. Select **Create webhook**.
3. Enter a **Name** for the webhook.
4. Enter the **Destination URL** for the system that will receive webhook requests.
5. Choose an **Authentication method**.
6. Enter the required credentials, headers, or signing secret.
7. (Optional) Select **Test delivery** to validate the destination before saving.
8. Select **Save**.

</TabItem> <TabItem label="API">

Make a `POST` request to the [Create a webhook](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/webhooks/methods/create/) endpoint:

<APIRequest
	path="/accounts/{account_id}/data-security/posture/webhooks"
	method="POST"
	json={{
		label: "Send to SIEM",
		destination_url: "https://example.com/webhook",
		authentication_type: "Bearer Auth",
		headers: [{ key: "Authorization", value: "Bearer <TOKEN>" }],
	}}
/>

</TabItem> <TabItem label="Terraform (v5)">

<Steps>

1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
   - `Zero Trust Write`

2. Create a webhook using the [`cloudflare_zero_trust_casb_webhook`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_casb_webhook) resource:

   ```tf
   variable "siem_token" {
     type      = string
     sensitive = true
   }

   resource "cloudflare_zero_trust_casb_webhook" "siem" {
     account_id          = var.cloudflare_account_id
     label               = "Send to SIEM"
     destination_url     = "https://example.com/webhook"
     authentication_type = "Bearer Auth"
     headers = [{
       key   = "Authorization"
       value = "Bearer ${var.siem_token}"
     }]
   }
   ```

   For HMAC signing, set `authentication_type = "HMAC-Signing"` and provide `signing_secret` instead of `headers`.

</Steps>

</TabItem> </Tabs>

Cloudflare only accepts destination URLs that use `https://` and are publicly reachable. URLs that resolve to localhost, loopback, private, or other reserved addresses are rejected.

## Authentication methods

CASB webhooks support the following authentication methods:

- **None**: Use this option if your destination does not require authentication.
- **Basic Auth**: Use this option when your destination expects HTTP Basic authentication. Requires an `Authorization` header with a value that starts with `Basic `.
- **Bearer Auth**: Use this option when your destination expects a bearer token. Requires an `Authorization` header with a value that starts with `Bearer `.
- **Static Headers**: Use this option when your destination requires one or more fixed custom headers. Requires at least one header. Header names must be unique.
- **HMAC-Signing**: Use this option when your destination validates signed requests. Requires a `signing_secret`.

## Test delivery

Use **Test delivery** to send a test request to the configured destination before saving a new webhook or after updating an existing webhook.

A successful test indicates that Cloudflare reached the destination URL and that the destination returned a response.

Test delivery does not send a live finding instance from your environment.

## Edit, turn off, or delete a webhook

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

To update an existing webhook:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Integrations** > **Webhooks**.
2. Select the webhook you want to update.
3. Modify the webhook configuration.
4. Select **Save**.

To turn a webhook off or on, use the status toggle on the **Webhooks** page.

To delete a webhook, open the webhook menu and select **Delete**.

</TabItem> <TabItem label="API">

To update a webhook, make a `PUT` request to the [Update a webhook](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/webhooks/methods/update/) endpoint with `label`, `destination_url`, `authentication_type`, and `status`. The request replaces the existing configuration, so include every header you want to keep. To keep a stored header value, send its `key` without a `value`.

For example, the following request turns a webhook off by setting `status` to `disabled`:

<APIRequest
	path="/accounts/{account_id}/data-security/posture/webhooks/{webhook_id}"
	method="PUT"
	json={{
		label: "Send to SIEM",
		destination_url: "https://example.com/webhook",
		authentication_type: "Bearer Auth",
		headers: [{ key: "Authorization" }],
		status: "disabled",
	}}
/>

To delete a webhook, make a `DELETE` request to the [Delete a webhook](/api/resources/zero_trust/subresources/casb/subresources/posture/subresources/webhooks/methods/delete/) endpoint:

<APIRequest
	path="/accounts/{account_id}/data-security/posture/webhooks/{webhook_id}"
	method="DELETE"
/>

</TabItem> <TabItem label="Terraform (v5)">

To update a webhook, change its attributes and run `terraform apply`. To turn the webhook off, set `status = "disabled"`.

To delete a webhook, remove the resource from your configuration and run `terraform apply`, or target the resource for destruction:

```sh
terraform destroy -target=cloudflare_zero_trust_casb_webhook.siem
```

</TabItem> </Tabs>

When you edit an existing webhook, Cloudflare does not display saved header values or signing secrets. To replace a stored value, enter a new value and save the webhook again.

## Manage existing webhooks with Terraform

### Import a webhook

To bring a webhook created in the dashboard or API under Terraform management, import it using your account ID and the webhook ID:

```sh
terraform import cloudflare_zero_trust_casb_webhook.siem '<ACCOUNT_ID>/<WEBHOOK_ID>'
```

Cloudflare does not return header values or signing secrets. After you import a webhook, set these values in your configuration so that Terraform can manage them.

### Look up webhooks

Use the [`cloudflare_zero_trust_casb_webhook`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/data-sources/zero_trust_casb_webhook) data source to read a single webhook, or [`cloudflare_zero_trust_casb_webhooks`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/data-sources/zero_trust_casb_webhooks) to list all webhooks in an account:

```tf
data "cloudflare_zero_trust_casb_webhook" "siem" {
	account_id = var.cloudflare_account_id
	webhook_id = "<WEBHOOK_ID>"
}

data "cloudflare_zero_trust_casb_webhooks" "all" {
	account_id = var.cloudflare_account_id
}
```

## Send a posture finding instance to a webhook

After you configure one or more webhook destinations, you can send posture finding instances directly from the findings workflow.

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Posture Findings**.
2. Choose **SaaS** or **Cloud**.
3. Choose the finding you want to review, then select **Manage**.
4. Select an instance.
5. In the instance details panel, select **Send webhook**.
6. Choose the webhook destination or destinations you want to use.
7. Select **Send webhooks**.

Cloudflare queues webhook sends in the background. A success message means that Cloudflare accepted the request for delivery.

For more information on finding workflows, refer to [Manage findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/).

To automatically send a webhook for matching findings without sending each one manually, refer to [Remediation Policies](/cloudflare-one/cloud-and-saas-findings/policies/).

## Payload format

CASB sends a JSON payload that describes the posture finding instance.

Webhook payloads include event metadata, finding details, asset details, and any additional metadata associated with the finding instance. The exact contents vary by integration and finding type.

Webhook payloads include a top-level `id`, `type`, `metadata`, and `data` object.

Depending on the finding, the `metadata` object can include event details such as the actor, destination, send time, and payload version.

The `data` object can include finding details, asset details, and additional metadata associated with the finding instance.

If your downstream system expects a custom schema, send the webhook to an intermediary service or workflow engine that transforms the payload before forwarding it to the final destination.

## Limitations

- CASB webhooks support posture finding instances only.
- CASB webhooks do not send content findings.
- Test delivery sends a test request, but does not send a live finding instance.

## Troubleshooting

If a webhook test or delivery fails:

- Verify that the destination URL uses `https://`.
- Verify that the destination is publicly reachable.
- Confirm that your authentication values, headers, and signing secret are correct.
- If the dashboard reports success but the destination does not process the event immediately, remember that finding instance sends are queued in the background.

For more information, refer to [CASB troubleshooting](/cloudflare-one/integrations/cloud-and-saas/troubleshooting/casb/).

# Zoom

import { Render, Steps } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Zoom",
		integrationAccountType: "Zoom Account",
	}}
/>

## Integration prerequisites

- A Zoom account
- Account Owner or Admin role in the Zoom account
- A Cloudflare Zero Trust account (free plan supports up to 2 integrations)

## Usage

### What CASB scans for

The Zoom integration scans your Zoom account for security misconfigurations and data risks across account settings, user accounts, meetings, and recordings. Findings are grouped by category and ordered by severity level.

Once CASB detects at least one finding, you can view and manage your findings in Cloudflare One under **Cloud & SaaS findings** > **Findings**.

### Plan tier notes

The following optional Zoom API scopes require a specific Zoom plan tier to produce findings:

- Dashboard scopes (`dashboard:read:*`) — require a Zoom Business or higher plan
- Cloud Recording scopes (`cloud_recording:read:*`) — require a Zoom Pro or higher plan
- Master Account scopes (`account:read:*:master`) — require a Zoom Enterprise ISV or reseller Master Account

If your Zoom account does not have the required tier for an optional scope, those scopes will produce no findings. This is expected behavior, not an error.

## Integration permissions

For the Zoom integration to function, Cloudflare CASB requires the following Zoom API scopes:

- `account:read:sub_account:admin`
- `account:read:settings:admin`
- `account:read:lock_settings:admin`
- `dashboard:read:list_meetings:admin`
- `dashboard:read:meeting:admin`
- `dashboard:read:list_meeting_participants_qos:admin`
- `dashboard:read:meeting_quality_score:admin`
- `group:read:list_groups:admin`
- `group:read:group:admin`
- `meeting:read:list_meetings:admin`
- `meeting:read:meeting:admin`
- `cloud_recording:read:list_user_recordings:admin`
- `cloud_recording:read:list_recording_files:admin`
- `cloud_recording:read:recording_settings:admin`
- `report:read:list_users:admin`
- `role:read:list_roles:admin`
- `role:read:role:admin`
- `user:read:list_users:admin`
- `user:read:user:admin`
- `user:read:settings:admin`

If the integration is installed on a Zoom Master Account, the following additional scopes are required:

- `account:read:settings:master`
- `account:read:lock_settings:master`
- `account:read:list_sub_accounts:master`
- `account:read:sub_account:master`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each scope, refer to the [Zoom API scopes reference](https://developers.zoom.us/docs/api/).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{ integrationName: "Zoom", slugRelativePath: "zoom" }}
/>

### Content scanning

| Finding type                                                             | FindingTypeID                          | Severity |
| ------------------------------------------------------------------------ | -------------------------------------- | -------- |
| Zoom: Meeting content has sensitive information with DLP Profile match   | `019ee18c-4853-7eeb-855f-67746af47aef` | Medium   |
| Zoom: Recording content has sensitive information with DLP Profile match | `019ee17b-5d4c-7d7f-8ca4-5f6ffe1b7ff5` | Medium   |

### Account security

| Finding type                                                       | FindingTypeID                          | Severity |
| ------------------------------------------------------------------ | -------------------------------------- | -------- |
| Zoom: Account security settings are unlocked                       | `019e6b65-af26-72c7-bee0-07dc77757f41` | High     |
| Zoom: Account two-factor authentication not enforced for all users | `019e6b65-a373-76cf-a3ee-03bc84031ede` | High     |
| Require Two Factor Authentication Disabled                         | `a52ccd9b-e4a0-47e5-90d9-1cbc62cac274` | Critical |
| Zoom: Account session lifetime exceeds policy                      | `019e6b65-aa52-7cfd-baa0-89de43a9a3d1` | Medium   |
| New Sign-In Required For Inactive Zoom Client Users Disabled       | `bc5acfa5-8d71-4cdf-aa4c-6834bed52969` | Low      |
| New Sign-In Required For Inactive Zoom Web Session Disabled        | `7406494b-1ebf-44fa-bc66-681b82943cbd` | Low      |

### Account password policy

| Finding type                                              | FindingTypeID                          | Severity |
| --------------------------------------------------------- | -------------------------------------- | -------- |
| Password Policy Enhanced Weak Password Detection Disabled | `d3d4fd13-e32f-4f5d-9e1f-e387473bbaff` | Critical |
| Zoom: Account password policy is weak                     | `019e6b65-a6e3-7059-ad1b-6c99068722d1` | Medium   |
| Password Policy Require Special Character Disabled        | `f452d1dd-7288-47ee-87c0-3b4dabef9871` | High     |
| Password Policy Require Minimum Password Length Disabled  | `23634933-ee61-4a0b-a58a-1beff30ca5cd` | High     |
| Password Policy Consecutive Characters Limit Disabled     | `69384e0f-1ec5-4f7f-a9bb-2d08c9f690aa` | Medium   |

### Account meeting settings

| Finding type                                                                 | FindingTypeID                          | Severity |
| ---------------------------------------------------------------------------- | -------------------------------------- | -------- |
| Zoom Account with End to End Encryption Disabled                             | `cb6ed5b0-c3a8-4f45-bee5-91e4d06ca8ac` | Critical |
| Zoom Account with End to End Encryption Setting Unlocked                     | `be5f4529-bd34-4805-ad04-17d9a87bd181` | Critical |
| Zoom Account with Enforce Login Setting Unlocked                             | `6ab84c16-de70-49cd-9f92-dd95f03d22d6` | High     |
| Zoom Account with Enforce Login for Scheduling Meetings Disabled             | `aa93f105-5ef6-43e3-93ef-d99f32a42ee6` | High     |
| Zoom Account with Waiting Room Disabled                                      | `62c8bd0f-7aa7-437f-b932-b3bf606ea432` | Medium   |
| Zoom Account with Waiting Room Setting Unlocked                              | `0c2e0cf8-2d8b-4d7f-9c1c-936728b3b95d` | Medium   |
| Zoom Account with Waiting Room for All Participants Not Set                  | `24dd0722-b624-46e3-a1ba-6e83bcd5f7f1` | Medium   |
| Zoom Account with Remote Computer Control Allowed                            | `915726df-9806-4a06-95e4-7c885d475f89` | Medium   |
| Zoom Account with Remote Computer Control Setting Unlocked                   | `15b0d52c-333e-4798-869f-5f9e2065d8a7` | Medium   |
| Zoom Account Allows Users to Control Other User Cameras                      | `73d49510-9a28-48ac-b37a-6efd0bcff37d` | Medium   |
| Zoom Account with Allow Users to Control Other User Cameras Setting Unlocked | `a57dca39-0f41-4467-936c-28e6b058be38` | Medium   |
| Zoom Account with File Transfer Setting Unlocked                             | `1f3ca22b-f722-4203-8409-b1b4ec8aee5f` | Medium   |
| Zoom Account with Alert on Guest Join Disabled                               | `2ed891a7-4f97-4ce7-98d1-92d479497cea` | Low      |
| Zoom Account with Alert on Guest Join Setting Unlocked                       | `5226c0b1-ac05-444b-ae19-d933a48452f2` | Low      |
| Zoom Account Allows Participants to Rename In-Meeting                        | `13b80a7f-07d4-4825-a04b-de1d0058b9c5` | Low      |
| Zoom Account with Embed Password in Join Link Setting Unlocked               | `dca25958-888d-45ff-b9a7-2d3ba43061f3` | Low      |

### Account meeting passwords

| Finding type                                                                | FindingTypeID                          | Severity |
| --------------------------------------------------------------------------- | -------------------------------------- | -------- |
| Zoom Account with Require Password for Scheduled Meetings Disabled          | `014891c9-5d9e-403a-9c79-dc5cea9d658e` | High     |
| Zoom Account with Require Password for Scheduling New Meetings Disabled     | `ec470c79-5e8a-4bb9-9e9f-41389ee2f9cd` | High     |
| Zoom Account with Require Password for Scheduling Meetings Setting Unlocked | `90897bc7-ef29-4781-b970-a2f170281e02` | High     |
| Zoom Account with Require Password for Instant Meetings Disabled            | `aca363ba-5675-44b8-8587-0894c6de242c` | High     |
| Zoom Account with Require Password for Instant Meetings Setting Unlocked    | `fd5951d0-fccb-446d-b91c-9defcf10e9d7` | High     |
| Zoom Account with Require Password for PMI Meetings Disabled                | `f51a46d4-ae75-4256-b06c-0d671c1693b3` | High     |
| Zoom Account with Require Password for PMI Meetings Setting Unlocked        | `12e38ee7-9ebc-454e-854d-093ebf65aa85` | High     |

### Account recording settings

| Finding type                                                                    | FindingTypeID                          | Severity |
| ------------------------------------------------------------------------------- | -------------------------------------- | -------- |
| Public Cloud Recording Downloads Setting Enabled                                | `9fb646a3-b62f-40d3-bc2b-ef3cb0ae9a26` | Critical |
| Require users to authenticate before viewing cloud recordings settings disabled | `b90f0acb-d34c-4d07-aca3-3cc6f979bb55` | Critical |
| Public Cloud Recording Downloads Setting Unlocked                               | `1c685a0e-7493-4602-af6e-d8b923e69d87` | High     |
| Cloud Recording Password Does Not Require Numbers                               | `221cb2ba-bd24-45a2-94ea-5cc1bb4c06e3` | High     |
| Cloud Recording Password Only Allows Numeric Characters                         | `6c33640c-db74-4f77-8607-f919d36ad53d` | High     |
| Cloud Recording Password Has a Minimum Length of Less than 9 Characters         | `a8ebbe07-7238-40a1-9048-184b4080b16d` | High     |
| Cloud Recording Password Does Not Require Special Characters                    | `421b47c4-301f-4c8e-8c64-17cb8d2a85ac` | High     |
| Cloud Recording Password Does Not Require Letters                               | `864dafc2-a4ce-4cce-ae3a-e1c7d5b9bbd5` | High     |
| Require Users to Authenticate Before Viewing Cloud Recordings Setting Unlocked  | `a5bb236b-2cbe-4029-87f2-0945b084271f` | Medium   |
| Host can delete cloud recordings unlocked                                       | `aa38d5af-59f6-4708-8da4-8c861c6ec571` | Low      |
| Recording IP Address Access Control Setting Disabled                            | `b08dec0a-c908-4053-9cdf-702354666061` | Low      |
| Recording IP Address Access Control Setting Unlocked                            | `cad4d24e-3ad6-4af2-acb4-22afc7f65851` | Low      |
| Prevent hosts from accessing their cloud recordings setting unlocked            | `da619ab4-6c0c-4266-bd1c-b76c53b7852b` | Low      |
| Auto delete cloud recordings after days setting unlocked                        | `0d5879d2-2f52-4a4c-9f42-afec0b27fa0a` | Low      |

### User accounts

| Finding type                                                | FindingTypeID                          | Severity |
| ----------------------------------------------------------- | -------------------------------------- | -------- |
| Zoom User not using SSO                                     | `f10eba44-b3be-44e1-a363-74c51e8ee18d` | High     |
| Zoom User with insecure host key                            | `09ee2fda-4cbf-4bea-af0c-7ea37aec8218` | High     |
| Zoom User email not verified                                | `7fec807a-8fd4-4390-bb44-b7446dc94617` | High     |
| Zoom: User has unsafe override of account security settings | `019e6b65-95b7-72a7-aecb-456591ad288b` | High     |
| Zoom: User is unverified                                    | `019e6b65-9c95-7de0-8763-0a8f982b87bf` | Medium   |
| Zoom: Non-admin role with privileged access                 | `019e6b65-8d1e-7834-85b5-af1154fef477` | Medium   |
| Zoom: User authenticates with a non-SSO login type          | `019e6b65-a004-7c34-98c4-103e9b6d1735` | Low      |
| Zoom: User has not logged in for 90+ days                   | `019e6b65-9926-7a70-8257-a2aadd1377a3` | Low      |
| Zoom User inactive for over 90 days                         | `7da16962-ddb1-4be3-b409-ea6ff32cdf91` | Low      |

### User meeting settings

| Finding type                                                     | FindingTypeID                          | Severity |
| ---------------------------------------------------------------- | -------------------------------------- | -------- |
| Zoom User with End to End Encryption Disabled                    | `a6037af0-6da8-4f33-89eb-b27f754f11f7` | Critical |
| Zoom User with Require Password for Instant Meetings Disabled    | `56e69129-5695-4b5d-87c8-61ec379b8cd9` | High     |
| Zoom User with Require Password for PMI Meetings Disabled        | `15b1e1a4-e4dd-4904-8cd4-ea79aedeb502` | High     |
| Zoom User with Require Password for Scheduling Meetings Disabled | `7d8e1cee-9d53-4d2e-9689-4f7b5d72998d` | High     |
| Zoom User Allows Other Users to Control Their Camera             | `d16f7e93-dbc9-4756-8e49-8cb34356f2a2` | Medium   |
| Zoom User with Waiting Room Disabled                             | `eb3297bc-ac58-406c-8a1b-b8fa39349d28` | Medium   |
| Zoom User with Waiting Room for All Participants Not Set         | `6ddce89a-ab01-4edc-b1cb-1d732072a57e` | Medium   |
| Zoom User with Remote Computer Control Allowed                   | `8f7e1aea-6ff0-4a4b-a48c-c147318d8f2c` | Medium   |
| Zoom User Allows Participants to Rename In-Meeting               | `3813256f-effe-4369-98f8-2d4df5494481` | Low      |

### Meeting security

| Finding type                                               | FindingTypeID                          | Severity |
| ---------------------------------------------------------- | -------------------------------------- | -------- |
| Zoom: Meeting without identity enforcement                 | `019e6b65-7f62-7b86-8d40-b1dadea58672` | High     |
| Zoom: Meeting without password                             | `019e6b65-7881-7ed2-a620-201bba497d4e` | High     |
| Zoom: Meeting recording not configured securely            | `019e6b65-8640-7be6-bfd8-2476350d056e` | High     |
| Zoom: Meeting has external domain host or alternative host | `019e6b65-89af-7718-9400-86d99195510e` | Medium   |
| Zoom: Meeting allows unsupervised participant access       | `019e6b65-7bf4-7050-8569-1eeed6db49e8` | Medium   |
| Zoom meeting waiting room disabled                         | `7e826eca-994d-43b5-a918-d8b72d1ff186` | Medium   |
| Zoom: Meeting uses Personal Meeting ID (PMI)               | `019e6b65-82d2-7025-91a8-ef9f460d63f1` | Low      |

### Recording security

| Finding type                                                                       | FindingTypeID                          | Severity |
| ---------------------------------------------------------------------------------- | -------------------------------------- | -------- |
| Zoom Cloud Recording Publicly Accessible                                           | `3018d9f5-3931-48a8-b107-39bd754f2a6f` | Critical |
| Zoom User with require passcode to access shared cloud recordings setting disabled | `c763f2f8-733b-42bf-88d9-afcc7c756eef` | Critical |
| Zoom Cloud Recording with no passcode                                              | `6c307678-2b50-433e-9cf2-a7d95498279a` | Critical |
| Zoom Cloud Recording with empty passcode                                           | `5922f3a2-d633-47c9-9fbe-f85027a95e1a` | Critical |
| Zoom Cloud Recording Passcode length is less than 8 characters                     | `87193ea3-6bc7-45ba-8e85-3c147b6ecb85` | Critical |
| Zoom Cloud Recording with numeric passcode                                         | `2bed56d4-2f25-4aab-bac1-1a30c6301f34` | Critical |
| Zoom Cloud Recording Passcode with no special characters                           | `7df598d5-01e5-42bb-9688-107a2ed378ad` | Critical |
| Require users to authenticate before viewing cloud recordings setting disabled     | `e1d3fe5c-44bb-417f-919a-c83f9d8ddc12` | Critical |
| Zoom: Shared recording without play passcode                                       | `019e6b65-af1a-7550-b82a-a154c7801ae2` | High     |

# Gemini for Google Workspace

import { Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Gemini for Google Workspace",
		integrationAccountType: "Google Workspace account",
	}}
/>

## Integration prerequisites

<Render file="casb/google/google-prereqs" product="cloudflare-one" />

## Integration permissions

<Render
	file="casb/integration-perms"
	product="cloudflare-one"
	params={{
		parentIntegration: "Google Workspace",
		parentSlug: "google-workspace",
	}}
/>

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{
		integrationName: "Gemini for Google Workspace",
		slugRelativePath: "gemini",
	}}
/>

### User account settings

| Finding type                                                                             | FindingTypeID                          | Severity | Description                                                                                                  |
| ---------------------------------------------------------------------------------------- | -------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| Google Workspace: Admin user with Gemini license with two-factor authentication disabled | `4d179f48-ab14-4f20-a45c-2bcf8228d95d` | High     | An administrator with a Gemini for Google Workspace license does not have two-factor authentication enabled. |
| Google Workspace: User with Gemini license with two-factor authentication disabled       | `1b33c200-c0cc-4c55-99fa-92c16a98976e` | Medium   | A user with a Gemini for Google Workspace license does not have two-factor authentication enabled.           |

### Inactive or suspended users

| Finding type                                                 | FindingTypeID                          | Severity | Description                                                                            |
| ------------------------------------------------------------ | -------------------------------------- | -------- | -------------------------------------------------------------------------------------- |
| Google Workspace: Admin user suspended with AI Ultra license | `0d19a636-e4ca-4ab1-b590-7e0ff19c7fef` | Low      | An administrator account with an AI Ultra (Gemini for Workspace) license is suspended. |
| Google Workspace: User suspended with AI Ultra license       | `e642db61-dc20-4742-89c4-b8041ed237cb` | Low      | A user account with an AI Ultra (Gemini for Workspace) license is suspended.           |

### Gemini licensing

<Render file="casb/google/gemini-licensing" product="cloudflare-one" />

# Gmail (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Gmail (FedRAMP)",
	}}
/>

<Render
	file="casb/google/integrations/gmail"
	product="cloudflare-one"
	params={{
		integrationName: "Gmail (FedRAMP)",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/gmail-fedramp",
	}}
/>

# Gmail

import { Render } from "~/components";

<Render
	file="casb/google/integrations/gmail"
	product="cloudflare-one"
	params={{
		integrationName: "Gmail",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/gmail",
	}}
/>

# Google Admin (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Google Admin (FedRAMP)",
	}}
/>

<Render
	file="casb/google/integrations/google-admin"
	product="cloudflare-one"
	params={{
		integrationName: "Google Admin (FedRAMP)",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/google-admin-fedramp",
	}}
/>

# Google Admin

import { Render } from "~/components";

<Render
	file="casb/google/integrations/google-admin"
	product="cloudflare-one"
	params={{
		integrationName: "Google Admin",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/google-admin",
	}}
/>

# Google Calendar (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Google Calendar (FedRAMP)",
	}}
/>

<Render
	file="casb/google/integrations/google-calendar"
	product="cloudflare-one"
	params={{
		integrationName: "Google Calendar (FedRAMP)",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/google-calendar-fedramp",
	}}
/>

# Google Calendar

import { Render } from "~/components";

<Render
	file="casb/google/integrations/google-calendar"
	product="cloudflare-one"
	params={{
		integrationName: "Google Calendar",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/google-calendar",
	}}
/>

# Google Drive (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Google Drive (FedRAMP)",
	}}
/>

<Render
	file="casb/google/integrations/google-drive"
	product="cloudflare-one"
	params={{
		integrationName: "Google Drive (FedRAMP)",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/google-drive-fedramp",
	}}
/>

# Google Drive

import { Render } from "~/components";

<Render
	file="casb/google/integrations/google-drive"
	product="cloudflare-one"
	params={{
		integrationName: "Google Drive",
		environmentName: "Google Workspace account",
		slugifiedName: "google-workspace/google-drive",
	}}
/>

# Google Workspace

import { DirectoryListing, Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Google Workspace",
		integrationAccountType: "Google Workspace account",
	}}
/>

This integration covers the following Google Workspace products:

<DirectoryListing />

## Integration prerequisites

<Render file="casb/google/google-prereqs" product="cloudflare-one" />

## Integration permissions

For the Google Workspace integration to function, Cloudflare CASB requires the following Google API permissions:

- `https://www.googleapis.com/auth/admin.directory.domain.readonly`
- `https://www.googleapis.com/auth/admin.directory.user.readonly`
- `https://www.googleapis.com/auth/admin.directory.user.security`
- `https://www.googleapis.com/auth/calendar`
- `https://www.googleapis.com/auth/cloud-platform.read-only`
- `https://www.googleapis.com/auth/drive.readonly`
- `https://www.googleapis.com/auth/gmail.settings.basic`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted. To learn more about each permission, refer to the [Google Workspace Admin SDK Directory API](https://developers.google.com/admin-sdk/directory/v1/guides/authorizing).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{
		integrationName: "Google Workspace",
		slugRelativePath: "google-workspace",
	}}
/>

### User account settings

<Render file="casb/google/user-account-settings" product="cloudflare-one" />

### Inactive or suspended users

<Render file="casb/google/inactive-suspended-users" product="cloudflare-one" />

### Gemini licensing

<Render file="casb/google/gemini-licensing" product="cloudflare-one" />

### File sharing

<Render file="casb/google/file-sharing" product="cloudflare-one" />

### Calendar sharing

<Render file="casb/google/calendar-sharing" product="cloudflare-one" />

### Data Loss Prevention (optional)

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

<Render file="casb/google/google-dlp" product="cloudflare-one" />

### Third-party apps

<Render file="casb/google/third-party-apps" product="cloudflare-one" />

### Gmail administrator settings

<Render file="casb/google/gmail-admin-settings" product="cloudflare-one" />

### Email forwarding

<Render file="casb/google/email-forwarding" product="cloudflare-one" />

# Admin Center (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Admin Center (FedRAMP)",
	}}
/>

<Render
	file="casb/microsoft/integrations/admin-center"
	product="cloudflare-one"
	params={{
		integrationName: "Admin Center (FedRAMP)",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/admin-center-fedramp",
	}}
/>

# Admin Center

import { Render } from "~/components";

<Render
	file="casb/microsoft/integrations/admin-center"
	product="cloudflare-one"
	params={{
		integrationName: "Admin Center",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/admin-center",
	}}
/>

# Microsoft 365

import { DirectoryListing, GlossaryTooltip, Render } from "~/components";

<Render
	file="casb/integration-description"
	product="cloudflare-one"
	params={{
		integrationName: "Microsoft 365 (M365)",
		integrationAccountType: "Microsoft 365 account",
	}}
/>

This integration covers the following Microsoft 365 products:

<DirectoryListing />

## Integration prerequisites

<Render file="casb/microsoft/m365-prereqs" product="cloudflare-one" />

## Integration permissions

For the Microsoft 365 integration to function, Cloudflare CASB requires the following delegated Microsoft Graph API permissions:

- `Application.Read.All`
- `Calendars.Read`
- `Domain.Read.All`
- `Group.Read.All`
- `InformationProtectionPolicy.Read.All`
- `MailboxSettings.Read`
- `offline_access`
- `RoleManagement.Read.All`
- `User.Read.All`
- `UserAuthenticationMethod.Read.All`
- `Files.Read.All`
- `AuditLog.Read.All`
- `AiEnterpriseInteraction.Read.All`

These permissions follow the principle of least privilege to ensure that only the minimum required access is granted.

Additionally, to [remediate findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#remediate-findings), CASB requires the following permissions:

- `Application.ReadWrite.All`
- `AuditLog.Read.All`
- `AiEnterpriseInteraction.Read.All`
- `Calendars.ReadWrite`
- `Domain.ReadWrite.All`
- `Files.ReadWrite.All`
- `Group.ReadWrite.All`
- `InformationProtectionPolicy.Read.All`
- `MailboxSettings.ReadWrite`
- `IdentityRiskyUser.ReadWrite.All`
- `RoleManagement.ReadWrite.Directory`
- `User.ReadWrite.All`
- `UserAuthenticationMethod.ReadWrite.All`
- `Directory.ReadWrite.All`
- `GroupMember.ReadWrite.All`
- `Organization.ReadWrite.All`
- `Mail.ReadWrite`

To learn more about each permission, refer to the [Microsoft Graph permissions documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).

## Security findings

<Render
	file="casb/security-findings"
	product="cloudflare-one"
	params={{
		integrationName: "Microsoft 365",
		slugRelativePath: "microsoft-365",
	}}
/>

### User account settings

<Render file="casb/microsoft/user-account-settings" product="cloudflare-one" />

### File sharing

<Render file="casb/microsoft/file-sharing" product="cloudflare-one" />

<Render file="casb/shared-links" product="cloudflare-one" />

### Data Loss Prevention (optional)

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

<Render file="casb/microsoft/m365-dlp-findings" product="cloudflare-one" />

### Microsoft 365 Copilot / AI

<Render file="casb/data-loss-prevention" product="cloudflare-one" />

<Render file="casb/microsoft/m365-copilot-findings" product="cloudflare-one" />

### Third-party apps

<Render file="casb/microsoft/third-party-apps" product="cloudflare-one" />

### Calendar sharing

<Render file="casb/microsoft/calendar-sharing" product="cloudflare-one" />

### Email administrator settings

<Render file="casb/microsoft/email-admin-settings" product="cloudflare-one" />

### Email forwarding

<Render file="casb/microsoft/email-forwarding" product="cloudflare-one" />

## Microsoft Information Protection (MIP) sensitivity labels

:::note
Requires [Cloudflare Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/).
:::

Microsoft provides [MIP sensitivity labels](https://learn.microsoft.com/en-us/microsoft-365/compliance/sensitivity-labels?view=o365-worldwide) to classify and protect sensitive data. When you add the CASB Microsoft 365 integration, Cloudflare will automatically retrieve the labels from your Microsoft account and populate them in a [DLP Profile](/cloudflare-one/data-loss-prevention/dlp-profiles/integration-profiles/).

<Render file="dlp/mip-nested-labels-limitation" product="cloudflare-one" />

# Microsoft 365 Copilot (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Microsoft 365 Copilot (FedRAMP)",
	}}
/>

<Render
	file="casb/microsoft/integrations/m365-copilot"
	product="cloudflare-one"
	params={{
		integrationName: "Microsoft 365 Copilot (FedRAMP)",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/copilot-fedramp",
	}}
/>

# Microsoft 365 Copilot

import { Render } from "~/components";

<Render
	file="casb/microsoft/integrations/m365-copilot"
	product="cloudflare-one"
	params={{
		integrationName: "Microsoft 365 Copilot",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/copilot",
	}}
/>

# OneDrive (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "OneDrive (FedRAMP)",
	}}
/>

<Render
	file="casb/microsoft/integrations/onedrive"
	product="cloudflare-one"
	params={{
		integrationName: "OneDrive (FedRAMP)",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/onedrive-fedramp",
	}}
/>

# OneDrive

import { Render } from "~/components";

<Render
	file="casb/microsoft/integrations/onedrive"
	product="cloudflare-one"
	params={{
		integrationName: "OneDrive",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/onedrive",
	}}
/>

# Outlook (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "Outlook (FedRAMP)",
	}}
/>

<Render
	file="casb/microsoft/integrations/outlook"
	product="cloudflare-one"
	params={{
		integrationName: "Outlook (FedRAMP)",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/outlook-fedramp",
	}}
/>

# Outlook

import { Render } from "~/components";

<Render
	file="casb/microsoft/integrations/outlook"
	product="cloudflare-one"
	params={{
		integrationName: "Outlook",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/outlook",
	}}
/>

# SharePoint (FedRAMP)

import { Render } from "~/components";

<Render
	file="casb/fedramp-availability"
	product="cloudflare-one"
	params={{
		integrationName: "SharePoint (FedRAMP)",
	}}
/>

<Render
	file="casb/microsoft/integrations/sharepoint"
	product="cloudflare-one"
	params={{
		integrationName: "SharePoint (FedRAMP)",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/sharepoint-fedramp",
	}}
/>

# SharePoint

import { Render } from "~/components";

<Render
	file="casb/microsoft/integrations/sharepoint"
	product="cloudflare-one"
	params={{
		integrationName: "SharePoint",
		environmentName: "Microsoft 365 account",
		slugifiedName: "microsoft-365/sharepoint",
	}}
/>

# CASB

Use this guide to troubleshoot common issues with Cloud Access Security Broker (CASB).

This guide covers troubleshooting steps for CASB integrations and webhooks. For integration-specific issues, refer to the integration's documentation.

## Integration fails to connect or returns an error

Integration connection problems are the most common issue during CASB setup. If you receive an error such as "There was an error creating the integration" or are redirected back to the dashboard without the integration appearing, follow these steps.

### Check permissions in the third-party application

Ensure the account you are using to authorize the integration has the necessary administrative privileges in the third-party application (for example, **Global Administrator** for Microsoft 365, **Super Admin** for Google Workspace, or **Organization Owner** for GitHub). Insufficient permissions are the leading cause of setup failures.

### Clear previous installations

If the SaaS application was previously integrated with a different Cloudflare account, you must manually revoke the old Cloudflare application from within the SaaS provider's admin console.

- **For Microsoft 365**: Go to **Microsoft 365 admin center** > **Enterprise applications** and delete the existing Cloudflare One application.
- **For Google Workspace**: Go to **Google Admin Console** > **Security** > **Access and data control** > **API controls** and remove the Cloudflare app from third-party app access.
- **For GitHub**: Go to your organization's **Settings** > **Third-party access** and revoke the Cloudflare CASB application.

After cleaning up the old app, wait a few minutes and then try the integration process again from the Cloudflare One dashboard.

### Verify OAuth permissions

During setup, CASB will ask you to approve a set of permissions. The permissions requested are required for the CASB service to scan for misconfigurations and, if you choose, to take remediation actions. While some permissions may seem broad (for example, `write` access), they are necessary for actions like quarantining a file or modifying sharing settings. Refer to the specific integration guide for a detailed list of required permissions.

## Findings are stale or not updating after remediation

A common point of confusion is when a resolved issue (for example, when a file is made private, or when a user is suspended) continues to appear as an active finding in the CASB dashboard.

### Understand scan frequency

CASB integrations do not provide real-time updates. Scans are performed periodically to discover new findings and validate the status of existing ones. The initial scan can take several hours, and subsequent scans run approximately every 24-48 hours.

### Force a re-scan

To trigger a new scan:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Cloud & SaaS findings** > **Integrations**.
2. Find your integration and select **Configure**.
3. Turn off **Scan for findings**.
4. After a few minutes, turn on **Scan for findings** again.

This action will queue a fresh scan of your integration. Allow several hours for your findings to reflect the new results.

## Remediation action fails in the dashboard

If you attempt to use a one-click remediation action (such as "Make private") on a finding, it may result in a **Failed** status, often with a timeout error.

### Verify permissions

The remediation failure may be due to the permissions for the Cloudflare app being changed or revoked in the SaaS application after the initial setup. Re-validate the integration to ensure all required permissions are still granted.

### Remediate manually

As a workaround, remediate the finding directly within the SaaS application (for example, change the file's sharing settings in Google Drive). CASB will clear the finding from the dashboard after the next successful scan.

## Webhook test or delivery fails

If Cloudflare cannot deliver a test request or a posture finding instance to your destination, follow these steps.

### Check destination requirements

Verify that the destination URL uses `https://` and is publicly reachable. Cloudflare rejects destinations that resolve to localhost, loopback, private, or other reserved addresses.

### Check authentication settings

Ensure that the webhook's authentication method matches what your receiver expects. Re-enter any bearer token, Basic auth credentials, static headers, or signing secret if needed.

### Understand delivery timing

Test delivery sends a test request immediately, but posture finding instance sends are queued in the background. A success message means that Cloudflare accepted the request for delivery.

## CASB is generating false positives

CASB may incorrectly flag items, such as flagging internally-shared files as public or archived Google Workspace users as inactive.

### Review finding details

Carefully examine the evidence provided in the finding. An object's status in the SaaS platform may not be accurate.

### Report the issue

If you confirm the finding is a false positive, report the behavior to Cloudflare Support. Provide the finding ID and as much detail as possible. This helps the Support team refine the detection logic for all customers.

### Hide the finding

While Cloudflare investigates the issue, you can [ignore the finding or hide individual instances](/cloudflare-one/cloud-and-saas-findings/manage-findings/#hide-findings) to remove it from your active list and reduce noise.

# Troubleshooting

import { DirectoryListing } from "~/components";

Explore guides to resolve issues with Cloudflare Zero Trust integrations, including CASB, webhooks, and third-party SaaS applications.

<DirectoryListing />

# Troubleshoot compute accounts

import { Render } from "~/components";

Cloudflare CASB detects when compute accounts are unhealthy or outdated. Common compute account issues include security or functionality updates and API token misconfigurations.

## Identify unhealthy compute accounts

To identify unhealthy compute accounts:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations** > **Cloud & SaaS integrations**.
2. Choose the integration you created for cloud scanning.
3. Select **Manage compute accounts**.

CASB will display the status of each compute account next to its name. If a compute account is broken or outdated, CASB will set its status to **Unhealthy**. If the status is **Healthy**, no action is required.

## Repair an unhealthy compute account

When CASB marks a compute account as **Unhealthy**, CASB will not use new scan configuration changes and new scan results will not appear in the dashboard.

To repair a compute account marked as **Unhealthy**, first [upgrade the compute account](#upgrade-a-compute-account). If the compute account is still unhealthy, [roll your API token](#roll-api-tokens).

## Upgrade a compute account

Upgrading a compute account applies the latest software features, bug fixes, and infrastructure changes to a cloud compute account. You should run upgrades periodically to keep the compute account software up to date or when recommended by Cloudflare to address an issue. CASB deploys compute account upgrades through Terraform updates.

To upgrade a compute account:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations** > **Cloud & SaaS integrations**.
2. Choose the integration you created for cloud scanning.
3. Select **Open connection instructions**.
4. Follow the instructions provided to validate your local Terraform and CLI configuration.
5. Under **Step 2: Deploy Terraform Configuration**, copy the template to your local configuration. This template will be the most up to date version of the integration's Terraform configuration.
6. In a local terminal, update the cached version of the CDS Terraform modules:
   ```bash
   terraform init --upgrade
   ```
7. Apply the upgraded Terraform configuration to your compute account:
   ```bash
   terraform apply
   ```

## Roll API tokens

:::caution
If you roll your API token in CASB but do not update it in your compute account, CASB will set your compute account's status as **Broken** and stop reporting scan results.
:::

You may need to roll the Cloudflare API token used for your compute account if a security or operational issue appears, your API token is compromised, or your API token is removed from your compute account.

<Render file="api-roll-token" product="fundamentals" />

4. Copy your API token.

Once you roll your API token in Cloudflare, you can update the API token value in your secrets manager for [Amazon Web Services (AWS)](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_update-secret-value.html) or [Google Cloud Platform (GCP)](https://cloud.google.com/secret-manager/docs/edit-secrets).

### Common token issues

#### `cloudflare-cds-secrets` does not exist in the compute account's secrets manager

To recreate the secret in your compute account:

1. Validate that you selected the correct region.
2. [Upgrade the compute account](#upgrade-a-compute-account) to recreate the secret.
3. [Update the secret value](#roll-api-tokens) in your compute account.

#### I no longer have access to the Cloudflare API token I created

[Roll your Cloudflare API token](#roll-api-tokens) and add it to your compute account. If the [status of the compute account](#identify-unhealthy-compute-accounts) is set to **Healthy**, the issue has been solved.

# Troubleshoot integrations

Cloudflare CASB detects when integrations are unhealthy or outdated.

Common integration issues include changes to SaaS app or cloud environment configurations, user access, or permission scope. Integrations may need to be updated to support new features or permissions.

## Identify unhealthy or outdated integrations

To identify unhealthy CASB integrations, go to **Integrations** > **Cloud & SaaS integrations**. If an integration is unhealthy, CASB will set its status to **Broken**. If an integration is outdated, CASB will set its status to **Upgrade**.

## Repair an unhealthy integration

:::note[Repair limitation]
If CASB does not support self-service repairs for an integration, you will need to [delete](/cloudflare-one/integrations/cloud-and-saas/#delete-an-integration) and recreate the integration to continue scanning.
:::

You can repair unhealthy CASB integrations through your list of integrations or findings.

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations** > **Cloud & SaaS integrations**.
2. Choose your unhealthy integration.
3. Select **Reauthorize**.
4. In your SaaS app or cloud environment, reauthorize your account.

## Upgrade an integration

Upgrading an outdated integration will allow the integration to access new features and permissions.

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Integrations** > **Cloud & SaaS integrations**.
2. Choose your outdated integration.
3. Select **Upgrade integration**.
4. In your SaaS app or cloud environment, upgrade your app and reauthorize your account.

# Active Directory (SAML)

import { GlossaryTooltip, Render } from "~/components";

:::caution
Microsoft recommends migrating your Active Directory Federation Service (AD FS) SSO to Microsoft Entra ID. For more information, refer to [Microsoft Learn](https://learn.microsoft.com/windows-server/identity/ad-fs/ad-fs-overview).

To set up the Microsoft Entra ID IdP integration with Cloudflare One, refer to [Microsoft Entra ID](/cloudflare-one/integrations/identity-providers/entra-id/).
:::

Active Directory is a directory service developed by Microsoft for Windows domain networks. It is included in most Windows Server operating systems as a set of processes and services. Active Directory integrates with Cloudflare Access using Security Assertion Markup Language (<GlossaryTooltip term="SAML">SAML</GlossaryTooltip>).

## Before you start

To get started, you need:

- An Active Directory Domain Controller where all users have an email attribute.
- Generic SAML enabled for your Access Identity Provider (IdP).
- A Microsoft server running with Active Directory Federation Services (AD FS) installed. All screenshots in these instructions are for Server 2012R2. Similar steps will work for newer versions.
- A browser safe certificate for Active Directory Federation Services (AD FS).

Once you fulfill the requirements above, you are ready to begin. Installation and basic configuration of Active Directory Federation Services (AD FS) is outside the scope of this guide. A detailed guide can be found in a [Microsoft KB](<https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/deployment-administrators-guide/gg188612(v=crm.8)>).

Then to begin the connection between Cloudflare Access and AD FS create a Relying Party Trust in AD FS.

## Create a Relying Party Trust

Run the Add Relying Party Trust wizard to begin SAML AD integration with Cloudflare Access.

To create a Relying Party Trust:

1. In **Windows Server**, launch the **ADFS Management** tool.

2. Select the **Relying Party Trusts** folder.

3. On the **Actions** sidebar, select **Add Relying Party Trust**. The **Add Relying Party Trust Wizard** launches.

4. In the left menu, choose **Select Data Source**.

5. Select the **Enter data about the relying party manually** option.

6. Select **Next**.

7. Enter a **Display name**. We suggest you use a recognizable name. Include any information regarding this connection in the **Notes** field.

8. Select **Next**. The **Choose Profile** step displays.

9. Select the **AD FS profile** option.

10. Select **Next**. The **Configure Certificate** step displays.

11. Leave the **Certificate** options at their defaults.

12. Select **Next**. The **Configure URL** step displays.

13. Select the **Enable support for the SAML 2.0 WebSSO protocol** option.

14. In the **Relying party SAML 2.0 SSO service URL** field, enter the following URL:

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

    <Render file="find-team-name" product="cloudflare-one" />

15. Select **Next**. The **Configure Identifiers** step displays.

    ![Add relying party trust wizard with callback URL pasted into open form field](~/assets/images/cloudflare-one/identity/adfs/adfs-7.png)

16. Paste your callback URL in the **Relying party trust identifier** field.

17. Select **Next**. In the **Configure Multi-factor Authentication Now?** step, you can configure multi-factor authentication.

18. Select **Next**. The **Choose Issuance Authorization Rules** step displays.

19. Select the **Permit all users to access this relying party** option.

20. Select **Next**. The **Ready to Add Trust** step displays.

21. Review your settings.

22. Select **Next**. Cloudflare now relies on AD FS for user-identity authorization.

The **Edit Claim Rules for CF Login** screen automatically displays.

## Create claim rules

Now create 2 Claim Rules so that AD FS can take information from Cloudflare and return it to create [Access policies](/cloudflare-one/access-controls/policies/).

If you closed the Add Relying Trust wizard, use Explorer to find the **Relying Party Trusts** folder, select the newly created RPT file, and select **Edit Claim Rules** in the **Action** sidebar.

To create Claim Rules:

1. In the **Edit Claim Rules for CF Login** window, select **Add Rule**. The **Choose Rule Type** step displays.

2. In the **Claim rule template** field, select **Send LDAP Attributes as Claims** from the drop-down list.

3. Select **Next**. The **Edit Rule — Send Email** step displays.

4. Enter a descriptive **Claim rule name**.

5. Select **Active Directory** from the **Attribute store** drop-down list.

6. Select **E-mail-Addresses** from the **LDAP Attribute** and **Outgoing Claim Type** drop-down lists.

:::note[AD FS groups]

If you wish to use AD FS groups in your SAML claims, use `token-groups - unqualified names` instead of `is-member-of-DL`. Using `is-member-of-DL` will display the group in the form of LDAP paths, whereas `token-groups - unqualified names` will return only the group name.
:::

7. Select **OK**. You return to the **Choose Rule Type** step.

8. Select **Transform an Incoming Claim** from the **Claim rule template** drop-down list to create the second rule.

9. Select **Next**. The **Edit - Create Transient Name Identifier** window displays.

10. Enter a descriptive **Claim rule name**.

11. Select **E-Mail Address** from the **Incoming claim type** drop-down list.

12. Select **Name ID** from the **Outgoing claim type** drop-down list.

13. Select **Transient Identifier** from the **Outgoing name ID format** drop-down list.

14. Ensure that the **Pass through all claim values** option is selected.

15. Select **OK**.

Both Claim Rules are now available to export to your Cloudflare Access account.

## Export the certificate

Now you'll configure Cloudflare to recognize AD FS by extracting the _token-signing certificate_ from AD FS.

To export the certificate:

1. Within the AD FS management console, select the **Service** under AD FS and choose the **Certificates** folder which contains the certificate to export.

2. In the **Certificates** card, right-click on the entry under **Token-signing**, and select **View certificate**. The **Certificates** window displays.

   ![Certificates window with token-signing certificate selected](~/assets/images/cloudflare-one/identity/adfs/adfs-16.png)

3. Select the **Details** tab, and select the **Copy to File** option.

4. The **Certificate Export Wizard** displays.

5. Select **Next**. The **Export File Format** window displays.

6. Select the **Base-64 encoded X.509 (.CER)** option.

7. Select **Next**.

8. Enter a name for the file.

9. Select **Next**.

10. Select **Finish**.

    Note the file path for later.

## Configure AD FS to sign SAML responses

To ensure that AD FS signs the full response when communicating with Cloudflare, open your local **PowerShell** and enter the following command:

```bash
Set-ADFSRelyingPartyTrust -TargetName "Name of RPT Display Name" -SamlResponseSignature "MessageAndAssertion"
```

## Configure Cloudflare One

To enable Cloudflare One to accept the claims and assertions sent from AD FS, follow these steps:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **SAML**.

4. Enter an IdP **Name**.

5. Under **Single Sign On URL** enter:

   ```txt
   https://hostnameOfADFS/adfs/ls/
   ```

   This is the default location. You can find your federation service identifier in AD FS.

6. In the **IdP Entity ID or Issuer URL** field, enter your Cloudflare Zero Trust team domain and include this callback at the end of the path: `/cdn-cgi/access/callback`. For example:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

7. Under **Signing certificate**, paste the exported certificate.

   There can be no spaces or return characters in the text field.

8. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to the identity provider you want to test.

## Download SP metadata (optional)

Some IdPs allow administrators to upload metadata files from their SP (service provider).

To get your Cloudflare metadata file:

1. Download your unique SAML metadata file at the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/saml-metadata
   ```

   In Cloudflare Access, you can find a link to this URL in the **Edit a SAML identity provider** dialog. The link returns a web page with your SAML SP data in XML format.

2. Save the file in XML format.

3. Upload the XML document to your **Active Directory** account.

## Example API Configuration

```json
{
	"config": {
		"issuer_url": "https://<your-team-name>.cloudflareaccess.com/",
		"sso_target_url": "https://adfs.example.com/adfs/ls/",
		"attributes": ["email"],
		"email_attribute_name": "",
		"sign_request": false,
		"idp_public_cert": "MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
	},
	"type": "saml",
	"name": "adfs saml example"
}
```

# authentik (OIDC)

import { DashButton, Render, Steps } from "~/components";

Connect authentik to Cloudflare Access with OpenID Connect (OIDC). OIDC is the recommended protocol when your identity provider supports both OIDC and SAML.

## Prerequisites

- An authentik account with administrator access
- A Cloudflare account with Zero Trust access
- A publicly accessible authentik deployment with a trusted TLS certificate

## 1. Create an application in authentik

Your Cloudflare Access team name is part of the redirect URI. Locate your team name before you configure authentik.

<Render file="find-team-name" product="cloudflare-one" />

<Steps>
1. Log in to authentik as an administrator and open the Admin interface.
2. Go to **Applications** > **Applications** and select **New Application**.
3. Enter the application details and select **Next**.
4. For the provider type, select **OAuth2/OpenID Connect**.
5. Configure the provider:
   1. Enter a provider name.
   2. Select an authorization flow.
   3. Add a redirect URI with the _Strict_ matching mode and _Authorization_ type:

      ```txt
      https://<YOUR_TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/callback
      ```

   4. Select a signing key. The signing key allows Access to verify tokens through the authentik JSON Web Key Set (JWKS) endpoint.
   5. Ensure that the provider includes the `openid`, `email`, and `profile` scope mappings.
6. Record the **Client ID**, **Client Secret**, and application slug.
7. Select **Submit**.
</Steps>

For more information about authentik provider settings, refer to [OAuth 2.0 provider](https://docs.goauthentik.io/add-secure-apps/providers/oauth2/).

## 2. Add authentik to Cloudflare Access

<Steps>
1. In Cloudflare Zero Trust, go to **Integrations** > **Identity providers**.

   <DashButton url="/?to=/:account/one/integrations/identity-providers" zeroTrust />

2. Select **Add new identity provider** > **OpenID Connect**.
3. Enter the following values. Replace `<AUTHENTIK_DOMAIN>` with your authentik hostname and `<APPLICATION_SLUG>` with the application slug from authentik.

   | Field | Value |
   | --- | --- |
   | Name | `authentik` |
   | App ID | The authentik **Client ID** |
   | Client secret | The authentik **Client Secret** |
   | Auth URL | `https://<AUTHENTIK_DOMAIN>/application/o/authorize/` |
   | Token URL | `https://<AUTHENTIK_DOMAIN>/application/o/token/` |
   | Certificate URL | `https://<AUTHENTIK_DOMAIN>/application/o/<APPLICATION_SLUG>/jwks/` |

4. Select **Save**.
</Steps>

The token and certificate endpoints must allow requests from Cloudflare. Interactive challenges or firewall rules on these endpoints can prevent Access from completing authentication.

## 3. Test the integration

<Steps>
1. In Cloudflare Zero Trust, go to **Integrations** > **Identity providers**.

   <DashButton url="/?to=/:account/one/integrations/identity-providers" zeroTrust />

2. Find the authentik identity provider and select **Test**.
3. Complete the authentik login flow.
4. Verify that Cloudflare displays a successful test result and the expected email address.
</Steps>

# authentik (SAML)

import { DashButton, Render, Steps } from "~/components";

Connect authentik to Cloudflare Access with Security Assertion Markup Language (SAML) 2.0.

## Prerequisites

- An authentik account with administrator access
- A Cloudflare account with Zero Trust access
- A publicly accessible authentik deployment with a trusted TLS certificate

## 1. Create an application in authentik

Your Cloudflare Access team name is part of the Assertion Consumer Service (ACS) URL. Locate your team name before you configure authentik.

<Render file="find-team-name" product="cloudflare-one" />

<Steps>
1. Log in to authentik as an administrator and open the Admin interface.
2. Go to **Applications** > **Applications** and select **New Application**.
3. Enter the application details and select **Next**.
4. For the provider type, select **SAML Provider**.
5. Configure the provider:
   1. Enter a provider name and select an authorization flow.
   2. For **ACS URL**, enter:

      ```txt
      https://<YOUR_TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/callback
      ```

   3. For **Audience**, enter the same Cloudflare Access callback URL.
   4. Select a **Signing Certificate** and keep **Sign assertions** turned on.
   5. For **Service Provider Binding**, select _Post_.
   6. For **Default NameID Policy**, select _Email address_.
6. Select **Create Application**.
</Steps>

For more information about authentik provider settings, refer to [Create a SAML provider](https://docs.goauthentik.io/add-secure-apps/providers/saml/create-saml-provider/).

## 2. Get the authentik provider details

<Steps>
1. In the authentik Admin interface, go to **Applications** > **Providers**.
2. Select the SAML provider that you created for Cloudflare Access.
3. Record the following values. Replace `<AUTHENTIK_DOMAIN>` with your authentik hostname and `<APPLICATION_SLUG>` with the associated application slug.

   | Cloudflare field | authentik value |
   | --- | --- |
   | Single Sign-On URL | `https://<AUTHENTIK_DOMAIN>/application/saml/<APPLICATION_SLUG>/` |
   | IdP Entity ID or Issuer URL | `https://<AUTHENTIK_DOMAIN>/application/saml/<APPLICATION_SLUG>/metadata/` |

4. Under **Download signing certificate**, select **Download**. Save the certificate for the Cloudflare configuration.
</Steps>

The metadata tab also contains the SAML endpoint, entity ID, and signing certificate. For more information, refer to [SAML Provider](https://docs.goauthentik.io/add-secure-apps/providers/saml/).

## 3. Add authentik to Cloudflare Access

<Steps>
1. In Cloudflare Zero Trust, go to **Integrations** > **Identity providers**.

   <DashButton url="/?to=/:account/one/integrations/identity-providers" zeroTrust />

2. Select **Add new identity provider** > **SAML**.
3. Enter a name for the identity provider.
4. Enter the **Single Sign-On URL** and **IdP Entity ID or Issuer URL** from authentik.
5. For **Signing certificate**, paste the certificate downloaded from authentik.
6. Select **Save**.
</Steps>

:::caution
Access cannot verify SAML responses after the authentik signing certificate expires or changes. Update the certificate in Cloudflare Access before you rotate it in authentik.
:::

## 4. Test the integration

<Steps>
1. In Cloudflare Zero Trust, go to **Integrations** > **Identity providers**.

   <DashButton url="/?to=/:account/one/integrations/identity-providers" zeroTrust />

2. Find the authentik identity provider and select **Test**.
3. Complete the authentik login flow.
4. Verify that Cloudflare displays a successful test result and the expected email address.
</Steps>

# AWS IAM (SAML)

import { Render } from "~/components";

AWS IAM Identity Center provides SSO identity management for users who interact with AWS resources (such as EC2 instances or S3 buckets). You can integrate AWS IAM with Cloudflare Zero Trust as a SAML identity provider, which allows users to authenticate to Zero Trust using their AWS credentials.

## Prerequisites

- Admin access to an IAM Identity Center [organization instance](https://docs.aws.amazon.com/singlesignon/latest/userguide/identity-center-instances.html)

## Set up AWS IAM as a SAML provider

To set up SAML with AWS IAM as your identity provider:

1. Open your [IAM Identity Center console](https://console.aws.amazon.com/singlesignon) and go to **Applications**.

2. Select the **Customer managed** tab.

3. Select **Add application**.

4. Select **I have an application I want to set up**.

5. For **Application type**, select **SAML 2.0**.

6. Select **Next**.

7. Enter a **Display name** for the application (for example, `Cloudflare One`).

8. Download the **IAM Identity Center SAML metadata file**. You will need this file later when configuring the identity provider in Cloudflare One.

9. Under **Application metadata**, select **Manually type your metadata values**.

10. In **Application ACS URL** and **Application SAML audience**, enter the following URL:

```txt
https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
```

<Render file="find-team-name" product="cloudflare-one" />

11. Select **Submit**.

12. Next, select the **Actions** dropdown menu and select _Edit attribute mappings_.

13. For the `Subject` user attribute, enter `${user:email}`.

14. (Recommended) Add user name attributes:

| User attribute | String value         |
| -------------- | -------------------- |
| `name`         | `${user:name}`       |
| `surName`      | `${user:familyName}` |

| `givenName`    | `${user:givenName}`  |

![Configuring attribute statements in IAM Identity Center](~/assets/images/cloudflare-one/identity/aws/aws-saml-attributes.png)

15. Select **Save changes**.

16. Under **Assign users and groups**, add individuals and/or groups that should be allowed to login to Cloudflare One.

17. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

18. Under **Your identity providers**, select **Add new identity provider**.

19. Select **SAML**.

20. Enter a **Name** for the IdP integration (for example, `AWS`).

21. Upload the **IAM Identity Center SAML metadata file** that you downloaded in Step 8.

22. (Recommended) Enable [**Sign SAML authentication request**](/cloudflare-one/integrations/identity-providers/generic-saml/#sign-saml-authentication-request).

23. Select **Save**.

To [test](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) that your connection is working, select **Test**.

## Example API configuration

```json
{
	"config": {
		"issuer_url": "https://portal.sso.eu-central-1.amazonaws.com/saml/assertion/b2yJrC4kjy3ZAS0a2SeDJj74ebEAxozPfiURId0aQsal3",
		"sso_target_url": "https://portal.sso.eu-central-1.amazonaws.com/saml/assertion/b2yJrC4kjy3ZAS0a2SeDJj74ebEAxozPfiURId0aQsal3",
		"attributes": ["email"],
		"email_attribute_name": "email",
		"sign_request": true,
		"idp_public_certs": [
			"MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
		]
	},
	"type": "saml",
	"name": "AWS IAM SAML example"
}
```

# Amazon Cognito

import { Render } from "~/components";

Amazon Cognito provides SSO identity management for end users of web and mobile apps. You can integrate Amazon Cognito as an OIDC identity provider for Cloudflare One.

## Prerequisites

- An Amazon Cognito [user pool](https://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-create-user-pool.html)

## Set up Amazon Cognito (OIDC)

### 1. Obtain Amazon Cognito settings

The following Amazon Cognito values are required to set up the integration:

- App (client) ID
- Client secret
- Auth URL
- Token URL
- Certificate (key) URL

To retrieve those values:

1. Log in to your Amazon Cognito admin portal.

2. Go to **User pools** and select your user pool.

3. Select the **App integration** tab.

4. Under **Domain**, copy your user pool domain or [configure a new domain](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain.html).

5. Make note of the following [Amazon Cognito OIDC endpoints](https://docs.aws.amazon.com/cognito/latest/developerguide/federation-endpoints.html):
   - **Auth URL**: `https://<your user pool domain>/oauth2/authorize`
   - **Token URL**: `https://<your user pool domain>/oauth2/token`
   - **Certificate (key) URL**: `https://cognito-idp.<region>.amazonaws.com/<your user pool ID>/.well-known/jwks.json` (This is the **Token signing key URL** shown in **User pool overview**.)

6. Under **App client list**, select **Create app client**.

7. For **App type**, select **Confidential client**.

8. Enter an **App client name** for your application.

9. Ensure that **Generate a client secret** is selected.

10. Configure the following **Hosted UI settings**:
    1. In **Allowed callback URLs**, add the following URL:

       ```txt
       https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
       ```

       <Render file="find-team-name" product="cloudflare-one" />

    2. Select **Identity providers** to use with this app client. At minimum, enable **Cognito user pool** as a provider.

    3. For **OAuth 2.0 grant types**, select **Authorization code grant**.

    4. For **OpenID Connect scopes**, select **OpenID**, **Email**, and **Profile**.

11. Select **Create app client**.

12. Next, select the app client you just created.

13. Copy its **Client ID** and **Client secret**.

### 2. Add Amazon Cognito as an identity provider

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **OpenID Connect**.

4. Name your identity provider and fill in the required fields with the information obtained from Amazon Cognito.

5. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.

6. (Optional) Under **Optional configurations**, enter [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that you wish to add to users' identity.

7. Select **Save**.

To [test](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) that your connection is working, select **Test**.

## Example API Configuration

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>",
		"auth_url": "https://<your user pool domain>/oauth2/authorize",
		"token_url": "https://<your user pool domain>/oauth2/token",
		"certs_url": "https://cognito-idp.<region>.amazonaws.com/<your user pool ID>/.well-known/jwks.json",
		"scopes": ["openid", "email", "profile"],
		"claims": ["sub", "cognito:username", "name", "cognito:groups"]
	},
	"type": "oidc",
	"name": "Amazon Cognito example"
}
```

# Centrify (SAML)

import { Render } from "~/components";

Centrify secures access to infrastructure, DevOps, cloud, and other modern enterprise so you can prevent the number one cause of breaches: privileged access abuse.

## Set up Centrify as a SAML provider

## 1. Create an application in Centrify

1. Log in to your **Centrify** admin portal and select **Apps**.

2. Select **Add Web Apps**.

3. Select the **Custom** tab.

4. Next to the **SAML** icon, select **Add**.

   ![Centrify Settings Add Application details page with template text](~/assets/images/cloudflare-one/identity/saml-centrify/saml-centrify-3.png)

5. Enter the required information for your application.

6. Select **Save**.

7. Select **Settings** in the left pane.

8. In the middle menu pane, select **Trust**.

9. Choose the **Manual Configuration** option.

10. In the **SP Entity ID** and **Assertion Consumer Service (ACS) URL fields**, enter the following URL:

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

    <Render file="find-team-name" product="cloudflare-one" />

11. Select **Save**.

12. In the middle menu pane, select **User Access**.

13. Select **Add**. The **Select Role** dialog displays.

14. Complete your roles access assignments. The Role rules display on the **User Access** card.

15. In the **User Access** card's middle menu pane, select **SAML Response**.

16. Select **Active** > **Add** to create a new **Attribute Name**, **Email**.

    ![Centrify SAML Response card with Settings Email Attribute selected](~/assets/images/cloudflare-one/identity/saml-centrify/saml-centrify-9.png)

17. Enter the user email addresses in the **Attribute Value** field.

18. Select **Save**.

19. Select **Settings** again from the left menu pane, and **Trust**.

20. Select the **Manual Configuration** option.

### 2. Add Centrify to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **SAML**.

4. Copy and paste the corresponding information from Centrify into the fields.

5. (Optional) To enable SCIM, refer to [Synchronize users and groups](/cloudflare-one/integrations/identity-providers/generic-saml/#synchronize-users-and-groups).

6. (Optional) Under **Optional configurations**, configure [additional SAML options](/cloudflare-one/integrations/identity-providers/generic-saml/#optional-configurations).

7. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to the identity provider you want to test.

## Download SP metadata (optional)

Some IdPs allow administrators to upload metadata files from their SP (service provider).

To get your Cloudflare metadata file:

1. Download your unique SAML metadata file at the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/saml-metadata
   ```

2. Save the file in XML format.

3. Upload the XML document to your **Centrify** account.

## Example API configuration

```json
{
	"config": {
		"issuer_url": "https://abc123.my.centrify.com/baaa2117-0ec0-4d76-84cc-abccb551a123",
		"sso_target_url": "https://abc123.my.centrify.com/applogin/appKey/baaa2117-0ec0-4d76-84cc-abccb551a123/customerId/abc123",
		"attributes": ["email"],
		"email_attribute_name": "",
		"sign_request": false,
		"idp_public_cert": "MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
	},
	"type": "saml",
	"name": "centrify saml example"
}
```

# Centrify

import { Render } from "~/components";

Centrify secures access to infrastructure, DevOps, cloud, and other modern enterprise so you can prevent the number one cause of breaches: privileged access abuse.

## Set up Centrify as an OIDC provider

### 1. Create an application in Centrify

1. Log in to the Centrify administrator panel.

2. Select **Apps**.

3. Select **Add Web Apps**.

4. Select the **Custom** tab, then select **Add OpenID Connect**.

5. On the **Add Web App** screen, select **Yes** to create an OpenID Connect application.

6. Enter an **Application ID**.

   ![Centrify Settings with Application ID added](~/assets/images/cloudflare-one/identity/centrify/centrify-4.png)

7. Select **Save**.

8. Select **Trust** in the **Settings** menu.

9. Enter a strong application secret on the **Trust** section.

10. Under **Service Provider Configuration** enter your application's authentication domain as the resource application URL.

11. Under **Authorized Redirect URIs**, select **Add**.

12. Under **Authorized Redirect URIs**, enter the following URL:

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

    <Render file="find-team-name" product="cloudflare-one" />

    ![Centrify Trust Identity Provider Configuration with team domain and callback](~/assets/images/cloudflare-one/identity/centrify/centrify-6.png)

13. Select **Save**.

14. Copy the following values:

- **Client ID**
- **Client Secret**
- **OpenID Connect Issuer URL**
- **Application ID** from the **Settings** tab

15. Go to the **User Access** tab.

16. Select the roles to grant access to your application.

### 2. Add Centrify to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Paste in the **Client ID**, **Client Secret**, **Centrify account URL** and **Application ID**.

4. (Optional) To enable SCIM, refer to [Synchronize users and groups](/cloudflare-one/integrations/identity-providers/generic-oidc/#synchronize-users-and-groups).

5. (Optional) Under **Optional configurations**, enter [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that you wish to add to your users' identity.
6. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to the identity provider you want to test.

## Example API Config

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>",
		"centrify_account": "https://abc123.my.centrify.com/",
		"centrify_app_id": "exampleapp"
	},
	"type": "centrify",
	"name": "my example idp"
}
```

# Citrix ADC (SAML)

Cloudflare One can integrate with Citrix ADC (formerly Citrix NetScaler ADC) as a SAML IdP. Documentation from Citrix shows you [how to configure Citrix ADC as a SAML IdP](https://docs.citrix.com/en-us/citrix-adc/12-1/aaa-tm/saml-authentication/citrix-adc-saml-idp.html). These steps are specific to Cloudflare One.

## Set up Citrix ADC (SAML)

To set up Citrix ADC (SAML) as your identity provider:

1. First, you'll need to configure 2 SAML certificates:

   - A certificate to **terminate TLS at the vServer**. Ensure that the certificate is issued by a publicly trusted CA.
   - A certificate for **signing SAML assertions**.

   If you do not already have a certificate for signing SAML assertions, you can use a self-signed certificate generated on Citrix ADC by following these steps:

   1. Go to **Traffic Management** > **SSL**.
   2. Select **Create and Install a Server Test Certificate**.

2. Select **Configuration** and enter a **Certificate File Name**, **Fully Qualified Domain Name**, and a select a **Country**.

   ![Citrix AD Create and Install Test Certificate interface with file name, domain name, and country](~/assets/images/cloudflare-one/identity/citrixadc/citrixadc-saml-2.png)

3. Create a publicly accessible authentication vServer and configure the user identity source (like, local users, LDAP) by following this [Citrix documentation](https://docs.citrix.com/en-us/citrix-adc/12-1/aaa-tm/authentication-virtual-server/ns-aaa-setup-auth-vserver-tsk.html).

   For the rest of this example, the user refers to the IdP address `idp.yourdomain.com`.

## Add a new profile

1. Go to **Security** > **AAA - Application Traffic** > **Policies** > **Authentication** > **Advanced Policies** > **SAML IDP** to add a new profile.

   Include the following required configuration details:

   | Field                              | Description                                                                            |
   | ---------------------------------- | -------------------------------------------------------------------------------------- |
   | **Name**                           | The certificate name you defined while [configuring SAML](#set-up-citrix-adc-saml)     |
   | **Assertion Consumer Service URL** | `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback`                |
   | **IdP Certificate Name**           | The IdP certificate name you defined while [configuring SAML](#set-up-citrix-adc-saml) |
   | **Issuer Name**                    | `https://idp.<yourdomain>.com/saml/login`                                              |
   | **Service Provider ID**            | `https://idp.<yourdomain>.com/saml/login`                                              |
   | **Name ID Format**                 | EmailAddress                                                                           |
   | **Attribute 1**                    | `email = AAA.USER.ATTRIBUTE("email")`                                                  |

   Cloudflare Access currently sends the IdP address in place of the _Service Provider ID_ for the AuthN request.

2. Create an Authentication Policy that refers to the Profile just created, and bind it to the authentication vServer mentioned above.

   ![Citrix AD Configure Authentication SAML IDP Policy](~/assets/images/cloudflare-one/identity/citrixadc/citrixadc-saml-4.png)

   To configure all of the above using just the CLI, run the following:

   ```json
   add authentication samlIdPProfile samlProf_CloudflareAccess \
       -samlIdPCertName SAML_Signing \
       -assertionConsumerServiceURL "https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback" \
       -samlIssuerName "https://idp.yourdomain.com/saml/login" \
       -rejectUnsignedRequests OFF \
       -NameIDFormat emailAddress \
       -Attribute1 email \
       -Attribute1Expr "AAA.USER.ATTRIBUTE(\"email\")" \
       -Attribute1Format Basic \
       -serviceProviderID "https://idp.yourdomain.com/saml/login"

   add authentication samlIdPPolicy samlPol_CloudflareAccess -rule true -action samlProf_CloudflareAccess
   bind authentication vserver nsidp -policy samlPol_CloudflareAccess
   ```

3. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

4. Under **Your identity providers**, select **Add new identity provider**.

5. Configure the fields as follows:

   | Field                        | Description                                      |
   | ---------------------------- | ------------------------------------------------ |
   | **Name**                     | Your chosen name                                 |
   | **Single Sign On URL**       | The FQDN of the IdP, with the path `/saml/login` |
   | **IdP Entity ID/Issuer URL** | As above                                         |
   | **Signing Certificate**      | The public certificate from the NetScaler        |
   | **Email attribute name**     | This is listed under **Optional configurations** |

6. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to the identity provider you want to test.

# Cloudflare as identity provider

import { Tabs, TabItem, APIRequest } from "~/components";

Cloudflare Access can use Cloudflare itself as an identity provider, allowing you to build Access policies that match on Cloudflare account membership. This is useful for scenarios where you want to restrict access to users who are members of a specific Cloudflare account, without requiring a third-party identity provider.

When a user authenticates through the Cloudflare identity provider, Access verifies their Cloudflare account membership and grants or denies access based on your policy configuration.

For newly created Zero Trust organizations, Cloudflare adds this identity provider automatically as the default login method, with **Restrict to account members** enabled. You do not need to set it up manually. The following steps describe how to add or reconfigure it.

## Set up Cloudflare as an identity provider

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Under **Your identity providers**, select **Add new identity provider**.
3. Select **Cloudflare**.
4. (Optional) Enable **Restrict to account members** if you want to limit authentication to users who are members of your Cloudflare account. When disabled, any user with a Cloudflare account can authenticate.
5. Select **Save**.

</TabItem> <TabItem label="API">

Make a `POST` request to the [Identity Providers](/api/resources/zero_trust/subresources/identity_providers/methods/create/) endpoint:

<APIRequest
	path="/accounts/{account_id}/access/identity_providers"
	method="POST"
	json={{
		name: "Cloudflare",
		type: "cloudflare",
		config: {
			restrict_to_account_members: true,
		},
	}}
/>

</TabItem></Tabs>

## Configuration options

| Option | Description | Default |
| --- | --- | --- |
| **Restrict to account members** | When enabled, only users who are members of your Cloudflare account can authenticate. When disabled, any Cloudflare user can authenticate (subject to your Access policies). | Disabled |

The **Default** column reflects the value when you add this identity provider manually. When Cloudflare configures it automatically for a new organization, **Restrict to account members** is enabled.

## Use Cloudflare account membership in policies

After configuring Cloudflare as an identity provider, you can use the **Cloudflare Account Member** selector in your [Access policies](/cloudflare-one/access-controls/policies/). This selector matches users based on their membership in a Cloudflare account.

- If you omit the account ID, the selector matches members of the current account (the account where the Access policy is configured).
- If you specify an account ID, the selector matches members of that specific account.

This is useful for cross-account access scenarios where you need to grant access to users from a different Cloudflare account.

# Microsoft Entra ID

import { Render, Tabs, TabItem, APIRequest } from "~/components";

You can integrate Microsoft Entra ID (formerly Azure Active Directory) with Cloudflare One and build policies based on user identity and group membership. Users will authenticate to Cloudflare One using their Entra ID credentials.

## Set up Entra ID as an identity provider

### 1. Obtain Entra ID settings

The following Entra ID values are required to set up the integration:

- Application (client) ID
- Directory (tenant) ID
- Client secret

To retrieve those values:

1.  Log in to the [Microsoft Entra admin center](https://entra.microsoft.com/).

2.  Go to **Applications** > **Enterprise applications**.

3.  Select **New application**, then select **Create your own application**.

4.  Name your application.

5.  Select **Register an application to integrate with Microsoft Entra ID (App you're developing)**. If offered, do not select any of the gallery applications. Select **Create**.

6.  Under **Redirect URI**, select the _Web_ platform and enter the following URL.

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

    <Render file="find-team-name" product="cloudflare-one" />

    ![Registering an application in Azure](~/assets/images/cloudflare-one/identity/azure/name-app.png)

7.  Select **Register**.

8.  Next, return to Microsoft Entra ID and go to **Applications** > **App registrations**.

9.  Select **All applications** and select the app you just created. Copy the **Application (client) ID** and **Directory (tenant) ID**. You will need these values when [adding Entra ID as an identity provider in step 3](/cloudflare-one/integrations/identity-providers/entra-id/#3-add-entra-id-as-an-identity-provider).

    ![Viewing the Application ID and Directory ID in Azure](~/assets/images/cloudflare-one/identity/azure/azure-values.png)

10. On the same page, under **Client credentials**, go to **Add a certificate or secret**. Select **New client secret**.

11. Name the client secret and choose an expiration period.

    :::note
    When the client secret expires, users will be unable to log in through Access. Take note of your expiry date to prevent login errors and renew your client secret when necessary.
    :::

12. After the client secret is created, copy its **Value** field. Store the client secret in a safe place, as it can only be viewed immediately after creation. You will need this client secret value when [adding Entra ID as an identity provider in step 3](/cloudflare-one/integrations/identity-providers/entra-id/#3-add-entra-id-as-an-identity-provider).

    ![Location of client secret in Azure](~/assets/images/cloudflare-one/identity/azure/client-cert-value.png)

### 2. Configure API permissions in Entra ID

1. Go to **App registrations** > **All applications** > select your application > **API permissions**.

2. Select **Add a permission**.

3. Select **Microsoft Graph**.

4. Select **Delegated permissions** and enable the following [permissions](https://learn.microsoft.com/graph/permissions-reference):
   - `email`
   - `offline_access`
   - `openid`
   - `profile`
   - `User.Read`
   - `Directory.Read.All`
   - `GroupMember.Read.All`

:::note
More narrow permissions may be used, however this is the set of permissions that are tested and supported by Cloudflare.
:::

5. Once all seven permissions are enabled, select **Add permissions**.

6. Select **Grant admin consent**.

   ![Configured permissions list in Azure](~/assets/images/cloudflare-one/identity/azure/configured-perms.png)

### 3. Add Entra ID as an identity provider

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **Azure AD**.

4. Enter the **Application (client) ID**, **Client secret**, and **Directory (tenant) ID** obtained from Microsoft Entra ID.

5. Select **Save**.

6. To [test](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) that your connection is working, select **Test**.

7. (Optional) Configure the following settings:
   - **Proof Key for Code Exchange**: Perform [PKCE](https://www.oauth.com/oauth2-servers/pkce/) on all login attempts.
   - **Support Groups**: Allow Cloudflare to read a user's Entra ID group membership.
   - **Entra ID Policy Sync**: Refer to our [Entra ID Conditional Access tutorial](/cloudflare-one/tutorials/entra-id-conditional-access/).
   - **Enable SCIM**: Refer to [Synchronize users and groups](#synchronize-users-and-groups).
   - **Email claim**: Enter the Entra ID claim that you wish to use for user identification (for example, `preferred_username`).
   - **OIDC Claims**: Enter [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that you wish to add to your users' identity.

</TabItem> <TabItem label="API">

Make a `POST` request to the [Identity Providers](/api/resources/zero_trust/subresources/identity_providers/methods/create/) endpoint:

<APIRequest
	path="/accounts/{account_id}/access/identity_providers"
	method="POST"
	json={{
		name: "Entra ID example",
		type: "azureAD",
		config: {
			client_id: "<your client id>",
			client_secret: "<your client secret>",
			directory_id: "<your azure directory uuid>",
			support_groups: true,
		},
	}}
/>

</TabItem> <TabItem label="Terraform">

:::note[Provider versions]
The following example requires Cloudflare provider version `4.40.0` or greater.
:::

1.  Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
    - `Access: Organizations, Identity Providers, and Groups Write`

2.  Configure the [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource:

        <Render file="access/entra-id-terraform" product="cloudflare-one" />

</TabItem> </Tabs>

#### UPN and email

If your organization's UPNs do not match users' email addresses, you must add a custom claim for email. For example, if your organization's email format is `user@domain.com` but the UPN is `u908080@domain.com`, you must create an email claim if you are configuring email-based policies.

By default, Cloudflare will first look for the unique claim name you created and configured in Cloudflare One to represent email (for example, `email_identifier`) in the `id_token` JSON response. If you did not configure a unique claim name, Cloudflare will then look for an `email` claim. Last, if neither claim exists, Cloudflare will look for the UPN claim.

To receive an email claim in the `id_token` from Microsoft Entra, you must:

1.  In the [Microsoft Entra admin center](https://entra.microsoft.com/), go to **Application** > **App registration** > **All applications** and select the relevant application.
2.  Under **Manage**, select **Token configuration**.
3.  Add a claim for email.

    ![Email claim for Entra](~/assets/images/cloudflare-one/identity/azure/entra-email-claim.png)

    The example above includes both a UPN claim and an email claim. Because an email claim was created in the Microsoft Entra configuration, Cloudflare will look for the `email` key-value pair in the JSON response.

4.  If you gave your email claim another name than `email`, you must update your configuration in Cloudflare One:

        a. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers** > **Azure AD** > **Edit**.

        b. Under **Optional configurations** > **Email claim**, enter the name of the claim representing your organization's email addresses.

#### Object ID

If you are concerned that users' emails or UPNs may change, you can pass the user's object ID (`oid`) from Microsoft Entra to Cloudflare Access. To configure Access to receive the object ID, refer to [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims). No additional configuration is required in Microsoft Entra.

## Synchronize users and groups

The Microsoft Entra ID integration allows you to synchronize IdP groups and automatically deprovision users using [SCIM](/cloudflare-one/team-and-resources/users/scim/).

<Render file="access/scim-policy-behavior" product="cloudflare-one" />

### Prerequisites

- Microsoft Entra ID P1 or P2 license

### 1. Enable SCIM in Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Find the Entra ID integration and select **Edit**.

3. Turn on **Enable SCIM** and **Support groups**.

4. (Optional) Configure the following settings:

* **Enable user deprovisioning**: [Revoke a user's active session](/cloudflare-one/access-controls/access-settings/session-management/#per-user) when they are removed from the SCIM application in Entra ID. This will invalidate all active Access sessions and prompt for reauthentication for any [Cloudflare One Client session policies](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/).
* **Remove user seat on deprovision**: [Remove a user's seat](/cloudflare-one/team-and-resources/users/seat-management/) from your Cloudflare One account when they are removed from the SCIM application in Entra ID.
* **SCIM identity update behavior**: Choose what happens in Cloudflare One when the user's identity updates in Entra ID.
	- _Automatic identity updates_: Automatically update the [User Registry identity](/cloudflare-one/team-and-resources/users/users/) when Entra ID sends an updated identity or group membership through SCIM. This identity is used for Gateway policies and Cloudflare One Client [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/); Access will read the user's updated identity when they reauthenticate.
	- _Group membership change reauthentication_: [Revoke a user's active session](/cloudflare-one/access-controls/access-settings/session-management/#per-user) when their group membership changes in Entra ID. This will invalidate all active Access sessions and prompt for reauthentication for any [Cloudflare One Client session policies](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/). Access will read the user's updated group membership when they reauthenticate.
	- _No action_: Update the user's identity the next time they reauthenticate to Access or the Cloudflare One Client.

5. Select **Regenerate Secret**. Copy the **SCIM Endpoint** and **SCIM Secret**. You will need to enter these values into Entra ID.

6. Select **Save**.

The SCIM secret never expires, but you can manually regenerate the secret at any time.

### 2. Configure SCIM in Entra ID

:::note
SCIM requires a separate enterprise application from the one created during [initial setup](#set-up-entra-id-as-an-identity-provider).
:::

1. In the Microsoft Entra ID menu, go to **Enterprise applications**.

2. Select **New application** > **Create your own application**.

3. Name your application (for example, `Cloudflare Access SCIM`).

4. Select **Integrate any other application you don't find in the gallery (Non-gallery)**. If offered, do not select any of the gallery applications. Select **Create**.

5. After you have created the application, go to **Provisioning** > select **New Configuration**.

6. In the **Tenant URL** field, enter the **SCIM Endpoint** obtained from your Entra ID integration in Cloudflare One [in the previous step](/cloudflare-one/integrations/identity-providers/entra-id/#1-enable-scim-in-zero-trust).

7. In the **Secret token** field, enter the **SCIM Secret** obtained from your Entra ID integration in Cloudflare One [in the previous step](/cloudflare-one/integrations/identity-providers/entra-id/#1-enable-scim-in-zero-trust).

8. Select **Test Connection** to ensure that the credentials were entered correctly. If the test fails, go to your Entra ID integration in Cloudflare One, select **Regenerate Secret**, select **Save**, and enter your new **SCIM Secret** in the **Secret token** field.

9. Select **Create**.

10. Once the SCIM application is created, [assign users and groups to the application](https://learn.microsoft.com/entra/identity/enterprise-apps/assign-user-or-group-access-portal).

:::note
Groups in this SCIM application should match the groups in your other [Cloudflare Access enterprise application](/cloudflare-one/integrations/identity-providers/entra-id/#set-up-entra-id-as-an-identity-provider). Matching the groups keeps the Gateway identity synchronized with the groups that Entra ID returns when the user authenticates to Access.
:::

11. Go to **Provisioning** and select **Start provisioning**.

12. For **Provisioning Mode**, the default mode should be set by Microsoft to _Automatic_.

13. On the **Overview** page in Entra ID, you will see the synchronization status.

To check which users and groups were synchronized, select **Provisioning logs**.

<Render file="access/verify-scim-provisioning" product="cloudflare-one" />

To monitor the exchange of identity details between Cloudflare Access and Microsoft Entra ID, go to the [Cloudflare dashboard](https://dash.cloudflare.com/) > **Zero Trust** > **Insights** > **Logs** > **SCIM provisioning logs** and view the [SCIM activity logs](/cloudflare-one/insights/logs/dashboard-logs/scim-logs/).

### Provisioning attributes

Provisioning attributes define the user properties that Entra ID will synchronize with Cloudflare Access. To modify your provisioning attributes, go to the **Attribute mapping** and select **Provision Microsoft Entra ID Users**.

If not already configured, Cloudflare recommends enabling the following user attribute mappings:

| customappsso Attribute         | Entra ID Attribute            | Recommendation                                                   |
| ------------------------------ | ----------------------------- | ---------------------------------------------------------------- |
| `userName`                     | `userPrincipalName` or `mail` | Required. Must match the user's email address in Cloudflare One. |
| `emails[type eq "work"].value` | `mail`                        | Required. Must match the user's email address in Cloudflare One. |
| `name.givenName`               | `givenName`                   | Recommended                                                      |
| `name.familyName`              | `surname`                     | Recommended                                                      |

## Entra groups in Zero Trust policies

### Automatic entry

When [SCIM synchronization is enabled](#synchronize-users-and-groups), your Entra group names will automatically appear in the Access and Gateway policy builders.

If building an Access policy, choose the _Azure Groups_ selector.
![Azure group names displayed in the Access policy builder](~/assets/images/cloudflare-one/identity/azure/azure-scim-groups.png)

If building a Gateway policy, choose the [_User Group Names_](/cloudflare-one/traffic-controls/identity-selectors/#user-group-names) selector.

### Manual entry

You can create Access and Gateway policies for groups that are not synchronized with SCIM. Entra ID exposes directory groups in a format that consists of random strings, the `Object Id`, that is distinct from the `Name`.

1. Make sure you enable **Support groups** as you set up Microsoft Entra ID in Cloudflare One.

2. In your Microsoft Entra dashboard, note the `Object Id` for the Entra group. In the example below, the group named Admins has an ID of `61503835-b6fe-4630-af88-de551dd59a2`.

   ![Viewing the Azure group ID on the Azure dashboard](~/assets/images/cloudflare-one/identity/azure/object-id.png)

3. If building an Access policy, choose the _Azure Groups_ selector. If building a Gateway policy, choose the _User Group IDs_ selector.

4. In the **Value** field, enter the `Object Id` for the Entra group.

   ![Entering an Azure group ID in Cloudflare One](~/assets/images/cloudflare-one/identity/azure/configure-group-n.png)

### Nested groups

#### Authentication

Access and Gateway policies for an Entra group will also apply to all [nested groups](https://learn.microsoft.com/entra/fundamentals/how-to-manage-groups#add-a-group-to-another-group). For example, if a user belongs to the group `US devs`, and `US devs` is part of the broader group `Devs`, the user would be allowed or blocked by all policies created for `Devs`.

#### SCIM provisioning

For SCIM provisioning, [nested groups are not supported](https://learn.microsoft.com/en-us/entra/identity/app-provisioning/how-provisioning-works#assignment-based-scoping). Microsoft Entra ID's SCIM implementation does not send information about nested group memberships to Cloudflare. Only users who are direct members of an explicitly assigned group will be provisioned. To ensure group memberships are correctly synchronized, you must flatten your groups in Entra ID by directly assigning users to the groups you want to provision.

Since the SCIM request from Microsoft does not include nested group information, neither Cloudflare nor Microsoft can provide a notification that nested groups are not being synchronized.

## Force user interaction during device client reauthentication

You can require users to re-enter their credentials into Entra ID whenever they [re-authenticate their Cloudflare One Client session](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/). To configure this setting:

1.  Make a `GET` request to the [Identity Providers endpoint](/api/resources/zero_trust/subresources/identity_providers/) and copy the response for the Entra ID identity provider.

    <APIRequest
    	path="/accounts/{account_id}/access/identity_providers/{identity_provider_id}"
    	method="GET"
    />

2.  [Update the Entra ID identity provider](/api/resources/zero_trust/subresources/identity_providers/methods/update/) using a `PUT` request. In the request body, include all existing configurations and set the `prompt` parameter to either `login` or `select_account`. For example:

        <APIRequest
        	path="/accounts/{account_id}/access/identity_providers/{identity_provider_id}"
        	method="PUT"
        	json={{
        		id: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
        		type: "azureAD",
        		uid: "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
        		name: "Entra ID",
        		version: "31e74e9b4f033e16b604552091a72295",
        		config: {
        			azure_cloud: "default",
        			client_id: "<CLIENT_ID>",
        			conditional_access_enabled: false,
        			directory_id: "<AZURE_DIRECTORY_ID>",
        			redirect_url: "https://<TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/callback",
        			prompt: "login",
        			support_groups: true,
        		},
        		scim_config: {
        			enabled: true,
        			user_deprovision: true,
        			seat_deprovision: false,
        			group_member_deprovision: false,
        			identity_update_behavior: "automatic",
        		},
        		scim_base_url: "https://<TEAM_NAME>.cloudflareaccess.com/populations/f174e90a-fafe-4643-bbbc-4a0ed4fc8415/scim/v2",
        	}}
        	code = {{
        		mark: [16]
        	}}
        />

# Facebook

import { Render } from "~/components";

Use these steps to set up Facebook as your identity provider.

1. Go to [developers.facebook.com](https://developers.facebook.com/). Create a Developer account if you do not have one. 

2. Select **Create App** at the top-right. The **Create an app** card displays.

3. Enter the **App name** and **App contact email**. Then, select **Next**.

4. In the **Add use cases** page, select **Authenticate and request data from users with Facebook Login**. Select **Next**.

5. Fill in the necessary information and select **Next** until you reach **Overview**. Then, select **Create app**.

6. In the **My Apps** page, go to **App settings** > **Basic**.

7. Copy the **App ID** and **App Secret**.

8. In the [Cloudflare dashboard](https://dash.cloudflare.com), go to **Zero Trust** > **Integrations** > **Identity providers**.

9. Under **Your identity providers**, select **Add an identity provider**.

10. Fill in the **App ID** and **App Secret** obtained from Facebook.

11. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/). PKCE will be performed on all login attempts.

12. Select **Save**.

13. Go back to **My Apps** in [developers.facebook.com](https://developers.facebook.com/), and select your app.

14. Under **App customization and requirements**, select **Customize the Authenticate and request data from users with Facebook Login use case**.

15. Select **Settings**, and ensure that **Use Strict Mode for redirect URIs** slider is set to **Yes**.

15. In the **Valid OAuth Redirect URIs** field, enter the following URL:

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

    <Render file="find-team-name" product="cloudflare-one" />

16. Select **Save Changes**.

To test that your connection is working, follow the steps on [SSO Integration](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one).

## Example API Configuration

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>"
	},
	"type": "facebook",
	"name": "my example idp"
}
```

# Generic OIDC

import { Tabs, TabItem, Render, APIRequest } from "~/components";

Cloudflare Access has a generic OpenID Connect (OIDC) connector to help you integrate IdPs not already set in Access.

## 1. Create an application in your identity provider

1. Visit your identity provider and create a client/app.

2. When creating a client/app, your IdP may request an **authorized redirect URI**. Enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

3. Copy the content of these fields:
   - Client ID
   - Client secret
   - Auth URL: The `authorization_endpoint` URL of your IdP
   - Token URL: The `token_endpoint` URL of your IdP
   - Certificate URL: The `jwks_uri` endpoint of your IdP to allow the IdP keys to sign the tokens

   You can find these values on your identity provider's **OIDC discovery endpoint**. Some providers call this the "well-known URL".

## 2. Add an OIDC provider to Cloudflare One

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Choose **OpenID Connect**.

4. Name your identity provider and fill in the required fields with the information obtained from your identity provider.

5. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/) if the protocol is supported by your IdP. PKCE will be performed on all login attempts.

6. (Optional) To enable SCIM, refer to [Synchronize users and groups](#synchronize-users-and-groups).

7. (Optional) Under **Optional configurations**, enter [custom OIDC claims](#oidc-claims) that you wish to add to users' identity. This information will be available in the [user identity endpoint](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/#user-identity).

8. Select **Save**.

</TabItem> <TabItem label="API">

Make a `POST` request to the [Identity Providers](/api/resources/zero_trust/subresources/identity_providers/methods/create/) endpoint:

<APIRequest
	path="/accounts/{account_id}/access/identity_providers"
	method="POST"
	json={{
		name: "Generic OIDC example",
		type: "oidc",
		config: {
			client_id: "<your client id>",
			client_secret: "<your client secret>",
			auth_url: "https://accounts.google.com/o/oauth2/auth",
			token_url: "https://accounts.google.com/o/oauth2/token",
			certs_url: "https://www.googleapis.com/oauth2/v3/certs",
			pkce_enabled: false,
			email_claim_name: "email",
			claims: ["employeeID", "groups"],
			scopes: ["openid", "email", "profile"],
		},
	}}
/>

</TabItem> <TabItem label="Terraform (v5)">

1.  Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
    - `Access: Organizations, Identity Providers, and Groups Write`

2.  Configure the [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource:

        	```tf
        	resource "cloudflare_zero_trust_access_identity_provider" "generic_oidc_example" {
        		account_id = var.cloudflare_account_id
        		name       = "Generic OIDC example"
        		type       = "oidc"
        		config 		 = {
        			client_id = "<your client id>"
        			client_secret = "<your client secret>"
        			auth_url = "https://accounts.google.com/o/oauth2/auth"
        			token_url = "https://accounts.google.com/o/oauth2/token"
        			certs_url = "https://www.googleapis.com/oauth2/v3/certs"
        			pkce_enabled = false
        			email_claim_name = "email"
        			claims = ["employeeID", "groups"]
        			scopes = ["openid", "email", "profile"]
        		}
        	}
        	```

</TabItem> </Tabs>

## 3. Test the connection

To test that your connection is working, go to **Authentication** > **Login methods** and select **Test** next to the login method you want to test. On success, a confirmation screen displays.

## Synchronize users and groups

The generic OIDC integration allows you to synchronize user groups and automatically deprovision users using [SCIM](/cloudflare-one/team-and-resources/users/scim/).

<Render file="access/scim-policy-behavior" product="cloudflare-one" />

### Prerequisites

Your identity provider must support SCIM version 2.0.

### 1. Enable SCIM in Cloudflare One

<Render
	file="access/enable-scim-on-dashboard"
	product="cloudflare-one"
	params={{ idp: "IdP" }}
/>

### 2. Configure SCIM in the IdP

Setup instructions vary depending on the identity provider. In your identity provider, you will either need to edit the [original SSO application](/cloudflare-one/integrations/identity-providers/generic-oidc/#1-create-an-application-in-your-identity-provider) or create a new SCIM application. Refer to your identity provider's documentation for more details. For example instructions, refer to our [Okta](/cloudflare-one/integrations/identity-providers/okta/#synchronize-users-and-groups) or [Jumpcloud](/cloudflare-one/integrations/identity-providers/jumpcloud-saml/#synchronize-users-and-groups) guides.

#### IdP groups

If you would like to build policies based on IdP groups:

- Ensure that your IdP sends a `groups` field. The naming must match exactly (case insensitive). All other values will be sent as a OIDC claim.
- If your IdP requires a new SCIM application, ensure that its groups match the groups in the [original SSO application](/cloudflare-one/integrations/identity-providers/generic-oidc/#1-create-an-application-in-your-identity-provider). Matching the groups keeps the Gateway identity synchronized with the groups that the IdP returns when the user authenticates to Access.

### 3. Verify SCIM provisioning

<Render file="access/verify-scim-provisioning" product="cloudflare-one" />

## Optional configurations

### Custom OIDC claims

All OIDC IdP integrations support the use of custom OIDC claims. Once configured, Access will add the claims to the [Access JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/) for consumption by your origin services. You can reference the custom OIDC claims in [Access policies](/cloudflare-one/access-controls/policies/) and [Gateway policies](/cloudflare-one/traffic-controls/identity-selectors/#oidc-claims), offering a means to control user access to applications based on custom identity attributes.

To add a custom OIDC claim to an IdP integration:

1.  In your identity provider, ensure that the custom claim is included in your OIDC ID token.
2.  In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
3.  Under **Your identity providers**, find your identity provider and select **Edit**.
4.  Under **OIDC Claims**, enter the name of your custom claim (for example, `oid`).
5.  Select **Save**.
6.  Select **Test** and verify that the custom claim appears in `oidc_fields`. For example,

        ```json
        	"oidc_fields": {
        		"oid": "54eb1ed2-7150-44e6-bbe4-ead24c132fd4"
        	},
        ```

You can now build an Access policy for the custom claim using the **OIDC Claim** or **IdP OIDC Claim** selector. You can also use custom OIDC claims as [identity-based selectors in Gateway policies](/cloudflare-one/traffic-controls/identity-selectors/#oidc-claims). The custom claim will be passed to origins behind Access in a [JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/#custom-saml-attributes-and-oidc-claims).

#### Email claim

You can specify a custom **Email claim** name that Access will use to identify user emails. This is useful if your IdP does not return the standard `email` claim in the OIDC ID token.

#### Multi-record OIDC claims

Cloudflare Access extends support for multi-record OIDC claims. These claims are parsed out and can be individually referenced in policies. This feature enables granular access control and precise user authorization in applications.

Cloudflare Access does not support partial OIDC claim value references or OIDC scopes.

## Supported algorithms for generic OIDC tokens

Cloudflare supports the following algorithms for verifying generic OIDC tokens:

- RS512
- RS256
- PS512
- ES256
- ES384
- ES512

# Generic SAML 2.0

import { Tabs, TabItem, Render } from "~/components";

Cloudflare One integrates with any identity provider that supports SAML 2.0. If your identity provider is not listed in the integration list of login methods in Cloudflare One, it can be configured using SAML 2.0 (or OpenID if OIDC based). Generic SAML can also be used if you would like to pass additional SAML headers or claims for an IdP in the integration list.

## Prerequisites

Minimum requirements for identity providers:

- The IdP must conform to SAML 2.0.
- The IdP must provide a **Single sign-on URL**, an **Entity ID or Issuer URL**, and a **Signing certificate**.
- The IdP must include the signing public key in the SAML response.

## 1. Create an application in your identity provider

Most identity providers allow users to create an **Application**. In this context, an application is a set of parameters that the identity provider will then pass on to Cloudflare to establish an integration.

The typical setup requirements are:

1. Create a new integration in the identity provider with the type set as **SAML**.
2. Set both the **Entity/Issuer ID** and the **Single sign-on URL** to:
   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```
   <Render file="find-team-name" product="cloudflare-one" />
3. Set the **Name ID/Email format** to `emailAddress`.
4. (Optional) Set the signature policy to _Always Sign_.

### (Optional) Upload SAML metadata

If your identity provider supports metadata file configuration, you can use the default or identity provider specific metadata endpoint:

- **Default:** `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/saml-metadata`
- **Identity provider specific:** `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/<identity-provider-id>/saml-metadata`, where `<identity-provider-id>` is the `id` value obtained from [List Access identity providers](/api/resources/zero_trust/subresources/identity_providers/methods/list/). Use this endpoint if your IdP requires a configuration not defined in the default metadata file.

To download the SAML metadata file, copy-paste the metadata endpoint into a web browser and save the page as an `.xml` file. Upload this XML file to the identity provider.

## 2. Add a SAML identity provider to Cloudflare One

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Select **Add new identity provider** and select **SAML**.
3. Choose a descriptive name for your identity provider.
4. Enter the **Single Sign on URL**, **IdP Entity ID or Issuer URL**, and **Signing certificate** obtained from your identity provider.
5. (Optional) To enable SCIM, refer to [Synchronize users and groups](#synchronize-users-and-groups).
6. (Optional) Under **Optional configurations**, configure [additional SAML options](#optional-configurations).
7. Select **Save**.

</TabItem> <TabItem label="Terraform (v5)">

1.  Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
    - `Access: Organizations, Identity Providers, and Groups Write`

2.  Configure the [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource:

        ```tf
        resource "cloudflare_zero_trust_access_identity_provider" "generic_saml_example" {
        	account_id = var.cloudflare_account_id
        	name       = "Generic SAML example"
        	type       = "saml"
        	config 		 = {
        		sso_target_url = "https://example.com/1234/sso/saml"
        		issuer_url = "https://example.com/1234"
        		idp_public_certs = ["-----BEGIN CERTIFICATE-----\nXXXXX\n-----END CERTIFICATE-----"]
        		sign_request = false
        		email_attribute_name = "email"
        		attributes = ["employeeID", "groups"]
        	}
        }
        ```

</TabItem> </Tabs>

:::caution

Set a reminder for the expiry date of the signing certificate obtained from your generic SAML identity provider. After the certificate expires, you will need to generate a new signing certificate and re-add it to your Cloudflare configuration via the Cloudflare dashboard or Terraform.

:::

## 3. Test the connection

You can now [test the IdP integration](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one). A success response should return the configured SAML attributes.

## Synchronize users and groups

The generic SAML integration allows you to synchronize user groups and automatically deprovision users using [SCIM](/cloudflare-one/team-and-resources/users/scim/).

<Render file="access/scim-policy-behavior" product="cloudflare-one" />

### Prerequisites

Your identity provider must support SCIM version 2.0.

### 1. Enable SCIM in Cloudflare One

<Render
	file="access/enable-scim-on-dashboard"
	product="cloudflare-one"
	params={{ idp: "IdP" }}
/>

### 2. Configure SCIM in the IdP

Setup instructions vary depending on the identity provider. In your identity provider, you will either need to edit the [original SSO application](#1-create-an-application-in-your-identity-provider) or create a new SCIM application. Refer to your identity provider's documentation for more details. For example instructions, refer to our [Okta](/cloudflare-one/integrations/identity-providers/okta/#synchronize-users-and-groups) or [JumpCloud](/cloudflare-one/integrations/identity-providers/jumpcloud-saml/#synchronize-users-and-groups) guides.

#### IdP groups

If you would like to build policies based on IdP groups:

- Ensure that your IdP sends a `groups` field. The naming must match exactly (case insensitive). All other values will be sent as a SAML attribute.
- If your IdP requires a new SCIM application, ensure that its groups match the groups in the [original SSO application](/cloudflare-one/integrations/identity-providers/generic-saml/#1-create-an-application-in-your-identity-provider). Matching the groups keeps the Gateway identity synchronized with the groups that the IdP returns when the user authenticates to Access.

### 3. Verify SCIM provisioning

<Render file="access/verify-scim-provisioning" product="cloudflare-one" />

## Optional configurations

SAML integrations support additional security and configuration options.

### Encrypt SAML assertions

SAML assertion encryption ensures that SAML assertions sent from your identity provider to Cloudflare Access are encrypted end-to-end.

Without encryption, SAML assertions are transmitted in plaintext after TLS termination, which means they could be visible to browser extensions or client-side malware. With encryption turned on, your IdP encrypts the assertion using a public certificate from Cloudflare, and only Access can decrypt it with the corresponding private key.

:::note
SAML assertion encryption is separate from [signed AuthN requests](/cloudflare-one/integrations/identity-providers/signed_authn/). Signing verifies the authenticity of SAML messages, while encryption adds an additional layer of security by protecting assertion contents. You can use both features together.
:::

To turn on SAML assertion encryption:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Select your SAML identity provider and select **Edit**.
3. Under **SAML encryption**, turn on the **Enable SAML encryption** toggle. Access will automatically generate an encryption certificate.
4. Copy the displayed certificate (in PEM format) or the certificate set ID.
5. In your identity provider, upload the Cloudflare encryption certificate and turn on assertion encryption. Refer to your IdP documentation for the exact configuration steps.
6. Select **Save**.

After you turn on encryption, Access will reject any unencrypted assertions from the configured IdP. If you turn off encryption, Access will reject any encrypted assertions until encryption is turned back on.

#### Supported encryption algorithms

Access supports the following encryption algorithms:

| Algorithm type | Supported values |
| --- | --- |
| Content encryption | AES-128-CBC, AES-256-CBC, AES-128-GCM, AES-256-GCM |
| Key transport | RSA-OAEP (XML Encryption 1.0 and 1.1), RSA-1.5 |

:::caution
RSA-1.5 is supported for compatibility but is considered insecure. Use RSA-OAEP when your identity provider supports it.
:::

:::note[FedRAMP environments]
FedRAMP High environments enforce stricter algorithm requirements. RSA-1.5 and RSA-OAEP 1.0 (`rsa-oaep-mgf1p`) are rejected. Use RSA-OAEP 1.1 with SHA-256 for key transport and AES-256-GCM or AES-256-CBC for content encryption.
:::

#### Rotate encryption certificates

Encryption certificates are valid for one year. Thirty days before a certificate expires, Access automatically generates a replacement certificate. The expiring certificate remains valid until its expiration date, giving you time to upload the new certificate to your IdP.

To manually rotate a certificate:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Select your SAML identity provider and select **Edit**.
3. Under **SAML encryption**, select **Rotate certificate**.
4. Upload the new certificate to your identity provider.

:::caution
If you rotate again before updating your IdP with the current certificate, the previous certificate will be immediately invalidated. This may break authentication for users whose IdP is still encrypting assertions with the old certificate.
:::

### Sign SAML authentication request

This optional configuration signs the [Access JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/) with the Cloudflare Access public key to ensure that the JWT is coming from a legitimate source. The Cloudflare public key can be obtained at `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/certs`.

### Require fresh authentication at the identity provider

You can ask your identity provider to reauthenticate the user for every SAML authentication request. This option applies whether the request is signed or unsigned.

This setting is available through the API. First, retrieve the identity provider's current configuration from the [Access identity provider endpoint](/api/resources/zero_trust/subresources/identity_providers/methods/get/). Then, send the complete configuration to the [update identity provider endpoint](/api/resources/zero_trust/subresources/identity_providers/methods/update/) with `force_authn` set to `true` in the `config` object. The default value is `false`.

When this option is turned on, Access sets `ForceAuthn` to `true` in each SAML authentication request. Access may also set `ForceAuthn` to `true` when a security check requires the user to reauthenticate, even if `force_authn` is `false`.

### Email attribute name

Many [Access policies](/cloudflare-one/access-controls/policies/) depend on a user's email address. Some identity providers have a different naming for the email address attribute (for example, `Email`, `e-mail`, `emailAddress`). This can typically be checked in the identity provider's SAML test option.

Example in Okta:

![Preview the SAML assertion from the Okta dashboard](~/assets/images/cloudflare-one/identity/saml-assertion.png)
![Determine the email attribute name from the SAML assertion](~/assets/images/cloudflare-one/identity/saml-attributes.png)

### SAML headers and attributes

Cloudflare Access supports SAML (Security Assertion Markup Language) attributes and SAML headers for all SAML IdP integrations.

[**SAML attributes**](#saml-attributes) refer to specific data points or characteristics that the IdP shares about the authenticated user. These attributes often include details like email address, name, or role, and are passed along to the service provider upon successful authentication.

[**SAML headers**](#saml-headers) are metadata in the SAML protocol communication which convey information about the sender, recipient, and the message itself. These headers can be leveraged to provide extra context or control over the communication.

#### SAML attributes

SAML attributes are added to the [Access JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/). These attributes can then be consumed by self-hosted or SaaS applications connected to Access. Any SAML attribute configured in the SAML integration must also be sent from the IdP.

Example in Okta:

![Configure Okta to send SAML attributes](~/assets/images/cloudflare-one/identity/attribute-statements.png)

How to receive these SAML attributes in Cloudflare:

![Configure Cloudflare to receive SAML attributes](~/assets/images/cloudflare-one/identity/attributes-cloudflare.png)

#### SAML headers

If an application specifically requires SAML attributes upon sign-in, then the attributes can be passed as headers. The **Attribute name** should be the value coming from your IdP (for example, `department`). You can assign any **Header name** to the attribute. The header name will appear in the response headers when Access makes the initial authorization request to `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback`.

#### Multi-record SAML attributes

Cloudflare Access extends support for multi-record SAML attributes such as groups. These attributes are parsed out and can be individually referenced in policies. This feature enables granular access control and precise user authorization in applications.

Cloudflare Access does not currently support partial attribute value references.

# GitHub

import { Render } from "~/components";

Cloudflare One allows your team to connect to your applications using their GitHub login. You do not need to have a GitHub organization to use the integration.

## Set up GitHub Access

To configure GitHub access in both GitHub and Cloudflare One:

1. Log in to [GitHub](https://github.com/).

2. Go to your account > **Settings** > **Developer Settings**.

3. In **Developer Settings**, select **OAuth Apps** and select **New OAuth app**.

4. On the **Register a new OAuth application** page, enter an **Application name**. Your users will see this application name on the login page.

5. In the **Homepage URL** field, enter your team domain:

   ```txt
   https://<your-team-name>.cloudflareaccess.com
   ```

   <Render file="find-team-name" product="cloudflare-one" />

6. In the GitHub **Authorization callback URL** field, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

7. Select **Register application**.

8. Make note of the **Client ID**.

9. Select **Generate a new client secret** and copy the client secret to a safe place.

10. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

11. Select **Add new identity provider** and select **GitHub**.

12. In **App ID**, enter the **Client ID** obtained from GitHub (refer to step 8).

13. In **Client secret**, enter the **Client secret** obtained from GitHub (refer to step 9).

14. Select **Save**.

15. Select **Finish setup** to launch a GitHub authorization page. You will be asked to grant the following permissions to Cloudflare Access:
	- Organizations and teams (read-only)
	- Email addresses (read-only)

16. Select **Authorize**.

To test that your connection is working, go to the [Cloudflare dashboard](https://dash.cloudflare.com/) > **Zero Trust** > **Integrations** > **Identity providers** and select **Test** next to your GitHub login method. If you have GitHub two-factor authentication enabled, you will need to first login to GitHub directly and return to Access.

:::note[Troubleshooting organization policies]

When using a GitHub organization policy, if a user joins the required organization after a failed login attempt, they will remain blocked. To fix this, they must revoke the application's access in their GitHub settings and log in again to update their permissions.

:::

## Example API Configuration

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>"
	},
	"type": "github",
	"name": "my example idp"
}
```

# Google Workspace (SAML/SCIM)

import { Render, Steps } from "~/components";

Connect Google Workspace to Cloudflare One using a custom SAML app. You can send group membership during authentication and configure System for Cross-domain Identity Management (SCIM) to synchronize user updates and deprovision users.

This setup uses the **SAML** identity provider in Cloudflare One. The dedicated [Google Workspace integration](/cloudflare-one/integrations/identity-providers/google-workspace/) does not support SCIM.

## Prerequisites

- A Google Workspace super administrator account.
- Your Cloudflare One team name.
- For SCIM, access to Google's custom SCIM profiles beta in your Google Admin console.

<Render file="find-team-name" product="cloudflare-one" />

## Set up SAML authentication

### 1. Create a custom SAML app in Google Workspace

Create a [custom SAML app in Google Workspace](https://knowledge.workspace.google.com/admin/apps/set-up-your-own-custom-saml-app):

<Steps>

1. In the [Google Admin console](https://admin.google.com/), go to **Apps** > **Web and mobile apps**.
2. Select **Add app** > **Add custom SAML app**, enter an app name, and select **Continue**.
3. Copy Google's **SSO URL** and **Entity ID**, and download the signing certificate. You will use these values when you [add the SAML identity provider to Cloudflare One](#2-add-the-saml-identity-provider-to-cloudflare-one).
4. In **Service provider details**, set both **ACS URL** and **Entity ID** to `https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback`.
5. Set **Name ID format** to **EMAIL** and **Name ID** to the user's primary email address.
6. In **Attribute mapping**, map the user's primary email address to an **App attribute** named `email`.
7. (Optional) To send group membership to Cloudflare One, under **Group membership (optional)**, search for and select the Google groups to include. Set **App attribute** to `groups`. Use this exact name so Cloudflare recognizes the values as IdP groups rather than a custom SAML attribute.
8. Select **Finish**, then turn on the app for the users who will authenticate to Cloudflare One.

</Steps>

Google includes only the selected groups that the signing-in user belongs to, directly or indirectly. You can select up to 75 groups. If you rename a group in Google, select it again in the group membership mapping. Refer to Google's [group membership mapping documentation](https://knowledge.workspace.google.com/admin/apps/about-group-membership-mapping).

Group membership is sent in the SAML assertion when a user authenticates. This mapping is separate from SCIM user provisioning; selecting groups as a SCIM provisioning scope does not send their memberships to Cloudflare One.

### 2. Add the SAML identity provider to Cloudflare One

<Steps>

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Select **Add new identity provider** and select **SAML**.
3. Choose a descriptive name for your identity provider.
4. Enter Google's **SSO URL**, **Entity ID**, and signing certificate in **Single Sign on URL**, **IdP Entity ID or Issuer URL**, and **Signing certificate**, respectively.
5. Under **Optional configurations**, set **Email attribute name** to `email`. If you configured group membership mapping, add `groups` to **SAML attributes**.
6. Select **Save**.

</Steps>

:::caution

Set a reminder for the signing certificate's expiry date. Before it expires, generate a new signing certificate in Google Workspace and update your Cloudflare configuration.

:::

For additional SAML settings or Terraform configuration, refer to [Generic SAML 2.0](/cloudflare-one/integrations/identity-providers/generic-saml/).

### 3. Test the connection

[Test the IdP integration](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) with a user who has access to the SAML app. If you configured group membership mapping, use a member of a mapped group and confirm that the returned identity includes the expected groups.

## Configure SCIM user provisioning (beta)

Google's custom SCIM profiles support automated user provisioning for SAML apps. The feature is in beta and must be available in your Google Admin console. Turn on the custom SAML app for at least one user before configuring SCIM.

:::caution[Use the SAML identity provider]

Enable SCIM on the **SAML** identity provider you created in Cloudflare One, not on the dedicated **Google Workspace** integration. Use the SCIM endpoint and secret from that same SAML identity provider.

:::

### 1. Enable SCIM in Cloudflare One

<Render
	file="access/enable-scim-on-dashboard"
	product="cloudflare-one"
	params={{ idp: "Google Workspace SAML" }}
/>

Turn on **Enable user deprovisioning** if Google deprovisioning events should revoke active Access sessions. Google's configured deprovisioning delay still applies.

### 2. Configure autoprovisioning in Google Workspace

<Steps>

1. In the [Google Admin console](https://admin.google.com/), go to **Apps** > **Web and mobile apps** and select the SAML app you created for Cloudflare One.
2. Under **Autoprovisioning**, select **Configure auto-provisioning**. Enter the Cloudflare **SCIM Secret** in **Access token**, then enter the **SCIM Endpoint** in **Endpoint URL**.
3. Review the discovered attribute mappings. Ensure that SCIM `userName` maps to the same primary email address used for SAML authentication.
4. (Optional) Limit provisioning to specific Google groups. This scopes which users are provisioned; it does not configure group membership synchronization. Users must also have access to the SAML app. Google excludes group members from secondary domains or outside your organization from provisioning.
5. Choose the deprovisioning actions and delays for users whose app access is turned off or whose Google accounts are suspended or deleted. Google can deactivate users or send a hard-delete request after the configured delay.
6. Choose a dedicated test user who has already authenticated to Cloudflare One and select **Test Connection**. This test creates, updates, and deletes the test user at the SCIM endpoint. Review the results before selecting **Finish**.
7. Return to the app's **Autoprovisioning** settings and set the toggle to **Active**. Finishing setup alone does not start provisioning.

</Steps>

This integration covers user lifecycle updates. Configure the [SAML `groups` attribute](#1-create-a-custom-saml-app-in-google-workspace) separately for group-based policies. Do not rely on SCIM provisioning scope to deliver group membership changes or revoke sessions when a user changes groups.

### 3. Verify SCIM provisioning

<Render file="access/verify-scim-provisioning" product="cloudflare-one" />

# Google Workspace

import { GlossaryTooltip, Render } from "~/components";

:::note

The Google Workspace IdP integration [is not supported](/cloudflare-one/access-controls/troubleshooting/#google-workspace-redirect-loop) if your Google Workspace account is protected by Access.
:::

You can integrate a Google Workspace (formerly G Suite) account with Cloudflare Access. Unlike the instructions for [generic Google authentication](/cloudflare-one/integrations/identity-providers/google/), the steps below will allow you to pull group membership information from your Google Workspace account.

Once integrated, users will log in with their Google Workspace credentials to reach resources protected by Cloudflare Access or to enroll their device into Cloudflare Gateway.

You do not need to be a Google Cloud Platform user to integrate Google Workspace as an identity provider with Cloudflare One. You will only need to open the Google Cloud Platform to configure IdP integration settings.

:::caution[SCIM requires SAML]

To use Google's built-in SCIM provisioning, follow the [Google Workspace (SAML/SCIM)](/cloudflare-one/integrations/identity-providers/google-workspace-saml/) guide instead of this integration. That guide covers SAML authentication, group membership mapping, and SCIM user provisioning through a custom Google SAML app.

:::

## Set up Google Workspace as an identity provider

### 1. Configure Google Workspace

1.  Log in to the Google Cloud Platform [console](https://console.cloud.google.com/). This is separate from your Google Workspace console.
2.  A Google Cloud project is required to enable Google Workspace APIs. If you do not already have a Google Cloud project, go to **IAM & Admin** > **Create Project**. Name the project and select **Create**.
3.  Go to **APIs & Services** and select **Enable APIs and Services**. The API Library will load.
4.  In the API Library, search for `admin` and select **Admin SDK API**.
5.  **Enable** the Admin SDK API.
6.  Return to the **APIs & Services** page and go to **Credentials**.
7.  Select **Configure Consent Screen**.

    ![Location to configure a Consent Screen in the Google Cloud Platform console.](~/assets/images/cloudflare-one/identity/google/configure-consent-screen.png)

8.  To configure the consent screen:
    1. Select **Get Started**.
    2. Enter an **App name** and a **User support email**.
    3. Choose **Internal** as the Audience Type. This Audience Type limits authorization requests to users in your Google Workspace and blocks users who have regular Gmail addresses.
    4. Enter your **Contact Information**. Google Cloud Platform requires an email in your account.
    5. Agree to Google's user data policy and select **Continue**.
    6. Select **Create**.
9.  The OAuth overview page will load. Select **Create OAuth Client**.

    ![Location to create an OAuth client in the Google Cloud Platform console.](~/assets/images/cloudflare-one/identity/google/create-oauth-client.png)

10. Choose _Web application_ as the **Application type** and give your OAuth Client ID a name.
11. Under **Authorized JavaScript origins**, in the **URIs** field, enter your team domain:

    ```txt
    https://<your-team-name>.cloudflareaccess.com
    ```

    <Render file="find-team-name" product="cloudflare-one" />

12. Under **Authorized redirect URIs**, in the **URIs** field, enter the following URL:

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

13. After creating the OAuth client, select the OAuth client that you just created. Google will present the **OAuth Client ID** value and **Client secret** value. The client secret field functions like a password and should not be shared. Copy both the **OAuth Client ID** value and **Client secret** value.
14. On your [Google Admin console](https://admin.google.com), go to **Security** > **Access and data control** > **API controls**.
15. In **API Controls**, select **Settings**.
16. Select **Internal apps** and check the box next to **Trust internal apps** to enable this option. The **Trust internal apps** setting is disabled by default and must be enabled for Cloudflare Access to work correctly.

    ![Location to trust internal apps in the Google Cloud Platform console.](~/assets/images/cloudflare-one/identity/gsuite/trust-internal-apps.png)

### 2. Add Google Workspace to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Select **Add new identity provider** and select **Google Workspace**.
3. Input the Client ID (**App ID** in the Cloudflare dashboard) and Client Secret fields generated previously. Additionally, enter the domain of your Google Workspace account.
4. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/). PKCE will be performed on all login attempts.
5. (Optional) Under **Optional configurations**, enter [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that you wish to add to your user's identity.
6. (Optional) Under **Authentication options**, choose a **Google prompt** and turn on **Use previous email as a login hint**.
7. Select **Save**. To complete setup, you must visit the generated link. If you are not the Google Workspace administrator, share the link with the administrator.
8. The generated link will prompt you to log in to your Google admin account and to authorize Cloudflare Access to view group information. After allowing permissions, you will see a success page from Cloudflare Access.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to Google Workspace. Your user identity and group membership should return.

:::caution[SCIM Provisioning (Beta)]
The SCIM provisioning integration with Google Workspace is not currently supported.
:::

:::note[`Failed to fetch group information from the identity provider` error]

To test successfully, you must [finish setup](https://community.cloudflare.com/t/google-workspace-failed-to-fetch-group-information-from-the-identity-provider/313361/2). Testing before finishing setup will result in a [`Failed to fetch user/group information from the identity provider` error](/cloudflare-one/access-controls/troubleshooting/#identity-provider-usergroup-info-error).

:::

## Authentication options

You can control how Google handles each authentication request. Configure these options in the dashboard or through the API.

The **Google prompt** setting supports these options:

| Dashboard option | API `prompt` value | Behavior                                                                                                                                                           |
| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| _Google default_ | Omit `prompt`      | Google chooses whether to display a prompt based on the current browser session.                                                                                   |
| _No prompt_      | `none`             | Google attempts authentication without displaying a prompt. If Google cannot complete silent authentication, Access retries once with the Google default behavior. |
| _Consent_        | `consent`          | Google asks the user to consent.                                                                                                                                   |
| _Select account_ | `select_account`   | Google displays the account chooser. Access does not send a login hint.                                                                                            |

To reuse a previous email, turn on **Use previous email as a login hint**. Access checks emails from previous Access logins on the same device. It sends `login_hint` when exactly one email matches the configured Google Workspace domain.

Access does not send a hint when multiple emails match. It also skips the hint for account selection and security reprompts. A login hint does not force Google to display the account chooser.

For more information about these values, refer to [Google's authentication request parameters](https://developers.google.com/identity/openid-connect/openid-connect#authenticationuriparameters).

### API configuration

First, retrieve the current identity provider configuration from the [Access identity provider endpoint](/api/resources/zero_trust/subresources/identity_providers/methods/get/). Then, send the complete configuration to the [update identity provider endpoint](/api/resources/zero_trust/subresources/identity_providers/methods/update/).

Add `prompt` and `use_login_hint` to the `config` object:

```json
{
	"config": {
		"client_id": "<YOUR_CLIENT_ID>",
		"client_secret": "<YOUR_CLIENT_SECRET>",
		"apps_domain": "example.com",
		"prompt": "select_account",
		"use_login_hint": true
	},
	"type": "google-apps",
	"name": "Google Workspace"
}
```

## Troubleshooting

### `Error 401: deleted_client`

<Render file="access/error-401" product="cloudflare-one" />

### Redirect loop when protecting Google apps with Access

Using Google Workspace as your identity provider while also protecting a Google-hosted application with an Access policy creates a circular redirect loop: Access sends users to Google to authenticate, but Google itself requires Access authentication first.

If you need to protect a Google-hosted application and use Google Workspace as your IdP for other applications, configure a second identity provider (for example, Okta or Microsoft Entra ID) specifically for the Access application protecting that Google-hosted app. Keep Google Workspace as the IdP for your other applications.

### Group membership not reflected in Access policies

Google Workspace group membership changes do not immediately propagate to Cloudflare Access. There are two delays to be aware of:

Google Workspace can take up to 24 hours to reflect group changes via the Admin SDK API. In addition, Cloudflare caches identity information for the duration of a user's active session, so a user who was added to or removed from a group will not see the policy change until their session expires or they re-authenticate.

To force an immediate update for a specific user, go to **Zero Trust** > **Team & Resources** > **Users**, select the user, choose **Action** > **Revoke**, and confirm. This invalidates the user's Access session token and forces re-authentication with refreshed group membership.

### Use multiple Google Workspace domains in Access policies

If your organization has a primary Google Workspace domain and one or more secondary domains (for example, `example.com` and `example.co.uk`), all users authenticate through the same Google Workspace organization. You do not need to add separate identity providers for each domain.

When building Access policies, you can target users across all domains by using group membership selectors rather than email domain selectors. Alternatively, use the **Emails ending in** selector with each domain as a separate policy rule connected with **OR** logic.

If you have multiple distinct Google Workspace organizations (not just multiple domains within one organization), you must add each organization as a separate Google Workspace identity provider in Cloudflare Zero Trust.

# Google

import { GlossaryTooltip, Render } from "~/components";

You can integrate Google authentication with Cloudflare Access without a Google Workspace account. The integration allows any user with a Google account to log in (if the [Access policy](/cloudflare-one/access-controls/policies/) allows them to reach the resource). Unlike the instructions for [Google Workspace](/cloudflare-one/integrations/identity-providers/google-workspace/), the steps below will not allow you to pull group membership information from a Google Workspace account.

You do not need to be a Google Cloud Platform user to integrate Google as an identity provider with Cloudflare One. You will only need to open the Google Cloud Platform to configure IdP integration settings.

## Set up Google as an identity provider

1. Log in to the Google Cloud Platform [console](https://console.cloud.google.com/). Create a new project, name the project, and select **Create**.

2. On the project home page, go to **APIs & Services** and on the sidebar select **Credentials**.

3. Select **Configure Consent Screen**.

   ![Location to configure a Consent Screen in the Google Cloud Platform console.](~/assets/images/cloudflare-one/identity/google/configure-consent-screen.png)

4. To configure the consent screen:
   1. Select **Get started**.
   2. Enter an **App name** and a **User support email**.
   3. Choose **External** as the Audience Type. Since this application is not being created in a Google Workspace account, any user with a Gmail address can log in.
   4. Enter your **Contact Information**. Google Cloud Platform requires an email in your account.
   5. Agree to Google's user data policy and select **Continue**.
   6. Select **Create**.

5. The OAuth overview page will load. On the OAuth overview screen, select **Create OAuth client**.

   ![Location to create an OAuth client in the Google Cloud Platform console.](~/assets/images/cloudflare-one/identity/google/create-oauth-client.png)

6. Choose _Web application_ as the **Application type** and give your OAuth Client ID a name.

7. Under **Authorized JavaScript origins**, in the **URIs** field, enter your team domain:

   ```txt
   https://<your-team-name>.cloudflareaccess.com
   ```

   <Render file="find-team-name" product="cloudflare-one" />

8. Under **Authorized redirect URIs**, in the **URIs** field, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

9. After creating the OAuth client, select the OAuth client that you just created. Google will present the **OAuth Client ID** value and **Client secret** value. The client secret field functions like a password and should not be shared. Copy both the **OAuth Client ID** value and **Client secret** value.

10. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

11. Under **Your identity providers**, select **Add new identity provider**. Choose **Google** on the next page.

12. Input the Client ID (**App ID** in the Cloudflare dashboard) and Client Secret fields generated previously.

13. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/). PKCE will be performed on all login attempts.

14. Select **Save**.

## Test your connection

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to Google.

## Example API Config

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>"
	},
	"type": "google",
	"name": "my example idp"
}
```

## Troubleshooting

### `Error 401: deleted_client`

<Render file="access/error-401" product="cloudflare-one" />

# IdP federation

import { Tabs, TabItem, CURL, Details } from "~/components";

IdP federation allows organizations with multiple Cloudflare accounts to use a single identity provider (IdP) configuration across accounts. Instead of configuring the same IdP (for example, Okta or Entra ID) separately in every account, you configure it once in a source account and share it with the other accounts in your organization.

Each recipient account gets a read-only IdP connection that routes authentication back to the source account through a bridge — a hidden application in the source account that brokers the cross-account login. End users sign in with their existing IdP credentials, and each account's Access policies evaluate the resulting identity just like any other IdP login.

## How it works

Setting up IdP federation is a two-step process:

1. **Create a federation grant.** A grant permits an IdP to be shared across accounts. Creating a grant also provisions a hidden bridge application in the source account.
2. **Share the grant.** Distribute the grant to specific accounts or to your entire organization. Each recipient account is automatically provisioned with a read-only IdP connection that points to the bridge.

When a user in a recipient account authenticates, the request is routed through the bridge to the source IdP. The source IdP handles authentication, and the resulting identity claims are passed back to the recipient account's Access policies.

## Prerequisites

- You must have permission to edit the source IdP in the source account.
- You must be a member of a [Cloudflare Organization](/fundamentals/organizations/).
- The source account must belong to a Cloudflare Organization.

## Share an IdP

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

The dashboard combines grant creation and sharing into a single flow. If a federation grant already exists for the IdP, it will be reused; otherwise, one is created automatically.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Find the IdP you want to share and select the three dots menu.
3. Select **Share**.
4. Select the recipient accounts you want to share the IdP with.
5. Review the sharing configuration and select **Confirm**.

The IdP is shared to the selected accounts automatically. Each recipient account receives a read-only IdP connection that points to the bridge in the source account.

</TabItem> <TabItem label="API">

Sharing an IdP via the API is a two-step process: create a federation grant, then share it with specific accounts or your entire organization.

#### 1. Create a federation grant

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/idp_federation_grants"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		idp_id: "<IDP_UUID>",
	}}
/>

The response includes the grant `id`, which you will use in the next step. To list all federation grants in your account:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/idp_federation_grants"
	method="GET"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

#### 2. Share the grant

You can share the grant with specific accounts or with your entire Cloudflare Organization. In the `recipients` array, target each recipient with one of the following fields:

- `recipient_account_id`: Shares the IdP with a single account. Repeat the field for each account you want to add.
- `organization_id`: Shares the IdP with every account in your Cloudflare Organization.

Specify only one of these fields per recipient. If you provide neither, the grant is shared with your entire Organization by default.

To share the grant with one or more specific accounts:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/shares"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		name: "Identity provider: OpenID Connect",
		recipients: [{ recipient_account_id: "<RECIPIENT_ACCOUNT_ID>" }],
		resources: [
			{
				resource_account_id: "<SOURCE_ACCOUNT_ID>",
				resource_id: "<GRANT_ID>",
				resource_type: "idp-federation-grant",
				meta: {},
			},
		],
	}}
/>

To share the grant with every account in your Organization, replace the `recipients` array with your Organization ID:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/shares"
	method="POST"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	json={{
		name: "Identity provider: OpenID Connect",
		recipients: [{ organization_id: "<ORGANIZATION_ID>" }],
		resources: [
			{
				resource_account_id: "<SOURCE_ACCOUNT_ID>",
				resource_id: "<GRANT_ID>",
				resource_type: "idp-federation-grant",
				meta: {},
			},
		],
	}}
/>

Each recipient account is automatically provisioned with a read-only IdP connection that points to the bridge. When you share with an Organization, every account in the Organization receives the connection.

</TabItem></Tabs>

## Stop Sharing an IdP

To stop sharing an IdP, delete the federation grant, as well as the share.

:::caution
Deleting the federation grant immediately removes the IdP connection from all recipient accounts. Any Access policies in those accounts that reference the federated IdP will no longer match, which may lock users out. Verify that recipient accounts have alternative authentication methods before you stop sharing.
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

The dashboard handles both grant and share deletion in a single flow.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Find the shared IdP and select the three dots menu.
3. Select **Unshare**.
4. Confirm the action.

</TabItem> <TabItem label="API">

Unfederating an IdP via the API is a two-step process. Deleting the grant stops the sharing and removes the read-only IdP from recipient accounts. You can optionally clean up the share record afterward.

#### 1. Delete the federation grant

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/idp_federation_grants/$GRANT_ID"
	method="DELETE"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>

#### 2. (Optional) Delete the share

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/shares/$SHARE_ID"
	method="DELETE"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
/>
</TabItem></Tabs>

## Limitations

- An account can federate at most one IdP as a source.
- A source IdP cannot be deleted while it has a federation grant associated with it. Delete the grant first.

# Identity providers

import { Render } from "~/components";


Cloudflare One integrates with your organization's identity provider to apply Cloudflare One and Secure Web Gateway policies. If you work with partners, contractors, or other organizations, you can integrate multiple identity providers simultaneously.

When you create a new Zero Trust organization, Cloudflare automatically configures the [Cloudflare identity provider](/cloudflare-one/integrations/identity-providers/cloudflare/) as your default login method. Your users can authenticate with their existing Cloudflare account credentials without any additional setup, and you can add more identity providers at any time.

You can also send a [one-time PIN (OTP)](/cloudflare-one/integrations/identity-providers/one-time-pin/) to approved email addresses. No configuration needed — simply add a user's email address to an [Access policy](/cloudflare-one/access-controls/policies/) and to the group that allows your team to reach the application. You can configure OTP and an identity provider at the same time to let users choose their own authentication method.

Adding an identity provider as a login method requires configuration both in the [Cloudflare dashboard](https://dash.cloudflare.com/) under **Zero Trust** > **Integrations** > **Identity providers** and with the identity provider itself. Consult our IdP-specific documentation to learn more about what you need to set up.

:::note

Cloudflare One supports social identity providers that do not require administrator accounts, open source providers, and corporate providers. Cloudflare also supports using signed AuthN requests with SAML providers.

:::

## Set up IdPs in Cloudflare One

<Render file="access/idp-integration" product="cloudflare-one" />

Your IdP will now be listed in the **Login methods** card.

## Test IdPs in Cloudflare One

To test if an IdP is correctly configured:

1. Go to **Integrations** > **Identity providers**.
2. Select **Test** next to the IdP you would like to test. This will attempt to connect to the IdP to verify if a valid connection is established.

### Your provider is connected

If your provider is connected, another window will open in your browser, with this message:

!["Your connection works!" message displayed for a successful IdP test](~/assets/images/cloudflare-one/identity/connected-idp.png)

### Your provider is not connected

If your provider is not connected, another window will open in your browser. Along with an error message, you will receive a detailed explanation of why the test has failed.

## Use The API

We recommend that you use our dashboard to configure your identity providers. However, if you would like to use the [Cloudflare API](https://api.cloudflare.com/), each of the identity provider topics covered here include an example API configuration snippet as well.

# JumpCloud (SAML)

import { Render } from "~/components";

[JumpCloud](https://jumpcloud.com/#platform) provides SSO identity management. Cloudflare Access integrates with JumpCloud as a SAML identity provider.

The following steps are specific to setting up JumpCloud with Cloudflare Access. For more information on configuring JumpCloud SSO application, refer to the [JumpCloud documentation](https://jumpcloud.com/support/integrate-with-cloudflare).

## Set up Jumpcloud as a SAML provider

### 1. Create an SSO application in JumpCloud

1. In the [JumpCloud Admin Portal](https://console.jumpcloud.com/#/home), go to **SSO Applications**.

2. Select **Add New Application**.

3. In the search bar, enter `Cloudflare` and select the **Cloudflare Access** application.

4. Select **Next**.

5. In **Display Label**, enter an application name.

6. Select **Save Application**.

7. Review the application summary and select **Configure Application**.

8. In the **SSO** tab, configure the following settings:
   1. In **IdP Entity ID**, enter your Cloudflare team domain:

      ```txt
      https://<your-team-name>.cloudflareaccess.com/
      ```

      <Render file="find-team-name" product="cloudflare-one" />

   2. Set both **SP Entity ID** and **ACS URL** to the following callback URL:

      ```txt
      https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
      ```

   3. (Optional) Configure SAML attributes that you want to send to Cloudflare Access.

   4. Scroll up to **JumpCloud Metadata** and select **Export Metadata**. Save this XML file for use in a [later step](#2-add-jumpcloud-to-zero-trust).

9. In the **User Groups** tab, [assign user groups](https://jumpcloud.com/support/get-started-applications-saml-sso#managing-employee-access-to-applications) to this application.

10. Select **Save**.

### 2. Add JumpCloud to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **SAML**.

4. Upload your JumpCloud XML metadata file.

5. (Optional) To enable SCIM, refer to [Synchronize users and groups](#synchronize-users-and-groups).

6. (Optional) Under **Optional configurations**, configure [additional SAML options](#optional-configurations).

7. Select **Save**.

You can now [test your connection](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) and create [Access policies](/cloudflare-one/access-controls/policies/) based on the configured login method and SAML attributes.

## Synchronize users and groups

The JumpCloud integration allows you to synchronize user groups and automatically deprovision users using [SCIM](/cloudflare-one/team-and-resources/users/scim/).

<Render file="access/scim-policy-behavior" product="cloudflare-one" />

### 1. Enable SCIM in Cloudflare One

<Render
	file="access/enable-scim-on-dashboard"
	product="cloudflare-one"
	params={{ idp: "JumpCloud" }}
/>

### 2. Configure SCIM in JumpCloud

1. In the [JumpCloud Admin Portal](https://console.jumpcloud.com/#/home), go to **SSO Applications**.
2. Select the Cloudflare application that was created when you [Set up JumpCloud as a SAML provider](/cloudflare-one/integrations/identity-providers/jumpcloud-saml/#set-up-jumpcloud-as-a-saml-provider).
3. Select the **SSO** tab.
4. To provision user groups, select **Include group attribute** and enter `groups`. The group attribute name has to exactly match `groups` or else it will be sent as a SAML attribute.
5. Select the **Identity Management** tab.
6. Make sure that **Enable management of User Groups and Group Membership in this application** is turned on.
7. Select **Configure**.
8. In the **Base URL** field, enter the **SCIM Endpoint** obtained from Cloudflare One.
9. In the **Token Key** field, enter the **SCIM Secret** obtained from Cloudflare One.
10. Select **Activate**. You will receive a confirmation that the Identity Management integration has been successfully verified.
11. Select **Save**.

<Render file="access/verify-scim-provisioning" product="cloudflare-one" />

### Provisioning attributes

Provisioning attributes define the user and group properties that JumpCloud will synchronize with Cloudflare Access. By default, JumpCloud will send the following attributes during a SCIM update event:

| JumpCloud user attribute | Cloudflare Access attribute |
| ------------------------ | --------------------------- |
| `email`                  | `email`                     |
| `firstname`              | `givenName`                 |
| `lastname`               | `surname`                   |

| JumpCloud group attribute | Cloudflare Access attribute |
| ------------------------- | --------------------------- |
| `name`                    | `groups`                    |

## Example API configuration

```json
{
	"config": {
		"issuer_url": "jumpcloud",
		"sso_target_url": "https://sso.myexample.jumpcloud.com/saml2/cloudflareaccess",
		"attributes": ["email", "name", "username"],
		"email_attribute_name": "",
		"sign_request": false,
		"idp_public_cert": "MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
	},
	"type": "saml",
	"name": "jumpcloud saml example"
}
```

# Keycloak (SAML)

import { Render } from "~/components";

Keycloak is an open source identity and access management solution built by JBoss.

## Set up Keycloak (SAML)

To set up Keycloak (SAML) as your identity provider:

1. In Keycloak, select the realm that you want Cloudflare Access to use.

2. Go to **Clients** > **Create client**.

3. For **Client type**, select **SAML**.

4. Under **Client ID**, enter your Cloudflare Access callback URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

5. Select **Next**.

6. Change **Name ID format** to **email**.

7. In **Valid redirect URIs**, enter your Cloudflare Access callback URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

8. In **Master SAML Processing URL**, enter the SAML endpoint for your Keycloak realm:

   ```txt
   https://<keycloak_domain>/realms/<realm_name>/protocol/saml
   ```

   Keycloak v17 and later use `/realms/<realm_name>/protocol/saml` by default. Keycloak v16 and earlier may use `/auth/realms/<realm_name>/protocol/saml` instead.

9. If you wish to enable client signatures, enable **Client Signature Required** and select **Save**.
   1. You will need to [follow the steps here to get the certificate and enable it in the Cloudflare dashboard](/cloudflare-one/integrations/identity-providers/signed_authn/).

   2. Import the Access certificate you downloaded into the **Keys** tab. Use **Certificate PEM** as the format.

10. Configure a protocol mapper for the user's email address.

    1. Go to **Clients** > your Cloudflare Access SAML client > **Client scopes**.
    2. Select the dedicated client scope for the client.
    3. Go to **Mappers** > **Add mapper** > **By configuration**.
    4. Select **User Property**.
    5. Set **Property** to `email` and **SAML Attribute Name** to `email`.

    Next, you will need to integrate with Cloudflare Access.

11. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

12. Under **Your identity providers**, select **Add new identity provider**.

13. Choose **SAML** on the next page.

    You will need to input the Keycloak details manually. The examples below should be replaced with the specific domains in use with Keycloak and Cloudflare Access.

    | Field                       | Example                                                            |
    | --------------------------- | ------------------------------------------------------------------ |
    | Single Sign-On URL          | `https://<keycloak_domain>/realms/<realm_name>/protocol/saml`      |
    | IdP Entity ID or Issuer URL | `https://<unique_id>.cloudflareaccess.com/cdn-cgi/access/callback` |
    | Signing certificate         | Use the X509 certificate from the Keycloak realm keys              |

14. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to the login method you want to test.

# LinkedIn

import { Render } from "~/components";

Cloudflare Access allows your users to use LinkedIn as their identity provider (IdP).

## Prerequisites

Sign in to your LinkedIn account before continuing. Configuring LinkedIn as a Cloudflare Access IdP requires a LinkedIn account.

## Set up LinkedIn as an IdP

To configure LinkedIn as an IdP:

1. Go to the [LinkedIn Developer Portal](https://www.linkedin.com/developers).

2. Select **Create App**.

3. On the **Create an app** page, enter an **App name** for your application.

4. Select a **LinkedIn Page** for your application or select **Create a new LinkedIn page** if you do not have a LinkedIn page.

5. Select **Upload a logo** and upload your company logo image file.

6. Select **API Terms of Use** to read the terms of use, and agree to the terms.

7. Select **Create app**.

8. In the **Products** tab of your LinkedIn application, select **Request Access** next to the **Sign In with LinkedIn using OpenID Connect** option.

9. In the **Auth** tab of your LinkedIn application, find the **Client ID** and **Client Secret**.

   ![LinkedIn account settings where you will copy the Client ID and Client Secret](~/assets/images/cloudflare-one/identity/linkedin/lin5.png)

10. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

11. Under **Your identity providers**, select **Add new identity provider**.

12. Select **LinkedIn** as your IdP.

13. In the **App ID** field, copy and paste the **Client ID** from step 9. In the **Client secret** field, copy and paste the **Client secret** from step 9.

14. Select **Save**.

15. In the **Auth** tab of your LinkedIn application, go to **OAuth 2.0 settings** and select the pencil icon next to **Authorized redirect URLs for your app**.

16. Enter the following URL:

    ```txt
    https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
    ```

    <Render file="find-team-name" product="cloudflare-one" />

To test that your connection is working, go to the [Cloudflare dashboard](https://dash.cloudflare.com/) > **Zero Trust** > **Integrations** > **Identity providers** and select **Test** next to your LinkedIn login method.

## Example API configuration

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>"
	},
	"type": "linkedin",
	"name": "my example idp"
}
```

# Okta (SAML)

import { Render } from "~/components";

Cloudflare One can integrate SAML with Okta as an identity provider.

## Set up Okta as a SAML provider

To set up SAML with Okta as your identity provider:

1. On your Okta admin dashboard, go to **Applications** > **Applications**.

2. Select **Create App Integration**.

3. In the pop-up dialog, select **SAML 2.0** and then elect **Next**.

4. Enter an app name and select **Next**.

   ![Entering your Cloudflare One callback URL into Okta](~/assets/images/cloudflare-one/identity/okta-saml/okta-saml-1.png)

5. In the **Single sign on URL** and the **Audience URI (SP Entity ID)** fields, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

6. In the **Attribute Statements** section, enter the following information:
   - **Name**: Enter `email`.
   - **Value**: Enter `user.email`.

7. (Optional) If you are using Okta groups, create a **Group Attribute Statement** with the following information:
   - **Name**: Enter `groups`.
   - **Filter**: Select _Matches regex_ and enter `.*`.

![Configuring attribute statements in Okta](~/assets/images/cloudflare-one/identity/okta-saml/okta-saml-2.png)

8. Select **Next**.

9. Select **I'm an Okta customer adding an internal app** and check **This is an internal app that we have created**.

![Configuring feedback options in Okta](~/assets/images/cloudflare-one/identity/okta-saml/okta-saml-3.png)

9. Select **Finish**.

10. In the **Assignments** tab, select **Assign** and assign individuals or groups you want to grant access to.

11. Select **Done**. The assigned individuals and groups will display in the **Assignments** tab.

![Assigning individuals and groups to Okta application](~/assets/images/cloudflare-one/identity/okta-saml/okta-saml-4.png)

12. To retrieve the SAML provider information, go to the **Sign On** tab and select **View Setup Instructions**. A new page will open showing the **Identity Provider Single Sign-on URL**, **Identity Provider Issuer**, and **X.509 Certificate**. Save this information for configuring your Cloudflare One settings.

![Retrieving SAML provider information in Okta](~/assets/images/cloudflare-one/identity/okta-saml/okta-saml-5.png)

13. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity provider**.

14. Under **Your identity providers**, select **Add new identity provider**, and select _SAML_.

15. Fill in the following information:
    - **Name**: Name your identity provider.
    - **Single Sign On URL**: Enter the Identity Provider Single-Sign-On URL from Okta.
    - **Issuer ID**: Enter the Identity Provider Issuer from Okta, for example `http://www.okta.com/<your-okta-entity-id>`.
    - **Signing Certificate**: Copy-paste the X.509 Certificate from Okta.

16. (Recommended) Enable **Sign SAML authentication request**.

17. (Recommended) Under **SAML attributes**, add the `email` and `groups` attributes. The `groups` attribute is required if you want to create policies based on [Okta groups](/cloudflare-one/traffic-controls/identity-selectors/#okta-saml).

![Adding optional SAML attributes in Cloudflare One](~/assets/images/cloudflare-one/identity/okta-saml/okta-saml-6.png)

18. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to Okta. A success response should return the configured SAML attributes.

:::caution

SAML attributes are only refreshed during authentications with the Okta identity provider. This means the Okta group membership is not updated unless a user logs in and out of the Cloudflare One Client, or logs in to an Access application.

:::

## Example API configuration

```json
{
	"config": {
		"issuer_url": "http://www.okta.com/exkbhqj29iGxT7GwT0h7",
		"sso_target_url": "https://dev-abc123.oktapreview.com/app/myapp/exkbhqj29iGxT7GwT0h7/sso/saml",
		"attributes": ["email", "group"],
		"email_attribute_name": "",
		"sign_request": false,
		"idp_public_certs": [
			"MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
		]
	},
	"type": "saml",
	"name": "okta saml example"
}
```

# Okta

import { Render } from "~/components";

Okta provides cloud software that helps companies manage and secure user authentication to modern applications, and helps developers build identity controls into applications, website web services, and devices. You can integrate Okta with Cloudflare One and build rules based on user identity and group membership. Cloudflare One supports Okta integrations using either the OIDC (default) or [SAML](/cloudflare-one/integrations/identity-providers/okta-saml/) protocol.

Additionally, you can configure Okta to use risk information from Cloudflare One [user risk scores](/cloudflare-one/team-and-resources/users/risk-score/) to create SSO-level policies. For more information, refer to [Send risk score to Okta](/cloudflare-one/team-and-resources/users/risk-score/#send-risk-score-to-okta).

## Prerequisites

- A [Zero Trust Organization](/cloudflare-one/setup/) with any subscription tier (including Free)
- A [Cloudflare One administrator role](/cloudflare-one/roles-permissions/) with `Access Edit` permissions

## Supported features

- **SP-initiated SSO**: When a user goes to an Access application, Access redirects them to sign in with Okta.
- **SCIM provisioning**: Synchronize Okta groups and automatically deprovision users. SCIM currently requires a separate [custom OIDC application](#synchronize-users-and-groups).

## Set up Okta as an OIDC provider (Okta App Catalog)

:::note[Active Directory limitation]
The Okta App Catalog template does not support synchronizing [Active Directory groups](https://help.okta.com/en-us/Content/Topics/Directory/ad-agent-import-groups.htm). If you would like to build policies using AD groups, use the Okta [OIDC app integration](#set-up-okta-as-an-oidc-provider-custom-app-integration) or [SAML app integration](/cloudflare-one/integrations/identity-providers/okta-saml/).
:::

To set up the Okta integration using the Okta Integration Network (OIN) App Catalog:

1. Log in to your Okta admin dashboard.
2. Go to **Applications** > **Applications**.
3. Select **Browse App Catalog**.
4. Search for `Cloudflare` and select the **Cloudflare One** app.
5. Select **Add integration**.
6. In **Application label**, enter a name for the application (for example, `Cloudflare Access`).
7. In **Team domain**, enter your Cloudflare Zero Trust team name (only the subdomain prefix, do not include `.cloudflareaccess.com`):

   ```txt
   <your-team-name>
   ```

   <Render file="find-team-name" product="cloudflare-one" />

8. In the **Sign On** tab, copy the **Client ID** and **Client secret** and paste these into `App ID` and `Client secret`.
9. Copy your Okta Account URL (without the `-admin` value) and copy it into the Cloudflare Okta setup field.

## Set up Okta as an OIDC provider (Custom App Integration)

1. Log in to your Okta admin dashboard and go to **Applications** > **Applications**.

2. Select **Create App Integration**.

3. For the **Sign-in method**, select **OIDC - OpenID Connect**.

   ![Creating an OIDC application in Okta](~/assets/images/cloudflare-one/identity/okta/okta-1.png)

4. For the **Application type**, select **Web Application**. Select **Next**.

5. Enter any name for the application. In the **Sign-in redirect URIs** field, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

6. Choose the desired **Assignment** option and select **Save**.

7. From the application view, go to the **Sign On** tab.

8. Scroll down to **Token claims** and select **Show legacy configuration** > **Edit**.

   ![Configuring the Groups claim filter in Okta](~/assets/images/cloudflare-one/identity/okta/okta-2.png)


9. Set **Groups claim filter** to _Matches regex_ and its value to `.*`.

:::note[Token claim expressions]
- Groups managed outside of Okta (for example, Microsoft Entra ID or Google groups) may require different regex values. For more information, refer to the Okta documentation on [Groups Claims](https://support.okta.com/help/s/article/Why-isnt-my-Groups-claim-returning-Active-Directory-groups) and [OpenID Connect Claims](https://support.okta.com/help/s/article/Can-we-retrieve-both-Active-Directory-and-Okta-groups-in-OpenID-Connect-claims).
- To configure more complex expressions, refer to Okta's [token claims documentation](https://help.okta.com/okta_help.htm?type=oie&locale=en&id=federated-claims-overview).
:::

10. In the **General** tab, copy the **Client ID** and **Client secret**.

    ![Finding your Client credentials in Okta](~/assets/images/cloudflare-one/identity/okta/okta-3.png)

<Render file="access/okta-zt-steps" product="cloudflare-one" />

## Synchronize users and groups

The Okta integration allows you to synchronize IdP groups and automatically deprovision users using [SCIM](/cloudflare-one/team-and-resources/users/scim/). To enable SCIM provisioning between Access and Okta, you need two separate app integrations in Okta:

- The OIDC application you created when adding Okta as an identity provider. You can create this application via the [Okta App Catalog](#set-up-okta-as-an-oidc-provider-okta-app-catalog) or via a [Custom App Integration](#set-up-okta-as-an-oidc-provider-custom-app-integration).
- A second Okta application of type **SCIM 2.0 Test App (Header Auth)**. This is technically a SAML app but is responsible for sending user and group info via SCIM.

<Render file="access/scim-policy-behavior" product="cloudflare-one" />

:::note
If you would like to only maintain one Okta app instance, Okta does support SAML and SCIM within the same application. Create a [generic SAML integration](/cloudflare-one/integrations/identity-providers/generic-saml/) and configure those values in the **Sign-On** field of your Okta SCIM application.
:::

### 1. Enable SCIM in Cloudflare One

<Render
	file="access/enable-scim-on-dashboard"
	product="cloudflare-one"
	params={{ idp: "Okta" }}
/>

### 2. Configure SCIM in Okta

1. On your Okta admin dashboard, go to **Applications** > **Applications**.

2. Select **Browse App Catalog**.

3. Search for `SCIM Header Auth` and select **SCIM 2.0 Test App (Header Auth)**.

4. Select **Add Integration**.

5. On the **General Settings** tab, name your application and select **Next**.

6. On the **Sign-on Options** tab, ensure that **SAML 2.0** is selected.

7. Under **Credential Details**, set **Application username format** to either _Okta Username_ or _Email_. This value will be used for the SCIM `userName` attribute.
   :::note
   The `userName` attribute must match the user's email address in Cloudflare One.
   :::

8. Select **Done** to create the integration.

9. On the **Provisioning** tab, select **Configure API Integration**.

10. Select **Enable API integration**.

11. In the **Base URL** field, enter the **SCIM Endpoint** obtained from Cloudflare One.

12. In the **API Token** field, enter the **SCIM Secret** obtained from Cloudflare One.

    ![Enter SCIM values into Okta](~/assets/images/cloudflare-one/identity/okta/enter-scim-values.png)

13. Select **Test API Credentials** to ensure that the credentials were entered correctly. Select **Save**.

14. On the **Provisioning** tab, select **Edit** and enable:
    - **Create Users**
    - **Update User Attributes**
    - **Deactivate Users**

    ![Configure provisioning settings in Okta](~/assets/images/cloudflare-one/identity/okta/enable-provisioning.png)

15. In the **Assignments** tab, add the users you want to synchronize with Cloudflare Access. You can add users in batches by assigning a group. If a user is removed from the application assignment via a either direct user assignment or removed from the group that was assigned to the app, this will trigger a deprovisioning event from Okta to Cloudflare.

16. In the **Push Groups** tab, add the Okta groups you want to synchronize with Cloudflare Access. These groups will display in the Access policy builder and are the group memberships that will be added and removed upon membership change in Okta.

    :::note
    Groups in this SCIM app Push Groups integration should match the groups in your base [OIDC app integration](/cloudflare-one/integrations/identity-providers/okta/#set-up-okta-as-an-oidc-provider). Matching the groups keeps the Gateway identity synchronized with the groups that Okta returns when the user authenticates to Access.
    :::

To verify the integration, select **View Logs** in the Okta SCIM application.

<Render file="access/verify-scim-provisioning" product="cloudflare-one" />

## Example API Configuration

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>",
		"okta_account": "https://dev-abc123.oktapreview.com"
	},
	"type": "okta",
	"name": "my example idp"
}
```

## Troubleshooting

### Failed to fetch user/group information from the identity

If you see the error `Failed to fetch user/group information from the identity`, double-check your Okta configuration:

- If your Okta tenant has more than 100 groups, include an Okta API token in the identity provider configuration. When the user's OIDC token contains no groups or reaches Okta's 100-group limit, Cloudflare uses the API token during authentication to fetch the user's complete group membership. This setting is specific to Okta and is not part of SCIM. The API token does not populate group options in the Access policy builder; those come from groups provisioned through SCIM.
- If Okta returns more than 100 groups in a user's OIDC token, Okta may omit some group memberships from the token. This is an Okta token claim limitation, not a Cloudflare limit. If a required group is omitted, Cloudflare cannot evaluate policies that depend on that group. To avoid this, narrow the Okta groups claim filter so that only groups used in Cloudflare policies are included. For more information, refer to [Okta's group functions and dynamic allowlists documentation](https://support.okta.com/help/s/article/limitations-of-group-functions-dynamic-allowlists?language=en_US).
- The request may be blocked by the [ThreatInsights feature](https://help.okta.com/en/prod/Content/Topics/Security/threat-insight/ti-index.htm) within Okta.

### Okta Groups selector is empty or missing groups in the policy builder

The **Okta Groups** selector in the Access policy builder is populated by groups provisioned through SCIM, not by the optional Okta API token. If groups are missing from the selector:

- In Okta, go to the SCIM application's **Push Groups** tab and push the groups you want to use in Access policies. For more information, refer to [Configure SCIM in Okta](#2-configure-scim-in-okta).
- Make sure the groups you push through SCIM match the groups returned in the OIDC `groups` claim.
- Adding an Okta API token will not add groups to the selector. The API token is only used during authentication to fetch a user's group membership.

# One-time PIN login

import { Tabs, TabItem, Render, APIRequest } from "~/components";

Cloudflare Access can send a one-time PIN (OTP) to approved email addresses as an alternative to integrating an identity provider. You can simultaneously configure OTP login and the identity provider of your choice to allow users to select their own authentication method.

New Zero Trust organizations use the [Cloudflare identity provider](/cloudflare-one/integrations/identity-providers/cloudflare/) as their default login method. OTP is no longer added automatically, but you can set it up at any time using the following steps.

For example, if your team uses Okta but you are collaborating with someone outside your organization, you can use OTP to grant access to guests.

<Render file="access/one-time-pin-warning" product="cloudflare-one" />

## Set up OTP

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Under **Your identity providers**, select **Add new identity provider**.
3. Select **One-time PIN**.

</TabItem> <TabItem label="API">

Make a `POST` request to the [Identity Providers](/api/resources/zero_trust/subresources/identity_providers/methods/create/) endpoint:

<APIRequest
	path="/accounts/{account_id}/access/identity_providers"
	method="POST"
	json={{
		name: "One-time PIN login",
		type: "onetimepin",
		config: {},
	}}
/>

</TabItem> <TabItem label="Terraform (v5)">

1.  Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
    - `Access: Organizations, Identity Providers, and Groups Write`

2.  Configure the [`cloudflare_zero_trust_access_identity_provider`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) resource:

        	```tf
        	resource "cloudflare_zero_trust_access_identity_provider" "onetimepin_login" {
        		account_id = var.cloudflare_account_id
        		name       = "One-time PIN login"
        		type       = "onetimepin"
        		config 		 = {}
        	}
        	```

</TabItem></Tabs>

To grant a user access to an application, simply add their email address to an [Access policy](/cloudflare-one/access-controls/policies/policy-management/#create-a-policy).

## Allow OTP emails through your email gateway

If your email gateway blocks, rate limits, or scans OTP emails, allowlist the sender domain `notify.cloudflare.com`, the sender address `noreply@notify.cloudflare.com`, and these IP addresses:

- `104.30.16.2`
- `104.30.16.3`
- `104.30.16.4`
- `104.30.16.5`
- `104.30.16.6`
- `104.30.16.7`

## Log in with OTP

To log in to Access using the one-time PIN:

1. Go to the application protected by Access.
2. On the Access login page, enter your email address and select **Send login code**.
   ![Enter email to sign in with OTP.](~/assets/images/cloudflare-one/identity/otp/otp1.png)
3. If the email is allowed by an Access policy, you will receive a PIN in your inbox. This secure PIN expires 10 minutes after the initial request.

:::note

By design, blocked users will not receive an email. The login page will always say **A code has been emailed to you**, regardless of whether or not an email was sent.
:::

4. Paste the PIN into the Access login page and select **Sign in**.
   ![Enter PIN to sign in.](~/assets/images/cloudflare-one/identity/otp/otp2.png)
   - If the code was valid, you will be redirected to the application.
   - If the code was invalid, you will see **That account does not have access.**
   - If you see **This One-Time PIN has already been used**, the code was already consumed. This typically occurs when an email security tool on your network automatically scans the email and follows the link before you enter the code. Select **Request new code** and try again.

:::note

Access only logs an authentication attempt after the user enters a code. If the user enters their email but never submits a code, the event will not appear in your [audit logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/#authentication-logs).
:::

## OTP behavior and limits

Keep the following behavior in mind when troubleshooting OTP logins:

- Each PIN is single-use.
- Requesting a new PIN invalidates the previous PIN.
- Cloudflare only sends the email if the user is allowed by an Access policy.
- Third-party mail security tools may consume the link before the user does, which makes the code appear already used.

If users repeatedly fail to sign in, request a fresh code and verify that your mail filtering or link-scanning product is allowlisting `noreply@notify.cloudflare.com`.

# OneLogin

import { Render } from "~/components";

OneLogin provides SSO identity management. Cloudflare Access supports OneLogin as an OIDC identity provider.

## Set up OneLogin as an OIDC provider

### 1. Create an application in OneLogin

1. Log in to your OneLogin admin portal.

2. Go to **Applications** > **Applications** and select **Add App**.

3. Search for `OIDC` and select **OpenId Connect (OIDC)** by OneLogin, Inc.

4. In **Display Name**, enter any name for your application. Select **Save**.

5. Next, go to **Configuration**. In the **Redirect URI** field, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

6. Select **Save**.

7. Go to **Access** and choose the **Roles** that can access this application. Select **Save**.

8. Go to **SSO** and select **Show client secret**.

9. Copy the **Client ID** and **Client Secret**.

### 2. Add OneLogin to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **OneLogin**.

4. Fill in the following information:
   - **Name**: Name your identity provider.
   - **App ID**: Enter your OneLogin client ID.
   - **Client secret**: Enter your OneLogin client secret.
   - **OneLogin account URL**: Enter your OneLogin domain, for example `https://<your-domain>.onelogin.com`.

5. (Optional) To enable SCIM, refer to [Synchronize users and groups](/cloudflare-one/integrations/identity-providers/generic-oidc/#synchronize-users-and-groups).

6. (Optional) Under **Optional configurations**, enter [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that you wish to add to your user's identity.

7. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to OneLogin.

## Example API Config

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>",
		"onelogin_account": "https://mycompany.onelogin.com"
	},
	"type": "onelogin",
	"name": "my example idp"
}
```

# OneLogin (SAML)

import { Render } from "~/components";

OneLogin provides SSO identity management. Cloudflare Access supports OneLogin as an SAML identity provider.

## Set up OneLogin as a SAML provider

## 1. Create an application in OneLogin

1. Log in to your OneLogin admin portal.

2. Select **Apps** > **Add Apps**.

3. Under **Find Applications**, search for **Cloudflare Access**.

4. Select the result sponsored by **Cloudflare, Inc**. You can customize the name or logo.

5. Select **Save**. You can change this information at any time.

6. Select the **Configuration** tab.

7. In the **Cloudflare Access Authorization Domain** field, paste your team domain:

   ```txt
   https://<your-team-name>.cloudflareaccess.com
   ```

   <Render file="find-team-name" product="cloudflare-one" />

8. Select the **Parameters** tab, select **Add Parameter** and enter your values for **Cloudflare Access Field**.

9. Select the **Access** tab

10. In Roles, use the mapping to programmatically and automatically assign users that can access the application.

    ![OneLogin SAML Application Access interface with available Roles listed](~/assets/images/cloudflare-one/identity/onelogin/onelogin-saml-6.png)

11. Select the **SSO** tab.

12. Copy the OneLogin **SAML 2.0 Endpoint (HTTP)** to the Cloudflare Single Sign On URL.

13. Copy the OneLogin **Issuer URL** to the Cloudflare **IdP Entity ID**.

14. Copy the **X.509 Certificate** to the Cloudflare **Signing Certificate**.

    ![OneLogin SAML Application SSO interface with SAML2.0 sign on method, Issuer URL, and X.509 Certificate](~/assets/images/cloudflare-one/identity/onelogin/onelogin-saml-7.png)

### 2. Add OneLogin to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **SAML**.

4. Input the details from your OneLogin account in the fields.

5. (Optional) To enable SCIM, refer to [Synchronize users and groups](/cloudflare-one/integrations/identity-providers/generic-saml/#synchronize-users-and-groups).

6. (Optional) Under **Optional configurations**, configure [additional SAML options](/cloudflare-one/integrations/identity-providers/generic-saml/#optional-configurations). If you added other SAML headers and attribute names to OneLogin, be sure to add them to Cloudflare.

7. Select **Save**.

To test that your connection is working, go to **Integrations** > **Identity providers** and select **Test** next to the login method you want to test.

## Download SP metadata (optional)

OneLogin SAML allows administrators to upload metadata files from the service provider.

To add a metadata file to your OneLogin SAML configuration:

1. Download your unique SAML metadata file at the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/saml-metadata
   ```

2. Save the file as an XML document.

3. Upload the XML document to **OneLogin**.

## Example API configuration

```json
{
	"config": {
		"issuer_url": "https://app.onelogin.com/saml/metadata/1b84ee45-d4fa-4373-8853-abz438942123",
		"sso_target_url": "https://sandbox.onelogin.com/trust/saml2/http-post/sso/123456",
		"attributes": ["email"],
		"email_attribute_name": "",
		"sign_request": false,
		"idp_public_cert": "MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
	},
	"type": "saml",
	"name": "onelogin saml example"
}
```

# PingFederate

import { Render } from "~/components";

The PingFederate offering from PingIdentity provides SSO identity management. Cloudflare Access supports PingFederate as a SAML identity provider.

## Set up PingFederate as an identity provider

1. Log in to your **Ping** dashboard and go to **Applications**.

2. Select **Add Application**.

3. Select **New SAML Application**.

4. Complete the fields for name, description, and category.

These can be any value. A prompt displays to select a signing certificate to use.

5. In the **SAML attribute configuration** dialog select **Email attribute** > **urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress**.

6. Go to **SP Connections** > **SP Connection** > **Credentials**.

7. Add the matching certificate that you upload into the Cloudflare SAML configuration for Ping. Select **Include the certificate in the signature `<KEYINFO>` element**.

:::note
There is an additional setting for PingFederate prior to 9.0.
:::

8. In the **Signature Policy** tab, disable the option to **Always Sign Assertion**.

9. Leave the option enabled for **Sign Response As Required**.

This ensures that SAML destination headers are sent during the integration.

In versions 9.0 above, you can leave both of these options enabled.

10. A prompt displays to download the SAML metadata from Ping.

This file shares several fields with Cloudflare Access so you do not have to input this data.

11. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

12. Under **Your identity providers**, select **Add new identity provider**.

13. Select SAML.

14. In the **IdP Entity ID** field, enter the following URL:

```txt
https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
```

<Render file="find-team-name" product="cloudflare-one" />

15. Fill the other fields with values from your Ping dashboard.

16. Select **Save**.

To test that your connection is working, go to **Authentication** > **Login methods** and select **Test** next to the login method you want to test.

## Example API configuration

```json
{
	"config": {
		"issuer_url": "https://example.cloudflareaccess.com/cdn-cgi/access/callback",
		"sso_target_url": "https://sso.connect.pingidentity.com/sso/idp/SSO.saml2?idpid=aebe6668-32fe-4a87-8c2b-avcd3599a123",
		"attributes": ["PingOne.AuthenticatingAuthority", "PingOne.idpid"],
		"email_attribute_name": "",
		"sign_request": false,
		"idp_public_cert": "MIIDpDCCAoygAwIBAgIGAV2ka+55MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UEC.....GF/Q2/MHadws97cZg\nuTnQyuOqPuHbnN83d/2l1NSYKCbHt24o"
	},
	"type": "saml",
	"name": "ping saml example"
}
```

# PingOne

import { Render } from "~/components";

The PingOne cloud platform from PingIdentity provides SSO identity management. Cloudflare Access supports PingOne as an OIDC identity provider.

## Set up PingOne as an OIDC provider

### 1. Create an application in PingOne

1. In your PingIdentity environment, go to **Connections** > **Applications**.
2. Select **Add Application**.
3. Enter an **Application Name**.
4. Select **OIDC Web App** and then **Save**.
5. Select **Resource Access** and add the **email** and **profile** scopes.
6. In the **Configuration** tab, select **General**.
7. Copy the **Client ID**, **Client Secret**, and **Environment ID** to a safe place. These IDs will be used in a later step to add PingOne to Cloudflare One.
8. In the **Configuration** tab, select the pencil icon.
9. In the **Redirect URIs** field, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

10. Select **Save**.

### 2. Add PingOne to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Under **Your identity providers**, select **Add new identity provider**.
3. Select **PingOne**.
4. Input the **Client ID**, **Client Secret**, and **Environment ID** generated previously.
5. (Optional) Enable [Proof of Key Exchange (PKCE)](https://www.oauth.com/oauth2-servers/pkce/). PKCE will be performed on all login attempts.
6. (Optional) To enable SCIM, refer to [Synchronize users and groups](/cloudflare-one/integrations/identity-providers/generic-oidc/#synchronize-users-and-groups).
7. (Optional) Under **Optional configurations**, enter [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) that you wish to add to your users' identity.
8. Select **Save**.

You can now [test your connection](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) and create [Access policies](/cloudflare-one/access-controls/policies/) based on the configured login method.

## Example API configuration

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>",
		"ping_env_id": "<your ping environment id>"
	},
	"type": "ping",
	"name": "my example idp"
}
```

# PingOne (SAML)

import { GlossaryTooltip } from "~/components";

The PingOne cloud platform from PingIdentity provides SSO identity management. Cloudflare Access supports PingOne as a SAML identity provider.

## Set up PingOne as a SAML provider

## 1. Create an application in PingOne

1. In your PingIdentity environment, go to **Connections** > **Applications**.

2. Select **Add Application**.

3. Enter an **Application Name**.

4. Select **SAML Application**.

5. Select **Configure**.

6. To fill in your Cloudflare Access metadata:
   1. Select **Import from URL**.
   2. Set the **Import URL** to:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/saml-metadata
   ```

   where `<your-team-name>` is your Cloudflare One <GlossaryTooltip term="team domain">team name</GlossaryTooltip>. 3. Select **Import**. 4. **Save** the configuration.

7. In the **Configuration** tab, select **Download metadata** and save the XML metadata file. This file will be used in a later step to add PingOne to Cloudflare One.

8. In the **Attribute Mappings** tab, add the following required attributes (case sensitive) and select **Save**.

   | Application attribute | Outgoing value |
   | --------------------- | -------------- |
   | `email`               | Email Address  |
   | `givenName`           | Given Name     |
   | `surName`             | Family Name    |


   These [SAML attributes](/cloudflare-one/integrations/identity-providers/generic-saml/#saml-attributes) tell Cloudflare Access who the user is.

9. Set the application to **Active**.

### 2. Add PingOne to Cloudflare One

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Select **SAML**.

4. Upload your PingOne XML metadata file.

5. (Optional) To enable SCIM, refer to [Synchronize users and groups](/cloudflare-one/integrations/identity-providers/generic-saml/#synchronize-users-and-groups).

6. (Optional) Under **Optional configurations**, configure [additional SAML options](/cloudflare-one/integrations/identity-providers/generic-saml/#optional-configurations).

7. Select **Save**.

You can now [test your connection](/cloudflare-one/integrations/identity-providers/#test-idps-in-cloudflare-one) and create [Access policies](/cloudflare-one/access-controls/policies/) based on the configured login method and SAML attributes.

# Signed AuthN requests (SAML)

import { GlossaryTooltip } from "~/components";

In a SAML request flow, Cloudflare Access functions as the service provider (SP) to the identity provider (IdP). Cloudflare Access sends a SAML request to your IdP. The signing certificate that you upload from your SAML provider verifies the response.

In some cases, administrators need to verify that the request from the SP is authentic. By validating both the requests from the SP and the responses from the IdP, teams can ensure that operations in the SAML relationship are signed in both directions.

Cloudflare Access supports this requirement in the form of Signed AuthN requests. When enabled, Access sends a signature embedded in an HTTP POST request that contains the AuthN details.

## Set up Signed AuthN requests

To set up Signed AuthN requests:

1. In Cloudflare One, go to **Integrations** > **Identity providers**.

2. Under **Your identity providers**, select **Add new identity provider**.

3. Choose **SAML** on the next page.

4. Complete the fields in the dialog.

5. Go to this URL to find the certificate:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/public-cert
   ```

   Ensure that your IdP validation uses the most recent certificate. Cloudflare Access routinely rotates the public key as a security measure.

   Cloudflare Access uses a certificate that includes the following 2 distinguished name fields:

   - **Issuer Distinguished Name** - `CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare`
   - **Subject Distinguished Name** - `CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare`

   Most IdP configurations require 3 components to enforce AuthN signature verification:

   - **Certificate issuer [distinguished name (DN)](https://knowledge.digicert.com/generalinformation/INFO1745.html)**
   - **Certificate subject distinguished name**
   - **Public certificate**

6. In your IdP account, replace your authorization domain with the <GlossaryTooltip term="team domain">team domain</GlossaryTooltip> generated by Cloudflare Access.

   This is an example format:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/public-cert
   ```

# Yandex

import { Render } from "~/components";

Yandex is a web search engine that also offers identity provider (IdP) services.

## Set up Yandex

To set up Yandex for Cloudflare Access:

1. Log in to your Yandex account.

2. Select **Open a new OAuth Application**.

3. Select **New client**.

4. Complete the required fields.

5. Choose **Yandex.Passport API** to set the basic scopes.

6. Select the **Access to email address**, **Access to user avatar,** and **Access to username, first name and surname, gender** options.

7. Select **Platform** and select **Web Services.**

8. In the **Callback URL #1** field, enter the following URL:

   ```txt
   https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
   ```

   <Render file="find-team-name" product="cloudflare-one" />

   ![Yandex Platform interface with Web services checked and callback URI in open form field](~/assets/images/cloudflare-one/identity/yandex/yandex-3.png)

9. Select **Add**.

10. Scroll to the **Platforms** card, and select **Submit**.

    **Yandex OAuth** card titled **Cloudflare Access App** displays.

11. Copy the **ID** and **Password**.

12. In Cloudflare One, go to **Integrations** > **Identity providers**.

13. Under **Your identity providers**, select **Add new identity provider**.

14. Select Yandex.

15. Paste the ID and password in the appropriate fields.

16. Select **Save**.

## Example API Config

```json
{
	"config": {
		"client_id": "<your client id>",
		"client_secret": "<your client secret>"
	},
	"type": "yandex",
	"name": "my example idp"
}
```

# CrowdStrike

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	params={{ one: "Crowdstrike" }}
	product="cloudflare-one"
/>

## Prerequisites

Device posture with Crowdstrike requires:

- Falcon Enterprise plan or above
- Crowdstrike agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

## Set up CrowdStrike as a service provider

### 1. Obtain CrowdStrike settings

The following CrowdStrike values are needed to set up the CrowdStrike posture check:

- Client ID
- Client Secret
- Base URL
- Customer ID

To retrieve those values:

1. Log in to your Falcon Dashboard.
2. Go to **Support and resources** > **API Clients and Keys**.
3. Select **Create API client** and enter any name for the client.
4. Turn on the following API permissions:
   | Scope | Permission |
   | ----- | ---------- |
   | Hosts | Read |
   | Zero Trust Assessment | Read |
5. Select **Create**.
6. Copy the **Client ID**, **Client Secret**, and **Base URL** to a safe place.
7. Go to **Host setup and management** > **Sensor downloads** and copy your **Customer ID**.

### 2. Add CrowdStrike as a service provider

<Render
	file="posture/add-service-provider"
	params={{ provider: "Crowdstrike" }}
	product="cloudflare-one"
/>

5. Enter the **Client ID** and **Client secret** you noted down above.
6. In **Rest API URL**, enter your **Base URL**.
7. Enter your **Customer ID**.
8. Choose a **Polling frequency** for how often Cloudflare Zero Trust should query CrowdStrike for information.
9. Select **Test and save**.

### 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	params={{ one: "Crowdstrike" }}
	product="cloudflare-one"
/>

## Device posture attributes

Device posture data is gathered from the [CrowdStrike Zero Trust Assessment APIs](https://falcon.us-2.crowdstrike.com/documentation/156/zero-trust-assessment-apis). To learn more about how scores are calculated, refer to the [CrowdStrike Zero Trust Assessment](https://falcon.us-2.crowdstrike.com/documentation/138/zero-trust-assessment) documentation.

| Selector      | Description                                                                                       | Value                                                                                           |
| ------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| OS            | OS signal score                                                                                   | `1` to `100`                                                                                    |
| Overall       | Overall ZTA score                                                                                 | `1` to `100`                                                                                    |
| Sensor config | Sensor signal score                                                                               | `1` to `100`                                                                                    |
| Version       | ZTA score version                                                                                 | `2.1.0`                                                                                         |
| State         | Current online status of the device                                                               | _Online_, _Offline_, or _Unknown_                                                               |
| Last seen     | Elapsed time since the device was last seen. Only returned if its state is `online` or `unknown`. | In the last 1 hour, 3 hours, 6 hours, 12 hours, 24 hours, 7 days, 30 days, or more than 30 days |

# Custom device posture integration

import { Render } from "~/components";

Cloudflare One allows you to enforce custom device posture checks on your applications. This involves configuring a Cloudflare One Client service-to-service integration that periodically calls the external API of your choice, whether it is a third-party endpoint provider or a home built solution. When called, the API will receive device identifying information from Cloudflare and be expected to return a value between `0` to `100`. You can then set up a device posture check that determines if the returned value counts as a pass or fail; for example, you could allow access to a user only if their device has a posture value greater than `60`.

```mermaid
sequenceDiagram
    participant Cloudflare One Client
		participant Cloudflare Access
    participant External API
    Cloudflare One Client->>Cloudflare Access: Client ID and Secret
		Cloudflare Access->>External API: Application token
		Cloudflare One Client->>External API: JSON with user and device identity
    External API-->>Cloudflare One Client: JSON with 0-100 result
```

## External API requirements

The custom service provider integration works with any API service that meets the following specifications. For an example of a custom device posture integration API, refer to our [Cloudflare Workers sample code](https://github.com/cloudflare/custom-device-posture-integration-example-worker).

### Authentication

The Cloudflare One Client authenticates to the external API through Cloudflare Access. The external API should [validate the application token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/) issued by Cloudflare Access to ensure that any requests which bypass Access (for example, due to a network misconfiguration) are rejected.

### Data passed to external API

Cloudflare will pass the following parameters to the configured API endpoint. You can use this data to identify the device and assign a posture score. For some devices, not all identifying information will apply, in which case the field will be blank. A maximum of 1,000 devices will be sent per a request.

| Field           | Description                                                  |
| --------------- | ------------------------------------------------------------ |
| `device_id`     | Device UUID assigned by the Cloudflare One Client            |
| `email`         | Email address used to authenticate the Cloudflare One Client |
| `serial_number` | Device serial number                                         |
| `mac_address`   | Device MAC address                                           |
| `virtual_ipv4`  | Device virtual IPv4 address                                  |
| `hostname`      | Device name                                                  |

:::note

Devices are identified by their serial numbers. You must ensure that each of your devices has a unique serial number. If multiple devices have the same serial number, Cloudflare and your external API will not be able to accurately match them.

:::

Example request body:

```json
{
  "devices": {
    [
      {
        "device_id": "9ece5fab-7398-488a-a575-e25a9a3dec07",
        "email": "jdoe@mycompany.com",
        "serial_number": "jdR44P3d",
        "mac_address": "74:1d:3e:23:e0:fe",
        "virtual_ipv4": "100.96.0.10",
        "hostname": "string",
      },
      {...},
      {...}
    ]
  }
}
```

### Expected response from external API

For each Cloudflare `device_id`, the API service is expected to return a posture score and optionally a third-party device ID.

| Field    | Description                                         |
| -------- | --------------------------------------------------- |
| `s2s_id` | Third party device ID (empty string if unavailable) |
| `score`  | Integer value between `0` - `100`                   |

Example response body:

```json
{
  "result": {
    "9ece5fab-7398-488a-a575-e25a9a3dec07": {
      "s2s_id": "",
      "score": 10
    },
    "device_id2": {...},
    "device_id3": {...}
  }
}
```

## Set up custom device posture checks

### 1. Create a service token

The Cloudflare One Client uses an Access Client ID and Access Client Secret to securely authenticate to the external API. If you do not already have an Access Client ID and Access Client Secret, [create a new service token](/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token).

### 2. Create an Access application

Next, secure the external API behind Cloudflare Access so that the Cloudflare One Client can authenticate with the service token. To add the API endpoint to Access:

1. [Create a self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for your API endpoint.
2. Add the following Access policy to the application. Make sure that **Action** is set to _Service Auth_ (not _Allow_).

   | Action       | Rule type | Selector      | Value          |
   | ------------ | --------- | ------------- | -------------- |
   | Service Auth | Include   | Service Token | `<TOKEN-NAME>` |

### 3. Add a service provider integration

To create a custom service-to-service integration:

<Render
	file="posture/add-service-provider"
	product="cloudflare-one"
	params={{ provider: "Custom service provider" }}
/>

5. In **Access client ID** and **Access client secret**, enter the Access service token used to authenticate to your external API.
6. In **Rest API URL**, enter the external API endpoint that Cloudflare will query for posture information (for example, `https://api.example.com`). For more information, refer to [External API requirements](#external-api-requirements).
7. In **Polling frequency**, choose how often Cloudflare One should query the external API for information.
8. Select **Test and save**. The test checks if Cloudflare can authenticate to the API URL using the provided Access credentials.

Next, [configure a device posture check](#4-configure-the-posture-check) to determine if a given posture score constitutes a pass or fail.

### 4. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "Custom service provider" }}
/>

## Device posture attributes

| Selector | Description                            | Value        |
| -------- | -------------------------------------- | ------------ |
| Score    | Posture score returned by external API | `0` to `100` |

# Service providers

Service-to-service integrations allow the Cloudflare One Client to get device posture data from a third-party API. To use this feature, you must [deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) to your devices and enable the desired posture checks.

## Supported Client modes

- Traffic and DNS mode
- Traffic only mode
- Posture only mode

## Supported operating systems

| Device posture check                                                                        | macOS | Windows | Linux | iOS | Android/ChromeOS |
| ------------------------------------------------------------------------------------------- | ----- | ------- | ----- | --- | ---------------- |
| [Custom integration](/cloudflare-one/integrations/service-providers/custom/)            | ✅    | ✅      | ✅    | ✅  | ✅               |
| [Crowdstrike](/cloudflare-one/integrations/service-providers/crowdstrike/)              | ✅    | ✅      | ✅    | ❌  | ❌               |
| [Kolide](/cloudflare-one/integrations/service-providers/kolide/)                        | ✅    | ✅      | ✅    | ❌  | ❌               |
| [Microsoft Endpoint Manager](/cloudflare-one/integrations/service-providers/microsoft/) | ✅    | ✅      | ❌    | ❌  | ❌               |
| [SentinelOne](/cloudflare-one/integrations/service-providers/sentinelone/)              | ✅    | ✅      | ✅    | ❌  | ❌               |
| [Tanium](/cloudflare-one/integrations/service-providers/taniums2s/)                     | ✅    | ✅      | ✅    | ❌  | ❌               |
| [Uptycs](/cloudflare-one/integrations/service-providers/uptycs/)                        | ✅    | ✅      | ✅    | ❌  | ❌               |
| [Workspace ONE](/cloudflare-one/integrations/service-providers/workspace-one/)          | ✅    | ✅      | ✅    | ❌  | ❌               |

# Kolide

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	params={{ one: "Kolide" }}
	product="cloudflare-one"
/>

## Prerequisites

- Kolide agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

## Set up Kolide as a service provider

### 1. Create a Client Secret in Kolide

1. Log in to your Kolide dashboard.
2. Select your profile and go to **Settings** > **Developers**.
3. Select **Create New Key**.
4. Enter a **Key Name** and select **Save**.
5. Copy the **Secret token** to a safe place. This will be your Client Secret.

### 2. Add Kolide as a service provider

<Render
	file="posture/add-service-provider"
	product="cloudflare-one"
	params={{ provider: "Kolide" }}
/>

5. Enter the **Client secret** you noted down above.
6. Choose a **Polling frequency** for how often Cloudflare One should query Kolide for information.
7. Select **Test and save**.

### 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "Kolide" }}
/>

## Device posture attributes

Device posture data is gathered from the [Kolide API](https://kolideapi.readme.io/reference/get_devices-id).

| Selector    | Description                                   |
| ----------- | --------------------------------------------- |
| Auth state  | The authorization status of the device, one of: 'Good', 'Notified', 'Will Block', or 'Blocked' |

# Microsoft Endpoint Manager

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	params={{ one: "Microsoft" }}
	product="cloudflare-one"
/>

## Prerequisites

Device posture with Microsoft Endpoint Manager requires:

- An Intune license
- Microsoft Endpoint Manager is managing the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

:::note
Linux devices are not supported. Microsoft Intune does not collect hardware details, including serial numbers, for Linux devices. Cloudflare matches devices using serial numbers, so posture checks return empty results for Linux.

Refer to Microsoft's [device details](https://learn.microsoft.com/en-us/intune/device-management/inventory-and-status/device-details) documentation.
:::

## 1. Obtain Microsoft Graph settings

The following values are required:

- Client secret
- Application (client) ID
- Direct (tenant) ID

To retrieve those values:

1. Log in to your Microsoft Dashboard.
2. Go to **App Registrations** and select **New Registrations**.
3. Copy the `Application (client) ID` value to a safe place. This will be your Client ID.
4. Copy the `Directory (tenant) ID` value to a safe place. This will be your Customer ID.
5. Go to **Certificates & Secrets** and select **New client secret**.
6. Fill in a description and how long the secret should be valid.
7. After completing the form, immediately copy the resulting secret. This will be your Client Secret.
8. Go to **API Permissions** and select **Add permission**.
9. Select **Microsoft Graph**.
10. Select **Application permissions**.
11. Add `DeviceManagementManagedDevices.Read.All`.
12. If the permission status shows **Not granted**, select **Grant admin consent**.

## 2. Add Intune as a service provider

<Render
	file="posture/add-service-provider"
	product="cloudflare-one"
	params={{ provider: "Microsoft Endpoint Manager" }}
/>

5. Enter the **Client ID**, **Client secret** and **Customer ID** as you noted down above.
6. Select a **Polling frequency** for how often Cloudflare One should query Microsoft Graph API for information.
7. Select **Test and save**.

## 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "Microsoft Endpoint Manager" }}
/>

## Device posture attributes

The Microsoft Endpoint Manager device posture check relies on information from the Microsoft Graph API. Refer to Microsoft's [ComplianceState](https://docs.microsoft.com/en-us/graph/api/resources/intune-devices-compliancestate?view=graph-rest-1.0) and [List managedDevices](https://docs.microsoft.com/en-us/graph/api/intune-devices-manageddevice-list?view=graph-rest-1.0) documentation for a list of properties returned by the API.

To learn more about how to control ComplianceState, refer to Microsoft's [compliance policies guide](https://docs.microsoft.com/en-us/mem/intune/protect/device-compliance-get-started).

# SentinelOne

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	params={{ one: "SentinelOne" }}
	product="cloudflare-one"
/>

## Prerequisites

- SentinelOne agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

## Set up SentinelOne as a service provider

### 1. Obtain SentinelOne settings

The following SentinelOne values are needed to set up the SentinelOne posture check:

- API Token
- REST API URL

To retrieve those values:

1. Log in to your SentinelOne Dashboard.
2. Go to **Settings** > **Users** > **Create new Service User**.
3. Select **Create New Service User**.
4. Enter a **Name** and **Expiration Date** and select **Next**.
5. Set **Scope of Access** to _Viewer_.
6. Select **Create User**. SentinelOne will generate an API Token for this user.
7. Copy the **API Token** to a safe location.
8. Select **Close**.
9. Copy the **Rest API URL** from your browser's address bar (for example, `https://<S1-DOMAIN>.sentinelone.net`).

### 2. Add SentinelOne as a service provider

<Render file="posture/add-service-provider" params={{ provider: "SentinelOne" }} product="cloudflare-one"/>
5. In **Client Secret**, enter your **API Token**.
6. In **Rest API URL**, enter `https://<S1-DOMAIN>.sentinelone.net`.
7. Choose a **Polling frequency** for how often Cloudflare One should query SentinelOne for information.
8. Select **Test and save**.

### 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "SentinelOne" }}
/>

## Device posture attributes

Device posture data is gathered from the SentinelOne Management APIs. For more information, refer to `https://<S1-DOMAIN>.sentinelone.net/api-doc/overview`.

| Selector          | Description                                                                                                                              |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Infected          | Whether the device is infected                                                                                                           |
| Active Threats    | Number of active threats on the device                                                                                                   |
| Is Active         | Whether the SentinelOne Agent is active                                                                                                  |
| Network status    | Whether the SentinelOne Agent is connected to the SentinelOne service                                                                    |
| Operational State | The [operational state](https://community.sentinelone.com/s/login/?ec=302&startURL=%2Fs%2Farticle%2F000005285) of the SentinelOne Agent. |

### Detect user risk behavior

SentinelOne provides endpoint detection and response (EDR) signals to determine [user risk score](/cloudflare-one/team-and-resources/users/risk-score/). User risk scores allow you to detect users that present security risks to your organization. For more information, refer to [Predefined risk behaviors](/cloudflare-one/team-and-resources/users/risk-score/#predefined-risk-behaviors).

# Tanium

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	params={{ one: "Tanium" }}
	product="cloudflare-one"
/>

## Prerequisites

- Either Tanium Cloud or on-premise installations of Tanium with the Benchmark entitlement
- Tanium agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

## Set up Tanium as a service provider

### 1. Get Tanium settings

The following Tanium values are needed to set up the Tanium posture check:

- Client Secret
- REST API URL

To retrieve the client secret, create an API token:

1. Log in to your Tanium instance.
2. Go to **Administration** > **API Tokens**.
3. Select **New API Token**.
4. Set **Expire in days** to an appropriate value for your organization. When this token expires, all device posture results will begin to fail unless updated.
5. Set **Trusted IP addresses** to `0.0.0.0/0`.
6. Select **Save**.
7. Copy the **Client Secret** to a safe place.

To retrieve the API URL, determine your Tanium Gateway root endpoint:

- Tanium Cloud: `https://<customerName>-api.cloud.tanium.com/plugin/products/gateway/graphql`
- Tanium On Prem: `https://<server>/plugin/products/gateway/graphql`

### 2. Add Tanium as a service provider

<Render
	file="posture/add-service-provider"
	product="cloudflare-one"
	params={{ provider: "Tanium" }}
/>

5. Enter the **Client Secret** and **REST API URL** you noted down above.
6. Choose a **Polling frequency** for how often Cloudflare One should query Tanium for information.
7. Select **Test and save**.

### 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "Tanium" }}
/>

## Device posture attributes

Device posture data is gathered from [Tanium's EndpointRisk API](https://developer.tanium.com/site/global/apis/graphql/spectaql/index.gsp#definition-EndpointRisk). To learn more about how scores are calculated, refer to the [Tanium risk score documentation](https://help.tanium.com/bundle/ug_benchmark_cloud/page/benchmark/risk_score.html).

| Selector      | Description                                                                     | Value                                                                                           |
| ------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Total score   | `totalScore` of the device.                                                     | `1` to `1000`                                                                                   |
| Risk level    | `riskLevel` of the device.                                                      | Low, medium, high, or critical                                                                  |
| EID last seen | Elapsed time since the device was last seen, based on its `datetime` attribute. | In the last 1 hour, 3 hours, 6 hours, 12 hours, 24 hours, 7 days, 30 days, or more than 30 days |

# Uptycs

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	product="cloudflare-one"
	params={{ one: "Uptycs" }}
/>

## Prerequisites

- Uptycs agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

## 1. Obtain Uptycs Settings

The following Uptycs values are needed to set up the Uptycs posture check:

- Client key
- Client Secret
- Customer ID

To obtain these values:

1. Open your Uptycs console.
2. Go to **Account Settings** > **API Key**.
3. Generate and download your `.json` file. This file will contain your **Client key**, **Client Secret** and **Customer ID**.

## 2. Add Uptycs as a service provider

<Render
	file="posture/add-service-provider"
	product="cloudflare-one"
	params={{ provider: "Uptycs" }}
/>

5. Enter the **Client ID**, **Client secret** and **Customer ID** as you noted down above.
6. Select a **Polling frequency** for how often Cloudflare One should query Uptycs for information.
7. Select **Test and save**.

## 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "Uptycs" }}
/>

## Device posture attributes

| Selector | Description                                       |
| -------- | ------------------------------------------------- |
| Score    | Zero Trust score assigned to the device by Uptycs |

# Workspace ONE

import { Render } from "~/components";

<Render
	file="posture/service-provider-intro"
	product="cloudflare-one"
	params={{ one: "Workspace ONE" }}
/>

## Prerequisites

- Workspace ONE agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Service providers",
  		link: "/cloudflare-one/integrations/service-providers/",
  	}}
  />

## 1. Obtain Workspace ONE Settings

The following Workspace ONE values are needed to set up the Workspace ONE posture check:

- ClientID
- Client Secret
- REST API URL
- Region-Specific token URL

To retrieve those values:

1. Log in to your Workspace ONE dashboard.
2. Go to **Groups & Settings** > **Configurations**.
3. Enter `OAuth` in the search bar labeled **Enter a name or category**.
4. Select **OAuth Client Management** in the results. The OAuth Client Management screen displays.
5. Select **Add**.
6. Enter values for the **Name**, **Description**, **Organization Group**, and **Role**.
7. Ensure that the **Status** is **Enabled**.
8. Select **Save**.
9. Copy the **Client ID** and **Client Secret** to a safe place.
10. To obtain your REST API URL, gp tp **Groups & Settings** > **All Settings** > **System** > **Advance** > **Site URLs** > **REST API URL**.
11. Retrieve the Region-Specific Token URL from Workspace ONE and copy it to a safe place.

## 2. Add Workspace ONE as a service provider

<Render
	file="posture/add-service-provider"
	product="cloudflare-one"
	params={{ provider: "Workspace ONE" }}
/>

5. Enter the **Client ID** and **Client secret** you noted down above.
6. Select a **Polling frequency** for how often Cloudflare One should query Workspace ONE for information.
7. Enter the **Region-specific token URL** and **REST API URL** you noted down above.
8. Select **Test and save**.

## 3. Configure the posture check

<Render
	file="posture/configure-posture-check"
	product="cloudflare-one"
	params={{ one: "Workspace ONE" }}
/>

## Device posture attributes

Workspace ONE posture checks work with the [Compliance flags](https://docs.vmware.com/en/VMware-Workspace-ONE-UEM/services/UEM_Managing_Devices/GUID-CompliancePolicies.html) in Workspace ONE. All compliance tests must pass for the device to be considered compliant.

# Connectivity options

import { Render } from "~/components";

<Render
  file="cloudflare-one-connectivity-options"
  product="networking-services"
  params={{
    unifiedRoutingURL: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing",
    greIpsecURL: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/",
    connectorURL: "/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliances/",
    cniURL: "/cloudflare-one/networks/connectors/cloudflare-wan/network-interconnect/",
    onRampsURL: "/cloudflare-one/networks/connectors/cloudflare-wan/on-ramps/"
  }}
/>

# Networks

import { DirectoryListing } from "~/components";

Learn how to connect your private networks to Cloudflare.

<DirectoryListing />

# Virtual networks

A virtual network is a private routing domain within your Cloudflare account. It defines which private resources are reachable from the Cloudflare network and keeps traffic separated between different environments, partners, or applications.

Every Cloudflare account has a default virtual network. You can create additional virtual networks to isolate routing between different parts of your infrastructure.

:::note
Cloudflare Tunnel and the Cloudflare One Client support multiple virtual networks. Cloudflare WAN connections (IPsec, GRE, CNI) currently use only the default virtual network.
:::

## When to use virtual networks

- **Environment separation** — Keep production and staging networks isolated. Traffic destined for `10.0.0.1` in production routes to a different destination than `10.0.0.1` in staging.
- **Partner isolation** — Connect multiple partners to your Cloudflare account without allowing them to reach each other. Each partner gets its own virtual network.
- **Overlapping IP space** — When different networks use the same IP ranges (common with RFC 1918 addresses), virtual networks let you route to the correct destination based on context, not just IP address.
- **Private application connectivity** — Connect Cloudflare Workers or CDN to backends in your private network. The virtual network tells Cloudflare where to route traffic for private IP addresses.
- **Public TCP/UDP to private origins** — Expose a private backend to the Internet for TCP or UDP traffic through a [Spectrum application](/spectrum/get-started/#create-a-spectrum-application-using-a-virtual-network-origin), without requiring a Load Balancer. The virtual network determines which tunnel or WAN connection carries the traffic to your origin.

## How virtual networks work

When traffic enters Cloudflare destined for a private IP address, Cloudflare looks up the route in the virtual network routing table. The virtual network determines:

- Which private destinations are reachable.
- Which connector (Tunnel or WAN connection) carries the traffic.
- How overlapping IP addresses are disambiguated.

```mermaid
flowchart TD
accTitle: Virtual network routing
accDescr: Shows how a Cloudflare account contains three virtual networks, each with their own routing table and connectors pointing to separate destinations. The same CIDR can exist in each virtual network as isolated routing domains.

    subgraph account ["Your Cloudflare account"]
        direction LR

        subgraph vnet_default ["Virtual Network: default"]
            routes_d("10.0.0.0/8 via Tunnel
            192.168.1.0/24 via IPsec")
            routes_d --> tunnel_d(["Tunnel"])
            routes_d --> ipsec_d(["IPsec"])
        end

        subgraph vnet_prod ["Virtual Network: production"]
            routes_p("10.0.0.0/8 via Tunnel")
            routes_p --> tunnel_p(["Tunnel (prod)"])
        end

        subgraph vnet_stg ["Virtual Network: staging"]
            routes_s("10.0.0.0/8 via Tunnel")
            routes_s --> tunnel_s(["Tunnel (stg)"])
        end
    end

    tunnel_d --> dc_legacy("Legacy DC
    10.0.0.0/8")
    ipsec_d --> dc_branch("Branch (IPsec)
    192.168.1.0/24")
    tunnel_p --> dc_prod("Production DC
    10.0.0.0/8")
    tunnel_s --> dc_stg("Staging DC
    10.0.0.0/8")

    classDef orange fill:#f48120,stroke:#d6710e,color:#fff
    classDef blue fill:#4b9fd5,stroke:#3a8bc2,color:#fff

    class tunnel_d,tunnel_p,tunnel_s orange
    class ipsec_d blue

    style vnet_default stroke:#999,stroke-width:2px,stroke-dasharray: 5 5
    style vnet_prod stroke:#f48120,stroke-width:2px
    style vnet_stg stroke:#f48120,stroke-width:2px
```

The same CIDR (`10.0.0.0/8`) can exist in each virtual network because they are isolated routing domains.

Each virtual network maintains its own routing table. Routes added to one virtual network do not appear in another virtual network routing table. However, if traffic does not match a route in the selected virtual network, Cloudflare may fall back to the default virtual network routing table for WAN routes.

You can add entries to a virtual network routing table through static route configuration or routes learned from BGP peering. Static routes are available for all connection types. BGP peering is available over IPsec/GRE tunnels and CNI (closed beta). For more information on how routes are prioritized within a virtual network, refer to [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/).

## Virtual networks across Cloudflare

Virtual network support varies by product:

| Product | Virtual network support | Details |
| --- | --- | --- |
| Cloudflare Tunnel | Multiple virtual networks | Assign CIDR routes to a virtual network when configuring your tunnel |
| Cloudflare One Client | Multiple virtual networks | Users land in a virtual network based on policy or client selection |
| Cloudflare Mesh | Not currently supported | — |
| Cloudflare WAN | Default only | All IPsec, GRE, and CNI connections use the default virtual network |

## The default virtual network

Every account has a `default` virtual network. If you do not specify a virtual network when creating routes or connections, they are assigned to the default.

For most deployments with a single private network, the default virtual network is all you need. Create additional virtual networks only when you need routing isolation.

:::note
Cloudflare WAN connections (IPsec, GRE, CNI) always use the default virtual network.
:::

## For network engineers

If you are familiar with enterprise networking concepts, a virtual network is analogous to a VRF (Virtual Routing and Forwarding):

- Each virtual network maintains its own routing table.
- Routes are isolated between virtual networks.
- The same IP prefix can exist in multiple virtual networks without conflict.
- BGP routes learned on a connection populate only that connection virtual network routing table. BGP peering is supported for IPsec/GRE tunnels and CNI (closed beta).

If you are familiar with cloud networking concepts, a virtual network is analogous to a VPC (Virtual Private Cloud).

## Create and manage virtual networks

To create and configure virtual networks, refer to [Virtual networks (Tunnel configuration)](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/).

To configure which routes belong to which virtual network, refer to [Add routes](/cloudflare-one/networks/routes/add-routes/).

To add static routes or configure BGP peering within the Cloudflare Virtual Network routing table, refer to [Configure routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/).

## Related resources

- [Private networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/) — Connect your infrastructure to Cloudflare
- [Routes](/cloudflare-one/networks/routes/add-routes/) — Define IP and hostname routes through your connectors
- [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/) — Route prioritization, ECMP, and BGP within the Cloudflare Virtual Network

# Granular permissions for Tunnels and Mesh nodes

import { Steps } from '~/components';

You can scope Cloudflare member permissions to individual [Cloudflare Tunnel](/tunnel/) instances and [Cloudflare Mesh](/mesh/) nodes, instead of granting account-wide access to every Tunnel and Mesh node. This enables least-privilege delegation for private networking operations — for example, letting a support operator stream logs from a single Tunnel without exposing the rest of your account.

Granular permissions are a parallel layer to [account-scoped roles](/fundamentals/manage-members/roles/#account-scoped-roles) — they do not replace them. Members who already hold an account-level role like `Cloudflare Access` or `Cloudflare Zero Trust` continue to have write access to every Tunnel and Mesh node in the account.

## How it works

For any API request on a specific Tunnel or Mesh node, access is granted if the principal has **either**:

- An account-level role that covers the resource (for example, `Cloudflare Access` or `Cloudflare Zero Trust`), **or**
- A [resource-scoped role](/fundamentals/manage-members/roles/#resource-scoped-roles) bound to that specific Tunnel or Mesh node.

[Resource enumeration endpoints](#resource-enumeration) (`GET /accounts/{id}/cfd_tunnel`, `GET /accounts/{id}/warp_connector`) return only the resources the principal has at least read access to.

## Grant a granular permission

Granular permissions are assigned through the standard [member management](/fundamentals/manage-members/manage/) flow.

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Manage Account** > **Members** and select **Invite Members**, or open an existing member to edit their permissions.
2. Add a permission policy and choose a [resource-scoped role](/fundamentals/manage-members/roles/#resource-scoped-roles) that targets Tunnels or Mesh nodes.
3. In the **Scope** section, choose **Specific resources**.
4. Set **Resource type** to one of:
   - **Cloudflare Tunnel instances** — for individual [Cloudflare Tunnel](/tunnel/) instances.
   - **Cloudflare Mesh nodes** — for individual [Cloudflare Mesh](/mesh/) nodes.
5. Select one or more specific Tunnels or Mesh nodes from the resource picker.
6. Save the policy.
</Steps>

You can attach multiple granular policies to the same member to cover different Tunnels and Mesh nodes with different roles.

## Resource enumeration

Listing endpoints are authorization-aware. When a principal calls a listing endpoint, the response is filtered to the resources they have at least read access to.

| Endpoint                                | Method | Returns                                                       |
| --------------------------------------- | ------ | ------------------------------------------------------------- |
| `/accounts/{account_id}/cfd_tunnel`     | `GET`  | Cloudflare Tunnel instances the principal can read or manage. |
| `/accounts/{account_id}/warp_connector` | `GET`  | Cloudflare Mesh nodes the principal can read or manage.       |
| `/accounts/{account_id}/teamnet/routes` | `GET`  | Routes attached to Tunnels the principal can read or manage.  |

Members with an account-level role that covers Tunnels and Mesh continue to see all resources in the account.

## Backward compatibility

- Existing account-level roles and API tokens continue to function as before.
- Existing automation that authenticates with an account-level token (for example, Terraform pipelines using a `Cloudflare Access` token) is unaffected.
- Granular permissions are opt-in. Granting one to a member adds capability; it never removes capability that the member already has from an account-level role.

## Related resources

- [Roles reference](/fundamentals/manage-members/roles/) — the full list of Cloudflare roles, including resource-scoped roles for Tunnels and Mesh nodes.
- [Role scopes](/fundamentals/manage-members/scope/) — how policy scopes work across account, domain, and resource layers.
- [Manage account members](/fundamentals/manage-members/manage/) — the member invite and edit flow.
- [Cloudflare Tunnel](/tunnel/)
- [Cloudflare Mesh](/mesh/)

# Connectors

import { DirectoryListing } from "~/components"


<DirectoryListing />

# Cloudflare Mesh

import { Render } from "~/components";

<Render file="mesh/pages/overview" product="cloudflare-one" />

# Best practices

import { Render } from "~/components";

<Render file="mesh/pages/best-practices" product="cloudflare-one" />

# How Cloudflare Mesh works

import { Render } from "~/components";

<Render file="mesh/pages/concepts" product="cloudflare-one" />

# High availability

import { Render } from "~/components";

<Render file="mesh/pages/features/high-availability" product="cloudflare-one" />

# Features

import { Render } from "~/components";

<Render file="mesh/pages/features/overview" product="cloudflare-one" />

# Routes

import { Render } from "~/components";

<Render file="mesh/pages/features/routes" product="cloudflare-one" />

# Get started

import { Render } from "~/components";

<Render file="mesh/pages/get-started" product="cloudflare-one" />

# Connect two devices privately with Cloudflare Mesh

import { Render } from "~/components";

<Render
	file="mesh/pages/guides/connect-client-devices"
	product="cloudflare-one"
/>

# Connect two servers with Cloudflare Mesh

import { Render } from "~/components";

<Render file="mesh/pages/guides/connect-servers" product="cloudflare-one" />

# Connect two private networks with Cloudflare Mesh

import { Render } from "~/components";

<Render file="mesh/pages/guides/connect-sites" product="cloudflare-one" />

# Guides

import { Render } from "~/components";

<Render file="mesh/pages/guides/overview" product="cloudflare-one" />

# Run Mesh in Docker / Kubernetes

import { Render } from "~/components";

<Render file="mesh/pages/guides/run-mesh-in-containers" product="cloudflare-one" />

# Run Mesh on Linux

import { Render } from "~/components";

<Render file="mesh/pages/guides/run-mesh-on-linux" product="cloudflare-one" />

# Platform

import { Render } from "~/components";

<Render file="mesh/pages/platform" product="cloudflare-one" />

# Troubleshoot Cloudflare Mesh connectivity

import { Render } from "~/components";

<Render file="mesh/pages/troubleshooting" product="cloudflare-one" />

# Cloudflare Tunnel

import { Render, Stream } from "~/components";

:::note[Looking to expose public applications?] 
This documentation covers Cloudflare Tunnel use cases for private networking and Zero Trust, like VPN replacement and private network access. For publishing public web applications, APIs, and services to the Internet through Cloudflare refer to the [Cloudflare Tunnel documentation](/tunnel). 
:::

Cloudflare Tunnel provides you with a secure way to connect your resources to Cloudflare without a publicly routable IP address. With Tunnel, you do not send traffic to an external IP — instead, a lightweight daemon in your infrastructure (`cloudflared`) creates [outbound-only connections](#outbound-only-connections) to Cloudflare's global network. Cloudflare Tunnel can connect HTTP web servers, [SSH servers](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/), [remote desktops](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/), and other protocols safely to Cloudflare. This way, your origins can serve traffic through Cloudflare without being vulnerable to attacks that bypass Cloudflare.

Refer to our [reference architecture](/reference-architecture/architectures/sase/) for details on how to implement Cloudflare Tunnel into your existing infrastructure.

<Stream
	id="4fa0fe257187f18929c07754a4a4df09"
	title="What is Cloudflare Tunnel?"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/ecc233ab-9a33-46e3-b339-b2d592fc0d00/public"
/>

## How it works

`cloudflared` establishes [outbound connections](#outbound-only-connections) (tunnels) between your resources and Cloudflare's global network. A tunnel is a persistent object identified by a UUID — it serves as the logical link between your origin and Cloudflare. Within the same tunnel, you can run as many `cloudflared` processes ([connectors](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/#connector)) as needed. Each connector sends traffic to the nearest Cloudflare data center.

![How an HTTP request reaches a private application connected with Cloudflare Tunnel](~/assets/images/cloudflare-one/connections/connect-apps/handshake.jpg)

### Outbound-only connections

Cloudflare Tunnel uses an outbound-only connection model to enable bidirectional communication. When you install and run `cloudflared`, `cloudflared` initiates an outbound connection through your firewall from the origin to the Cloudflare global network.

Once the connection is established, traffic flows in both directions over the tunnel between your origin and Cloudflare. Most firewalls allow outbound traffic by default. `cloudflared` takes advantage of this standard by connecting out to the Cloudflare network from the server you installed `cloudflared` on. You can then configure your firewall to allow only these outbound connections and block all inbound traffic, effectively blocking access to your origin from anything other than Cloudflare. This setup ensures that all traffic to your origin is securely routed through the tunnel.

:::note[Authenticated Origin Pulls does not apply]
Because Cloudflare Tunnel does not use an inbound listener on your origin, [Authenticated Origin Pulls](/ssl/origin-configuration/authenticated-origin-pull/) has no effect on hostnames routed through Cloudflare Tunnel. Origin traffic is already authenticated using your tunnel's connector credentials.
:::

## Next steps

- Create a tunnel using the [Cloudflare dashboard](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) or [API](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/).
- [Download `cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/), the server-side daemon that connects your infrastructure to Cloudflare.
- Review useful [Tunnel terms](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/) to familiarize yourself with the concepts used in Tunnel documentation.

# Cipher suites

Cloudflare Tunnel connections use the cipher suites supported by `cloudflared`, which relies on the Go TLS library for its TLS implementation. These cipher suites apply to both the TLS connection between Cloudflare's network and `cloudflared`, and the HTTPS connection between `cloudflared` and your origin. In both cases, `cloudflared` negotiates the most secure cipher suite supported by both sides. All tunnel connections use TLS 1.3 and post-quantum encryption by default.

The following table lists the cipher suites supported by `cloudflared`:

| Protocol support | Cipher suites |
|------------------|----------------|
| TLS 1.3 only | `TLS_AES_128_GCM_SHA256`<br />`TLS_AES_256_GCM_SHA384`<br />`TLS_CHACHA20_POLY1305_SHA256` |
| TLS 1.2 only | `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`<br />`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`<br />`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`<br />`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`<br />`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`<br />`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` |
| Up to and including TLS 1.2 | `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`<br />`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`<br />`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`<br />`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA` |

# Configure a tunnel

import { DirectoryListing  } from "~/components";

After [creating your Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/), you can configure various aspects of how `cloudflared` runs and connects your infrastructure to Cloudflare's network. This section covers advanced configuration options to optimize tunnel performance, security, and availability.

<DirectoryListing descriptions />

## Common configuration scenarios

### Optimize for production

For production deployments, consider the following steps:

- [Deploy replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/deploy-replicas/) - Run multiple `cloudflared` instances for redundancy.
- [Configure logging](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#loglevel) - Set appropriate log levels for monitoring and troubleshooting.
- [Review system requirements](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/system-requirements/) - Ensure your infrastructure meets performance needs.
- [Configure firewall rules](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) - Implement egress-only traffic patterns for security.

### Secure your tunnel

All tunnel connections between `cloudflared` and Cloudflare's network are secured with TLS 1.3 and post-quantum encryption by default, ensuring your traffic is protected against current and future cryptographic threats.

Enhance tunnel security with:

- [Tunnel token management](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/) - Control access to your tunnel credentials.
- [Egress-only firewall rules](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) - Allow only necessary outbound connections.
- Least privilege permissions - Run `cloudflared` as a non-root user with minimal permissions needed for tunnel operation.

### Improve reliability

Maximize tunnel uptime with:

- [Multiple replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/#cloudflared-replicas) - Deploy `cloudflared` across different hosts.
- [Health alerts](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/notifications/) - Get notified when your tunnel is degraded or goes down.
- [Health metrics](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#metrics) - Monitor tunnel resource usage to identify potential bottlenecks.
- [Load balancing](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/#cloudflare-load-balancers/) - Distribute traffic across tunnel connections.
- [Automatic failover](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/) - Leverage built-in connection redundancy.

## Next steps

- [Monitor your tunnels](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/) to track performance and troubleshoot issues.
- [Configure routes](/cloudflare-one/networks/routes/add-routes/) to control how traffic reaches your applications.
- [Set up private networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/) for internal resource access.

# Origin parameters

import { Render } from "~/components";

<Render
  file="tunnel/origin-parameters"
  product="cloudflare-one"
  params={{
    configurationFileURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
    kubectlTutorialURL: "/cloudflare-one/tutorials/kubectl/",
    validatingJwtURL: "/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/"
  }}
/>

# Tunnel permissions

import { TabItem, Tabs, Render, APIRequest, DashButton } from "~/components";

A remotely-managed tunnel only requires the tunnel token to run. Anyone with access to the token will be able to run the tunnel.

## Get the tunnel token

To get the token for a remotely-managed tunnel:

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">
1. In the Cloudflare dashboard, go to **Networking** > **Tunnels**.

   <DashButton url="/?to=/:account/tunnels" />

2. Select a tunnel to open its detail page.
3. On the **Overview** tab, select **Add a replica** to reveal the installation command. Copy the command into a text editor (do not run the command). The token is the `eyJ...` string.

</TabItem>
<TabItem label="API">

Make a `GET` request to the [Cloudflare Tunnel token](/api/resources/zero_trust/subresources/tunnels/subresources/cloudflared/subresources/token/methods/get/) endpoint:

<APIRequest
	path="/accounts/{account_id}/cfd_tunnel/{tunnel_id}/token"
	method="GET"
/>

```sh output
{
  "success": true,
  "errors": [],
  "messages": [],
  "result": "eyJhIjoiNWFiNGU5Z..."
}
```

The token value can be found in the `result`.

</TabItem>

<TabItem label="Terraform (v5)">

<Render file="terraform/get-tunnel-token" product="cloudflare-one" />

</TabItem>

</Tabs>

## Rotate a token without service disruption

Cloudflare recommends rotating the tunnel token at a regular cadence to reduce the risk of token compromise. You can rotate a token with minimal disruption to users as long as the tunnel is served by at least two [`cloudflared` replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/). To ensure service availability, we recommend performing token rotations outside of working hours or in a maintenance window.

To rotate a tunnel token:

1. Refresh the token on Cloudflare:

	<Tabs syncKey="dashPlusAPI">
	<TabItem label="Dashboard">
	1. In the Cloudflare dashboard, go to **Networking** > **Tunnels**.

	   <DashButton url="/?to=/:account/tunnels" />

	2. Select a tunnel to open its detail page.
	3. On the **Overview** tab, select **Refresh token**.
	4. Copy the `cloudflared` installation command for your operating system. This command contains the new token.

	</TabItem>
	<TabItem label="API">
		1. Generate a random base64 string (minimum size 32 bytes) to use as a tunnel secret:

				```sh
				openssl rand -base64 32
				```

				```sh output
				AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=
				```

	2.  Make a `PATCH` request to the [Cloudflare Tunnel](/api/resources/zero_trust/subresources/tunnels/methods/edit/) endpoint:

			<APIRequest
				path="/accounts/{account_id}/cfd_tunnel/{tunnel_id}"
				method="PATCH"
				json={{
					name: "Example tunnel",
					tunnel_secret: "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=",
				}}
			/>

			```sh output {18}
			{
				"success": true,
				"errors": [],
				"messages": [],
				"result": {
					"id": "f70ff985-a4ef-4643-bbbc-4a0ed4fc8415",
					"account_tag": "699d98642c564d2e855e9661899b7252",
					"created_at": "2024-12-04T22:03:26.291225Z",
					"deleted_at": null,
					"name": "Example tunnel",
					"connections": [],
					"conns_active_at": null,
					"conns_inactive_at": "2024-12-04T22:03:26.291225Z",
					"tun_type": "cfd_tunnel",
					"metadata": {},
					"status": "inactive",
					"remote_config": true,
					"token": "eyJhIjoiNWFiNGU5Z..."
				}
			}
			```

	3.  Copy the `token` value shown in the output.

	</TabItem>
	</Tabs>

	After refreshing the token, `cloudflared` can no longer establish new connections to Cloudflare using the old token. However, existing connectors will remain active and the tunnel will continue serving traffic.

2. On half of your `cloudflared` replicas, reinstall the `cloudflared` service with the new token. For example, on a Linux host:

   ```sh
	 sudo cloudflared service uninstall
   sudo cloudflared service install <NEW_TOKEN>
   ```

3. Confirm that the service started correctly:

   ```sh
   sudo systemctl status cloudflared
   ```

   While these replicas are connecting to Cloudflare with the new token, traffic will automatically route through the other replicas.

5. Wait 10 minutes for traffic to route through the new connectors.

6. Repeat steps 2, 3, and 4 for the second half of the replicas.

The tunnel token is now fully rotated. The old token is no longer in use.

## Rotate a compromised token

If your tunnel token is compromised, we recommend taking the following steps:

1. Refresh the token using the dashboard or API. Refer to Step 1 of [Rotate a token without service disruption](#rotate-a-token-without-service-disruption).
2. [Delete all connections](/api/resources/zero_trust/subresources/tunnels/subresources/connections/methods/delete/) between `cloudflared` and Cloudflare:

   <APIRequest
   	path="/accounts/{account_id}/cfd_tunnel/{tunnel_id}/connections"
   	method="DELETE"
   />

   This will clean up any unauthorized connections and prevent users from connecting to your network.

3. On each `cloudflared` replica, update `cloudflared` to use the new token. For example, on a Linux host:

   ```sh
	 sudo cloudflared service uninstall
   sudo cloudflared service install <NEW_TOKEN>
   ```

4. Confirm that the service started correctly:
   ```sh
   sudo systemctl status cloudflared
   ```

The tunnel token is now fully rotated. The old token is no longer in use.

## Account-scoped roles

<Render file="tunnel/account-scoped-roles" product="cloudflare-one" />

## Resource-scoped permissions

You can also scope permissions to individual [Cloudflare Tunnel](/tunnel/) instances instead of granting account-wide access. Refer to [Granular permissions for Tunnels and Mesh nodes](/cloudflare-one/networks/connectors/granular-permissions/).

# Tunnel run parameters

import { Render } from "~/components";

<Render
  file="tunnel/run-parameters"
  product="cloudflare-one"
  params={{
    configurationFileURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
    metricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/",
    usageMetricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/",
    tunnelWithFirewallURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#region-us",
    tunnelPermissionsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/tunnel-permissions/",
    createRemoteTunnelURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/",
    faqURL: "/cloudflare-one/faq/cloudflare-tunnels-faq/"
  }}
/>

# Tunnel with firewall

import { Render } from "~/components";

You can implement a positive security model with Cloudflare Tunnel by blocking all ingress traffic and allowing only egress traffic from `cloudflared`. Only the services specified in your tunnel configuration will be exposed to the outside world.

## Ports

The parameters below can be configured for egress traffic inside of a firewall.

How you configure your firewall depends on the firewall type:

- If your firewall supports domain-based rules (FQDN allowlists), you can allow outbound connections to the hostnames listed below.
- If your firewall requires IP-based rules, allow outbound connections to all listed IP addresses for each domain.

Ensure port `7844` is allowed for both TCP and UDP protocols (for `http2` and `quic`).

### Required for tunnel operation

`cloudflared` connects to Cloudflare's global network on port `7844`. To use Cloudflare Tunnel, your firewall must allow outbound connections to the following destinations on port `7844` (via UDP if using the `quic` protocol or TCP if using the `http2` protocol).

<Render file="tunnel/firewall/required-ports" product="cloudflare-one" />

#### SNI-enforcing firewalls
<Render file="tunnel/firewall/sni-hostnames" product="cloudflare-one" />

### Region US

<Render
  file="tunnel/firewall/us-region-ports"
  product="cloudflare-one"
  params={{ regionURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#region" }}
/>

### Region FedRAMP High

<Render
  file="tunnel/firewall/fedramp-high-ports"
  product="cloudflare-one"
  params={{ tunnelTokenURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/" }}
/>

### Optional

Opening port `443` enables some optional features. Failure to allow these connections may prompt a log error, but `cloudflared` will still run correctly.

#### `api.cloudflare.com`

Allows `cloudflared` to query if software updates are available.

| IPv4                                                                                                | IPv6                                                                                                                                                                    | Port | Protocols   |
| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | ----------- |
| `104.19.192.29` `104.19.192.177` `104.19.192.175` `104.19.193.29` `104.19.192.174` `104.19.192.176` | `2606:4700:300a::6813:c0af` `2606:4700:300a::6813:c01d` `2606:4700:300a::6813:c0ae` `2606:4700:300a::6813:c11d` `2606:4700:300a::6813:c0b0` `2606:4700:300a::6813:c0b1` | 443  | TCP (HTTPS) |

#### `update.argotunnel.com`

Allows `cloudflared` to query if software updates are available.

| IPv4                            | IPv6                                          | Port | Protocols   |
| ------------------------------- | --------------------------------------------- | ---- | ----------- |
| `104.18.25.129` `104.18.24.129` | `2606:4700::6812:1881` `2606:4700::6812:1981` | 443  | TCP (HTTPS) |

#### `github.com`

Allows `cloudflared` to download the latest release and perform a software update.

| IPv4                                                                                                                      | IPv6                                                                                                                      | Port | Protocols   |
| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---- | ----------- |
| [GitHub's IPs](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses) | [GitHub's IPs](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses) | 443  | TCP (HTTPS) |

#### `<your-team-name>.cloudflareaccess.com`

Allows `cloudflared` to validate the Access JWT. Only required if the [`access`](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/cloudflared-parameters/origin-parameters/#access) setting is enabled.

| IPv4                            | IPv6                                                    | Port | Protocols   |
| ------------------------------- | ------------------------------------------------------- | ---- | ----------- |
| `104.19.194.29` `104.19.195.29` | `2606:4700:300a::6813:c31d` `2606:4700:300a::6813:c21d` | 443  | TCP (HTTPS) |

#### `pqtunnels.cloudflareresearch.com`

Allows `cloudflared` to report [post-quantum key exchange](https://blog.cloudflare.com/post-quantum-tunnel/) errors to Cloudflare.

| IPv4                        | IPv6                                        | Port | Protocols   |
| --------------------------- | ------------------------------------------- | ---- | ----------- |
| `104.18.4.64` `104.18.5.64` | `2606:4700::6812:540` `2606:4700::6812:440` | 443  | TCP (HTTPS) |

#### `cfd-features.argotunnel.com`

| IPv4           | IPv6           | Port           | Protocols      |
| -------------- | -------------- | -------------- | -------------- |
| Not applicable | Not applicable | Not applicable | Not applicable |

Performing a DNS query for a `TXT` record to this hostname allows `cloudflared` to determine which version of [UDP datagram](/changelog/post/2025-07-15-udp-improvements/) to use when connecting via the `quic` protocol. If your firewall filters egress DNS queries by FQDN, you may need to allow queries for this domain to ensure optimal `quic` performance.

## Firewall configuration

### Cloud VM firewall

If you host your services on a virtual machine (VM) instance in a cloud provider, you may set up instance-level firewall rules to block all ingress traffic and allow only egress traffic. For example, on Google Cloud Platform (GCP), you may delete all ingress rules, leaving only the relevant egress rules. This is because GCP's firewall denies ingress traffic unless it matches an explicit rule.

### OS firewall

Alternatively, you may use operating system (OS)-level firewall rules to block all ingress traffic and allow only egress traffic. For example, if your server runs on Linux, you may use `iptables` to set up firewall rules:

1. Check your current firewall rules.

   ```sh
   sudo iptables -L
   ```

2. Allow `localhost` to communicate with itself.

   ```sh
   sudo iptables -A INPUT -i lo -j ACCEPT
   ```

3. Allow already established connection and related traffic.

   ```sh
   sudo iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
   ```

4. Allow new SSH connections.

   ```sh
   sudo iptables -A INPUT -p tcp --dport ssh -j ACCEPT
   ```

5. Drop all other ingress traffic.
   :::caution[Warning]

   Be very careful with the following command. If you did not preserve the current SSH connection or allow new SSH connections, you would be logged out and unable to SSH back into the system again.
   :::

   ```sh
   sudo iptables -A INPUT -j DROP
   ```

6. After setting the firewall rules, use this command to check the current `iptables` settings:

   ```sh
   sudo iptables -L
   ```

7. Run your tunnel and check that all configured services are still accessible to the outside world via the tunnel, but not via the external IP address of the server.

8. By default, rules you add via the `iptables` command are stored only in memory and do not persist on reboot. There are many different ways to save and reload your firewall rules, depending on your Linux distribution. For example, on Debian you can use the [`iptables-persistent`](https://packages.debian.org/sid/iptables-persistent) package:

   ```sh
   sudo apt install iptables-persistent
   sudo netfilter-persistent save
   ```

## Test connectivity

### Test with dig

To test your connectivity to Cloudflare, you can use the `dig` command to query the hostnames listed above. Note that `cloudflared` defaults to connecting with IPv4.

```sh
dig A region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.167
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.67
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.57
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.107
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.27
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.7
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.227
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.47
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.37
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.77
...
```

```sh
dig AAAA region1.v2.argotunnel.com
```

```sh output
...
;; ANSWER SECTION:
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::1
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::2
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::3
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::4
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::5
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::6
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::7
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::8
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::9
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::10
...
```

```sh
dig A region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.13
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.193
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.33
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.233
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.53
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.63
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.113
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.73
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.43
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.23
...
```

```sh
dig AAAA region2.v2.argotunnel.com
```

```sh output
...
;; ANSWER SECTION:
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::1
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::2
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::3
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::4
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::5
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::6
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::7
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::8
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::9
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::10
...
```

### Test with PowerShell

On Windows, you can use PowerShell commands if `dig` is not available.

To test DNS:

```powershell
Resolve-DnsName -Name _v2-origintunneld._tcp.argotunnel.com SRV
```

```txt output
Name                                     Type   TTL   Section    NameTarget                     Priority Weight Port
----                                     ----   ---   -------    ----------                     -------- ------ ----
_v2-origintunneld._tcp.argotunnel.com       SRV    112   Answer     region2.v2.argotunnel.com         2        1      7844
_v2-origintunneld._tcp.argotunnel.com       SRV    112   Answer     region1.v2.argotunnel.com         1        1      7844
```

To test ports:

```powershell
tnc region1.v2.argotunnel.com -port 443
```

```txt output
ComputerName     : region1.v2.argotunnel.com
RemoteAddress    : 198.41.192.227
RemotePort       : 443
InterfaceAlias   : Ethernet
SourceAddress    : 10.0.2.15
TcpTestSucceeded : True
```

```powershell
tnc region1.v2.argotunnel.com -port 7844
```

```txt output
ComputerName     : region1.v2.argotunnel.com
RemoteAddress    : 198.41.192.227
RemotePort       : 7844
InterfaceAlias   : Ethernet
SourceAddress    : 10.0.2.15
TcpTestSucceeded : True
```

# Deploy cloudflared replicas

import { Render } from "~/components";

To deploy multiple instances of `cloudflared`, you can create and configure one tunnel and run it on multiple hosts. If your tunnel runs as a service, only one `cloudflared` instance is allowed per host.

You can run the same tunnel across various `cloudflared` processes for up to 100 connections (25 replicas) per tunnel. Cloudflare Load Balancers and DNS records can still point to the tunnel and its UUID. Traffic will be sent to all `cloudflared` processes associated with the tunnel.

:::tip[Deploy replicas in Kubernetes]
For information about running `cloudflared` in a Kubernetes deployment, refer to the [Kubernetes guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/kubernetes/).
:::

## Remotely-managed tunnels

1. To create a remotely-managed tunnel, follow the [dashboard setup guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).
2. On the **Tunnels** page, select your newly created tunnel. The tunnel overview page displays all active replicas.
3. Select **Edit**.
4. Select the operating system of the host where you want to deploy a replica.
5. Copy the installation command and run it on the host.

The new replica will appear on the tunnel overview page. All replicas serve the same routes and use the same configuration parameters.

## Locally-managed tunnels

1. To create a locally-managed tunnel, complete Steps 1 through 5 in the [CLI setup guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/).

2. Run your newly created tunnel.

   ```sh
   cloudflared tunnel run <NAME>
   ```

   This will start a `cloudflared` instance and generate a unique `connector_id`.

3. In a separate window or on another host, run the same command again:

   ```sh
   cloudflared tunnel run <NAME>
   ```

   This will initialize another `cloudflared` instance and generate another `connector_id`.

4. Run `tunnel info` to show each `cloudflared` instance running your tunnel:

   ```sh
   cloudflared tunnel info <NAME>
   ```

This will output your tunnel UUID as well as two Connector IDs, one for each `cloudflared` process running your tunnel. With this command, you can also see that your tunnel is now being served by eight connections.

# Tunnel availability and failover

import { Details, GlossaryTooltip, Render } from "~/components";

Our lightweight and open-source connector, [`cloudflared`](https://github.com/cloudflare/cloudflared), was built to be highly available without any additional configuration requirements. When you run a tunnel, `cloudflared` establishes four outbound-only connections between the origin server and the Cloudflare network. These four connections are made to four different servers spread across at least two distinct data centers. This model ensures high availability and mitigates the risk of individual connection failures. This means in event a single connection, server, or data center goes offline, your resources will remain available.

## `cloudflared` replicas

You can deploy additional instances of `cloudflared` for availability and failover. These instances are called replicas. Each replica establishes four new connections to Cloudflare, providing additional points of ingress to your origin. All replicas point to the same tunnel, so if a single host running `cloudflared` goes down, the remaining replicas continue to serve traffic.

<Render
	file="tunnel/availability/cloudflared-replicas-diagram"
	product="cloudflare-one"
/>

Replicas do not support traffic steering (such as round-robin or hash-based routing). When a request arrives at Cloudflare, it is forwarded to the geographically closest replica. If that connection fails, Cloudflare retries with other replicas, but there is no guarantee about which one is chosen. If you need intelligent traffic distribution, use [Cloudflare Load Balancers](#cloudflare-load-balancers) instead.

### When to use `cloudflared` replicas

- To provide additional points of availability for a single tunnel.
- To allocate failover nodes within your network.
- To update the configuration of a tunnel [without downtime](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/update-cloudflared/#update-with-multiple-cloudflared-instances).

For setup instructions, refer to [Deploy cloudflared replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/deploy-replicas/).

## Cloudflare Load Balancers

[Cloudflare Load Balancing](/load-balancing/) proactively steers traffic away from unhealthy origins and intelligently distributes the traffic load based on your choice of [steering algorithms](/load-balancing/understand-basics/traffic-steering/). Unlike [`cloudflared` replicas](#cloudflared-replicas) which all use the same tunnel, a typical load balancer setup requires creating multiple tunnels. Most customers will create one tunnel per data center and one load balancer pool per tunnel.

```mermaid
graph LR
    accTitle: Load balancing traffic to applications behind Cloudflare Tunnel

    A[Internet] --> C{Cloudflare <br> Load Balancer}
    B[Cloudflare One Client] --> C
    M[Cloudflare WAN] --> C
    C -- Tunnel 1 --> cf1
    C -- Tunnel 2 --> cf2
    subgraph F[Data center 2]
        cf2[cloudflared <br> server]
        S3[App server]
        S4[App server]
        cf2-->S3
        cf2-->S4
    end
    subgraph E[Data center 1]
        cf1[cloudflared <br> server]
        S1[App server]
        S2[App server]
        cf1-->S1
        cf1-->S2
    end
```

### When to use load balancers

- To intelligently steer traffic based on latency, geolocation, or other signals.
- To implement failover logic if a tunnel reaches an inactive state.
- To get a [health alert](/notifications/notification-available/#load-balancing) when a tunnel reaches an inactive state.
- To distribute traffic more evenly across your Cloudflare Tunnel-accessible origins or endpoints.

For setup instructions, refer to [Public load balancers](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/public-load-balancers/) or [Private Network Load Balancing](/load-balancing/private-network/) depending on your [use case](#types-of-load-balancers).

### Types of load balancers

There are two types of load balancers that you can use with Cloudflare Tunnel endpoints:

- [Public load balancers](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/public-load-balancers/) steer traffic from the Internet to applications published on a Cloudflare domain. Use this method if your service is served by Cloudflare Tunnel via a [published application route](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application).
- [Private load balancers](/load-balancing/private-network/) steer traffic from Cloudflare One Clients, Cloudflare WAN, and other <GlossaryTooltip term = "on-ramp">on-ramps</GlossaryTooltip> to an internal IP on your private network. Use this method if your service is connected to Cloudflare Tunnel via a [CIDR route](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/).

:::note
[Private hostname routes](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/) are not currently compatible with Load Balancing. If your service is connected via a hostname route, use `cloudflared` [replicas](#cloudflared-replicas) for high availability.
:::

# System requirements

import { Render, TabItem, Tabs, TunnelCalculator } from "~/components";

Our connector, `cloudflared`, was designed to be lightweight and flexible enough to be effectively deployed on Raspberry Pi, your laptop or a server in a data center. <Render file="tunnel/availability/tunnel-capacity-intro" product="cloudflare-one" />

## Recommendations

For most use cases, we recommend the following baseline configuration:

- Run a [`cloudflared` replica](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/#cloudflared-replicas) on two dedicated host machines per network location. Using two hosts enables server-side redundancy.
- Size each host with minimum 4GB of RAM and 4 CPU cores.
- Allocate 50,000 [ports](#number-of-ports) to the `cloudflared` process on each host.

This setup is usually sufficient to handle traffic from 8,000 Cloudflare One Client users (4,000 per host). The actual amount of resources used by `cloudflared` will depend on many variables, including the number of requests per second, bandwidth, network path and hardware. As additional users are onboarded, or if network traffic increases beyond your existing [tunnel capacity](#estimated-throughput), you can scale your tunnel by adding an additional `cloudflared` host in that location.

### Number of ports

When `cloudflared` receives a request from a device, it uses the ports on the host machine to evaluate and forward the request to your origin service. Every machine by system design is hardware-limited to a maximum 65,535 ports. Additionally, each service on the machine has a limited number of ports that it can consume. For this reason, we recommend the following deployment model:

- `cloudflared` should be deployed on a dedicated host machine. This model is typically appropriate, but there may be serverless or clustered workflows where a dedicated host is not possible.
- The host machine should allocate 50,000 ports to be available for use by the `cloudflared` service. The remaining ports are reserved for system administrative processes.

<Render file="tunnel/availability/port-configuration" product="cloudflare-one" />

### Private DNS

DNS queries utilize [more system resources](#estimated-throughput) compared to TCP and non-DNS UDP requests. To optimize service availability, Cloudflare recommends splitting [private DNS traffic](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/private-dns/) into its own Cloudflare Tunnel. The tunnel should run on a dedicated host and only include routes for your internal DNS resolver IPs.

### ulimits

<Render file="tunnel/availability/ulimits" product="cloudflare-one" />

## Estimated throughput

Most private network traffic proxied by `cloudflared` falls in one of two categories:

- TCP requests (more common, less resource intensive)
- UDP requests (less common, more resource intensive)

TCP traffic uses and releases ports almost instantaneously. This means that in order to overload a `cloudflared` instance with 50,000 available ports, your organization would need to continuously generate 50,001 TCP requests per second.

UDP traffic is more unique. DNS queries - usually the bulk of UDP traffic - are held by ports in `cloudflared` for five seconds. Non-DNS UDP traffic holds each port for the duration of the connection, which can be any amount of time. This means that in order to overload a `cloudflared` instance with 50,000 available ports, you would need to continuously generate either 10,000 DNS queries to your private resolver per second, or a cumulative 50,000 non-DNS UDP requests over a shorter time than your connection reset rate.

### Calculate your tunnel capacity

Our [baseline recommendations](#recommendations) serve as a starting point for a Cloudflare Tunnel deployment. Once you have a representative population of users engaging with your network for at least a week, you can customize tunnel sizing according to your own traffic patterns.

To calculate your tunnel capacity:

1. Set up a [metrics service](/cloudflare-one/tutorials/grafana/) when you run the tunnel.
2. After a week or so, query the following [tunnel metrics](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/#cloudflared-metrics):
   - `cloudflared_tcp_total_sessions`
   - `cloudflared_udp_total_sessions`
3. Compute the average **TCP requests per second** and **Non-DNS UDP requests per second** by dividing total sessions by total time.
4. In your private DNS resolver, obtain the average **Private DNS requests per second**.
5. Input your values into our sizing calculator:

<TunnelCalculator />

You can use these results to determine if your tunnel is appropriately sized. To increase your tunnel capacity, add identical host machines running [`cloudflared` replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/#cloudflared-replicas).

# Ansible

import { Render } from "~/components";

Ansible is a software tool that enables at scale management of infrastructure. Ansible is agentless — all it needs to function is the ability to SSH to the target and Python installed on the target.

Ansible works alongside Terraform to streamline the Cloudflare Tunnel setup process. In this guide, you will use Terraform to deploy an SSH server on Google Cloud and create a [locally-managed tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/) that makes the server available over the Internet. Terraform will automatically run an Ansible playbook that installs and configures `cloudflared` on the server.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

## Prerequisites

To complete the steps in this guide, you will need:

- [A Google Cloud Project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project) and [GCP CLI installed and authenticated](https://cloud.google.com/sdk/docs/install).
- <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/terraform/">Basic knowledge of Terraform</a> and <a href="https://developer.hashicorp.com/terraform/tutorials/certification-associate-tutorials/install-cli">Terraform installed</a>.
- [A zone on Cloudflare](/fundamentals/manage-domains/add-site/).
- [A Cloudflare API token](/fundamentals/api/get-started/create-token/) with `Cloudflare Tunnel` and `DNS` permissions.

## 1. Install Ansible

Refer to the [Ansible installation instructions](https://docs.ansible.com/ansible/latest/installation_guide/index.html).

## 2. (Optional) Create an SSH key pair

Terraform and Ansible require an unencrypted SSH key to connect to the GCP server. If you do not already have a key, you can generate one as follows:

1. Open a terminal and type the following command:

   ```sh
   ssh-keygen -t rsa -f ~/.ssh/gcp_ssh -C <username in GCP>
   ```

2. When prompted for a passphrase, press the `Enter` key twice to leave it blank. Terraform cannot decode encrypted private keys.

Two files will be generated: `gcp_ssh` which contains the private key, and `gcp_ssh.pub` which contains the public key.

## 3. Create a configuration directory

1. Create a folder for your Terraform and Ansible configuration files:

   ```sh
   mkdir ansible-tunnel
   ```

2. Change to the new directory:

   ```sh
   cd ansible-tunnel
   ```

## 4. Create Terraform configuration files

### Define input variables

<Render file="terraform/input-variables" product="cloudflare-one" />

### Assign values to the variables

<Render file="terraform/variable-values" product="cloudflare-one" />

### Configure Terraform providers

You will need to declare the [providers](https://registry.terraform.io/browse/providers) used to provision the infrastructure.

1.  In your configuration directory, create a `.tf` file:

    ```sh
    touch providers.tf
    ```

2.  Add the following providers to `providers.tf`. The `random` provider is used to generate a tunnel secret.

        <Render file="terraform/providers-v5" product="cloudflare-one" />

### Configure Cloudflare resources

The following configuration will modify settings in your Cloudflare account.

1.  In your configuration directory, create a `.tf` file:

    ```sh
    touch Cloudflare-config.tf
    ```

2.  Add the following resources to `Cloudflare-config.tf`:

        ```tf

        # Creates a new remotely-managed tunnel for the GCP VM.
        resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" {
        	account_id    = var.cloudflare_account_id
        	name          = "Ansible GCP tunnel"
        	config_src    = "cloudflare"
        }

        # Reads the token used to run the tunnel on the server.
        data "cloudflare_zero_trust_tunnel_cloudflared_token" "gcp_tunnel_token" {
        	account_id 	= var.cloudflare_account_id
        	tunnel_id 	= cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
        }

        # Creates the CNAME record that routes http_app.${var.cloudflare_zone} to the tunnel.
        resource "cloudflare_dns_record" "http_app" {
        	zone_id = var.cloudflare_zone_id
        	name    = "http_app"
        	content = "${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id}.cfargotunnel.com"
        	type    = "CNAME"
        	ttl     = 1
        	proxied = true
        }

        # Configures tunnel with a published application for clientless access.
        resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gcp_tunnel_config" {
        	tunnel_id  = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
        	account_id = var.cloudflare_account_id
        	config     = {
        		ingress 	= [
        			{
        				hostname = "http_app.${var.cloudflare_zone}"
        				service  = "http://localhost:80"
        			},
        			{
        				service  = "http_status:404"
        			}
        		]
        	}
        }
        ```

### Configure GCP resources

The following configuration defines the specifications for the GCP virtual machine and installs Python3 on the machine. Python3 allows Ansible to configure the GCP instance instead of having to run a <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/terraform/#create-a-startup-script">startup script</a> on boot.

1. In your configuration directory, create a `.tf` file:

   ```sh
   touch GCP-config.tf
   ```

2. Open the file in a text editor and copy and paste the following example. Be sure to insert your own GCP username and SSH key pair.

   ```txt
   # Selects the OS for the GCP VM.
   data "google_compute_image" "image" {
   family  = "ubuntu-2204-lts"
   project = "ubuntu-os-cloud"
   }

   # Sets up a GCP VM instance.
   resource "google_compute_instance" "http_server" {
   name         = "ansible-inst"
   machine_type = var.machine_type
   zone         = var.zone
   tags         = []
   boot_disk {
       initialize_params {
       image = data.google_compute_image.image.self_link
       }
   }
   network_interface {
       network = "default"
       access_config {
       // Ephemeral IP
       }
   }
   scheduling {
       preemptible = true
       automatic_restart = false
   }

   // Installs Python3 on the VM.
   provisioner "remote-exec" {
       inline = [
       "sudo apt update", "sudo apt install python3 -y",  "echo Done!"
       ]
       connection {
       host = self.network_interface.0.access_config.0.nat_ip
       user = "<username in GCP>"
       type = "ssh"
       private_key= file("<path to private key>")
       }
   }
   provisioner "local-exec" {
       // If specifying an SSH key and user, add `--private-key <path to private key> -u var.name`
       command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u <username in GCP> --private-key <path to private key> -i ${self.network_interface.0.access_config.0.nat_ip}, playbook.yml"
   }

   metadata = {
       cf-email     = var.cloudflare_email
       cf-zone      = var.cloudflare_zone
       ssh-keys     = "<username in GCP>:${file("<path to public key>")}"
   }
   depends_on = [
       local_file.tf_ansible_vars_file
   ]
   }
   ```

### Export variables to Ansible

The following Terraform resource exports the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/">tunnel token</a> and other variables to <code>tf_ansible_vars_file.yml</code>. Ansible will use the tunnel token to configure and run <code>cloudflared</code> on the server.

1.  In your configuration directory, create a new `tf` file:

    ```sh
    touch export.tf
    ```

2.  Copy and paste the following content into `export.tf`:

        ```tf
        resource "local_file" "tf_ansible_vars_file" {
        	content = <<-DOC
        		# Ansible vars_file containing variable values from Terraform.
        		tunnel_id: ${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id}
        		tunnel_name: ${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.name}
        		tunnel_token: ${data.cloudflare_zero_trust_tunnel_cloudflared_token.gcp_tunnel_token.token}
        		DOC

        	filename = "./tf_ansible_vars_file.yml"
        }
        ```

## 5. Create the Ansible playbook

Ansible playbooks are YAML files that declare the configuration Ansible will deploy.

1. Create a new `.yml` file:

   ```sh
   touch playbook.yml
   ```

2. Open the file in a text editor and copy and paste the following content:

```yml
---
- hosts: all
  become: yes
  # Import tunnel variables into the VM.
  vars_files:
    - ./tf_ansible_vars_file.yml
  # Execute the following commands on the VM.
  tasks:
    - name: Download the cloudflared Linux package.
      shell: wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
    - name: Depackage cloudflared.
      shell: sudo dpkg -i cloudflared-linux-amd64.deb
    - name: Install the tunnel as a systemd service.
      shell: "cloudflared service install {{ tunnel_token }}"
    - name: Start the tunnel.
      systemd:
        name: cloudflared
        state: started
        enabled: true
        masked: no
    - name: Deploy an example Apache web server on port 80.
      shell: apt update && apt -y install apache2
    - name: Edit the default Apache index file.
      copy:
        dest: /var/www/html/index.html
        content: |
          <!DOCTYPE html>
          <html>
          <body>
            <h1>Hello Cloudflare!</h1>
            <p>This page was created for a Cloudflare demo.</p>
          </body>
          </html>
```

[Keywords](https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.html#play) define how Ansible will execute the configuration. In the example above, the `vars_files` keyword specifies where variable definitions are stored, and the `tasks` keyword specifies the actions Ansible will perform.

[Modules](https://docs.ansible.com/ansible/2.9/user_guide/modules.html) specify what tasks to complete. In this example, the `copy` module creates a file and populates it with content.

## 6. Deploy the configuration

Once you have created the configuration files, you can deploy them through Terraform. The Ansible deployment happens within the Terraform deployment when the `ansible-playbook` command is run.

1. Initialize your configuration directory:

   ```sh
   terraform init
   ```

2. (Optional) Preview everything that will be created:

   ```sh
   terraform plan
   ```

3. Deploy the configuration:

   ```sh
   terraform apply
   ```

It may take several minutes for the GCP instance and tunnel to come online. You can view your new tunnel in the <a href="https://dash.cloudflare.com/">Cloudflare dashboard</a> under <strong>Zero Trust</strong> &gt; <strong>Networks</strong> &gt; <strong>Connectors</strong> &gt; <strong>Cloudflare Tunnels</strong>.

## 7. Test the connection

To test, open a browser and go to `http://http_app.<CLOUDFLARE_ZONE>.com` (for example, `http_app.example.com`). You should see the **Hello Cloudflare!** test page.

# AWS

import { Render } from "~/components";

This guide covers how to connect an Amazon Web Services (AWS) virtual machine to Cloudflare using our lightweight connector, `cloudflared`.

We will deploy:

- An EC2 virtual machine that runs a basic HTTP server.
- A Cloudflare Tunnel that allows users to connect to the service via either a public hostname or a private IP address.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

### Prerequisites

To complete the following procedure, you will need to:

- [Add a website to Cloudflare](/fundamentals/manage-domains/add-site/)
- [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) on an end-user device

## 1. Create a VM instance in AWS

<Render
	file="tunnel/deployment-guides/deploy-aws-ec2-instance"
	product="cloudflare-one"
/>

## 2. Create a Cloudflare Tunnel

<Render
	file="tunnel/deployment-guides/cloud-create-tunnel"
	product="cloudflare-one"
	params={{ one: "AWS instance", two: "aws" }}
/>

## 3. Connect using a public hostname

<Render
	file="tunnel/deployment-guides/cloud-public-hostname"
	product="cloudflare-one"
/>

## 4. Connect using a private IP

<Render
	file="tunnel/deployment-guides/cloud-private-ip"
	product="cloudflare-one"
	params={{
		networkName: "virtual private cloud (VPC)",
		resourceName: "AWS instance",
		exampleIp: "172.31.19.0",
		splitTunnelCidr: "172.16.0.0/12",
		calcExclude: ["172.31.19.0", "172.24.0.0/16"],
	}}
/>

:::caution

<Render file="aws-resolver" product="cloudflare-one" />

:::

## Firewall configuration

To secure your AWS instance, you can configure your [Security Group rules](https://docs.aws.amazon.com/vpc/latest/userguide/security-group-rules.html) to deny all inbound traffic and allow only outbound traffic to the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#required-for-tunnel-operation">Cloudflare Tunnel IP addresses</a>. All Security Group rules are Allow rules; traffic that does not match a rule is blocked. Therefore, you can delete all inbound rules and leave only the relevant outbound rules.

:::note

If you delete the inbound rule for port `22`, you will be unable to SSH back into the instance.
:::

After configuring your Security Group rules, verify that you can still access the service through Cloudflare Tunnel via its <a href="#3-connect-using-a-public-hostname">public hostname</a> or <a href="#4-connect-using-a-private-ip">private IP</a>. The service should no longer be accessible from outside Cloudflare Tunnel -- for example, if you go to `http://ec2-44-202-59-16.compute-1.amazonaws.com` the test page should no longer load.

# Azure

import { Render } from "~/components";

This guide covers how to connect an Azure Virtual Machine to Cloudflare using our lightweight connector, `cloudflared`.

We will deploy:

- An Azure VM that runs a basic HTTP server.
- A Cloudflare Tunnel that allows users to connect to the service via either a public hostname or a private IP address.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

### Prerequisites

To complete the following procedure, you will need to:

- [Add a website to Cloudflare](/fundamentals/manage-domains/add-site/)
- [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) on an end-user device

## 1. Create a VM instance in Azure

1. In the Azure portal, go to **Virtual Machines** > **Create** > **Azure virtual machine**.

2. Select a **Resource group** or create a new one.

   ![Azure group](~/assets/images/cloudflare-one/connections/connect-apps/azure-1.png)

3. Enter a name for the VM and select a region. For **Image**, select _Ubuntu Server 24.04 LTS_. For **Size**, select an appropriate size (for example, _Standard_B1s_).

4. Under **Administrator account**, select **SSH public key** and enter your key pair.

   ![Azure keypair](~/assets/images/cloudflare-one/connections/connect-apps/azure-2.png)

5. Under **Inbound port rules**, allow SSH (`22`). For testing purposes, also allow HTTP (`80`) and HTTPS (`443`).

   ![Azure ports](~/assets/images/cloudflare-one/connections/connect-apps/azure-3.png)

6. Select **Review + create**, then **Create**.

7. Once the VM is running, copy its **Public IP address** from the VM overview page. Also record the **Private IP address** — Azure by default uses the `10.0.0.0/8` subnet.

8. SSH into the instance:

   ```sh
   ssh -i "your-key.pem" azureuser@<PUBLIC_IP>
   ```

9. Run `sudo su` to gain full admin rights to the VM.

10. For testing purposes, you can deploy a basic Apache web server on port `80`:

    ```bash
    apt update

    apt -y install apache2

    cat <<EOF > /var/www/html/index.html
    <html><body><h1>Hello Cloudflare!</h1>
    <p>This page was created for a Cloudflare demo.</p>
    </body></html>
    EOF
    ```

11. To verify that the Apache server is running, open a browser and go to `http://<PUBLIC_IP>` (make sure to connect over `http`, not `https`). You should see the **Hello Cloudflare!** test page.

## 2. Create a Cloudflare Tunnel

<Render
	file="tunnel/deployment-guides/cloud-create-tunnel"
	product="cloudflare-one"
	params={{ one: "Azure VM", two: "azure" }}
/>

## 3. Connect using a public hostname

<Render
	file="tunnel/deployment-guides/cloud-public-hostname"
	product="cloudflare-one"
/>

## 4. Connect using a private IP

<Render
	file="tunnel/deployment-guides/cloud-private-ip"
	product="cloudflare-one"
	params={{
		networkName: "Azure Virtual Network (VNet)",
		resourceName: "Azure VM",
		exampleIp: "10.0.0.4",
		splitTunnelCidr: "10.0.0.0/8",
		calcExclude: ["10.0.0.4", "10.0.1.0/24"],
	}}
/>

## Firewall configuration

To secure your Azure VM, you can configure your [Network Security Group (NSG)](https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview) to deny all inbound traffic and allow only outbound traffic to the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#required-for-tunnel-operation">Cloudflare Tunnel IP addresses</a>. All NSG rules are evaluated by priority; traffic that does not match an allow rule is blocked by the default deny rules. Therefore, you can delete all custom inbound rules and leave only the relevant outbound rules.

:::note

If you delete the inbound rule for port `22`, you will be unable to SSH back into the VM.
:::

After configuring your NSG rules, verify that you can still access the service through Cloudflare Tunnel via its <a href="#3-connect-using-a-public-hostname">public hostname</a> or <a href="#4-connect-using-a-private-ip">private IP</a>. The service should no longer be accessible from outside Cloudflare Tunnel — for example, direct access to the VM's public IP should no longer work.

# GCP

import { Render } from "~/components";

This guide covers how to connect a Google Cloud Project (GCP) virtual machine to Cloudflare using our lightweight connector, `cloudflared`.

We will deploy:

- A Google Cloud Project (GCP) virtual machine that runs a basic HTTP server.
- A Cloudflare Tunnel that allows users to connect to the service via either a public hostname or a private IP address.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

### Prerequisites

To complete the following procedure, you will need to:

- [Add a website to Cloudflare](/fundamentals/manage-domains/add-site/)
- [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) on an end-user device

## 1. Create a VM instance in GCP

<Render
	file="tunnel/deployment-guides/deploy-gcp-vm"
	product="cloudflare-one"
/>

## 2. Create a Cloudflare Tunnel

<Render
	file="tunnel/deployment-guides/cloud-create-tunnel"
	product="cloudflare-one"
	params={{ one: "GCP VM", two: "gcp" }}
/>

## 3. Connect using a public hostname

<Render
	file="tunnel/deployment-guides/cloud-public-hostname"
	product="cloudflare-one"
/>

## 4. Connect using a private IP

<Render
	file="tunnel/deployment-guides/cloud-private-ip"
	product="cloudflare-one"
	params={{
		networkName: "VPC network",
		resourceName: "GCP VM",
		exampleIp: "10.0.0.4",
		splitTunnelCidr: "10.0.0.0/8",
		calcExclude: ["10.0.0.4", "10.0.1.0/24"],
	}}
/>

## Firewall configuration

To secure your VM instance, you can [configure your VPC firewall rules](https://cloud.google.com/firewall/docs/using-firewalls) to deny all ingress traffic and allow only egress traffic to the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#required-for-tunnel-operation">Cloudflare Tunnel IP addresses</a>. Since GCP denies ingress traffic by [default](https://cloud.google.com/firewall/docs/firewalls#default_firewall_rules), you can delete all ingress rules and leave only the relevant egress rules.

:::note

If you delete the default `allow-ssh` rule, you will be unable to SSH back into the VM.
:::

After configuring your VPC firewall rules, verify that you can still access the service through Cloudflare Tunnel via its <a href="#3-connect-using-a-public-hostname">public hostname</a> or <a href="#4-connect-using-a-private-ip">private IP</a>. The service should no longer be accessible from outside Cloudflare Tunnel -- for example, if you go to `http://<EXTERNAL IP>` the test page should no longer load.

# Environments

import { DirectoryListing } from "~/components"

Deploy Cloudflare Tunnel in your environment — from cloud VMs to Kubernetes clusters, with Infrastructure as Code support.

<DirectoryListing />

# Kubernetes

import { GlossaryTooltip, Render } from "~/components";

[Kubernetes](https://kubernetes.io/) is a container orchestration tool that is used to deploy applications onto physical or virtual machines, scale the deployment to meet traffic demands, and push updates without downtime. The Kubernetes cluster, or environment, where the application instances are running is connected internally through a private network. You can install the `cloudflared` daemon inside of the Kubernetes cluster in order to connect applications inside of the cluster to Cloudflare.

This guide will cover how to expose a Kubernetes service to the public Internet using a <GlossaryTooltip term="remotely-managed tunnel">remotely-managed</GlossaryTooltip> Cloudflare Tunnel. For the purposes of this example, we will deploy a basic web application alongside `cloudflared` in Google Kubernetes Engine (GKE). The same principles apply to any other Kubernetes environment (such as `minikube`, `kubeadm`, or a cloud-based Kubernetes service) where `cloudflared` can connect to Cloudflare's network.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

:::note[Locally-managed tunnels]
If you are looking to set up a <GlossaryTooltip term="locally-managed tunnel">locally-managed tunnel</GlossaryTooltip> in Kubernetes, refer to the [example code in GitHub](https://github.com/cloudflare/argo-tunnel-examples/tree/master/named-tunnel-k8s).
:::

## Architecture

![Diagram showing how a user connects to Kubernetes services through Cloudflare Tunnel](~/assets/images/cloudflare-one/connections/connect-apps/kubernetes-tunnel.png)

As shown in the diagram, we recommend setting up `cloudflared` as an adjacent [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) to the application deployments. Having a separate Kubernetes deployment for `cloudflared` allows you to scale `cloudflared` independently of the application. In the `cloudflared` deployment, you can spin up <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/">multiple replicas</a> running the same Cloudflare Tunnel — there is no need to build a dedicated tunnel for each `cloudflared` pod. Each `cloudflared` replica / pod can reach all Kubernetes services in the cluster.

:::note
We do not recommend using `cloudflared` in autoscaling setups because downscaling (removing replicas) will break existing user connections to that replica. Additionally, `cloudflared` does not load balance across replicas; replicas are strictly for high availability. To load balance traffic to your nodes, you can use [Cloudflare Load Balancer](/load-balancing/private-network/) or a third-party load balancer.
:::

Once the cluster is connected to Cloudflare, you can configure Cloudflare Tunnel routes to control how `cloudflared` will proxy traffic to services within the cluster. For example, you may wish to publish certain Kubernetes applications to the Internet and restrict other applications to internal Cloudflare One Client users.

## Prerequisites

To complete the following procedure, you will need:

- [A Google Cloud Project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project)
- [A zone on Cloudflare](/fundamentals/manage-domains/add-site/)

## 1. Create a GKE cluster

To create a new Kubernetes cluster in Google Cloud:

1. Open [Google Cloud](https://console.cloud.google.com/) and go to **Kubernetes Engine**.
2. In **Clusters**, select **Create**.
3. Name the cluster. In this example, we will name it `cloudflare-tunnel`.
4. (Optional) Choose your desired region and other cluster specifications. For this example, we will use the default specifications.
5. Select **Create**.
6. To connect to the cluster:
   1. Select the three-dot menu.
   2. Select **Connect**.
   3. Select **Run in Cloud Shell** to open a terminal in the browser.
   4. Select **Authorize**.
   5. Press `Enter` to run the pre-populated `gcloud` command.
   6. (Recommended) In the Cloud Shell menu, select **Open Editor** to launch the built-in IDE.
7. In the Cloud Shell terminal, run the following command to check the cluster status:

   ```sh
   kubectl get all
   ```

   ```sh output
   NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
   service/kubernetes   ClusterIP   34.118.224.1   <none>        443/TCP   15m
   ```

## 2. Create pods for the web app

A pod represents an instance of a running process in the cluster. In this example, we will deploy the [httpbin](https://httpbin.org/) application with two pods and make the pods accessible inside the cluster at `httpbin-service:80`.

1. Create a folder for your Kubernetes manifest files:

   ```sh
   mkdir tunnel-example
   ```

2. Change into the directory:

   ```sh
   cd tunnel-example
   ```

3. In the `tunnel-example` directory, create a new file called `httpbin.yaml`. This file defines the Kubernetes deployment for the httpbin app.

   ```yaml title="httpbin.yaml"
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: httpbin-deployment
     namespace: default
   spec:
     replicas: 2
     selector:
       matchLabels:
         app: httpbin
     template:
       metadata:
         labels:
           app: httpbin
       spec:
         containers:
           - name: httpbin
             image: kennethreitz/httpbin:latest
             imagePullPolicy: IfNotPresent
             ports:
               - containerPort: 80
   ```

4. Create a new `httpbinsvc.yaml` file. This file defines a Kubernetes service that allows other apps in the cluster (such as `cloudflared`) to access the set of httpbin pods.

   ```yaml title="httpbinsvc.yaml"
   apiVersion: v1
   kind: Service
   metadata:
     name: httpbin-service
     namespace: default
   spec:
     type: LoadBalancer
     selector:
       app: httpbin
     ports:
       - port: 80
         targetPort: 80
   ```

5. Use the following command to run the application inside the cluster:

   ```sh
   kubectl create -f httpbin.yaml -f httpbinsvc.yaml
   ```

6. Check the status of your deployment:

   ```sh
   kubectl get all
   ```

   ```sh output
   NAME                                     READY   STATUS    RESTARTS   AGE
   pod/httpbin-deployment-bc6689c5d-b5ftk   1/1     Running   0          79s
   pod/httpbin-deployment-bc6689c5d-cbd9m   1/1     Running   0          79s

   NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
   service/httpbin-service   LoadBalancer   34.118.225.147   34.75.201.60   80:31967/TCP   79s
   service/kubernetes        ClusterIP      34.118.224.1     <none>         443/TCP        24h

   NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
   deployment.apps/httpbin-deployment   2/2     2            2           79s

   NAME                                           DESIRED   CURRENT   READY   AGE
   replicaset.apps/httpbin-deployment-bc6689c5d   2         2         2       79s
   ```

## 3. Create a tunnel

To create a Cloudflare Tunnel:

<ol>
	<li>
		In the <a href="https://dash.cloudflare.com/">Cloudflare dashboard</a>,
		go to <strong>Networking</strong> &gt; <strong>Tunnels</strong>.
	</li>
	<li>
		Select <strong>Create a tunnel</strong>.
	</li>
	<li>
		Enter a name for your tunnel (for example, <code>gke-tunnel</code>).
	</li>
	<li>
		Select <strong>Create Tunnel</strong>.
	</li>
	<li>
		<p>
			Choose your operating system and select{" "}
			<strong>Docker</strong>.
		</p>
		<p>
			Applications must be packaged into a containerized image before you can
			run it in Kubernetes. Therefore, we will use the <code>cloudflared</code>{" "}
			Docker container image to deploy the tunnel in Kubernetes.
		</p>
	</li>
	<li>
		Instead of running the installation command, copy just the token value
		rather than the whole command. The token value is of the form{" "}
		<code>eyJhIjoiNWFiNGU5Z...</code> You will need the token for the Kubernetes
		manifest file.
	</li>
</ol>

Leave the Cloudflare Tunnel browser tab open while we focus on the Kubernetes deployment.

## 4. Store the tunnel token

`cloudflared` uses a tunnel token to run a remotely-managed Cloudflare Tunnel. You can store the tunnel token in a [Kubernetes secret](https://kubernetes.io/docs/concepts/configuration/secret/).

1. In GKE Cloud Shell, create a `tunnel-token.yaml` file with the following content. Make sure to replace `<YOUR_TUNNEL_TOKEN>` with your tunnel token (`eyJhIjoiNWFiNGU5Z...`).

   ```yaml title="tunnel-token.yaml"
   apiVersion: v1
   kind: Secret
   metadata:
     name: tunnel-token
   stringData:
     token: <YOUR_TUNNEL_TOKEN>
   ```

2. Create the secret:

   ```sh
   kubectl create -f tunnel-token.yaml
   ```

3. Check the newly created secret:

   ```sh
   kubectl get secrets
   ```

   ```sh output
   NAME        TYPE     DATA   AGE
   tunnel-token   Opaque   1      100s
   ```

## 5. Create pods for cloudflared

To run the Cloudflare Tunnel in Kubernetes:

1. Create a Kubernetes deployment for a remotely-managed Cloudflare Tunnel:

   ```yaml title="tunnel.yaml"
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: cloudflared-deployment
     namespace: default
   spec:
     replicas: 2
     selector:
       matchLabels:
         pod: cloudflared
     template:
       metadata:
         labels:
           pod: cloudflared
       spec:
         securityContext:
           sysctls:
             # Allows ICMP traffic (ping, traceroute) to resources behind cloudflared.
             - name: net.ipv4.ping_group_range
               value: "65532 65532"
         containers:
           - image: cloudflare/cloudflared:latest
             name: cloudflared
             env:
               # Defines an environment variable for the tunnel token.
               - name: TUNNEL_TOKEN
                 valueFrom:
                   secretKeyRef:
                     name: tunnel-token
                     key: token
             command:
               # Configures tunnel run parameters
               - cloudflared
               - tunnel
               - --no-autoupdate
               - --loglevel
               - info
               - --output
               - json
               - --metrics
               - 0.0.0.0:2000
               - run
             livenessProbe:
               httpGet:
                 # Cloudflared has a /ready endpoint which returns 200 if and only if
                 # it has an active connection to Cloudflare's network.
                 path: /ready
                 port: 2000
               failureThreshold: 1
               initialDelaySeconds: 10
               periodSeconds: 10
   ```

2. Deploy `cloudflared` to the cluster:

   ```sh
   kubectl create -f tunnel.yaml
   ```

   Kubernetes will install the `cloudflared` image on two pods and run the tunnel using the command `cloudflared tunnel --no-autoupdate --loglevel info --output json --metrics 0.0.0.0:2000 run`.

   The `--output json` flag formats each console log line as a JSON object. This format is useful for log collection systems that consume JSON.

   `cloudflared` will consume the tunnel token from the `TUNNEL_TOKEN` environment variable.

3. Check the status of your cluster:

   ```sh
   kubectl get all
   ```

   ```sh output
   NAME                                          READY   STATUS    RESTARTS   AGE
   pod/cloudflared-deployment-6d5f9f9666-85l5w   1/1     Running   0          21s
   pod/cloudflared-deployment-6d5f9f9666-wb96x   1/1     Running   0          21s
   pod/httpbin-deployment-bc6689c5d-b5ftk        1/1     Running   0          3m36s
   pod/httpbin-deployment-bc6689c5d-cbd9m        1/1     Running   0          3m36s

   NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
   service/httpbin-service   LoadBalancer   34.118.225.147   34.75.201.60   80:31967/TCP   3m36s
   service/kubernetes        ClusterIP      34.118.224.1     <none>         443/TCP        24h

   NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
   deployment.apps/cloudflared-deployment   2/2     2            2           22s
   deployment.apps/httpbin-deployment       2/2     2            2           3m37s

   NAME                                                DESIRED   CURRENT   READY   AGE
   replicaset.apps/cloudflared-deployment-6d5f9f9666   2         2         2       22s
   replicaset.apps/httpbin-deployment-bc6689c5d        2         2         2       3m37s
   ```

You should see two `cloudflared` pods and two `httpbin` pods with a `Running` status. If your `cloudflared` pods keep restarting, check the `command` syntax in `tunnel.yaml` and make sure that the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/">tunnel run parameters</a> are in the correct order.

## 6. Verify tunnel status

To print logs for a `cloudflared` instance:

```sh
kubectl logs pod/cloudflared-deployment-6d5f9f9666-85l5w
```

```txt output
{"level":"info","message":"Starting tunnel","time":"2025-06-20T22:00:47Z","tunnelID":"64c359b6-e111-40ec-a3a9-199c2a656613"}
...
```

## 7. Add a tunnel route

Now that the tunnel is up and running, we can route the httpbin service through the tunnel.

<ol>
	<li>
		In the <a href="https://dash.cloudflare.com/">Cloudflare dashboard</a>, go
		to <strong>Networking</strong> &gt; <strong>Tunnels</strong> and select your
		tunnel.
	</li>
	<li>
		On the <strong>Routes</strong> tab, select <strong>Add route</strong> &gt;{" "}
		<strong>Published application</strong>.
	</li>
	<li>
		Enter a hostname for the application (for example,{" "}
		<code>httpbin.&lt;your-domain&gt;.com</code>).
	</li>
	<li>
		Under <strong>Service</strong>, enter{" "}
		<code>{"http://httpbin-service"}</code>. <code>httpbin-service</code> is the
		name of the Kubernetes service defined in <code>httpbinsvc.yaml</code>.
	</li>
	<li>
		Select <strong>Add route</strong>.
	</li>
</ol>

## 8. Test the connection

To test, open a new browser tab and go to `httpbin.<your-domain>.com`. You should see the httpbin homepage.

You can optionally <a href="/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/">create an Access application</a> to control who can access the service.

# Terraform

import { Render } from "~/components";

[Terraform](https://www.terraform.io/) is an infrastructure as code software tool that allows you to deploy services from different providers using a standardized configuration syntax. When creating a Terraform configuration file, you define the final state of the configuration rather than the step-by-step procedure. This allows you to easily deploy, modify, and manage your Tunnels alongside your other infrastructure.

In this guide, you will use Terraform to deploy:

- A Google Cloud Project (GCP) virtual machine that runs an HTTP test server
- A Cloudflare Tunnel that makes the server available over the Internet
- A Cloudflare Access policy that defines who can connect to the server

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

## Prerequisites

To complete the following procedure, you will need:

- [A Google Cloud Project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project)
- [A zone on Cloudflare](/fundamentals/manage-domains/add-site/)
- Enabled [one-time PIN login](/cloudflare-one/integrations/identity-providers/one-time-pin/) or integrated an [identity provider](/cloudflare-one/integrations/identity-providers/)

## 1. Install Terraform

Refer to the [Terraform installation guide](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) for your operating system.

## 2. Install the gcloud CLI

1. [Install the gcloud CLI](https://cloud.google.com/sdk/docs/install) so that Terraform can interact with your GCP account.

2. Authenticate with the CLI by running:

   ```sh
   gcloud auth application-default login
   ```

## 3. Create a Cloudflare API token

[Create an API token](/fundamentals/api/get-started/create-token/) so that Terraform can interact with your Cloudflare account. At minimum, your token should include the following permissions:

| Type    | Item                      | Permission |
| ------- | ------------------------- | ---------- |
| Account | Cloudflare Tunnel         | Edit       |
| Account | Access: Apps and Policies | Edit       |
| Zone    | DNS                       | Edit       |

## 4. Create a configuration directory

<Render file="terraform/config-directory" product="cloudflare-one" />

## 5. Create Terraform configuration files

### Define input variables

<Render file="terraform/input-variables" product="cloudflare-one" />

### Assign values to the variables

<Render file="terraform/variable-values" product="cloudflare-one" />

### Configure Terraform providers

<Render file="terraform/providers" product="cloudflare-one" />

### Configure Cloudflare resources

<Render file="terraform/cloudflare-resources" product="cloudflare-one" />

### Configure GCP resources

<Render file="terraform/gcp-resources" product="cloudflare-one" />

### Create a startup script


<Render file="terraform/startup-script" product="cloudflare-one" />

## 6. Deploy Terraform

<Render file="terraform/deploy-terraform" product="cloudflare-one" />

It may take several minutes for the GCP instance and tunnel to come online. You can view your new tunnel in the [Cloudflare dashboard](https://dash.cloudflare.com/) under **Networking** > **Tunnels**, and your Access application and policy under **Zero Trust** > **Access controls** > **Applications**. The new DNS records are available on the [**DNS Records** page](/dns/manage-dns-records/how-to/create-dns-records/).

:::note[Remove Terraform resources]

If you need to roll back the configuration, run `terraform destroy` to delete everything created through Terraform. Both `terraform apply` and `terraform destroy` prompt for user input before applying the changes. To run without requiring user input, you can add the `-auto-approve` flag to the command.
:::

## 7. Test the connection

1. In **Networking** > **Tunnels**, verify that your tunnel is active.

2. In **Access controls** > **Applications**, verify that your Cloudflare email is allowed by the Access policy.

3. From any device, open a browser and go to `http_app.<CLOUDFLARE_ZONE>` (for example, `http_app.example.com`).

   You will see the Access login page if you have not recently logged in.

4. Log in with your Cloudflare email.

   You should see the [HTTPBin](https://httpbin.org/) homepage.

# Other tunnel types

import { DirectoryListing } from "~/components";

Cloudflare recommends creating a [remotely-managed tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/) for most use cases. Remotely-managed tunnels store their configuration on Cloudflare, which allows you to manage the tunnel from any machine using the dashboard, API, or Terraform.

The following pages cover alternative tunnel workflows that are intended for specific scenarios such as local development, testing, or legacy configurations.

<DirectoryListing />

# Configuration file

import { Code } from "~/components";

:::note

<a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/#quick-tunnels">Quick tunnels</a> do not need a configuration
file.

:::

Locally-managed tunnels run as an instance of `cloudflared` on your machine. You can configure `cloudflared` properties by modifying <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/">command line parameters</a> or by editing the tunnel <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#4-create-a-configuration-file">configuration file</a>.

The CLI provides a quick way to handle configurations if you are connecting a single service through `cloudflared`. The tunnel configuration file is useful if you are connecting multiple services and need to configure properties or exceptions for specific origins. In the configuration file, you can define top-level properties for your `cloudflared` instance as well as <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/">origin-specific properties</a>. For a full list of configuration options, type `cloudflared tunnel help` in your terminal.

In the absence of a configuration file, `cloudflared` will proxy outbound traffic through port `8080`.

<h2>File structure for private networks</h2>
<p>
	If you are <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/">exposing a private network</a> to end users running the Cloudflare One Client, the configuration file only requires your tunnel UUID and credentials file path:
</p>
<Code
	code={`tunnel: <Tunnel-UUID>\ncredentials-file: /path/<Tunnel-UUID>.json`}
	lang="yml"
/>

:::note
In `cloudflared` 2023.9.0 and later, `warp-routing.enabled` is no longer supported and will produce a configuration warning. Private network traffic is routed automatically when you [add a private network route](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#5-start-routing-traffic).
:::

## File structure for published applications

If you are exposing local services to the Internet, you can assign a public hostname to each service:

```yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  - hostname: gitlab.widgetcorp.tech
    service: http://localhost:80
  - hostname: gitlab-ssh.widgetcorp.tech
    service: ssh://localhost:22
  - service: http_status:404
```

Configuration files that contain ingress rules must always include a catch-all rule that concludes the file. In this example, `cloudflared` will respond with a `404` status code when the request does not match any of the previous hostnames.

### How traffic is matched

In `cloudflared` version `2026.10.0` and later, you can normalize URLs before matching ingress rules. This improves security for rules that require [Cloudflare Access JWT validation](/tunnel/reference/origin-parameters/#access-settings).

Normalization removes dot segments (`.` and `..`) according to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-5.2.4). It also converts backslashes to forward slashes and merges repeated slashes.

Normalization is off by default in version `2026.10.0`. To turn it on, set [`--disable-path-normalization=false`](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#disable-path-normalization) before `run`.

Test your path-based routing before the [planned default change](/changelog/post/2026-10-22-cloudflared-path-normalization/). Configurations without rules requiring Access validation are unaffected.

These examples apply when normalization is on and your tunnel has rules requiring Access validation. Incoming paths are shown as received by `cloudflared`:

| Incoming path    | Path used for ingress matching | Path sent to origin |
| ---------------- | ------------------------------ | ------------------- |
| `/hello/../path` | `/path`                        | `/hello/../path`    |
| `/hello//path`   | `/hello/path`                  | `/hello//path`      |
| `/hello\path`    | `/hello/path`                  | `/hello\path`       |

Here is an example configuration file that specifies several rules:

```yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  # Rules map traffic from a hostname to a local service:
  - hostname: example.com
    service: https://localhost:8000
  # Rules can match the request's path to a regular expression:
  - hostname: static.example.com
    path: \.(jpg|png|css|js)$
    service: https://localhost:8001
  # Rules can match the request's hostname to a wildcard character:
  - hostname: "*.example.com"
    service: https://localhost:8002
  # An example of a catch-all rule:
  - service: https://localhost:8003
```

#### Wildcards

You can use wildcards to match traffic to multiple subdomains. For example, if you set the `hostname` key to `*.example.com`, both `alpha.example.com` and `beta.example.com` will route traffic to your origin. `cloudflared` does not support wildcards in the middle of the hostname, such as `test.*.example.com`.

You can also enter regular expressions for the `path` key. For example, if `hostname` is `static.example.com` and `path` is `\.(jpg|png|css|js)$`, matching URLs could include `https://static.example.com/data.js`, `http://static.example.com/images/photo.jpg`, and so on. Cloudflare parses the path regex using the [Go `syntax` package](https://pkg.go.dev/regexp/syntax).

:::note[Path forwarding]
`cloudflared` matches request paths to evaluate rules, but it forwards the full request path to your service without modifying or stripping it. URL normalization for ingress matching does not change this behavior.

For example, if an ingress rule matches `path: /api`, a request to `https://example.com/api/users` is sent to your service as `http://localhost:8000/api/users`.

If your application requires path stripping or URL rewriting, you can rewrite the URL at the Cloudflare edge using [URL Rewrite Rules](/rules/transform/url-rewrite/), or forward traffic from `cloudflared` to a local reverse proxy (such as Nginx or Traefik) configured to strip path prefixes before reaching your application.
:::

### Services

In addition to HTTP, `cloudflared` supports protocols like SSH, RDP, arbitrary TCP services, and Unix sockets. You can also route traffic to the built-in `hello_world` test server or respond to traffic with an HTTP status. For a full list of supported service types, refer to <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/protocols/">Protocols for published applications</a>.

```yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  # Example of a request over TCP:
  - hostname: example.com
    service: tcp://localhost:8000
  # Example of an HTTP request over a Unix socket:
  - hostname: staging.example.com
    service: unix:/home/production/echo.sock
  # Example of a request mapping to the Hello World test server:
  - hostname: test.example.com
    service: hello_world
  # Example of a rule responding to traffic with an HTTP status:
  - service: http_status:404
```

### Origin configuration

If you need to proxy traffic to multiple origins within one instance of `cloudflared`, you can define the way `cloudflared` sends requests to each service by specifying <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/">configuration options</a> as part of your ingress rules.

In the following example, the top-level configuration `connectTimeout: 30s` sets a 30-second connection timeout for all services within that instance of `cloudflared`. The ingress rule for `service: localhost:8002` then configures an exception to the top-level configuration by setting `connectTimeout` for that service at `10s`. The 30-second connection timeout still applies to all other services.

```yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
originRequest: # Top-level configuration
  connectTimeout: 30s

ingress:
  # The localhost:8000 service inherits all root-level configuration.
  # In other words, it will use a connectTimeout of 30 seconds.
  - hostname: example.com
    service: localhost:8000
  - hostname: example2.com
    service: localhost:8001
  # The localhost:8002 service overrides some root-level config.
  - service: localhost:8002
    originRequest:
      connectTimeout: 10s
      disableChunkedEncoding: true
  # Some built-in services such as `http_status` do not use any configuration.
  # The service below will simply respond with HTTP 404.
  - service: http_status:404
```

### Validate ingress rules

To validate the ingress rules in your configuration file, run:

```sh
cloudflared tunnel ingress validate
```

This will ensure that the set of ingress rules specified in your config file is valid.

### Test ingress rules

To verify that `cloudflared` will proxy the right traffic to the right local service, use `cloudflared tunnel ingress rule`. This checks a URL against every rule, from first to last, and shows the first rule that matches. For example:

```sh
cloudflared tunnel ingress rule https://foo.example.com
```

```sh output
Using rules from /usr/local/etc/cloudflared/config.yml
Matched rule #3
	hostname: *.example.com
	service: https://localhost:8000
```

## Update a configuration file

When making changes to the configuration file for a given tunnel, we suggest relying on <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/">`cloudflared` replicas</a> to propagate the new configuration with minimal downtime.

1. Have a `cloudflared` instance running with the original version of the configuration file.
2. Start a `cloudflared` replica running with the updated version of the configuration file.
3. Wait for the replica to be fully running and usable.
4. Stop the first instance of `cloudflared`.

Your `cloudflared` will now be running with the updated version of your configuration file.

:::note[Traffic handling]

When the first instance of `cloudflared` is stopped, long-lived HTTP requests (for example, Websocket) and TCP connections (for example, SSH) will be dropped. UDP flows will also be dropped, as they are modeled based on timeouts. When the new replica connects, it will handle all new traffic, including new HTTP requests, TCP connections, and UDP flows.

:::

# Create a locally-managed tunnel

import { Code, Render, TabItem, Tabs } from "~/components";

Follow this step-by-step guide to get your first tunnel up and running using the CLI.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

## Prerequisites

Before you start, make sure you:

- [Add a website to Cloudflare](/fundamentals/manage-domains/add-site/).
- [Change your domain nameservers to Cloudflare](/dns/zone-setups/full-setup/setup/).

## 1. Download and install `cloudflared`

<Tabs> <TabItem label="Windows">

1. Download `cloudflared` on your machine. Visit the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/">downloads</a> page to find the right package for your OS.

2. Rename the executable to `cloudflared.exe`

3. In PowerShell, change directory to your Downloads folder and run `.\cloudflared.exe --version`. It should output the version of `cloudflared`. Note that `cloudflared.exe` could be `cloudflared-windows-amd64.exe` or `cloudflared-windows-386.exe` if you have not renamed it.

   ```powershell
   PS C:\Users\Administrator\Downloads\cloudflared-stable-windows-amd64> .\cloudflared.exe --version
   ```

</TabItem> <TabItem label="macOS">

To download and install `cloudflared`:

```sh
brew install cloudflared
```

Alternatively, you can <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/">download the latest Darwin amd64 release</a> directly.

</TabItem> <TabItem label="Linux">

**Debian and Ubuntu APT**

Use the apt package manager to install `cloudflared` on compatible machines.

<Render file="tunnel/cloudflared-debian-install" product="cloudflare-one" />

**RHEL RPM**

Use the rpm package manager to install `cloudflared` on compatible machines.

1. Add Cloudflare's repository:

   ```sh
   curl -fsSl https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
   ```

2. Update repositories and install cloudflared:

   ```sh
   sudo yum update && sudo yum install cloudflared
   ```

**Arch Linux**

`cloudflared` is in the Arch Linux [`community` repository](https://wiki.archlinux.org/title/official_repositories#community).
Use `pacman` to install `cloudflared` on compatible machines.

```sh
pacman -Syu cloudflared
```

**Other**

Alternatively you can download the `cloudflared` binary or the linux packages to your machine and install manually. Visit the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/">downloads</a> page to find the right package for your OS.

</TabItem> <TabItem label="Build from source">

To build the latest version of `cloudflared` from source:

```sh
git clone https://github.com/cloudflare/cloudflared.git
cd cloudflared
make cloudflared
go install github.com/cloudflare/cloudflared/cmd/cloudflared
```

Depending on where you installed `cloudflared`, you can move it to a known path as well.

```sh
mv /root/cloudflared/cloudflared /usr/bin/cloudflared
```

</TabItem> </Tabs>

## 2. Authenticate `cloudflared`

```sh
cloudflared tunnel login
```

Running this command will:

- Open a browser window and prompt you to log in to your Cloudflare account. After logging in to your account, select your hostname.
- Generate an account certificate, the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/#certpem">cert.pem file</a>, in the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/#default-cloudflared-directory">default `cloudflared` directory</a>.

## 3. Create a tunnel and give it a name

```sh
cloudflared tunnel create <NAME>
```

Running this command will:

- Create a tunnel by establishing a persistent relationship between the name you provide and a UUID for your tunnel. At this point, no connection is active within the tunnel yet.
- Generate a <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/#credentials-file">tunnel credentials file</a> in the <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/#default-cloudflared-directory">default `cloudflared` directory</a>.
- Create a subdomain of `.cfargotunnel.com`.

From the output of the command, take note of the tunnel's UUID and the path to your tunnel's credentials file.

Confirm that the tunnel has been successfully created by running:

```sh
cloudflared tunnel list
```

## 4. Create a configuration file

1.  In your `.cloudflared` directory, create a <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/">`config.yml` file</a> using any text editor. This file will configure the tunnel to route traffic from a given origin to the hostname of your choice.

2.  Add the following fields to the file:

    If you are connecting a <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/">published application</a>:

    ```yml
    url: http://localhost:8000
    tunnel: <Tunnel-UUID>
    credentials-file: /root/.cloudflared/<Tunnel-UUID>.json
    ```

    If you are connecting a <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/">private network</a>:

    <Code code={`tunnel: <Tunnel-UUID>\ncredentials-file: /root/.cloudflared/<Tunnel-UUID>.json`} lang="yml" />

3.  Confirm that the configuration file has been successfully created by running:

    ```sh
    cat config.yml
    ```

## 5. Start routing traffic

1. To route a <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/">published application</a> through the tunnel:

   ```sh
   cloudflared tunnel route dns <UUID or NAME> <hostname>
   ```
	 This command will create a `CNAME` record pointing to `<UUID>.cfargotunnel.com`.

<p>2. If you are connecting a private network, route a private IP address or CIDR through the tunnel:</p>

<Code code="cloudflared tunnel route ip add <IP/CIDR> <UUID or NAME>" lang="sh" />

<p>3. Confirm that the route has been successfully established:</p>

<Code code="cloudflared tunnel route ip show" lang="sh" />

## 6. Run the tunnel

Run the tunnel to proxy incoming traffic from the tunnel to any number of services running locally on your origin.

```sh
cloudflared tunnel run <UUID or NAME>
```

If your configuration file has a custom name or is not in the `.cloudflared` directory, add the `--config` flag and specify the path.

```sh
cloudflared tunnel --config /path/your-config-file.yml run <UUID or NAME>
```

:::note

Cloudflare Tunnel can install itself as a system service on Linux and Windows and as a launch agent on macOS. For more information, refer to <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/as-a-service/">run as a service</a>.

:::

## 7. Check the tunnel

To get information on the tunnel you just created, run:

```sh
cloudflared tunnel info <UUID or NAME>
```

# Locally-managed tunnels

import { DirectoryListing, Render } from "~/components";

<Render
	file="tunnel/locally-managed/overview"
	product="cloudflare-one"
	params={{
		remoteTunnelURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/",
	}}
/>

<DirectoryListing />

# Useful terms

import { Render } from "~/components";

<Render
	file="tunnel/locally-managed/local-tunnel-terms"
	product="cloudflare-one"
	params={{
		generalTermsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/",
		configFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
		permissionsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/tunnel-permissions/",
	}}
/>

# Tunnel permissions

import { Render } from "~/components";

<Render
	file="tunnel/locally-managed/tunnel-permissions"
	product="cloudflare-one"
	params={{
		defaultDirectoryURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/#default-cloudflared-directory",
	}}
/>

# Useful commands

import { Render } from "~/components";

<Render
	file="tunnel/locally-managed/useful-commands"
	product="cloudflare-one"
	params={{
		replicasURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/",
		logsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/",
		updateURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/update-cloudflared/",
		loadBalancerPoolURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/public-load-balancers/",
		publishedApplicationURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/#file-structure-for-published-applications",
	}}
/>

## Manage private networks

| Command                                                    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cloudflared tunnel route ip add <IP/CIDR> <NAME or UUID>` | Adds any network route space (represented as a CIDR) to your routing table. That network space becomes reachable for requests egressing from a user's machine as long as it is using the Cloudflare One Client and is enrolled in the same account that is running the tunnel chosen here. Further, those requests will be proxied to the specified tunnel, and reach an IP in the given CIDR, as long as that IP is reachable from the tunnel. To assign the IP route to a specific [Virtual Network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/), use the `--vnet` option. |
| `cloudflared tunnel route ip show` (or `list`)             | Shows your organization's private routing table. You can use additional flags to filter the results.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `cloudflared tunnel route ip delete`                       | Deletes the row for a given CIDR from your routing table. That portion of your network will no longer be reachable by the Cloudflare One Client.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `cloudflared tunnel route ip get <IP/CIDR>`                | Checks which row of the routing table will be used to proxy a given IP. This helps check and validate your configuration.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `cloudflared tunnel vnet add <NAME or UUID>`               | Creates a Virtual Network to which IP routes can be assigned. To make this Virtual Network the default for your Zero Trust organization, use the `-d` flag.                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `cloudflared tunnel vnet delete <NAME or UUID>`            | Deletes the Virtual Network with the given name or UUID. Before you can delete a Virtual Network, you must first delete all IP routes assigned to the Virtual Network.                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `cloudflared tunnel vnet list`                             | Displays all active Virtual Networks, the default Virtual Network, and their creation times.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |

# Run as a service

import { DirectoryListing, Render } from "~/components";

<Render
	file="tunnel/locally-managed/as-a-service/overview"
	product="cloudflare-one"
/>

<DirectoryListing />

# Linux

import { Render } from "~/components";

<Render
	file="tunnel/locally-managed/as-a-service/linux"
	product="cloudflare-one"
	params={{
		cliSetupGuideURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/",
		configFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
		routeTrafficURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#5-start-routing-traffic",
	}}
/>

# macOS

import { Render } from "~/components";

<Render
	file="tunnel/locally-managed/as-a-service/macos"
	product="cloudflare-one"
	params={{
		cliSetupGuideURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/",
		configFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
		routeTrafficURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#5-start-routing-traffic",
	}}
/>

# Windows

import { Render } from "~/components";

<Render
	file="tunnel/locally-managed/as-a-service/windows"
	product="cloudflare-one"
	params={{
		configFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
		downloadsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/",
		credentialsFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/#credentials-file",
		createConfigFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#4-create-a-configuration-file",
		routeTrafficURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#5-start-routing-traffic",
	}}
/>

# Copyrights

***

[https://github.com/BurntSushi/toml](https://github.com/BurntSushi/toml)

The MIT License (MIT)

Copyright (c) 2013 TOML authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

***

[https://github.com/Sirupsen/logrus](https://github.com/Sirupsen/logrus)

The MIT License (MIT)

Copyright (c) 2014 Simon Eskildsen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

***

[https://github.com/beorn7/perks/](https://github.com/beorn7/perks/)

Copyright (C) 2013 Blake Mizerany

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

***

[https://github.com/certifi/gocertifi](https://github.com/certifi/gocertifi)

This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
one at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/).

***

[https://github.com/coreos/go-oidc/](https://github.com/coreos/go-oidc/)
[https://github.com/coreos/go-systemd/](https://github.com/coreos/go-systemd/)

Apache License
Version 2.0, January 2004
[http://www.apache.org/licenses/](http://www.apache.org/licenses/)

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
   Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
   stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
   that You distribute, all copyright, patent, trademark, and
   attribution notices from the Source form of the Work,
   excluding those notices that do not pertain to any part of
   the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
   distribution, then any Derivative Works that You distribute must
   include a readable copy of the attribution notices contained
   within such NOTICE file, excluding those notices that do not
   pertain to any part of the Derivative Works, in at least one
   of the following places: within a NOTICE text file distributed
   as part of the Derivative Works; within the Source form or
   documentation, if provided along with the Derivative Works; or,
   within a display generated by the Derivative Works, if and
   wherever such third-party notices normally appear. The contents
   of the NOTICE file are for informational purposes only and
   do not modify the License. You may add Your own attribution
   notices within Derivative Works that You distribute, alongside
   or as an addendum to the NOTICE text from the Work, provided
   that such additional attribution notices cannot be construed
   as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

```
  To apply the Apache License to your work, attach the following
  boilerplate notice, with the fields enclosed by brackets "{}"
  replaced with your own identifying information. (Don't include
  the brackets!)  The text should be enclosed in the appropriate
  comment syntax for the file format. We also recommend that a
  file or class name and description of purpose be included on the
  same "printed page" as the copyright notice for easier
  identification within third-party archives.
```

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

```
   http://www.apache.org/licenses/LICENSE-2.0
```

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

***

[https://github.com/facebookgo/grace](https://github.com/facebookgo/grace)

BSD License

For grace software

Copyright (c) 2015, Facebook, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name Facebook nor the names of its contributors may be used to
  endorse or promote products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

***

[https://github.com/getsentry/raven-go](https://github.com/getsentry/raven-go)

Copyright (c) 2013 Apollic Software, LLC. All rights reserved.
Copyright (c) 2015 Functional Software, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
  copyright notice, this list of conditions and the following disclaimer
  in the documentation and/or other materials provided with the
  distribution.
* Neither the name of Apollic Software, LLC nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

***

[https://github.com/glycerine/rbtree](https://github.com/glycerine/rbtree)

Copyright (C) 2012 Yasushi Saito

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

***

[https://github.com/golang/protobuf](https://github.com/golang/protobuf)

Go support for Protocol Buffers - Google's data interchange format

Copyright 2010 The Go Authors. All rights reserved.
[https://github.com/golang/protobuf](https://github.com/golang/protobuf)

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

```
* Redistributions of source code must retain the above copyright
```

notice, this list of conditions and the following disclaimer. \* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution. \* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

***

[https://github.com/lib/pq](https://github.com/lib/pq)

Copyright (c) 2011-2013, 'pq' Contributors
Portions Copyright (C) 2011 Blake Mizerany

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

***

[https://godoc.org/github.com/matttproud/golang\\\_protobuf\\\_extensions/pbutil](https://godoc.org/github.com/matttproud/golang\\_protobuf\\_extensions/pbutil)

```
                             Apache License
                       Version 2.0, January 2004
                    http://www.apache.org/licenses/
```

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
   Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
   stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
   that You distribute, all copyright, patent, trademark, and
   attribution notices from the Source form of the Work,
   excluding those notices that do not pertain to any part of
   the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
   distribution, then any Derivative Works that You distribute must
   include a readable copy of the attribution notices contained
   within such NOTICE file, excluding those notices that do not
   pertain to any part of the Derivative Works, in at least one
   of the following places: within a NOTICE text file distributed
   as part of the Derivative Works; within the Source form or
   documentation, if provided along with the Derivative Works; or,
   within a display generated by the Derivative Works, if and
   wherever such third-party notices normally appear. The contents
   of the NOTICE file are for informational purposes only and
   do not modify the License. You may add Your own attribution
   notices within Derivative Works that You distribute, alongside
   or as an addendum to the NOTICE text from the Work, provided
   that such additional attribution notices cannot be construed
   as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

```
  To apply the Apache License to your work, attach the following
  boilerplate notice, with the fields enclosed by brackets "{}"
  replaced with your own identifying information. (Don't include
  the brackets!)  The text should be enclosed in the appropriate
  comment syntax for the file format. We also recommend that a
  file or class name and description of purpose be included on the
  same "printed page" as the copyright notice for easier
  identification within third-party archives.
```

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

```
   http://www.apache.org/licenses/LICENSE-2.0
```

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

***

[https://github.com/mitchellh/go-homedir](https://github.com/mitchellh/go-homedir)

The MIT License (MIT)

Copyright (c) 2013 Mitchell Hashimoto

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

***

[https://github.com/pkg/errors](https://github.com/pkg/errors)

Copyright (c) 2015, Dave Cheney [dave@cheney.net](mailto:dave@cheney.net)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

***

[https://github.com/prometheus/client\_golang](https://github.com/prometheus/client_golang)
[https://github.com/prometheus/client\_model](https://github.com/prometheus/client_model)
[https://github.com/prometheus/common](https://github.com/prometheus/common)
[https://github.com/prometheus/procfs](https://github.com/prometheus/procfs)

```
                             Apache License
                       Version 2.0, January 2004
                    http://www.apache.org/licenses/
```

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
   Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
   stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
   that You distribute, all copyright, patent, trademark, and
   attribution notices from the Source form of the Work,
   excluding those notices that do not pertain to any part of
   the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
   distribution, then any Derivative Works that You distribute must
   include a readable copy of the attribution notices contained
   within such NOTICE file, excluding those notices that do not
   pertain to any part of the Derivative Works, in at least one
   of the following places: within a NOTICE text file distributed
   as part of the Derivative Works; within the Source form or
   documentation, if provided along with the Derivative Works; or,
   within a display generated by the Derivative Works, if and
   wherever such third-party notices normally appear. The contents
   of the NOTICE file are for informational purposes only and
   do not modify the License. You may add Your own attribution
   notices within Derivative Works that You distribute, alongside
   or as an addendum to the NOTICE text from the Work, provided
   that such additional attribution notices cannot be construed
   as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

```
  To apply the Apache License to your work, attach the following
  boilerplate notice, with the fields enclosed by brackets "[]"
  replaced with your own identifying information. (Don't include
  the brackets!)  The text should be enclosed in the appropriate
  comment syntax for the file format. We also recommend that a
  file or class name and description of purpose be included on the
  same "printed page" as the copyright notice for easier
  identification within third-party archives.
```

Copyright \[yyyy] \[name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

```
   http://www.apache.org/licenses/LICENSE-2.0
```

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

***

[https://github.com/urfave/cli](https://github.com/urfave/cli)

MIT License

Copyright (c) 2016 Jeremy Saenz & Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

***

[https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml)

```
                             Apache License
                       Version 2.0, January 2004
                    http://www.apache.org/licenses/
```

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

   "License" shall mean the terms and conditions for use, reproduction,
   and distribution as defined by Sections 1 through 9 of this document.

   "Licensor" shall mean the copyright owner or entity authorized by
   the copyright owner that is granting the License.

   "Legal Entity" shall mean the union of the acting entity and all
   other entities that control, are controlled by, or are under common
   control with that entity. For the purposes of this definition,
   "control" means (i) the power, direct or indirect, to cause the
   direction or management of such entity, whether by contract or
   otherwise, or (ii) ownership of fifty percent (50%) or more of the
   outstanding shares, or (iii) beneficial ownership of such entity.

   "You" (or "Your") shall mean an individual or Legal Entity
   exercising permissions granted by this License.

   "Source" form shall mean the preferred form for making modifications,
   including but not limited to software source code, documentation
   source, and configuration files.

   "Object" form shall mean any form resulting from mechanical
   transformation or translation of a Source form, including but
   not limited to compiled object code, generated documentation,
   and conversions to other media types.

   "Work" shall mean the work of authorship, whether in Source or
   Object form, made available under the License, as indicated by a
   copyright notice that is included in or attached to the work
   (an example is provided in the Appendix below).

   "Derivative Works" shall mean any work, whether in Source or Object
   form, that is based on (or derived from) the Work and for which the
   editorial revisions, annotations, elaborations, or other modifications
   represent, as a whole, an original work of authorship. For the purposes
   of this License, Derivative Works shall not include works that remain
   separable from, or merely link (or bind by name) to the interfaces of,
   the Work and Derivative Works thereof.

   "Contribution" shall mean any work of authorship, including
   the original version of the Work and any modifications or additions
   to that Work or Derivative Works thereof, that is intentionally
   submitted to Licensor for inclusion in the Work by the copyright owner
   or by an individual or Legal Entity authorized to submit on behalf of
   the copyright owner. For the purposes of this definition, "submitted"
   means any form of electronic, verbal, or written communication sent
   to the Licensor or its representatives, including but not limited to
   communication on electronic mailing lists, source code control systems,
   and issue tracking systems that are managed by, or on behalf of, the
   Licensor for the purpose of discussing and improving the Work, but
   excluding communication that is conspicuously marked or otherwise
   designated in writing by the copyright owner as "Not a Contribution."

   "Contributor" shall mean Licensor and any individual or Legal Entity
   on behalf of whom a Contribution has been received by Licensor and
   subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   copyright license to reproduce, prepare Derivative Works of,
   publicly display, publicly perform, sublicense, and distribute the
   Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
   this License, each Contributor hereby grants to You a perpetual,
   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
   (except as stated in this section) patent license to make, have made,
   use, offer to sell, sell, import, and otherwise transfer the Work,
   where such license applies only to those patent claims licensable
   by such Contributor that are necessarily infringed by their
   Contribution(s) alone or by combination of their Contribution(s)
   with the Work to which such Contribution(s) was submitted. If You
   institute patent litigation against any entity (including a
   cross-claim or counterclaim in a lawsuit) alleging that the Work
   or a Contribution incorporated within the Work constitutes direct
   or contributory patent infringement, then any patent licenses
   granted to You under this License for that Work shall terminate
   as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
   Work or Derivative Works thereof in any medium, with or without
   modifications, and in Source or Object form, provided that You
   meet the following conditions:

   (a) You must give any other recipients of the Work or
   Derivative Works a copy of this License; and

   (b) You must cause any modified files to carry prominent notices
   stating that You changed the files; and

   (c) You must retain, in the Source form of any Derivative Works
   that You distribute, all copyright, patent, trademark, and
   attribution notices from the Source form of the Work,
   excluding those notices that do not pertain to any part of
   the Derivative Works; and

   (d) If the Work includes a "NOTICE" text file as part of its
   distribution, then any Derivative Works that You distribute must
   include a readable copy of the attribution notices contained
   within such NOTICE file, excluding those notices that do not
   pertain to any part of the Derivative Works, in at least one
   of the following places: within a NOTICE text file distributed
   as part of the Derivative Works; within the Source form or
   documentation, if provided along with the Derivative Works; or,
   within a display generated by the Derivative Works, if and
   wherever such third-party notices normally appear. The contents
   of the NOTICE file are for informational purposes only and
   do not modify the License. You may add Your own attribution
   notices within Derivative Works that You distribute, alongside
   or as an addendum to the NOTICE text from the Work, provided
   that such additional attribution notices cannot be construed
   as modifying the License.

   You may add Your own copyright statement to Your modifications and
   may provide additional or different license terms and conditions
   for use, reproduction, or distribution of Your modifications, or
   for any such Derivative Works as a whole, provided Your use,
   reproduction, and distribution of the Work otherwise complies with
   the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
   any Contribution intentionally submitted for inclusion in the Work
   by You to the Licensor shall be under the terms and conditions of
   this License, without any additional terms or conditions.
   Notwithstanding the above, nothing herein shall supersede or modify
   the terms of any separate license agreement you may have executed
   with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
   names, trademarks, service marks, or product names of the Licensor,
   except as required for reasonable and customary use in describing the
   origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
   agreed to in writing, Licensor provides the Work (and each
   Contributor provides its Contributions) on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   implied, including, without limitation, any warranties or conditions
   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
   PARTICULAR PURPOSE. You are solely responsible for determining the
   appropriateness of using or redistributing the Work and assume any
   risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
   whether in tort (including negligence), contract, or otherwise,
   unless required by applicable law (such as deliberate and grossly
   negligent acts) or agreed to in writing, shall any Contributor be
   liable to You for damages, including any direct, indirect, special,
   incidental, or consequential damages of any character arising as a
   result of this License or out of the use or inability to use the
   Work (including but not limited to damages for loss of goodwill,
   work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses), even if such Contributor
   has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
   the Work or Derivative Works thereof, You may choose to offer,
   and charge a fee for, acceptance of support, warranty, indemnity,
   or other liability obligations and/or rights consistent with this
   License. However, in accepting such obligations, You may act only
   on Your own behalf and on Your sole responsibility, not on behalf
   of any other Contributor, and only if You agree to indemnify,
   defend, and hold each Contributor harmless for any liability
   incurred by, or claims asserted against, such Contributor by reason
   of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

```
  To apply the Apache License to your work, attach the following
  boilerplate notice, with the fields enclosed by brackets "{}"
  replaced with your own identifying information. (Don't include
  the brackets!)  The text should be enclosed in the appropriate
  comment syntax for the file format. We also recommend that a
  file or class name and description of purpose be included on the
  same "printed page" as the copyright notice for easier
  identification within third-party archives.
```

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

```
   http://www.apache.org/licenses/LICENSE-2.0
```

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

***

[https://zombiezen.com/go/capnproto2](https://zombiezen.com/go/capnproto2)

go-capnproto is licensed under the terms of the MIT license reproduced below.

\===============================================================================

Copyright (C) 2014 the go-capnproto authors and contributors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

\===============================================================================

# Downloads

import { Render } from "~/components";

Cloudflare Tunnel requires the installation of a lightweight server-side daemon, `cloudflared`, to connect your infrastructure to Cloudflare. If you are [creating a tunnel through the dashboard](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/), you can simply copy-paste the installation command shown in the dashboard.

To download and install `cloudflared` manually, use one of the following links.

<Render file="tunnel/downloads" product="cloudflare-one" />

To update `cloudflared`, refer to [these instructions](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/update-cloudflared/).

# License

Apache License
Version 2.0, January 2004
[http://www.apache.org/licenses/](http://www.apache.org/licenses/)

```
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

  "License" shall mean the terms and conditions for use, reproduction,
  and distribution as defined by Sections 1 through 9 of this document.

  "Licensor" shall mean the copyright owner or entity authorized by
  the copyright owner that is granting the License.

  "Legal Entity" shall mean the union of the acting entity and all
  other entities that control, are controlled by, or are under common
  control with that entity. For the purposes of this definition,
  "control" means (i) the power, direct or indirect, to cause the
  direction or management of such entity, whether by contract or
  otherwise, or (ii) ownership of fifty percent (50%) or more of the
  outstanding shares, or (iii) beneficial ownership of such entity.

  "You" (or "Your") shall mean an individual or Legal Entity
  exercising permissions granted by this License.

  "Source" form shall mean the preferred form for making modifications,
  including but not limited to software source code, documentation
  source, and configuration files.

  "Object" form shall mean any form resulting from mechanical
  transformation or translation of a Source form, including but
  not limited to compiled object code, generated documentation,
  and conversions to other media types.

  "Work" shall mean the work of authorship, whether in Source or
  Object form, made available under the License, as indicated by a
  copyright notice that is included in or attached to the work
  (an example is provided in the Appendix below).

  "Derivative Works" shall mean any work, whether in Source or Object
  form, that is based on (or derived from) the Work and for which the
  editorial revisions, annotations, elaborations, or other modifications
  represent, as a whole, an original work of authorship. For the purposes
  of this License, Derivative Works shall not include works that remain
  separable from, or merely link (or bind by name) to the interfaces of,
  the Work and Derivative Works thereof.

  "Contribution" shall mean any work of authorship, including
  the original version of the Work and any modifications or additions
  to that Work or Derivative Works thereof, that is intentionally
  submitted to Licensor for inclusion in the Work by the copyright owner
  or by an individual or Legal Entity authorized to submit on behalf of
  the copyright owner. For the purposes of this definition, "submitted"
  means any form of electronic, verbal, or written communication sent
  to the Licensor or its representatives, including but not limited to
  communication on electronic mailing lists, source code control systems,
  and issue tracking systems that are managed by, or on behalf of, the
  Licensor for the purpose of discussing and improving the Work, but
  excluding communication that is conspicuously marked or otherwise
  designated in writing by the copyright owner as "Not a Contribution."

  "Contributor" shall mean Licensor and any individual or Legal Entity
  on behalf of whom a Contribution has been received by Licensor and
  subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of
  this License, each Contributor hereby grants to You a perpetual,
  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  copyright license to reproduce, prepare Derivative Works of,
  publicly display, publicly perform, sublicense, and distribute the
  Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of
  this License, each Contributor hereby grants to You a perpetual,
  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
  (except as stated in this section) patent license to make, have made,
  use, offer to sell, sell, import, and otherwise transfer the Work,
  where such license applies only to those patent claims licensable
  by such Contributor that are necessarily infringed by their
  Contribution(s) alone or by combination of their Contribution(s)
  with the Work to which such Contribution(s) was submitted. If You
  institute patent litigation against any entity (including a
  cross-claim or counterclaim in a lawsuit) alleging that the Work
  or a Contribution incorporated within the Work constitutes direct
  or contributory patent infringement, then any patent licenses
  granted to You under this License for that Work shall terminate
  as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the
  Work or Derivative Works thereof in any medium, with or without
  modifications, and in Source or Object form, provided that You
  meet the following conditions:

  (a) You must give any other recipients of the Work or
      Derivative Works a copy of this License; and

  (b) You must cause any modified files to carry prominent notices
      stating that You changed the files; and

  (c) You must retain, in the Source form of any Derivative Works
      that You distribute, all copyright, patent, trademark, and
      attribution notices from the Source form of the Work,
      excluding those notices that do not pertain to any part of
      the Derivative Works; and

  (d) If the Work includes a "NOTICE" text file as part of its
      distribution, then any Derivative Works that You distribute must
      include a readable copy of the attribution notices contained
      within such NOTICE file, excluding those notices that do not
      pertain to any part of the Derivative Works, in at least one
      of the following places: within a NOTICE text file distributed
      as part of the Derivative Works; within the Source form or
      documentation, if provided along with the Derivative Works; or,
      within a display generated by the Derivative Works, if and
      wherever such third-party notices normally appear. The contents
      of the NOTICE file are for informational purposes only and
      do not modify the License. You may add Your own attribution
      notices within Derivative Works that You distribute, alongside
      or as an addendum to the NOTICE text from the Work, provided
      that such additional attribution notices cannot be construed
      as modifying the License.

  You may add Your own copyright statement to Your modifications and
  may provide additional or different license terms and conditions
  for use, reproduction, or distribution of Your modifications, or
  for any such Derivative Works as a whole, provided Your use,
  reproduction, and distribution of the Work otherwise complies with
  the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise,
  any Contribution intentionally submitted for inclusion in the Work
  by You to the Licensor shall be under the terms and conditions of
  this License, without any additional terms or conditions.
  Notwithstanding the above, nothing herein shall supersede or modify
  the terms of any separate license agreement you may have executed
  with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade
  names, trademarks, service marks, or product names of the Licensor,
  except as required for reasonable and customary use in describing the
  origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or
  agreed to in writing, Licensor provides the Work (and each
  Contributor provides its Contributions) on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  implied, including, without limitation, any warranties or conditions
  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
  PARTICULAR PURPOSE. You are solely responsible for determining the
  appropriateness of using or redistributing the Work and assume any
  risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory,
  whether in tort (including negligence), contract, or otherwise,
  unless required by applicable law (such as deliberate and grossly
  negligent acts) or agreed to in writing, shall any Contributor be
  liable to You for damages, including any direct, indirect, special,
  incidental, or consequential damages of any character arising as a
  result of this License or out of the use or inability to use the
  Work (including but not limited to damages for loss of goodwill,
  work stoppage, computer failure or malfunction, or any and all
  other commercial damages or losses), even if such Contributor
  has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing
  the Work or Derivative Works thereof, You may choose to offer,
  and charge a fee for, acceptance of support, warranty, indemnity,
  or other liability obligations and/or rights consistent with this
  License. However, in accepting such obligations, You may act only
  on Your own behalf and on Your sole responsibility, not on behalf
  of any other Contributor, and only if You agree to indemnify,
  defend, and hold each Contributor harmless for any liability
  incurred by, or claims asserted against, such Contributor by reason
  of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

  To apply the Apache License to your work, attach the following
  boilerplate notice, with the fields enclosed by brackets "[]"
  replaced with your own identifying information. (Don't include
  the brackets!)  The text should be enclosed in the appropriate
  comment syntax for the file format. We also recommend that a
  file or class name and description of purpose be included on the
  same "printed page" as the copyright notice for easier
  identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

## Runtime Library Exception to the Apache 2.0 License:

```
As an exception, if you use this Software to compile your source code and
portions of this Software are embedded into the binary product as a result,
you may redistribute such product without providing attribution as would
otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
```

# Update cloudflared

import { GlossaryTooltip, TabItem, Tabs, Render, DashButton } from "~/components";

Updates will cause `cloudflared` to restart which will impact traffic currently being served. You can perform zero-downtime upgrades by using Cloudflare's [Load Balancer product](#update-with-cloudflare-load-balancer) or by using [multiple `cloudflared` instances](#update-with-multiple-cloudflared-instances).

## Update the `cloudflared` service

Refer to the following commands to update `cloudflared` for a <GlossaryTooltip term="remotely-managed tunnel">remotely-managed tunnel</GlossaryTooltip> or a <GlossaryTooltip term="locally-managed tunnel">locally-managed tunnel</GlossaryTooltip>. Locally-managed tunnels must be set up to <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/as-a-service/">run as a service</a> for the following commands to execute successfully.

<Tabs> <TabItem label="Windows">

Run the following command:

```powershell
cloudflared update
```

After running `cloudflared update` to update `cloudflared`, you must restart the service for it to take effect. Run:

```powershell
net start cloudflared
```

</TabItem> <TabItem label="macOS">

1. Update the `cloudflared` package:

```sh
brew upgrade cloudflared
```

2. Restart the service:

```sh
sudo launchctl stop com.cloudflare.cloudflared
sudo launchctl unload /Library/LaunchDaemons/com.cloudflare.cloudflared.plist
sudo launchctl load /Library/LaunchDaemons/com.cloudflare.cloudflared.plist
sudo launchctl start com.cloudflare.cloudflared
```

</TabItem> <TabItem label="Debian">

**If installed via apt:**

1. Update the `cloudflared` package:

```sh
sudo apt-get update && sudo apt-get install --only-upgrade cloudflared
```

2. Restart the service:

```sh
sudo systemctl restart cloudflared.service
```

**If installed via `dpkg -i`:**

Use the following commands if you installed `cloudflared` using the `dpkg` package manager. <Render file="tunnel/package-manager" product="cloudflare-one" />

1. Update the `cloudflared` package:

```sh
curl --location --output cloudflared.deb "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$(dpkg --print-architecture).deb" && sudo dpkg -i cloudflared.deb
```

2. Restart the service:

```sh
sudo systemctl restart cloudflared.service
```

</TabItem> <TabItem label="Red Hat">

1. Update the `cloudflared` package:

```sh
sudo yum update cloudflared
```

2. Restart the service:

```sh
sudo systemctl restart cloudflared.service
```

</TabItem> <TabItem label="Docker">

**If you created a remotely-managed tunnel using the dashboard:**

1. In the Cloudflare dashboard, go to **Networking** > **Tunnels**.

   <DashButton url="/?to=/:account/tunnels" />

2. Select your tunnel to open its detail page.
3. On the **Overview** tab, copy the Docker installation command. The copied command will contain your token.
4. Paste this command into a terminal window.

This command creates a new container from the latest `cloudflared` image. You can now delete the old container.

:::caution

Cloudflare recommends creating remotely-managed tunnels when working with Docker.

:::

**If you created a remotely or locally-managed tunnel using the API, run the following command:**

```sh
docker run --pull always cloudflare/cloudflared:latest tunnel --no-autoupdate run --token <TOKEN>
```

**If you created a locally-managed tunnel using the CLI:**

1. Mount your local `.cloudflared` directory into the Docker container using a volume.

2. Run the following command to update `cloudflared`:

   ```sh
   docker run --pull always -v <PATH-TO-YOUR-LOCAL-CLOUDFLARED>:/home/nonroot/.cloudflared cloudflare/cloudflared:latest tunnel --no-autoupdate run <TUNNEL-ID>
   ```

</TabItem> <TabItem label="Other">

If you installed `cloudflared` from GitHub-provided binaries or from source, run the following command:

```sh
cloudflared update
```

If you installed `cloudflared` with a package manager, you must update it using the same package manager. <Render file="tunnel/package-manager" product="cloudflare-one" />

</TabItem> </Tabs>

## Update with Cloudflare Load Balancer

You can update `cloudflared` without downtime by using Cloudflare's Load Balancer product with your Cloudflare Tunnel deployment.

1. Install a new instance of `cloudflared` and <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/">create</a> a new Tunnel.
2. Configure the instance to point traffic to the same locally-available service as your current, active instance of `cloudflared`.
3. <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/public-load-balancers/">Add the address</a> of the new instance of `cloudflared` into your Load Balancer pool as priority 2.
4. Swap the priority such that the new instance is now priority 1 and monitor to confirm traffic is being served.
5. Once confirmed, you can remove the older version from the Load Balancer pool.

## Update with multiple `cloudflared` instances

If you are not using Cloudflare's Load Balancer, you can use multiple instances of `cloudflared` to update without the risk of downtime.

1. Install a new instance of `cloudflared` and <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/">create</a> a new Tunnel.
2. Configure the instance to point traffic to the same locally-available service as your current, active instance of `cloudflared`.
3. In the Cloudflare DNS dashboard, <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/dns/">replace</a> the address of the current instance of `cloudflared` with the address of the new instance. Save the record.
4. Remove the now-inactive instance of `cloudflared`.

:::note[Traffic handling]

When the old replica is stopped, it will drop long-lived HTTP requests (for example, WebSocket) and TCP connections (for example, SSH). UDP flows will also be dropped, as they are modeled based on timeouts. When the new replica connects, it will handle all new traffic, including new HTTP requests, TCP connections, and UDP flows.

:::

### Run multiple instances in Windows

Windows systems require services to have a unique name and display name. You can run multiple instances of `cloudflared` by creating `cloudflared` services with unique names.

1. Install and configure `cloudflared`.
2. Next, create a service with a unique name and point to the `cloudflared` executable and configuration file.

```powershell
sc.exe create <unique-name> binPath='<path-to-exe>' --config '<path-to-config>' displayname="Unique Name"
```

3. Proceed to create additional services with unique names.

4. You can now start each unique service.

```powershell
sc.exe start <unique-name>
```

# Create a tunnel (API)

import { Render, APIRequest } from "~/components";

Follow this guide to set up a Cloudflare Tunnel using the API.

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

## Create an API token

[Create an API token](/fundamentals/api/get-started/create-token/) with the following permissions:

| Type    | Item              | Permission |
| ------- | ----------------- | ---------- |
| Account | Cloudflare Tunnel | Edit       |
| Zone    | DNS               | Edit       |

## 2. Create a tunnel

Make a `POST` request to the [Cloudflare Tunnel](/api/resources/zero_trust/subresources/tunnels/subresources/cloudflared/methods/create/) endpoint:

<APIRequest
  path="/accounts/{account_id}/cfd_tunnel"
  method="POST"
  json={{
    name: "api-tunnel",
    config_src: "cloudflare",
  }}
/>

```sh output
{
  "success": true,
  "errors": [],
  "messages": [],
  "result": {
    "id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
    "account_tag": "699d98642c564d2e855e9661899b7252",
    "created_at": "2025-02-18T22:41:43.534395Z",
    "deleted_at": null,
    "name": "example-tunnel",
    "connections": [],
    "conns_active_at": null,
    "conns_inactive_at": "2025-02-18T22:41:43.534395Z",
    "tun_type": "cfd_tunnel",
    "metadata": {},
    "status": "inactive",
    "remote_config": true,
    "credentials_file": {
      "AccountTag": "699d98642c564d2e855e9661899b7252",
      "TunnelID": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
      "TunnelName": "api-tunnel",
      "TunnelSecret": "bTSquyUGwLQjYJn8cI8S1h6M6wUc2ajIeT7JotlxI7TqNqdKFhuQwX3O8irSnb=="
    },
    "token": "eyJhIjoiNWFiNGU5Z..."
  }
}
```

Copy the `id` and `token` values shown in the output. You will need these values to configure and run the tunnel.

The next steps depend on whether you want to [publish an application to the Internet](#3a-publish-an-application) or [connect a private network](#3b-connect-a-network).

## 3a. Publish an application

Before you publish an application through your tunnel, you must:

- [Add a website to Cloudflare](/fundamentals/manage-domains/add-site/).
- [Change your domain nameservers to Cloudflare](/dns/zone-setups/full-setup/setup/).

Follow these steps to publish an application to the Internet. If you are looking to connect a private resource, skip to the [Connect a network](#3b-connect-a-network) section.

1. Make a [`PUT` request](/api/resources/zero_trust/subresources/tunnels/subresources/cloudflared/subresources/configurations/methods/update/) to route your [local service URL](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/protocols/) to a public hostname. For example,

		<APIRequest
			path="/accounts/{account_id}/cfd_tunnel/{tunnel_id}/configurations"
			method="PUT"
			json={{
				config: {
					ingress: [
						{
							hostname: "app.example.com",
							service: "http://localhost:8001",
							originRequest: {},
						},
						{
							service: "http_status:404",
						},
					],
				},
			}}
		/>
   :::note
   If you add a multi-level subdomain (more than one level of subdomain), you must [order an Advanced Certificate for the hostname](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/common-errors/#i-see-this-site-cant-provide-a-secure-connection).
   :::

   Your ingress rules must include a catch-all rule at the end. In this example, `cloudflared` will respond with a 404 status code when the request does not match any of the previous hostnames.

2. [Create a DNS record](/api/resources/dns/subresources/records/methods/create/) for your application:

		<APIRequest
			path="/zones/{zone_id}/dns_records"
			method="POST"
			json={{
				type: "CNAME",
				proxied: true,
				name: "app.example.com",
				content: "c1744f8b-faa1-48a4-9e5c-02ac921467fa.cfargotunnel.com",
			}}
		/>

   This DNS record allows Cloudflare to proxy `app.example.com` traffic to your Cloudflare Tunnel (`<tunnel-id>.cfargotunnel.com`).

This application will be publicly available on the Internet once you [run the tunnel](#4-install-and-run-the-tunnel). To allow or block specific users, [create an Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).

## 3b. Connect a network

To connect a private network through your tunnel, [add a tunnel route](/api/resources/zero_trust/subresources/networks/subresources/routes/methods/create/):

<APIRequest
  path="/accounts/{account_id}/teamnet/routes"
  method="POST"
  json={{
    network: "172.16.0.0/16",
    tunnel_id: "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
    comment: "Example private network route",
  }}
/>

`cloudflared` can now route traffic to these destination IPs. To configure Zero Trust policies and connect as a user, refer to [Connect private networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/).

## 4. Install and run the tunnel

Install `cloudflared` on your server and run the tunnel using the `token` value obtained in [2. Create a tunnel](#2-create-a-tunnel). You can also get the tunnel token using the [Cloudflare Tunnel token](/api/resources/zero_trust/subresources/tunnels/subresources/cloudflared/subresources/token/methods/get/) endpoint.

<Render file="tunnel/install-and-run-tunnel" product="cloudflare-one" />

## 5. Verify tunnel status

To check if the tunnel is serving traffic:

<APIRequest
  path="/accounts/{account_id}/cfd_tunnel/{tunnel_id}"
  method="GET"
  parameters={{
    tunnel_id: "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
  }}
/>

```sh output
{
  "success": true,
  "errors": [],
  "messages": [],
  "result": {
    "id": "c1744f8b-faa1-48a4-9e5c-02ac921467fa",
    "account_tag": "699d98642c564d2e855e9661899b7252",
    "created_at": "2025-02-18T22:41:43.534395Z",
    "deleted_at": null,
    "name": "example-tunnel",
    "connections": [
      {
        "colo_name": "bos01",
        "uuid": "2xz99mfm-a59e-4924-gyh9-z9vafaw6k0i2",
        "id": "2xz99mfm-a59e-4924-gyh9-z9vafaw6k0i2",
        "is_pending_reconnect": false,
        "origin_ip": "10.1.0.137",
        "opened_at": "2025-02-19T19:11:12.101642Z",
        "client_id": "4xh4eb3f-cz0j-2aso-hu6i-36207018771a",
        "client_version": "2025.2.0"
      },
      {
        "colo_name": "phl01",
        "uuid": "axe2socu-2fb5-3akx-b860-898zyes3cs9q",
        "id": "axe2socu-2fb5-3akx-b860-898zyes3cs9q",
        "is_pending_reconnect": false,
        "origin_ip": "10.1.0.137",
        "opened_at": "2025-02-19T19:11:12.006297Z",
        "client_id": "4xh4eb3f-cz0j-2aso-hu6i-36207018771a",
        "client_version": "2025.2.0"
      },
      {
        "colo_name": "phl01",
        "uuid": "9b5y0wm9-ca7f-ibq6-8ff4-sm53xekfyym1",
        "id": "9b5y0wm9-ca7f-ibq6-8ff4-sm53xekfyym1",
        "is_pending_reconnect": false,
        "origin_ip": "10.1.0.137",
        "opened_at": "2025-02-19T19:11:12.004721Z",
        "client_id": "4xh4eb3f-cz0j-2aso-hu6i-36207018771a",
        "client_version": "2025.2.0"
      },
      {
        "colo_name": "bos01",
        "uuid": "g6cdeiz1-80f5-3akx-b18b-3y0ggktoxwkd",
        "id": "g6cdeiz1-80f5-3akx-b18b-3y0ggktoxwkd",
        "is_pending_reconnect": false,
        "origin_ip": "10.1.0.137",
        "opened_at": "2025-02-19T19:11:12.110765Z",
        "client_id": "4xh4eb3f-cz0j-2aso-hu6i-36207018771a",
        "client_version": "2025.2.0"
      }
    ],
    "conns_active_at": "2025-02-19T19:11:12.004721Z",
    "conns_inactive_at": null,
    "tun_type": "cfd_tunnel",
    "metadata": {},
    "status": "healthy",
    "remote_config": true
  }
}
```

A healthy tunnel will have four connections to Cloudflare's network.

# Create a tunnel (dashboard)

import { Render, Stream, DashButton } from "~/components";

Follow this step-by-step guide to create your first [remotely-managed tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/#remotely-managed-tunnel).

<Render file="tunnel/connectivity-prechecks-note" product="cloudflare-one" />

## 1. Create a tunnel

<Stream id="4b75ad2aa58700602e94b148827687a2" title="How to set up Cloudflare Tunnel" thumbnail="https://pub-d9bf66e086fb4b639107aa52105b49dd.r2.dev/tunnel%204_%20set%20up%20tunnel.png" />

<Render file="tunnel/create-tunnel" product="cloudflare-one" />

The next steps depend on whether you want to [publish an application to the Internet](#2a-publish-an-application) or [connect a private network](#2b-connect-a-network).

## 2a. Publish an application

Follow these steps to publish an application to the Internet. If you are looking to connect a private resource, skip to the [Connect a network](#2b-connect-a-network) section.

:::note[Prerequisites]
Before you publish an application through your tunnel, you must [add a website to Cloudflare](/fundamentals/manage-domains/add-site/).
:::

<Render file="tunnel/add-published-application" product="cloudflare-one" />

Anyone on the Internet can now access the application at the specified hostname. To allow or block specific users, [create an Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).

## 2b. Connect a network

To connect a private network through your tunnel, add a CIDR route:

1. Go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. Select **Create route**, then choose **Tunnel CIDR**.
3. Select the tunnel you just created.
4. In **Network**, enter the private IP address or CIDR range of your service (for example, `10.0.0.1` or `10.0.0.0/24`).
5. Select **Create route**.

`cloudflared` can now route traffic to these destination IPs. To configure Zero Trust policies and connect as a user, refer to [Connect an IP/CIDR](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/).

:::note
If you would like to route to a private application using its hostname instead of its IP, refer to [Connect a private hostname](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/).
:::

## 3. View your tunnel

After saving the tunnel, you will be redirected to the **Networking** > **Tunnels** page. Your tunnel should be listed with a `Healthy` status. If your tunnel status is `Inactive`, `Down`, or `Degraded`, refer to the [troubleshooting documentation](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/common-errors/#tunnel-status) for recommended next steps.

# Get started

import { DirectoryListing, GlossaryTooltip } from "~/components"

To create and manage tunnels, you will need to install and authenticate <GlossaryTooltip term="cloudflared"><code>cloudflared</code></GlossaryTooltip> on your origin server. `cloudflared` is what connects your server to Cloudflare's global network.

<DirectoryListing />

# Useful terms

import { DashButton } from "~/components";

Review terminology for Cloudflare Tunnels.

## Tunnel

A tunnel is a secure, outbound-only pathway you can establish between your origin and Cloudflare's global network. Each tunnel you create will be assigned a [name](#tunnel-name) and a [UUID](#tunnel-uuid).

## Tunnel UUID

A tunnel UUID is an alphanumeric, unique ID assigned to a tunnel. The tunnel UUID can be used whenever you need to reference a specific tunnel.

## Tunnel name

A tunnel name is a unique, user-friendly identifier that you choose for a tunnel. Since a tunnel can proxy traffic to multiple services, tunnel names do not need to be hostnames. For example, you can assign your tunnel a name that represents your application/network, a particular server, or the cloud environment where it runs.

## Connector

The connector, referred to as `cloudflared`, establishes connectivity from your origin server to the Cloudflare global network. Each `cloudflared` instance creates four long-lived connections to at least two distinct data centers within Cloudflare's global network. This built-in redundancy means that if an individual connection, server, or data center goes down, your origin remains available.

`cloudflared` operates as a secure connector daemon rather than a local reverse proxy. It forwards requests directly to the configured local service without altering request URI paths or performing local load balancing.

## Replica

A replica is an additional instance of `cloudflared` running the same tunnel on a different host. You can create and configure a tunnel once, then run it through multiple replicas for redundancy. DNS records and Cloudflare Load Balancers continue to point to the tunnel (`UUID.cfargotunnel.com`), while Cloudflare distributes traffic across the available replicas. There is no guarantee about which replica will be chosen — Cloudflare routes to the geographically closest one. Replicas are typically deployed to keep a tunnel available if a host running `cloudflared` goes offline.

## Remotely-managed tunnel

A remotely-managed tunnel is a [tunnel](#tunnel) that was created in the Cloudflare dashboard under **Networking** > **Tunnels**. Tunnel configuration is stored in Cloudflare, which allows you to manage the tunnel from the dashboard or using the [API](/api/resources/zero_trust/subresources/tunnels/subresources/cloudflared/subresources/configurations/methods/get/).

<DashButton url="/?to=/:account/tunnels" />

## Locally-managed tunnel

A locally-managed tunnel is a [tunnel](#tunnel) that was created by running `cloudflared tunnel create <NAME>` on the command line. Tunnel configuration is stored in your local [cloudflared directory](#default-cloudflared-directory). For terminology specific to locally-managed tunnels, refer to the [Locally-managed tunnel glossary](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/).

## Quick tunnels

Quick Tunnels generate a random `trycloudflare.com` subdomain that sends traffic to a local service. For more information, refer to [Quick Tunnels](/tunnel/get-started/quick-tunnels/).

## Virtual networks

A [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) is a software abstraction that allows you to logically segregate resources on your private network. Virtual networks are especially useful for exposing resources which have overlapping IP routes. To connect to a resource, end users would select a virtual network in their Cloudflare One Client settings before entering the destination IP.

# Monitor tunnels

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Log streams

import { Render, DashButton } from "~/components";

Tunnel logs record all activity between a `cloudflared` instance and Cloudflare's global network, as well as all activity between `cloudflared` and your origin server. These logs allow you to investigate connectivity or performance issues with a Cloudflare Tunnel. You can configure your server to store persistent logs, or you can stream real-time logs from any client machine.

## View logs on the server

<Render
	file="tunnel/logs/view-logs-on-server"
	product="cloudflare-one"
	params={{
		loglevelURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#loglevel",
		logfileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#logfile",
		runParametersURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#add-run-parameters-to-tunnel-service",
	}}
/>

## View logs on your local machine

You can view real-time logs for a Cloudflare Tunnel via the dashboard or from any machine that has `cloudflared` installed. With remote log streams, you do not need to SSH into the server that is running the tunnel. To get remote logs, the tunnel must be active and able to receive requests.

### Dashboard

:::note
Tunnel log streams require [edit permissions](/fundamentals/manage-members/roles/) for Cloudflare Tunnel. Due to the sensitive nature of these logs, read-only roles (such as `Zero Trust Read Only`) do not have access.
:::

<Render file="tunnel/logs/view-logs-dashboard" product="cloudflare-one" />

### CLI

The `cloudflared` daemon can stream logs from any tunnel in your account to the local command line. `cloudflared` must be installed on both your local machine and the origin server.

<Render file="tunnel/logs/view-logs-cli" product="cloudflare-one" />

#### Filter logs

<Render
	file="tunnel/logs/filter-logs"
	product="cloudflare-one"
	params={{
		loglevelURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#loglevel",
	}}
/>

#### View logs for a replica

If you are running multiple `cloudflared` instances for the same tunnel (also known as [replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/)), you must specify an individual instance to stream logs from:

1. In the Cloudflare dashboard, go to **Networking** > **Tunnels** and select your tunnel.

   <DashButton url="/?to=/:account/tunnels" />

2. Find the **Connector ID** for the `cloudflared` instance you want to view.
3. Specify the Connector ID in `cloudflared tail`:
   ```sh
   cloudflared tail --connector-id <CONNECTOR ID> <UUID>
   ```

### Performance considerations

- The logging session will only be held open for one hour. All logging systems introduce some level of performance overhead, and this limit helps prevent long term impact to your tunnel's end-to-end latencies.
- When streaming logs for a high throughput tunnel, Cloudflare intentionally prioritizes service stability over log delivery. To reduce the number of dropped logs, try [requesting fewer logs](#filter-logs). To ensure that you are seeing all logs, [view logs on the server](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/#view-logs-on-the-server) instead of streaming the logs remotely.

## Network session logs

<Render
	file="tunnel/logs/network-session-logs"
	product="cloudflare-one"
	params={{
		tailURL: "#view-logs-for-a-replica",
	}}
/>

# Metrics

import { Render } from "~/components";

<Render file="tunnel/metrics/intro" product="cloudflare-one" />

## Default metrics server address

<Render
	file="tunnel/metrics/default-address"
	product="cloudflare-one"
	params={{
		logsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/",
	}}
/>

## Configure the metrics server address

<Render
	file="tunnel/metrics/configure-address"
	product="cloudflare-one"
	params={{
		updateRunParametersURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#add-run-parameters-to-tunnel-service",
		grafanaTutorialURL: "/cloudflare-one/tutorials/grafana/",
	}}
/>

## Available metrics

### cloudflared metrics

<Render file="tunnel/metrics/cloudflared" product="cloudflare-one" />

### Prometheus metrics

<Render file="tunnel/metrics/prometheus" product="cloudflare-one" />

### Go runtime metrics

<Render file="tunnel/metrics/go-runtime" product="cloudflare-one" />

# Notifications

import { AvailableNotifications } from "~/components"

Administrators can receive an alert when Cloudflare Tunnels in an account change their health or deployment status. Notifications can be delivered via email, webhook, and third-party services.

## Manage notifications

Tunnel notifications are configured on the [Cloudflare dashboard](https://dash.cloudflare.com/). For more information, refer to [Create a notification](/notifications/get-started/#create-an-alert).

## Available notifications

<AvailableNotifications product="Tunnel" />

# Private networks

With Cloudflare Zero Trust, you can connect private networks and the services running in those networks to Cloudflare's global network. This involves installing a [connector](#connectors) on the private network, and then [setting up routes](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2b-connect-a-network) which define the IP addresses available in that environment. Unlike [published applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/), private network routes can expose both HTTP and non-HTTP resources.

To reach private network IPs, end users must connect their device to Cloudflare and enroll in your Zero Trust organization. The most common method is to install the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on their device, or you can onboard their network traffic to Cloudflare using [Cloudflare Mesh](/mesh/), [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/), or [Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-tunnel/).

Administrators can optionally set [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) to control access to services based on user identity and device posture.

:::note
The restriction described in [Delivering videos with Cloudflare](/fundamentals/reference/policies-compliances/delivering-videos-with-cloudflare/) does not apply to private network routes. You can use these routes for large-file transfers, backups, and high-traffic workloads such as media servers.
:::

## Connectors

Here are the different ways you can connect your private network to Cloudflare:

- [**Cloudflare Mesh**](/mesh/) creates a private network between mesh nodes, client devices, and the services behind them. Each participant is assigned a [Mesh IP](/cloudflare-one/networks/routes/reserved-ips/#device-ips) for direct connectivity. Mesh nodes install on a Linux server and act as subnet routers for site-to-site, bidirectional, and mesh networking. Client devices install the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) for device-to-device and device-to-network connectivity.
- [**Cloudflare Tunnel (`cloudflared`)**](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/) installs on a server in your private network and creates a secure, outbound-only tunnel to Cloudflare. `cloudflared` only proxies traffic initiated from a user to a server. Any service or application running behind the tunnel will use the server's default routing table for server-initiated connectivity.
- [**Cloudflare WAN**](/cloudflare-one/networks/connectors/cloudflare-wan/) connects entire network locations to Cloudflare using anycast GRE or IPsec tunnels configured on your existing networking equipment.

# Connect an IP/CIDR

import { DashButton, Render } from "~/components";

This guide covers how to enable secure remote access to private IP addresses using `cloudflared` and the Cloudflare One Client. You can connect an entire private network, a subnet, or an application defined by a static IP.

## 1. Connect the server to Cloudflare

To connect your infrastructure with Cloudflare Tunnel:

<Render file="tunnel/connect-private-network" product="cloudflare-one" />

3. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

4. Select **Create route** > **Tunnel CIDR**.

5. Select the tunnel you just created, then enter the IP/CIDR range that you wish to route through the tunnel (for example, `10.0.0.1` or `10.0.0.0/8`).

6. (Optional) Select a [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) for this tunnel route. This step is only needed if the route's IP/CIDR range overlaps with another route in your account. If you do not select a virtual network, the IP route will be assigned to the `default` network.

7. Select **Create route**.

## 2. Set up the client

<Render file="tunnel/warp-to-tunnel-client" product="cloudflare-one" />

## 3. Route private network IPs through the Cloudflare One Client

<Render file="tunnel/warp-to-tunnel-route-ips" product="cloudflare-one" params={{ one: "private network"}}/>

## 4. (Recommended) Filter network traffic with Gateway

<Render file="tunnel/filter-network-traffic" product="cloudflare-one" />

### Enable the Gateway proxy

<Render file="tunnel/enable-gateway-proxy" product="cloudflare-one" />

### Zero Trust policies

<Render file="tunnel/catch-all-policy" product="cloudflare-one"/>

If you have applications clearly defined by IPs or hostnames, we recommend [creating an Access application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) and managing user access alongside your SaaS and other web apps. Alternatively, if you prefer to secure a private network using a traditional firewall model, you can build Gateway network and DNS policies for IP ranges and domains.

For more information on building Gateway policies, refer to [Secure your first application](/learning-paths/replace-vpn/build-policies/create-policy/) and [Common network policies](/cloudflare-one/traffic-controls/network-policies/common-policies/#restrict-access-to-private-networks).

## 5. Connect as a user

End users can now reach HTTP or TCP-based services on your network by visiting any IP address in the range you have specified.

To allow users to reach the service using its private hostname instead of its IP, refer to [Private DNS](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/private-dns/).

To expose an IP from this range to public TCP or UDP traffic, refer to [Create a Spectrum application using a virtual network](/spectrum/get-started/#create-a-spectrum-application-using-a-virtual-network-origin).

### Troubleshooting

#### Device configuration

To check that their device is properly configured, the user can visit `https://help.teams.cloudflare.com/` to ensure that:

- The page returns **Your network is fully protected**.
- In **HTTP filtering**, both **WARP** and **Gateway Proxy** are enabled.
- The **Team name** matches the Zero Trust organization from which you created the tunnel.

#### Router configuration

Check the local IP address of the device and ensure that it does not fall within the IP/CIDR range of your private network. For example, some home routers will make DHCP assignments in the `10.0.0.0/24` range, which overlaps with the `10.0.0.0/8` range used by most corporate private networks. When a user's home network shares the same IP addresses as the routes in your tunnel, their device will be unable to connect to your application.

To resolve the IP conflict, you can either:

- Reconfigure the user's router to use a non-overlapping IP range. Compatible routers typically use `192.168.1.0/24`, `192.168.0.0/24` or `172.16.0.0/24`.
- Tighten the IP range in your Split Tunnel configuration to exclude the `10.0.0.0/24` range. This will only work if your private network does not have any hosts within `10.0.0.0/24`.
- Change the IP/CIDR of your private network so that it does not overlap with a range commonly used by home networks.

# Connect a private hostname

import { Render, Details, GlossaryTooltip } from "~/components";
import TunnelHostnameRoutingDiagram from "~/components/cf/TunnelHostnameRoutingDiagram.astro";

Instead of managing static IP lists and routes, you can connect users to private HTTP and non-HTTP applications using their hostnames (for example, `wiki.internal.local`). Private hostname routes are especially useful when the application has an unknown or ephemeral IP, which often occurs when infrastructure is provisioned by a third-party cloud provider.

<TunnelHostnameRoutingDiagram />

When a user requests a private hostname, Cloudflare Gateway assigns an <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip> to route the traffic through your tunnel to the correct private IP address. By default, this IP is drawn from a Cloudflare-owned public IPv4 range (`172.64.128.0/20`) rather than Carrier-Grade NAT (CGNAT) space, so it does not trigger [Google Chrome's Local Network Access restrictions](#google-chrome-restricts-access-to-private-hostnames). You can also [configure a custom range](/cloudflare-one/networks/routes/configure-initial-resolved-ips/) if it conflicts with your existing network. For a deep dive into the architecture and packet flow, refer to our [announcement blog post](https://blog.cloudflare.com/tunnel-hostname-routing/).

## Supported on-ramps/off-ramps

The table below summarizes the Cloudflare One products that are compatible with private hostname routing. Refer to the table legend for guidance on interpreting the table.

✅ Product works with no caveats <br/>
🚧 Product can be used with some caveats <br/>
❌ Product cannot be used <br/>

### Device connectivity

End users can connect to private hostnames using the following traffic on-ramps:

<Render file="gateway/egress-selector-onramps" product="cloudflare-one" />

### Private network connectivity

Private hostname routing works with the off-ramps below. Other traffic off-ramps require IP-based routes.

| Connector                                                                                  | Compatibility | Minimum version |
| ------------------------------------------------------------------------------------------ | ------------- | -- |
| [cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/)                        | ✅            | 2025.7.0 |
| [Cloudflare Mesh](/mesh/features/routes/#hostname-routes) | ✅             | 2026.6.822.0 (Linux) |
| [Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-gateway/)                                                             | ❌             | |

## Connect a private hostname

This section covers how to enable remote access to a private hostname application using `cloudflared`.

### Prerequisites

Before you can connect to private hostnames, you must enable the Gateway proxy.

<Render file="tunnel/enable-gateway-proxy" product="cloudflare-one" />

Your devices must also forward the following traffic to Cloudflare:

- Initial resolved IPs:
  <Render file="gateway/egress-selector-cgnat-ips" product="cloudflare-one" />
- DNS queries for your private hostname

Configuration steps vary depending on your [device on-ramp](#device-connectivity):

<Details header="Cloudflare One Clients">
	1. <Render file="gateway/egress-selector-split-tunnels"
		product="cloudflare-one"/>
	2. In [Local Domain
	Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/),
	delete the top-level domain for your private hostname. This configures WARP to
	send the DNS query to Cloudflare Gateway for resolution.
</Details>

<Details header="Cloudflare Mesh">

To attract a hostname's traffic to a Mesh node instead of a `cloudflared` tunnel, add a [hostname route](/mesh/features/routes/#hostname-routes) to the node. The <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip> listed above must route through Cloudflare on both the Mesh node and client device profiles, and — for a private hostname — the node must be able to resolve the hostname (via its local hosts file or a Gateway resolver policy). Refer to [Hostname routes](/mesh/features/routes/#hostname-routes).

</Details>

<Details header="Cloudflare WAN">

1. Ensure that the <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip> listed above [route through Cloudflare WAN](/cloudflare-wan/configuration/how-to/configure-routes/) to Cloudflare.
2. [Point the DNS resolver](/cloudflare-wan/zero-trust/cloudflare-gateway/#dns-filtering) for your Cloudflare WAN network to Cloudflare Gateway.

</Details>

### 1. Connect the application to Cloudflare

<Render file="tunnel/create-tunnel" product="cloudflare-one" />

8. After the tunnel is connected, go to the tunnel's **Routes** tab and select **Add route**, then select **Private hostname**.

9. Enter the fully qualified domain name (FQDN) that represents your application (for example, `wiki.internal.local`).

   <Render
   	file="tunnel/hostname-format-restrictions"
   	product="cloudflare-one"
   />

10. Select **Save**.

### 2. Configure DNS resolution

When Gateway receives a request for your private hostname, it must resolve the hostname to a private IP address. There are two ways to configure this, depending on your network topology.

#### Scenario A: Use the system resolver (Default)

By default, `cloudflared` uses the private DNS resolver configured on its host machine (for example, in `/etc/resolv.conf` on Linux).

If the machine running `cloudflared` can already resolve `wiki.internal.local` to its private IP using the local system resolver, no further configuration is required. You can skip to [Step 3](#3-recommended-filter-network-traffic-with-gateway).

#### Scenario B: Use a specific private DNS server (Advanced)

<Render
	file="tunnel/hostname-route-dns-advanced"
	product="cloudflare-one"
	params={{ hostname: "wiki.internal.local" }}
/>

### 3. (Recommended) Filter network traffic with Gateway

<Render file="tunnel/filter-network-traffic" product="cloudflare-one" />

<Render file="tunnel/catch-all-policy" product="cloudflare-one" />

#### Option 1: Access application (recommended)

You can create an [Access self-hosted application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) for your private hostname and configure [Access policies](/cloudflare-one/access-controls/policies/) within that application. This option allows you to manage user access alongside your SaaS and other web apps.

#### Option 2: Gateway firewall policies

If you prefer to secure the application using a traditional firewall model, you can build Gateway network policies using the [SNI](/cloudflare-one/traffic-controls/network-policies/#sni) or [SNI Domain](/cloudflare-one/traffic-controls/network-policies/#sni-domain) selector. For an additional layer of protection, add a Gateway DNS policy to allow or block the [Host](/cloudflare-one/traffic-controls/dns-policies/#host) or [Domain](/cloudflare-one/traffic-controls/dns-policies/#domain) from resolving.

    <Details header="Example network policies">
    The following example consists of two policies: the first allows specific users to reach your application, and the second blocks all other traffic.

    1. Allow company employees

      <Render file="gateway/policies/restrict-access-to-private-networks-allow" product="cloudflare-one" params={{ selector: "SNI", value: "wiki.internal.local" }} />

    2. Catch-all block policy

      <Render file="gateway/policies/restrict-access-to-private-networks-block" product="cloudflare-one" />
    </Details>

    <Details header="Example DNS policy">

      <Render file="gateway/policies/restrict-access-to-private-networks-dns" product="cloudflare-one" />
    </Details>

:::note[SNI selector limitations]

<Render file="gateway/selectors/sni-443" product="cloudflare-one" />

Additionally, SNI selectors will only apply to Cloudflare One Client traffic. If your users will be connecting from other [on-ramps](#device-connectivity), you can allow or block network traffic using the [Destination IP](/cloudflare-one/traffic-controls/network-policies/#destination-ip) selector instead of SNI.
:::

:::note[Origins with private or self-signed certificates]

If your private HTTPS application presents a certificate that is not issued by a publicly trusted certificate authority (for example, a self-signed certificate or one issued by your organization's internal CA), Gateway cannot validate the certificate and returns [Error 526](/cloudflare-one/traffic-controls/troubleshooting/) when [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) is turned on.

To reach the application, create a Gateway HTTP Allow policy that matches it and set the [untrusted certificate action](/cloudflare-one/traffic-controls/http-policies/#untrusted-certificates) to _Pass through_. This also applies when you secure the application with an [Access self-hosted application](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) over HTTPS on port `443`, because the browser-based login flow depends on TLS decryption.
:::

### 4. Test the connection

End users can now reach the application by going to its private hostname. For example, to connect to a private web application, open a browser and go to `wiki.internal.local`.

#### Troubleshooting

If you cannot connect, verify the following:

1. **Confirm DNS resolution** - From the device, confirm that you can successfully resolve the private hostname:

   ```sh
   nslookup wiki.internal.local
   ```

   ```sh output
   Server:		127.0.2.2
   Address:	127.0.2.2#53

   Non-authoritative answer:
   Name:	wiki.internal.local
   Address: 172.64.128.48
   ```

   The query should resolve using [WARP's DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) and return a Gateway <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>. If the query fails to resolve or returns a different IP, check your [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) configuration and [Gateway resolver policies](/cloudflare-one/traffic-controls/resolver-policies/).

2. **Check Gateway logs** - Review your [Gateway network logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) to see if the connection is being blocked by a policy.

3. **Verify tunnel status** - Confirm that your tunnel is healthy and connected by checking [tunnel status](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/).

4. **Test connectivity to initial resolved IP** - When you connect to the application using its private hostname, the device should make a connection to the <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>:

   ```sh
   curl -v4 http://wiki.internal.local
   ```

   ```sh output
   * Trying 172.64.128.48:80...
   * Connected to wiki.internal.local (172.64.128.48) port 80
   ...
   ```

   If the request fails, confirm that the initial resolved IP [routes through the WARP tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/). You can also check your [tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/) to confirm that requests are routing to the application's private IP.

5. **Check for certificate errors** - If your private HTTPS application returns [Error 526](/cloudflare-one/traffic-controls/troubleshooting/), Gateway could not validate the origin's certificate. Create a Gateway HTTP Allow policy that matches the application and set the [untrusted certificate action](/cloudflare-one/traffic-controls/http-policies/#untrusted-certificates) to _Pass through_.

## Limitations

### Google Chrome restricts access to private hostnames

<Render file="gateway/egress-selector-chrome-issue" product="cloudflare-one" />

# Connect with cloudflared

`cloudflared` is a daemon that runs on a host machine in your private network and proxies traffic from Cloudflare to local services. The tunnel created by `cloudflared` is outbound-only, meaning it only handles requests initiated from a user to your private network. Server-initiated requests (from applications behind the tunnel) use the server's default routing table and do not pass through the tunnel.

On the client side, end users connect to Cloudflare's global network using the Cloudflare One Client. The Cloudflare One Client can be rolled out to your entire organization in just a few minutes using your in-house MDM tooling. When users connect to an IP address or hostname made available through Cloudflare Tunnel, WARP sends their connection through Cloudflare's network and down the corresponding tunnel to the internal service. Traffic to services behind the tunnel will carry the local source IP address of the host machine running the `cloudflared` daemon.

![Diagram displaying connections between a device, Cloudflare, and a private network.](~/assets/images/cloudflare-one/connections/private-ips-diagram.png)

To enable remote access to your private network, refer to the following guides:
- [**Connect a private hostname**](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/): Route network traffic to an internal application using its hostname.
- [**Connect an IP/CIDR**](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/): Route traffic to an internal IP address or CIDR range.

# Private DNS

import { Render, DashButton } from "~/components"

By default, all DNS requests on the user device are resolved by Cloudflare's [public DNS resolver](/1.1.1.1/) except for common top level domains used for local resolution (such as `localhost`). You can connect an internal DNS resolver to Cloudflare and use it to resolve non-publicly routed domains.

## Configure private DNS

To resolve private DNS queries:

1. [Connect your private network](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/) with Cloudflare Tunnel.

2. Under **Networking** > **Routes**, verify that the IP address of your internal DNS resolver is included in the tunnel.

   <DashButton url="/?to=/:account/magic-networks/routes" />

		:::note

		Ensure that **Split Tunnels** are configured to [include traffic to private IPs and hostnames](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/#3-route-private-network-ips-through-the-cloudflare-one-client).

		:::

3. Route specific DNS queries to your internal DNS resolver using one of the following options:

	- [Create a Local Domain Fallback entry](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) that points to the internal DNS resolver. For example, you can instruct the Cloudflare One Client to resolve all requests for `myorg.privatecorp` through an internal resolver at `10.0.0.25` rather than attempting to resolve this publicly.
	- Alternatively, [create a resolver policy](/cloudflare-one/traffic-controls/resolver-policies/#create-a-resolver-policy) that points to the internal DNS resolver. <Render file="gateway/resolver-policies-intro" product="cloudflare-one"/>

4. [Enable the Gateway proxy](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy) for TCP and UDP.

5. Finally, ensure that your tunnel uses QUIC as the default [transport protocol](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#protocol). This will enable `cloudflared` to proxy UDP-based traffic which is required in most cases to resolve DNS queries.

The Cloudflare One Client will now send DNS queries to your internal DNS resolver for resolution. To learn more, refer to [How the Cloudflare One Client handles DNS requests](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/#how-the-warp-client-handles-dns-requests).

## Test the setup

For testing, run a `dig` command for the internal DNS service:

```sh
dig AAAA www.myorg.privatecorp
```

The `dig` command will work because `myorg.privatecorp` was configured above as a fallback domain. If you skip that step, you can still force `dig` to use your private DNS resolver:

```sh
dig @10.0.0.25 AAAA www.myorg.privatecorp
```

Both `dig` commands will fail if the Cloudflare One Client is disabled on your end user's device.

## Troubleshooting

Use the following troubleshooting strategies if you are running into issues while configuring private DNS with Cloudflare Tunnel.

- Ensure that `cloudflared` is connected to Cloudflare by visiting **Networking** > **Tunnels** in the Cloudflare dashboard.

- Ensure that `cloudflared` is running with the `quic` protocol (search for `Initial protocol quic` in its logs).

- Ensure that the machine where `cloudflared` is running is allowed to egress via UDP to port 7844 to talk out to Cloudflare.

- Ensure that end-user devices are enrolled into the Cloudflare One Client by visiting [https://help.teams.cloudflare.com](https://help.teams.cloudflare.com).

- Double-check the [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence) for your [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/). Ensure that a more global Block or Allow policy will not supersede application-specific policies.

- Check your [Gateway network logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/#network-logs) to see whether your UDP DNS resolutions are being allowed or blocked.

- Ensure that your internal DNS resolver is available over a routable private IP address. You can check that by trying the `dig` command on your machine running `cloudflared`.

- Check your set up by using `dig ... +tcp` to force the DNS resolution to use TCP instead of UDP.

# Virtual networks

import { Details, Render, Tabs, TabItem, DashButton } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>  | All plans                                                     |

| System   | Availability |
| -------- | ------------ |
| Windows  | ✅           |
| macOS    | ✅           |
| Linux    | ✅           |
| iOS      | ✅           |
| Android  | ✅           |
| ChromeOS | ✅           |

</Details>

<Render file="tunnel/virtual-networks-intro" product="cloudflare-one" />

## Use cases

Here are a few scenarios where virtual networks may prove useful:

- Manage production and staging environments that use the same address space.
- Manage acquisitions or mergers between organizations that use the same address space.
- Allow IT professional services to access their customer's network for various administration and management purposes.
- Allow developers or homelab users to deterministically route traffic through their home network to enforce additional security controls.
- Guarantee additional segmentation (beyond just policy enforcement) between networks and resources for security reasons, while keeping all configuration within a single Cloudflare account.

## Prerequisites

- [Install `cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) on each private network.
- [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on user devices.

## Create a virtual network

In this example, "private network" refers to a distinct environment (such as staging or production) that has its own overlapping IP address space (`10.128.0.1/32` staging and `10.128.0.1/32` production). If your environments use non-overlapping IPs, you do not need a separate tunnel for each. Instead, you can add multiple routes to a single tunnel.

<Tabs>
  <TabItem label="Dashboard">
      To route overlapping IPs over virtual networks:

      1. Create two unique virtual networks:
         1. In the Cloudflare dashboard, go to **Networking** > **Routes** > **Virtual networks**.

            <DashButton url="/?to=/:account/magic-networks/routes/virtual-networks" />

         2. Select **Create virtual network**.
         3. Name your virtual network `staging-vnet` and select **Save**.
         4. Repeat Steps 1a-1d to create another virtual network called `production-vnet`.
      2. Create a Cloudflare Tunnel for each private network with overlapping IPs (one tunnel per isolated environment, for example staging and production):
         1. Go to **Networking** > **Tunnels**.
         2. Select **Create a tunnel**.
         3. Name your tunnel `Staging tunnel` and select **Create**.
         4. Install the connector within your staging environment.
         5. Go to **Networking** > **Routes**.

            <DashButton url="/?to=/:account/magic-networks/routes" />

         6. Select **Create route** > **Tunnel CIDR**.
         7. Select `Staging tunnel`, enter `10.128.0.1/32` as the network, and select *staging-vnet* as the virtual network. Select **Create route**.
         8. Repeat Steps 2a-2d to create another tunnel called `Production tunnel`. Be sure to install the connector within your production environment.
         9. Repeat Steps 2e-2g to create a route for `Production tunnel` with `10.128.0.1/32` assigned to *production-vnet*.

      We now have two overlapping IP addresses routed over `staging-vnet` and `production-vnet` respectively. You can use the Cloudflare One Client to [switch between virtual networks](#connect-to-a-virtual-network).

  </TabItem>

    <TabItem label="Terraform (v5)">
    To route overlapping IPs over virtual networks:
    	1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
    		- `Cloudflare Tunnel Write`

    	2. Create two unique virtual networks:
    		```tf
    		resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "staging_vnet" {
    			account_id = var.cloudflare_account_id
    			name       = "staging-vnet"
    			comment    = "Staging virtual network"
    			is_default = false
    		}

    		resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "production_vnet" {
    			account_id = var.cloudflare_account_id
    			name       = "production-vnet"
    			comment    = "Production virtual network"
    			is_default = false
    		}
    		```

    	3. Create a Cloudflare Tunnel for each private network with overlapping IPs (one tunnel per isolated environment, for example staging and production):
    		```tf
    		resource "cloudflare_zero_trust_tunnel_cloudflared" "staging_tunnel" {
    			account_id = var.cloudflare_account_id
    			name       = "Staging tunnel"
    			config_src = "cloudflare"
    		}

    		resource "cloudflare_zero_trust_tunnel_cloudflared" "production_tunnel" {
    			account_id = var.cloudflare_account_id
    			name       = "Production tunnel"
    			config_src = "cloudflare"
    		}
    		```

    	4. Route `10.128.0.1/32` through `Staging tunnel` and assign it to `staging-vnet`. Route `10.128.0.1/32` through `Production tunnel` and assign it to `production-vnet`.

    			```tf
    			resource "cloudflare_zero_trust_tunnel_cloudflared_route" "staging_tunnel_route" {
    				account_id         = var.cloudflare_account_id
    				tunnel_id          = cloudflare_zero_trust_tunnel_cloudflared.staging_tunnel.id
    				network            = "10.128.0.1/32"
    				comment            = "Staging tunnel route"
    				virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.staging_vnet.id
    			}

    			resource "cloudflare_zero_trust_tunnel_cloudflared_route" "production_tunnel_route" {
    				account_id         = var.cloudflare_account_id
    				tunnel_id          = cloudflare_zero_trust_tunnel_cloudflared.production_tunnel.id
    				network            = "10.128.0.1/32"
    				comment            = "Production tunnel route"
    				virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.production_vnet.id
    			}
    			```
    	5. [Get the token](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/#get-the-tunnel-token) for each tunnel.

    	6. Using the tunnel tokens, run `Staging tunnel` in your staging environment and run `Production tunnel` in your production environment. Refer to [Install and run the tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/#4-install-and-run-the-tunnel).

  </TabItem>

  <TabItem label="Locally-managed tunnels">
      To route overlapping IPs over virtual networks for [locally-managed tunnels](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/):

      1. Create a Cloudflare Tunnel for each private network with overlapping IPs (one tunnel per isolated environment, for example staging and production):

         1. Within your staging environment, authenticate `cloudflared`:

            ```sh
            cloudflared login
            ```

         2. Create a tunnel to connect your staging network to Cloudflare.

            ```sh
            cloudflared tunnel create staging-tunnel
            ```

         3. Within your production environment, authenticate `cloudflared`:

            ```sh
            cloudflared login
            ```

         4. Create a tunnel to connect your production network to Cloudflare.

            ```sh
            cloudflared tunnel create production-tunnel
            ```

      The following steps may be executed from any `cloudflared` instance.

      2. Create two unique virtual networks.

         ```sh
         cloudflared tunnel vnet add staging-vnet
         cloudflared tunnel vnet add production-vnet
         ```

      3. Before moving on, run the following command to verify that your newly created virtual networks are listed correctly:

         ```sh
         cloudflared tunnel vnet list
         ```

    	:::note[Default virtual network]
    	All accounts come pre-configured with a virtual network named `default`. You can choose a new default by typing `cloudflared tunnel vnet update --default <virtual-network-name>`.
    	:::

4.  Configure your tunnels with the IP/CIDR range of your private networks, and assign the tunnels to their respective virtual networks.

         ```sh
         cloudflared tunnel route ip add --vnet staging-vnet 10.128.0.3/32 staging-tunnel
         cloudflared tunnel route ip add --vnet production-vnet 10.128.0.3/32 production-tunnel
         ```

        {/* :::note
        If no `--vnet` option is specified, the tunnel will be assigned to the default virtual network; this applies to any pre-existing private networks in your account.
        ::: */}

5.  Verify that the IP routes are listed correctly:

             ```sh
             cloudflared tunnel route ip list
             ```

          We now have two overlapping IP addresses routed over `staging-vnet` and `production-vnet` respectively.

          6. Within your staging environment, create a [configuration file](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/) for `staging-tunnel`. The configuration file will be structured as follows:

             ```txt
             tunnel: <Tunnel-UUID>
             credentials-file: /root/.cloudflared/credentials-file.json
             ```

          7. Run your tunnel.

             ```sh
             cloudflared tunnel run staging-tunnel
             ```

          8. Within your production environment, repeat Steps 6 and 7 for `production-tunnel`.

          You can use now the Cloudflare One Client to [switch between virtual networks](#connect-to-a-virtual-network).

  </TabItem>
</Tabs>

## Delete a virtual network

<Tabs>
  <TabItem label="Dashboard">
      To delete a virtual network:

      1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

         <DashButton url="/?to=/:account/magic-networks/routes" />

      2. On the **Routes** tab, check that no routes are assigned to the virtual network you are trying to delete. If the virtual network is in use, delete those routes or reassign them to a different virtual network first.

      3. Go to the **Virtual networks** tab and find your virtual network.

      4. Select the three-dot menu and choose **Delete**.

      You can optionally delete the tunnel associated with your virtual network.

  </TabItem>

  <TabItem label="Locally-managed tunnels">
      To delete a virtual network for [locally-managed tunnels](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/):

      1. Delete all IP routes in the virtual network. For example,

         ```sh
         cloudflared tunnel route ip delete --vnet staging-vnet 10.128.0.3/32
         ```

      2. (Optional) Delete the tunnel associated with the virtual network.

         ```sh
         cloudflared tunnel delete staging-tunnel
         ```

      3. Delete the virtual network.

         ```sh
         cloudflared tunnel vnet delete staging-vnet
         ```

      You can verify that the virtual network was successfully deleted by typing `cloudflared tunnel vnet list`.

  </TabItem>
</Tabs>

## Connect to a virtual network

### Windows, macOS, and Linux

<Tabs>
<TabItem label="Version 2026.2+">

1. Open the Cloudflare One Client.
2. Go to **Home**.
3. In the **VNET** dropdown, choose the virtual network you want to connect to (for example, `staging-vnet`).

</TabItem>
<TabItem label="Version 2026.1 and earlier">

1. Open the Cloudflare One Client.
2. Go to **Settings** > **Traffic and DNS mode** > **Virtual Networks**.
3. Choose the virtual network you want to connect to, for example `staging-vnet`.

</TabItem>
</Tabs>

When you visit `10.128.0.3/32`, the Cloudflare One Client will route your request to the staging environment.

### iOS, Android, and ChromeOS

1. Launch the Cloudflare One Agent app.
2. Go to **Advanced** > **Connection options** > **Virtual networks**.
3. Choose the virtual network you want to connect to, for example `staging-vnet`.

When you visit `10.128.0.3/32`, the Cloudflare One Client will route your request to the staging environment.

# DNS records

import { Render } from "~/components";

<Render file="tunnel/dns-records-intro" product="cloudflare-one" />

## Create a DNS record

<Render
  file="tunnel/dns-records-create"
  product="cloudflare-one"
  params={{
    certPemURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/local-tunnel-terms/#certpem"
  }}
/>

## Cloudflare settings

<Render file="tunnel/dns-cloudflare-settings" product="cloudflare-one" />

# Published applications

Cloudflare Tunnel allows you to publish local applications to the Internet via a public hostname. For example, you can [add a published application route](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application) that points `docs.example.com` to `https://localhost:8080`. Anyone can now view your application by going to `docs.example.com` in their web browser.

Cloudflare can route traffic down your Cloudflare Tunnel using a [DNS record](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/dns/) or [Cloudflare Load Balancer](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/public-load-balancers/). You can configure either option from the Cloudflare dashboard by pointing a DNS `CNAME` record or a load balancer pool to your Cloudflare Tunnel subdomain (`<UUID>.cfargotunnel.com`, or `<UUID>.fed-cfargotunnel.com` on accounts in Cloudflare's FedRAMP High environment). You can also associate these records with your tunnel from `cloudflared` directly.

For TCP or UDP applications, Cloudflare can also route traffic through a [Spectrum application](/spectrum/get-started/#create-a-spectrum-application-using-a-virtual-network-origin) attached to a [virtual network](/cloudflare-one/networks/virtual-networks/).

:::note

You do not need a paid Cloudflare Access plan to publish an application via Cloudflare Tunnel. [Access seats](/cloudflare-one/team-and-resources/users/seat-management/) are only required if you want to [secure the application using Access policies](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/), such as requiring users to log in via an identity provider.

:::

# Protocols for published applications

import { Render } from "~/components";

When you [add a published application route](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application) to a Cloudflare Tunnel, you are instructing Cloudflare to proxy requests for your public hostname to a service running privately behind `cloudflared`.

<Render
  file="tunnel/protocols-table"
  product="cloudflare-one"
  params={{
    disableTlsVerificationURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/#notlsverify",
    locallyManagedTunnelsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/#file-structure-for-published-applications"
  }}
/>

# Public load balancers

import { Render, DashButton, Details } from "~/components";

A [public load balancer](/load-balancing/load-balancers/) allows you to distribute traffic across the servers that are running your [published applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/).

When you add a [published application route](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application) to your Cloudflare Tunnel, Cloudflare generates a subdomain of `cfargotunnel.com` with the UUID of the created tunnel. You can add the application to a load balancer pool by using `<UUID>.cfargotunnel.com` as the [endpoint address](/load-balancing/understand-basics/load-balancing-components/#endpoints) and specifying the application hostname (`app.example.com`) in the [endpoint host header](/load-balancing/additional-options/override-http-host-headers/). Load Balancer does not support directly adding `app.example.com` as an endpoint if the service is behind Cloudflare Tunnel.

## Create a public load balancer

### Prerequisites

- A Cloudflare Tunnel with a [published application route](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application)

### Create a load balancer

<Render
  file="tunnel/availability/load-balancer-create"
  product="cloudflare-one"
  params={{
    publishedAppRouteURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application",
    tunnelIdLocation: "the [Cloudflare dashboard](https://dash.cloudflare.com/) under **Networking** > **Tunnels**"
  }}
/>

Refer to the [Load Balancing documentation](/load-balancing/) for more details on load balancer settings and configurations.

### Optional Cloudflare settings

The application will default to the Cloudflare settings for the load balancer hostname, including [Rules](/rules/), [Cache Rules](/cache/how-to/cache-rules/) and [WAF rules](/waf/). You can change the settings for your hostname in the [Cloudflare dashboard](https://dash.cloudflare.com/).

## Common architectures

Review common load balancing configurations for published applications behind Cloudflare Tunnel.

### One app per load balancer

For this example, assume we have a web application that runs on servers in two different data centers. We want to connect the application to Cloudflare so that users can access the application from anywhere in the world. Additionally, we want Cloudflare to load balance between the servers such that if the primary server fails, the secondary server receives all traffic.

```mermaid
graph LR
		subgraph LB["Public load balancer <br> app.example.com "]
			subgraph P1[Pool 2]
				E1(["**Endpoint:** &lt;UUID_1&gt;.cfargotunnel.com<br> **Host header**: server2.example.com"])
			end
			subgraph P2[Pool 1]
				E2(["**Endpoint:** &lt;UUID_2&gt;.cfargotunnel.com<br> **Host header**: server1.example.com"])
			end
		end
		R@{ shape: text, label: "app.example.com" }
		R--> LB
    P1 -- Tunnel 1 --> cf1
    P2 -- Tunnel 2 --> cf2
		subgraph D2[Private network]
			subgraph r1[Region eu-west-1]
			cf1@{ shape: processes, label: "cloudflared <br> **Route:** server2.example.com" }
			S1(["Server 2<br> 10.0.0.1:80"])
			cf1-->S1
			end
			subgraph r2[Region us-east-1]
			cf2@{ shape: processes, label: "cloudflared <br> **Route:** server1.example.com" }
			S3(["Server 1 <br> 10.0.0.2:80"])
			cf2-->S3
			end
		end

		style r1 stroke-dasharray: 5 5
		style r2 stroke-dasharray: 5 5
```

As shown in the diagram, a typical setup includes:
- A dedicated Cloudflare Tunnel per data center.
- One load balancer pool per tunnel. The load balancer hostname is set to the user-facing application hostname (`app.example.com`).
- One load balancer endpoint per pool. The endpoint host header is set to the `cloudflared` published application hostname (`server1.example.com`)
- At least two `cloudflared` [replicas](#session-affinity-and-replicas) per tunnel in their respective data centers, in case a `cloudflared` host machine goes down.

Users can now connect to the application using the load balancer hostname (`app.example.com`). Note that this configuration is only valid for [Active-Passive failover](/load-balancing/load-balancers/common-configurations/#active---passive-failover), since each pool only supports one endpoint per tunnel.

### Multiple apps per load balancer

The following diagram illustrates how to steer traffic to two different applications on a private network using a single load balancer.

```mermaid
graph LR
		subgraph LB["Public load balancer <br> lb.example.com"]
			subgraph P1[Pool for App 1]
				E1(["**Endpoint:** &lt;UUID_1&gt;.cfargotunnel.com<br> **Host header**: app1.example.com"])
				E2(["**Endpoint:** &lt;UUID_2&gt;.cfargotunnel.com<br> **Host header**: app1.example.com"])
			end
			subgraph P2[Pool for App 2]
				E3(["**Endpoint:** &lt;UUID_1&gt;.cfargotunnel.com<br> **Host header**: app2.example.com"])
				E4(["**Endpoint:** &lt;UUID_2&gt;.cfargotunnel.com<br> **Host header**: app2.example.com"])
			end
		end
		R@{ shape: text, label: "app1.example.com <br> app2.example.com" }
		R--> LB
    E1 -- Tunnel 1 -->cf1
		E3 -- Tunnel 1 --> cf1
		E2 -- Tunnel 2 --> cf2
		E4 -- Tunnel 2 --> cf2

		subgraph N[Private network]
			cf2[cloudflared <br> **Route:** app1.example.com <br> **Route:** app2.example.com]
			S3(["App 1 <br> 10.0.0.1:80"])
			cf2-->S3
			cf2-->S1
			cf1[cloudflared <br> **Route:** app1.example.com <br> **Route:** app2.example.com]
			S1(["App 2 <br> 10.0.0.2:80"])
			cf1-->S1
			cf1-->S3
		end
```

This load balancing setup includes:

- Two Cloudflare Tunnels with identical routes to both applications.
- One load balancer pool per application.
- Each load balancer pool has an endpoint per tunnel.
- A [DNS record](#dns-records) for each application that points to the load balancer hostname.

Users can now access all applications through the load balancer. Since there are multiple tunnel endpoints per pool, this configuration supports [Active-Active Failover](/load-balancing/load-balancers/common-configurations/#active---active-failover). Active-Active uses all available endpoints in the pool to process requests simultaneously, providing better performance and scalability by load balancing traffic across them.

#### DNS records

When you configure a published application route via the dashboard, Cloudflare will automatically generate a `CNAME` DNS record that points the application hostname (`app1.example.com`) to the tunnel subdomain (`<UUID>.cfargotunnel.com`). You can [edit these DNS records](/dns/manage-dns-records/how-to/create-dns-records/#edit-dns-records) so that they point to the load balancer hostname instead.

:::note
Tunnel routes configured via the API or CLI require [manually creating DNS records](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/dns/).
:::

Here is an example of what your DNS records will look like before and after setting up [Multiple apps per load balancer](#multiple-apps-per-load-balancer):

**Before**:
| Type | Name | Content |
| ---- | ---- | ------- |
| CNAME | app1 | `<UUID_1>.cfargotunnel.com` |
| CNAME | app2 | `<UUID_1>.cfargotunnel.com` |
| CNAME | app1 | `<UUID_2>.cfargotunnel.com` |
| CNAME | app2 | `<UUID_2>.cfargotunnel.com`  |

**After**:
| Type | Name | Content |
| ---- | ---- | ------- |
| LB   | `lb.example.com` | n/a |
| CNAME | app1 | `lb.example.com` |
| CNAME | app2 | `lb.example.com` |

## Known limitations

### Monitors and TCP tunnel origins

<Render
  file="tunnel/availability/load-balancer-tcp-monitors"
  product="cloudflare-one"
  params={{
    publishedAppRouteURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-publish-an-application"
  }}
/>

### Session affinity and replicas

The load balancer does not distinguish between [replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/) of the same tunnel. If you run the same tunnel UUID on two separate hosts, the load balancer treats both hosts as a single endpoint. To maintain [session affinity](/load-balancing/understand-basics/session-affinity/) between a client and a particular host, you will need to connect each host to Cloudflare using a different tunnel UUID.

### Local connection preference

<Render
  file="tunnel/availability/load-balancer-local-connection"
  product="cloudflare-one"
  params={{
    replicasURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/"
  }}
/>

# Common errors

import { GlossaryTooltip, Render } from "~/components";

This section covers the most common errors you might encounter when connecting resources with Cloudflare Tunnel. If you do not see your issue listed below, refer to [Troubleshooting Cloudflare One](/cloudflare-one/troubleshooting/), view your [Tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/), or [contact Cloudflare Support](/support/contacting-cloudflare-support/).

## Tunnel status

You can check your tunnel's connection status either from the Cloudflare dashboard (by going to **Networking** > **Tunnels**) or by running the `cloudflared tunnel list` command. Each tunnel displays a status that reflects its current connection state:

<Render file="tunnel/tunnel-status" product="cloudflare-one" />

:::note[Tunnel status scope]

The tunnel status only reflects the connection between `cloudflared` and the Cloudflare network. Tunnel status does not indicate whether `cloudflared` can successfully reach your internal services. As a result, a tunnel can appear `Healthy` while users are still unable to connect to an application.
:::

<Render
	file="tunnel/common-errors"
	product="cloudflare-one"
	params={{
		logsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/",
		protocolURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/protocols/",
		configFileURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
		originServerNameURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/#originservername",
		caPoolURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/#capool",
		noTLSVerifyURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/#notlsverify",
		httpsOriginURL: "/tunnel/troubleshooting/https-origins/",
		ulimitsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/system-requirements/#ulimits",
	}}
/>

## My tunnel randomly disconnects.

Long-lived connections initiated through Cloudflare One, such as SSH sessions, can last up to eight hours. However, disruptions along the service path may result in more frequent disconnects. Often, these disconnects are caused by regularly scheduled maintenance events such as data center, server, or service updates and restarts. If you believe these events are not the cause of disconnects in your environment, collect the relevant [client logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) and [Tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/) and contact Support.

If the disconnects mainly affect idle SSH sessions, WebSocket connections, or other long-lived connections, the transport protocol may be relevant.

When `cloudflared` uses QUIC, idle sessions can be more sensitive to network devices that aggressively time out UDP traffic. If idle connections drop repeatedly, try one or more of the following:

- Configure application-layer keepalives, such as `ServerAliveInterval` for SSH.
- Test with `cloudflared` set to `protocol: http2`.
- Review local firewalls, NAT devices, and upstream network equipment for short UDP idle timers.

For connection setup failures caused by blocked QUIC traffic, refer to the QUIC troubleshooting sections above.

## `ping` and `traceroute` commands do not work.

To ping an IP address behind Cloudflare Tunnel, your system must allow ICMP traffic through `cloudflared`. For configuration instructions, refer to the [ICMP proxy documentation](/cloudflare-one/traffic-controls/proxy/#icmp).

## I see `Error: This route's network is inside an existing subnet's network at "100.96.0.0/12"`.

This error occurs when you try to add a CIDR route that falls within the Cloudflare One Client's <GlossaryTooltip term="WARP CGNAT IP">CGNAT IP range</GlossaryTooltip>. The `100.96.0.0/12` range, which covers addresses from `100.96.0.1` to `100.111.255.254`, is reserved for internal WARP routing and cannot be added as a Cloudflare Tunnel route. To connect your private network, you will need to change its IP/CIDR so that it does not overlap with `100.96.0.0/12`.

## I see `This site can't provide a secure connection.`

If you see an error with the title `This site can't provide a secure connection` and a subtitle of `<hostname> uses an unsupported protocol`, you must [order an Advanced Certificate](/ssl/edge-certificates/advanced-certificate-manager/manage-certificates/#create-a-certificate).

If you added a [multi-level subdomain](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-connect-an-application) (more than one level of subdomain), you must [order an Advanced Certificate for the hostname](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2a-connect-an-application) as Cloudflare's Universal certificate will not cover the public hostname by default.


For more information on Tunnel errors, view your [Tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/) or [contact Cloudflare Support](/support/contacting-cloudflare-support/).

# Connectivity pre-checks

import { Aside, Render, TabItem, Tabs } from "~/components";

This guide helps you validate connectivity between your environment and [Cloudflare Tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) before deploying [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/). You will run DNS and network checks from the same host machine that will run `cloudflared` to help you identify issues that may prevent `cloudflared` from connecting to Cloudflare Tunnel endpoints.

<Aside
	type="tip"
	title="Automated pre-checks in cloudflared 2026.5.2 and later"
>
	Starting with `cloudflared` version `2026.5.2`, these checks run
	**automatically** every time you start a tunnel — including DNS resolution,
	UDP/TCP connectivity on port `7844`, and access to the management API. If you
	have already installed `cloudflared`, run `cloudflared tunnel diag` to execute
	the checks without starting a tunnel, and skip the manual steps in this guide.
	The manual workflow below remains useful when you need to validate your
	network **before** installing `cloudflared` — for example, when a SecOps team
	wants to confirm firewall rules ahead of a deployment.
</Aside>

Running these checks before you install `cloudflared` sets your deployment up for success and narrows down the cause of any later connectivity issues.

This guide is structured as follows:

1. [Before you start](#before-you-start): Read prerequisites and terminology.

2. [DNS test with dig](#2-dns-test-with-dig): Confirm that DNS resolves Cloudflare Tunnel endpoints to the expected IPs.

3. [Test network connectivity](#3-test-network-connectivity): Verify that your firewall allows outbound traffic on port `7844` (TCP and UDP).

4. [Get help](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/connectivity-prechecks/#4-get-help): What to collect and who to contact if tests fail.

## 1. Before you start

### Prerequisites

You must have:

- A host machine connected to the Internet where you plan to run `cloudflared`. The tests must run from the same environment where `cloudflared` will run (same network, same firewall path).

- A terminal session with permission to run `dig` and `nc` (netcat), or similar software.

`cloudflared` is platform-agnostic and supports a wide range of operating systems. For details, refer to [Tunnel system requirements](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/system-requirements/).

### Terminology

When troubleshooting connectivity to Cloudflare, it is important to distinguish between:

- Host machine: The server or virtual machine (VM) where you will run `cloudflared`.

- Environment: The broader setup containing the host machine (network and firewall configuration).

Cloudflare Tunnel errors can originate from the environment (for example, DNS or firewall policies), even though they surface as `cloudflared` errors on the host machine. This guide focuses on the environment, not on `cloudflared` itself.

`cloudflared` establishes [outbound-only connections](/cloudflare-one/networks/connectors/cloudflare-tunnel/#outbound-only-connection) to Cloudflare's global network over port `7844`. The specific destinations and ports are documented in [Tunnel with firewall](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/).

## 2. DNS test with dig

Cloudflare Tunnel requires outbound connectivity to `region1.v2.argotunnel.com` and `region2.v2.argotunnel.com` (or to the equivalent `us-region1` and `us-region2` endpoints when using the [US region](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#region-us), or `fed-region1` and `fed-region2` when using the [FedRAMP High region](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#region-fedramp-high)).

For a successful and healthy deployment, `cloudflared` should have [four active replicas](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-availability/) with connectivity to both regions (that is, both `region1.v2.argotunnel.com` and `region2.v2.argotunnel.com`, or both `us-region1` and `us-region2`).

First, you need to verify that your DNS resolver returns the expected IP addresses for Cloudflare Tunnel endpoints.

### 2.1. Test DNS with your current resolver

Depending on whether you are testing a global region or the US region, run one of the following commands:

<Tabs>
<TabItem label="Global region">

```sh
dig A region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.167
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.67
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.57
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.107
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.27
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.7
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.227
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.47
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.37
region1.v2.argotunnel.com. 86400 IN	A	198.41.192.77
...
```

```sh
dig AAAA region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::1
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::2
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::3
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::4
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::5
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::6
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::7
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::8
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::9
region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a0::10
...
```

```sh
dig A region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.13
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.193
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.33
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.233
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.53
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.63
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.113
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.73
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.43
region2.v2.argotunnel.com. 86400 IN	A	198.41.200.23
...
```

```sh
dig AAAA region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::1
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::2
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::3
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::4
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::5
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::6
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::7
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::8
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::9
region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a8::10
...
```

</TabItem>
<TabItem label="US region">

```sh
dig A us-region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.1
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.2
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.3
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.4
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.5
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.6
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.7
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.8
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.9
us-region1.v2.argotunnel.com. 86400 IN	A	198.41.218.10
...
```

```sh
dig AAAA us-region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::1
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::2
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::3
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::4
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::5
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::6
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::7
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::8
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::9
us-region1.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a1::10
...
```

```sh
dig A us-region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.1
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.2
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.3
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.4
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.5
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.6
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.7
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.8
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.9
us-region2.v2.argotunnel.com. 86400 IN	A	198.41.219.10
...
```

```sh
dig AAAA us-region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::1
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::2
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::3
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::4
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::5
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::6
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::7
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::8
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::9
us-region2.v2.argotunnel.com. 86400 IN	AAAA	2606:4700:a9::10
...
```

</TabItem>
<TabItem label="FedRAMP High region">

```sh
dig A fed-region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.1
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.2
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.3
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.4
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.5
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.6
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.7
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.8
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.9
fed-region1.v2.argotunnel.com. 300 IN	A	162.159.234.10
...
```

```sh
dig AAAA fed-region1.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::1
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::2
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::3
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::4
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::5
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::6
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::7
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::8
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::9
fed-region1.v2.argotunnel.com. 300 IN	AAAA	2a06:98c1:4d::10
...
```

```sh
dig A fed-region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.1
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.2
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.3
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.4
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.5
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.6
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.7
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.8
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.9
fed-region2.v2.argotunnel.com. 300 IN	A	172.64.234.10
...
```

```sh
dig AAAA fed-region2.v2.argotunnel.com
```

```sh output
;; ANSWER SECTION:
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::1
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::2
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::3
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::4
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::5
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::6
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::7
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::8
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::9
fed-region2.v2.argotunnel.com. 300 IN	AAAA	2606:4700:f6::10
...
```

</TabItem>
</Tabs>

The `ANSWER SECTION` should include the expected IP addresses for Cloudflare Tunnel endpoints.

If you receive:

- Status `NOERROR` with valid IP addresses - Your DNS resolver is successfully returning addresses for the Tunnel hostname. Continue to [Test network connectivity](#3-test-network-connectivity).

- Status `SERVFAIL`, `NXDOMAIN`, or an empty answer - Your DNS resolver cannot resolve the Tunnel endpoint. Continue to [Compare against `1.1.1.1`](#compare-against-1111).

### 2.2. Compare against `1.1.1.1`

If your original `dig` response is empty or does not match the documented IPs, test again using Cloudflare's public resolver `1.1.1.1`:

```sh
dig A region1.v2.argotunnel.com @1.1.1.1
```

#### If only `1.1.1.1` works

If `1.1.1.1` returns the correct IPs, but your original resolver does not, your local DNS resolver is misconfigured or blocked.

To resolve:

- Configure the host machine to use `1.1.1.1` as its resolver.
- If you must keep using your existing resolver, then investigate with your system administrator or ISP why it is returning different IPs. A recursive resolver should return the same response as the authoritative DNS server. If this cannot be fixed, the issue lies within your local environment and must be resolved before deploying Cloudflare Tunnel.

#### If neither resolver works

If neither your original resolver nor `1.1.1.1` returns an answer, your firewall may be blocking DNS queries to Cloudflare Tunnel endpoints.

To resolve:

- Check for firewall rules blocking DNS traffic altogether (UDP on port `53`) or specific DNS queries related to Cloudflare.
- If you are behind a managed DNS or security appliance, contact that provider to understand why queries to `region1.v2.argotunnel.com` and other Cloudflare Tunnel endpoints are blocked.

Once DNS resolution returns the expected IPs from your DNS resolver, proceed to connectivity testing in step 3.

## 3. Test network connectivity

After confirming that your DNS resolver returns the correct IPs, test whether your host machine can send packets to Cloudflare on port `7844` using both UDP and TCP.

Choose one of the IPs from your `dig` output (for example, `198.41.192.167`) and run the following tests.

### 3.1. Test UDP connectivity

```sh
nc -uvz -w 3 198.41.192.167 7844
```

Example output:

```sh
Connection to 198.41.192.167 port 7844 [udp/*] succeeded!
```

### 3.2. Test TCP connectivity

```sh
nc -vz -w 3 198.41.192.167 7844
```

Example output:

```sh
Connection to 198.41.192.167 port 7844 [tcp/*] succeeded!
```

### 3.3 Interpret results

These tests answer two key questions:

- Can the host machine send a UDP packet to Cloudflare Tunnel endpoints?
- Can the host machine send a TCP packet to Cloudflare Tunnel endpoints?

If either protocol succeeds, `cloudflared` can use that protocol to establish the tunnel.

You have already confirmed DNS is working in the previous steps. These connectivity tests now verify whether your environment allows traffic to Cloudflare on port `7844`. By default, `cloudflared` automatically falls back to whichever protocol is available.

If a [protocol](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#protocol) is blocked but you force `cloudflared` to use it (for example, forcing QUIC when UDP is blocked), the tunnel will fail to connect.

#### Both UDP and TCP succeed

Your firewall allows outbound traffic and return traffic to Cloudflare's tunnel endpoint on port `7844`. `cloudflared` can connect using either `quic` (UDP) or `http2` (TCP). If both UDP and TCP succeed and your DNS test in the previous section was successful, you can successfully deploy Cloudflare Tunnel in this environment.

#### UDP succeeds, TCP fails

Outbound UDP is allowed, but TCP on port `7844` is blocked or inspected.

`cloudflared` will only be able to connect using `quic`. If you force `http2` in your configuration while TCP is blocked, the tunnel will fail.

To resolve: Either allow TCP on your local network firewall on port `7844` or stop forcing `http2` to allow `cloudflared` to connect over `QUIC` instead. Refer to the [Protocol](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#protocol) parameter documentation for more information.

#### TCP succeeds, UDP fails

Outbound TCP is allowed, but UDP on port `7844` is blocked.

`cloudflared` will only be able to connect using `http2`. If you force `quic` while UDP is blocked, the tunnel will fail.

To resolve: Either allow UDP on the local network firewall on port `7844` or stop forcing QUIC to allow `cloudflared` to connect over HTTP/2 instead. Refer to the [Protocol](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#protocol) parameter documentation for more information.

#### Both UDP and TCP fail

Packets are being dropped somewhere between the host and the [Cloudflare Tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/).

This usually indicates a firewall policy or upstream security control that does not allow outbound traffic (or return traffic) on port `7844`.

To resolve: Allow all traffic over port `7844` on the local network firewall. If this does not resolve the issue, troubleshoot with your ISP or service provider.

## 4. Get help

If either DNS or network test failed, it will likely be a problem in your local environment. You will need to debug with your administrator, ISP or cloud provider. If you believe the issue is with Cloudflare, please provide detailed information when contacting support.

<Render
	file="warp/support-ticket-best-practices-tunnel"
	product="cloudflare-one"
/>

# Tunnel diagnostic logs

import { Details, Render } from "~/components";

Cloudflare Tunnel generates a set of diagnostic logs that can be used to troubleshoot issues with `cloudflared`. A diagnostic report collects data from a single instance of `cloudflared` running on the local machine.

## Get diagnostic logs

The steps for getting diagnostic logs depend on your `cloudflared` deployment environment.

### Prerequisites

- `cloudflared` version 2024.12.2 or later installed on the host

### Host environment

These instructions apply to remotely-managed and locally-managed tunnels running directly on the host machine.

<Render
  file="tunnel/diagnostics/host-environment"
  product="cloudflare-one"
  params={{
    metricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/#configure-the-metrics-server-address"
  }}
/>

### Docker

<Render
  file="tunnel/diagnostics/docker"
  product="cloudflare-one"
  params={{
    metricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/",
    defaultMetricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/#default-metrics-server-address"
  }}
/>

### Kubernetes

<Render
  file="tunnel/diagnostics/kubernetes"
  product="cloudflare-one"
  params={{
    metricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/",
    defaultMetricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/#default-metrics-server-address"
  }}
/>

## cloudflared-diag files

<Render
  file="tunnel/diagnostics/tunnel-diag-file"
  product="cloudflare-one"
  params={{
    runParametersURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/",
    logsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/",
    metricsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/#available-metrics",
    loglevelURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/#view-logs-on-the-server"
  }}
/>

# Troubleshoot tunnels

import { DirectoryListing } from "~/components"

Explore resources to help you resolve issues with Cloudflare Tunnel connectivity and configuration.

<DirectoryListing />

# Private network connectivity

import { Render } from "~/components";

<Render file="tunnel/troubleshoot-private-networks" product="cloudflare-one" />

# gRPC

import { DashButton, GlossaryTooltip, Render } from "~/components";

gRPC is a Remote Procedure Call (RPC) framework that allows client applications to call methods on a remote server as if they were running on the same local machine. You can connect gRPC servers and clients to Cloudflare's global network, making it easier to build applications that use services across different data centers and environments.

<Render file="tunnel/grpc-support" product="cloudflare-one" /> <br /> <br />
In this example, we will connect a gRPC server to Cloudflare using the
`cloudflared` <GlossaryTooltip term="daemon">daemon</GlossaryTooltip>, secure
the server with Gateway policies, and open a gRPC channel to the server using
the Cloudflare One Client.

## 1. Set up a gRPC server

1. To set up a gRPC Python application, follow this [quick start guide](https://grpc.io/docs/languages/python/quickstart/).

2. Start the server:

```sh
~/grpc/examples/python/helloworld $ python3 greeter_server.py
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1721770418.373806    3677 config.cc:230] gRPC experiments enabled: call_status_override_on_cancellation, event_engine_dns, event_engine_listener, http2_stats_fix, monitoring_experiment, pick_first_new, trace_record_callops, work_serializer_clears_time_cache
Server started, listening on 50051
```

## 2. Connect the server to Cloudflare

To establish a secure, outbound-only connection to Cloudflare:

<Render file="tunnel/connect-private-network" product="cloudflare-one" />

3. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

4. Select **Create route** > **Tunnel CIDR**. Select the tunnel you just created, enter the private IP or CIDR address of your server, and select **Create route**.

## 3. Route private network IPs through the Cloudflare One Client

<Render
	file="tunnel/warp-to-tunnel-route-ips"
	product="cloudflare-one"
	params={{ one: "private network" }}
/>

## 4. (Recommended) Create a Gateway policy

You can configure [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) to either block or allow access to the gRPC server. The following example consists of two policies: the first allows gRPC connections from devices that pass [device posture checks](/cloudflare-one/reusable-components/posture-checks/), and the second blocks all other traffic. Make sure that the Allow policy has higher [priority](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence).

### 1. Allow secured devices

| Selector                     | Operator | Value                                     | Logic | Action |
| ---------------------------- | -------- | ----------------------------------------- | ----- | ------ |
| Destination Port             | is       | `50051`                                   | And   | Allow  |
| Destination IP               | is       | `172.31.0.133`                            | And   |        |
| Passed Device Posture Checks | is       | `macOS firewall (Firewall)`               | And   |        |
| Passed Device Posture Checks | is       | `macOS disk encryption (Disk encryption)` |       |        |

### 2. Block everything else

| Selector       | Operator | Value           | Action |
| -------------- | -------- | --------------- | ------ |
| Destination IP | in       | `172.31.0.0/16` | Block  |

For more details on setting up the Gateway proxy, refer to [Filter network traffic with Gateway](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/#4-recommended-filter-network-traffic-with-gateway).

## 5. Set up the client

gRPC clients can connect to the server by installing the Cloudflare One Client on the device and enrolling in your Zero Trust organization. When the client makes a request to a private IP exposed through Cloudflare Tunnel, WARP routes the connection through Cloudflare's network to the corresponding tunnel.

To set up the gRPC client:

1. [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on your device in Traffic and DNS mode.
2. [Create device enrollment rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) to determine which devices can enroll to your Zero Trust organization.
3. Install gRPC on the device by following this [quick start guide](https://grpc.io/docs/languages/python/quickstart/).
4. Modify `greeter.py` to point to the private IP of your gRPC server. This is the same private IP configured in your [Cloudflare Tunnel routes](#2-connect-the-server-to-cloudflare). For example,

```python null {6}
def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    print("Will try to greet world ...")
    with grpc.insecure_channel("172.31.0.133:50051") as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name="you"))
    print("Greeter client received: " + response.message)
```

## 6. Test the connection

1. On the client device, ensure that the Cloudflare One Client is `Connected`.
2. Run the gRPC client application:

```sh
~/grpc/examples/python/helloworld $ python3 greeter_client.py
Will try to greet world ...
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1721771484.489711 4414247 config.cc:230] gRPC experiments enabled: call_status_override_on_cancellation, event_engine_dns, event_engine_listener, http2_stats_fix, monitoring_experiment, pick_first_new, trace_record_callops, work_serializer_clears_time_cache
Greeter client received: Hello, you!
```

You can view [Tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/#view-logs-on-your-local-machine) to validate that requests are coming into the tunnel and reaching the gRPC server as intended.

# Use cases

Cloudflare Tunnel creates a secure, outbound-only connection between your services and Cloudflare by deploying a lightweight connector in your environment. Here is how to use tunnels with some specific services:

* [SSH](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/)
* [RDP](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/)
* [SMB](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/smb/)
* [gRPC](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/grpc/)

To expose a private origin to public TCP or UDP traffic, refer to [Create a Spectrum application using a virtual network](/spectrum/get-started/#create-a-spectrum-application-using-a-virtual-network-origin).

# SMB

import { DashButton, Render } from "~/components";

The Server Message Block (SMB) protocol allows users to read, write, and access shared resources on a network. Due to security risks, firewalls and ISPs usually block public connections to an SMB file share. With Cloudflare Tunnel, you can provide secure and simple SMB access to users outside of your network.

:::note
SMB is sensitive to network latency. When a share is accessed over a remote connection rather than a local network, the protocol's block-based, round-trip-heavy design can slow transfers. For guidance on improving SMB performance over a WAN, refer to Microsoft's [Performance tuning for SMB file servers](https://learn.microsoft.com/en-us/windows-server/administration/performance-tuning/role/file-server/smb-file-server).
:::

Cloudflare Zero Trust offers two solutions for connecting to SMB servers:

- [Private subnet routing with the Cloudflare One Client to Tunnel](#connect-to-smb-server-with-the-cloudflare-one-client-to-tunnel)
- [Public hostname routing with `cloudflared access`](#connect-to-smb-server-with-cloudflared-access)

## Set up an SMB server on Linux

While SMB was developed for Microsoft Windows, Samba provides SMB connectivity from UNIX-like and BSD systems. A Samba server can be set up using this [guide](https://ubuntu.com/tutorials/install-and-configure-samba#1-overview) on an Ubuntu machine.

## Connect to SMB server with the Cloudflare One Client to Tunnel

You can use Cloudflare Tunnel to create a secure, outbound-only connection from your server to Cloudflare's global network. This requires running the `cloudflared` daemon on the server. Users reach the service by installing the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on their device and enrolling in your Zero Trust organization. Remote devices will be able to connect as if they were on your private network. By default, all devices enrolled in your organization can access the service unless you build policies to allow or block specific users.

### 1. Connect the server to Cloudflare

<Render file="tunnel/connect-private-network" product="cloudflare-one" />

3. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

4. Select **Create route** > **Tunnel CIDR**. Select the tunnel you just created, enter the private IP or CIDR address of your server, and select **Create route**.
5. (Optional) [Set up Zero Trust policies](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/#4-recommended-filter-network-traffic-with-gateway) to fine-tune access to your server.

### 2. Set up the client

<Render file="tunnel/warp-to-tunnel-client" product="cloudflare-one" />

### 3. Route private network IPs through the Cloudflare One Client

<Render
	file="tunnel/warp-to-tunnel-route-ips"
	params={{ one: "private network" }}
	product="cloudflare-one"
/>

### 4. Connect as a user

#### macOS

1. In the Finder menu, select **Go** > **Connect to Server**.

2. Enter `smb://<smb-server-ip-address>/sambashare`.

   ![Connect to SMB server in macOS](~/assets/images/cloudflare-one/connections/connect-apps/use-cases/smb-connect.png)

3. Sign in with the username and password created while setting up the server.

#### Windows

1. Open File Explorer and right-click **Network** > **Map Network Drive**.
2. For **Folder**, enter `\\<server-private-ip>\sambashare`.
3. Select **Connect using different credentials**.
4. Select **Finish**.
5. Sign in with the username and password created while setting up the server.

## Connect to SMB server with `cloudflared access`

Cloudflare Tunnel can also route applications through a public hostname, which allows users to connect to the application without the Cloudflare One Client. This method requires having `cloudflared` installed on both the server machine and on the client machine, as well as an active zone on Cloudflare. The traffic is proxied over this connection, and the user logs in to the server with their Cloudflare Access credentials.

The public hostname method can be implemented in conjunction with routing over the Cloudflare One Client so that there are multiple ways to connect to the server. You can reuse the same tunnel for both the private network and public hostname routes.

### 1. Connect the server to Cloudflare

1. Create a Cloudflare Tunnel by following our [dashboard setup guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).

2. In the Cloudflare dashboard, go to **Networking** > **Tunnels** and select your tunnel.

   <DashButton url="/?to=/:account/tunnels" />

3. On the **Routes** tab, select **Add route**, then select **Published application**.

4. Choose a domain from the drop-down menu and specify any subdomain (for example, `smb.example.com`).

5. For **Service**, select _SMB_ and enter the SMB listening port (for example, `localhost:445`). SMB drives listen on port `139` or `445` by default.

6. Select **Add route**.

### 2. (Recommended) Create an Access application

By default, anyone on the Internet can connect to the server using the hostname of the published application. To allow or block specific users, create a [self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) in Cloudflare Access.

### 3. Connect as a user

1. [Install `cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) on the client machine.

2. Run the following command to open an SMB listening port. You can specify any available port on the client machine.

   ```sh
   cloudflared access tcp --hostname smb.example.com --url localhost:8445
   ```

   This command can be wrapped as a desktop shortcut so that end users do not need to use the command line.

3. [Open your SMB client](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/smb/#4-connect-as-a-user) and configure the client to point to `smb://localhost:8445/sambashare`. Do not input the hostname.

4. Sign in with the username and password created while setting up the server.

#### Windows-specific requirements

If you are using a Windows machine and cannot specify the port for SMB, you might need to disable the local server. The local server on a client machine uses the same default port `445` for CIFS/SMB. By listening on that port, the local server can block the `cloudflare access` connection.

:::caution

The Windows Server service supports share actions over a network like file, print, and named-pipe. Disabling this service can cause those actions to fail to start.
:::

To disable the local server on a Windows machine:

1. Select **Win**+**R** to open the Run window.
2. Type `services.msc` and select **Enter**.
3. Locate the local server process, likely called `Server`.
4. Stop the service and set **Startup type** to _Disabled_.
5. Repeat steps 3 and 4 for `TCP/IP NetBIOS Helper`.

# Render a VNC client in the browser

import { Render, DashButton } from "~/components";

A Virtual Network Computer (VNC) server provides users with remote access to a computer's desktop environment. Cloudflare can render a VNC terminal in the browser without any client-side software or configuration.

Browser-rendered VNC requires connecting the VNC server to Cloudflare and routing traffic through a public hostname. To access the VNC server, users go to the public hostname URL and log in through Cloudflare Access using your configured identity provider. Cloudflare will apply your [Access policies](/cloudflare-one/access-controls/policies/) and, when a user is allowed, render a VNC client in their browser.

:::note
There are a number of different VNC server versions, deployments, and instances. This guide uses TightVNC running an XFCE desktop, but browser-rendered VNC will work with most configurations.
:::

## Prerequisites

- An [active domain on Cloudflare](/fundamentals/manage-domains/add-site/).
- The domain uses either a [full setup](/dns/zone-setups/full-setup/) or a [partial (`CNAME`) setup](/dns/zone-setups/partial-setup/).

## 1. Set up a VNC server

For demonstration purposes, we will create a TightVNC server on an Ubuntu virtual machine (VM) hosted in Google Cloud Project (GCP). We will configure the VNC server to run XFCE, a lightweight desktop environment suitable for remote access. If you already have a VNC server installed, you can skip this step and [go to Step 2](#2-connect-the-server-to-cloudflare).

1. Open a terminal window for your Ubuntu VM.

2. Install XFCE and TightVNC by running the following command:

		```sh
		sudo apt update
		sudo apt install xfce4 xfce4-goodies dbus-x11 tightvncserver -y

		```

		This command installs the desktop, some helpful utilities, and the VNC server software.

3. To initialize the VNC server:

	1. Create a VNC server instance:

		```sh
		vncserver
		```

	2. You will be prompted to set a password. This password will be used to connect to your VNC server. It is limited to 8 characters.

		TightVNC will now create configuration files and start a VNC session on display `:1` (which uses port `5901`).

	3. You will be asked if you want to create a view-only password. You can press `n` for no.

	4. Kill this initial session so that you can edit its configuration:

		```sh
		vncserver -kill :1
		```
4. Configure VNC to launch the XFCE desktop:

	1. Create a VNC configuration directory if it is missing:

	```sh
	mkdir -p ~/.vnc
	```

	2. Open the `xstartup` file using a text editor. For example,

	```sh
	vim ~/.vnc/xstartup
	```

	3. Update the file to the following configuration:

	```bash
	#!/bin/sh
	unset SESSION_MANAGER
	unset DBUS_SESSION_BUS_ADDRESS
	startxfce4
	```

	4. Make the file executable:

	```sh
	chmod +x ~/.vnc/xstartup
	```

5. Start the VNC server again:
	```sh
	vncserver -localhost :1
	```

	The `-localhost` flag ensures the VNC server only listens for connections from the VM itself, not from the public Internet. Your VNC server is now running on port `5901`, but it is only accessible from `localhost` (`127.0.0.1`) inside the VM.

6. (Recommended) Test the VNC server with an existing VNC client to verify any missing packages or configuration changes. For example, to test a VNC server hosted on GCP:

	1. Open a terminal on the client machine.

	2. Connect to the VNC server over SSH, forwarding your local port `5901` to the VNC server's listening port:

		```sh
		gcloud compute ssh [YOUR_VM_NAME] --zone=[YOUR_ZONE] -- -L 5901:localhost:5901
		```

	3. Open your preferred VNC viewer application.

	4. In the VNC viewer, connect to the address `localhost:5901` and enter your VNC server password.

	You should see the Ubuntu VM desktop.

7. (Optional) Configure the VNC server to start on boot:

	1. Find the full path to the `vncserver` command:

		```sh
		which vncserver
		```
		```sh output
		/usr/bin/vncserver
		```
	2. Create a new service configuration file:

	```sh
	sudo vim /etc/systemd/system/vncserver@.service
	```

	3. Copy and paste the following content. Replace `[YOUR_USERNAME]` with the VNC server user. If needed, update `/usr/bin/vncserver` to your `vncserver` path.

		```toml
		[Unit]
		Description=Start TightVNC server at startup
		After=syslog.target network.target

		[Service]
		Type=forking
		User=[YOUR_USERNAME]
		WorkingDirectory=/home/[YOUR_USERNAME]

		PIDFile=/home/[YOUR_USERNAME]/.vnc/%H:%i.pid

		ExecStartPre=-/usr/bin/vncserver -kill :%i > /dev/null 2>&1
		ExecStart=/usr/bin/vncserver -localhost :%i
		ExecStop=/usr/bin/vncserver -kill :%i

		[Install]
		WantedBy=multi-user.target
		```

		4. Reload `systemd` to read in the new service file:

		```sh
		sudo systemctl daemon-reload
		```

		5. Enable the service to start at boot:

		```sh
		sudo systemctl enable vncserver@1.service
		```

		The `1` variable configures the VNC service to use display `:1` (which runs on port `5901`).

		6. By default, `systemd` user services only run when that user is logged in. To allow your VNC service to start on boot (before you log in), enable user linger for your user:

		```sh
		sudo loginctl enable-linger [YOUR_USERNAME]
		```

		7. Start the service:

		```sh
		sudo systemctl start vncserver@1.service
		```

		8. Check its status:

		```sh
		sudo systemctl status vncserver@1.service
		```

		The VNC server will now start automatically every time the VM boots.

## 2. Connect the server to Cloudflare

1. Create a Cloudflare Tunnel by following the [dashboard setup guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).

2. Go to **Networking** > **Tunnels**, then select your tunnel.

   <DashButton url="/?to=/:account/tunnels" />

3. On the **Routes** tab, select **Add route**, then select **Published application**.

4. Choose a domain from the drop-down menu and specify any subdomain (for example, `vnc.example.com`).

5. For **Service**, select _TCP_ and enter `localhost:<5901>`. If the VNC server is on a different machine from where you installed the tunnel, enter `<SERVER_IP>:5901`.

	Replace `5901` with your VNC server's listening port. To determine your VNC listening port, run `sudo ss -lnpt` and look for `vnc` in the list of processes.

6. Save the route.

Your VNC server is now ready to accept inbound requests from Cloudflare.

## 3. Create an Access application for VNC

Create a Cloudflare Access application that users can access through their browser:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **Self-hosted and private**.

4. Select **Add public hostname** and enter your published application hostname (`vnc.example.com`).

5. Turn on **Allow access through browser-based RDP, SSH, or VNC sessions**, then select _VNC_.

6. <Render file="access/add-access-policies" product="cloudflare-one" />

	<Render file="access/self-hosted-app/browser-rendering-policies" product="cloudflare-one" />

7. Select **Create**.

## 4. Connect as a user

Users can now access the remote desktop environment directly in their web browser without installing any VNC client software.

To connect to the VNC server:

1. Open a browser and go to the public hostname URL (for example, `https://vnc.example.com`).
2. Log in to Cloudflare Access with your configured identity provider.
3. Enter the VNC server password.

You should see the remote VNC server desktop rendered in your browser. All connections are secured through Cloudflare's network, and access is controlled by your Access policies.

# RDP

The Remote Desktop Protocol (RDP) provides a graphical interface for users to connect to a computer remotely. RDP is most commonly used to facilitate simple remote access to machines or workstations which users cannot physically access. However, this also makes RDP connections the frequent subject of attacks, since a misconfiguration can inadvertently allow unauthorized access to the machine.

With Cloudflare Zero Trust, you can make your RDP server available over the Internet without the risk of opening any inbound ports on your local server.

Cloudflare offers three ways to secure RDP:

- [Browser-based RDP](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-browser/)
- [RDP with Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-device-client/)
- [RDP with client-side cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-cloudflared-authentication/)

# Connect to RDP in a browser

import { Render, GlossaryTooltip, Details, Tabs, TabItem, APIRequest, Badge } from "~/components";

Users can connect to an RDP server without installing an RDP client or the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on their device. Browser-based RDP leverages [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), which creates a secure, outbound-only connection from your RDP server to Cloudflare's global network. Setup involves running the `cloudflared` daemon on the RDP server (or any other host machine within the private network) and routing RDP traffic over a public hostname.

There are two ways for users to [reach the RDP server in their browser](#4-connect-as-a-user):

- **App Launcher (recommended)**: Users can log in to the [Access App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) with their Cloudflare Access credentials and then initiate an RDP connection within the browser to their Windows machine. Users will authenticate to the Windows machine using their pre-configured Windows username and password. Cloudflare does not manage any credentials on the Windows server.
- **Direct URL**: A user may also navigate directly to the Windows server at `https://<app-domain>/rdp/<vnet-id>/<target-ip>/<port>`, where `vnet-id` is the <GlossaryTooltip term="Virtual network">virtual network</GlossaryTooltip> assigned to the Cloudflare Tunnel route. The authentication flow is the same as for the App Launcher; first users must log in to Cloudflare Access and then use their Windows credentials to authenticate to the Windows machine.

Browser-based RDP can be used in conjunction with [the Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-device-client/) so that there are multiple ways to connect to the server. You can reuse the same Cloudflare Tunnel when configuring each connection method.

## Prerequisites

- An [active domain on Cloudflare](/fundamentals/manage-domains/add-site/).
- The domain uses either a [full setup](/dns/zone-setups/full-setup/) or a [partial (`CNAME`) setup](/dns/zone-setups/partial-setup/).
- An RDP server running a supported [Windows operating system](#rdp-server-operating-systems).
- The RDP server's [security layer](#known-limitations) allows TLS (set to **Negotiate** or **SSL**, not the legacy **RDP** option).

## 1. Connect the server to Cloudflare

<Render file="tunnel/connect-private-network" product="cloudflare-one" />

<Render
	file="tunnel/connect-private-network-infra-access"
	product="cloudflare-one"
/>

## 2. Add a target

A target represents a single resource in your infrastructure (such as a server, Kubernetes cluster, database, or container) that users will connect to through Cloudflare.

<p> Create a target for each Windows machine that requires RDP access.
To create a new target:</p>

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Targets**.
2. Select **Add a target**.
3. In **Target hostname**, enter a user-friendly name for the <GlossaryTooltip term="target">target</GlossaryTooltip>. We recommend using the server hostname, for example `production-server`. The target hostname does not need to be unique and can be reused for multiple targets. Hostnames are used to define the targets secured by an Access application; they are not used for DNS address resolution.
	<Details header="Hostname format restrictions">
			- Case insensitive
			- Contain no more than 253 characters
			- Contain only alphanumeric characters, `-`, or `.` (no spaces allowed)
			- Start and end with an alphanumeric character
	</Details>
4. In **IP addresses**, enter the IPv4 and/or IPv6 address of the target resource. The dropdown menu will not populate until you type in the full IP address.
:::note
If the target IP does not appear in the dropdown, go to **Networking** > **Routes** and confirm that the IP routes through Cloudflare Tunnel.
:::
5. In the dropdown menu, select the IP address and [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) where the resource is located. This IP address and virtual network pairing is now assigned to this target and cannot be reused in another target by design.
6. Select **Add target**.
</TabItem>
<TabItem label="API">

Make a `POST` request to the [Infrastructure Access Targets](/api/resources/zero_trust/subresources/access/subresources/infrastructure/subresources/targets/methods/create/) endpoint:

<APIRequest
  path="/accounts/{account_id}/infrastructure/targets"
  method="POST"
  json={{
    hostname: "infra-access-target",
    ip: {
      ipv4: {
        ip_addr: "187.26.29.249",
        virtual_network_id: "c77b744e-acc8-428f-9257-6878c046ed55",
      },
      ipv6: {
        ip_addr: "64c0:64e8:f0b4:8dbf:7104:72b0:ec8f:f5e0",
        virtual_network_id: "c77b744e-acc8-428f-9257-6878c046ed55",
      },
    },
  }}
/>

</TabItem>
<TabItem label="Terraform">

:::note[Provider versions]
The following example requires Cloudflare provider version `>=4.45.0`.
:::

1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/4.45.0/docs/resources/api_token):
	- `Zero Trust Write`

2. Configure the [`cloudflare_zero_trust_infrastructure_access_target`](https://registry.terraform.io/providers/cloudflare/cloudflare/4.45.0/docs/resources/zero_trust_infrastructure_access_target) resource:

	```tf
	resource "cloudflare_zero_trust_infrastructure_access_target" "infra-ssh-target" {
		account_id = var.cloudflare_account_id
			hostname   = "infra-access-target"
			ip = {
				ipv4 = {
					ip_addr = "187.26.29.249"
					virtual_network_id = "c77b744e-acc8-428f-9257-6878c046ed55"
				}
				ipv6 = {
					ip_addr = "64c0:64e8:f0b4:8dbf:7104:72b0:ec8f:f5e0"
					virtual_network_id = "c77b744e-acc8-428f-9257-6878c046ed55"
				}
			}
	}
	```

</TabItem>
</Tabs>

Next, create an Access application to secure the target.

## 3. Create a DNS record

To make your RDP targets (that is, your Windows machines) available through the browser, you will need a [Cloudflare DNS record](/dns/manage-dns-records/how-to/create-dns-records/) for the domain and subdomain that users will connect to. This domain will be used to access any targets that are available to users through your Access application (see Step 4).

For example, if want users to connect to targets on `rdp.example.com`, [create a DNS record](/dns/manage-dns-records/how-to/create-dns-records/#create-dns-records) for `rdp.example.com`. You can create either an `A`, `AAAA`, or `CNAME` record:

<Details header="A record" open={false}>
The following DNS record points your public subdomain (`rdp`) to an IPv4 address in the [Class E address space](https://datatracker.ietf.org/doc/html/rfc5735).

- **Type**: _A_
- **Name**: `rdp`
- **IPv4 address**: `240.0.0.0`
- **Proxy status**: On

</Details>

<Details header="AAAA record" open={false}>
The following DNS record points your public subdomain (`rdp`) to the IPv6 [discard address range](https://www.rfc-editor.org/rfc/rfc6666.html):

- **Type**: _AAAA_
- **Name**: `rdp`
- **IPv6 address**: `100::`
- **Proxy status**: On

</Details>

<Details header="CNAME record" open={false}>
The following `CNAME` record points your public subdomain (`rdp`) to a fully qualified domain name.

- **Type**: _CNAME_
- **Name**: `rdp`
- **Target**: `www.rdp.example.com`
- **Proxy status**: On

The CNAME **Target** field is unrelated to the RDP targets configured in Step 2.

</Details>

The DNS record does not need to point to an active destination IP address or hostname; the DNS record just needs to be valid. Cloudflare's RDP proxy will handle the routing to the correct RDP target.

## 4. Create an Access application

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **Self-hosted and private**.

4. Select **Add public hostname**.

        :::note
        Browser-based RDP is only compatible with public hostnames. If you add a private hostname or IP, RDP functionality will not be available in this application.
        :::

5.  <Render
    	file="access/self-hosted-app/choose-domain"
    	product="cloudflare-one"
    />

        :::note
        You can only enable browser-based RDP on domains and subdomains, not for specific paths. The selected domain and subdomain must also have a corresponding DNS record (refer to [Step 3](#3-create-a-dns-record)).
        :::

6.  Turn on **Allow access through browser-based RDP, SSH, or VNC sessions**, then select _RDP_ from the dropdown menu.

7.  In **Target criteria**, select the [target hostname(s)](#2-add-a-target) that define your RDP servers. The application definition will apply to all targets that share the selected target hostname, including any targets added in the future.

8. In **Port**, enter the [RDP listening port](https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/change-listening-port) of your server. It will likely be port `3389`.

9. (Optional) If you run RDP on more than one port, select **Add new target criteria** and reconfigure the same target hostname(s) with the different port number.

10. <Render file="access/add-access-policies" product="cloudflare-one" />

	<Render file="access/self-hosted-app/browser-rendering-policies" product="cloudflare-one" />

11. (Optional) In your Access policy, configure [connection settings](#connection-settings) to restrict clipboard and file transfer actions between the user's local machine and the browser-based RDP session.

12. Configure how users will authenticate:

    <ol>
    <li>
    Select the [identity providers](/cloudflare-one/integrations/identity-providers/) you want to enable for your application.
    </li>
    <li>
    (Recommended) If you plan to only allow access via a single IdP, turn on **Apply instant authentication**. End users will not be shown the [Cloudflare Access login page](/cloudflare-one/reusable-components/custom-pages/access-login-page/). Instead, Cloudflare will redirect users directly to your SSO login event.
    </li>
    <li> <b>Authenticate with Cloudflare One Client</b> is not supported for browser-based RDP and should remain turned off. </li>
    </ol>

13. <Render file="access/session-duration" product="cloudflare-one" />

14. (Optional) Go to the **Additional settings** tab to customize the application experience:

	- **App Launcher customization**: The [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) allows users to view the Windows servers that they can access using browser-based RDP. Cloudflare recommends keeping **Show application in App Launcher** turned on. Without the App Launcher, users will need to know each target's direct URL.

        :::note
        Ensure that users match an Allow rule in your [App Launcher policies](/cloudflare-one/access-controls/access-settings/app-launcher/#enable-the-app-launcher).
        :::

	- <Render file="access/access-block-page" product="cloudflare-one" />

	<Render file="access/self-hosted-app/advanced-settings" product="cloudflare-one" />

15. Select **Create**.

## 5. (Recommended) Modify order of precedence in Gateway

By default, Cloudflare will evaluate Access application policies after evaluating all [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/). To evaluate Access applications before or after specific Gateway policies:

<ol>
<li>
In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**. In **Network**, [create a Network policy](/cloudflare-one/traffic-controls/network-policies/) with the following configuration:

    | Selector                     | Operator | Value     | Action |
    | ---------------------------- | -------- | --------- | ------ |
    | Access Infrastructure Target | is       | _Present_ | Allow  |

</li>

<li> Ensure that <strong>Enforce Cloudflare One Client session duration</strong> is turned off, otherwise users will be blocked from accessing RDP targets. </li>

<li>
	Update the policy's [order of
	precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence)
	using the dashboard or API.
</li>

</ol>

<p> This Gateway policy will apply to all Access for Infrastructure targets, including RDP and SSH. </p>

:::note
Users must pass the policies in your Access application before they are granted access. The Gateway Allow policy is strictly for routing and connectivity purposes.
:::

## 6. Connect as a user

To connect to a Windows machine over RDP:

1. Open a browser and go to your App Launcher URL:

   ```text
   https://<your-team-name>.cloudflareaccess.com
   ```

   Replace `<your-team-name>` with your Zero Trust <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

2. Follow the prompts to log in to your identity provider.

   Once you have authenticated, the App Launcher will display tiles showing the applications that you are authorized to use. Windows servers (targets) available through browser-based RDP will also appear as tiles. If a target is reachable through multiple Access applications, the target will have a tile per Access application.

3. Select the target you want to connect to.

   The App Launcher tile will launch a URL of the form `https://<app-domain>/rdp/<vnet-id>/<target-ip>/<port>`. You may also navigate directly to this URL.

   :::note[Virtual network ID]
   `vnet-id` refers to the [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) (VNET) that the RDP target is assigned to in your Cloudflare Tunnel configuration. If you did not specify a VNET when routing the target through Cloudflare Tunnel, the target is automatically added to the default VNET.

   To fetch a list of all VNETs and their IDs, make a `GET` request to the [List Virtual Networks](/api/resources/zero_trust/subresources/networks/subresources/virtual_networks/methods/list/) endpoint. The default VNET will have the parameter `"is_default_network": true`.
   :::

4. Select the port that you want to connect to. The port selection screen only appears if the Access application allows RDP traffic on multiple ports (for example, port `3389` and port `65321`).
5. (Optional) In your browser settings, allow the Access application to access the clipboard. Clipboard access is subject to [policy restrictions](#configure-connection-settings) configured by your administrator.

   :::note
   Automatic clipboard sharing only works by default in Chromium-based browsers; Firefox requires additional configuration. Refer to [Known limitations](#known-limitations) for details.
   :::

6. Enter your Windows username and password. For more information on how to format your username, refer to [User identifier formats](#user-identifier-formats).

You now have access to the remote Windows desktop.

## Connection settings

Connection settings restrict data transfer between the user's local machine and the browser-based RDP session. You can control text (copy and paste) and file transfers. Text controls manage clipboard content. File controls <Badge text="Beta" variant="caution" size="small" /> manage file uploads and downloads. These controls are configured per policy, so you can grant different permissions to different groups of users.

### Default behavior

For new policies, both text controls and file controls are denied by default. You must explicitly allow each action. Existing applications retain full text clipboard access for backward compatibility. File controls are denied unless explicitly enabled.

### Available settings

Text controls and file controls use the same directional options:

| Setting                                | Description                                                                                                      |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| _Client to remote RDP session allowed_ | Users can transfer data from their local client into the browser-based RDP session.                              |
| _Remote RDP session to client allowed_ | Users can transfer data from the browser-based RDP session to their local client.                                |
| _Both directions allowed_              | Users can transfer data in both directions.                                                                      |
| _Disable copying/pasting_              | Users are not allowed to transfer data between the browser-based RDP session and their local client.             |

For example, you can allow text copy and paste in both directions while restricting file transfers to uploads only.

When a user attempts a restricted clipboard action, the clipboard content is replaced with a message informing them that the action is not allowed. When file transfer is restricted, upload methods are disabled and download buttons do not appear in the control panel.

### Configure connection settings

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Locate your browser-based RDP application and select **Configure**.
3. Select the **Policies** tab.
4. Create a new policy or select an existing policy to edit.
5. Expand **Connection context**.
6. Under **Connection settings**, configure the following settings:
   - **Text controls** — Select a directional setting for text copy and paste.
   - **File controls** — Select a directional setting for file uploads and downloads.
7. Select **Save policy**.

</TabItem>
<TabItem label="API">

When [creating or updating an Access policy](/api/resources/zero_trust/subresources/access/subresources/policies/) for an RDP application, configure the allowed formats in each direction. Use `text` for text clipboard and `file` for file transfer. For example, the following policy allows text clipboard in both directions but only allows file uploads (local to remote).

<APIRequest
	path="/accounts/{account_id}/access/policies"
	method="POST"
	json={{
		name: "Allow engineers with clipboard and upload",
		decision: "allow",
		include: [
			{
				email_domain: {
					domain: "example.com",
				},
			},
		],
		connection_rules: {
			rdp: {
				allowed_clipboard_local_to_remote_formats: ["text", "file"],
				allowed_clipboard_remote_to_local_formats: ["text"],
			},
		},
	}}
/>

</TabItem>
<TabItem label="Terraform">

Using the `connection_rules` attribute within a [`cloudflare_zero_trust_access_policy`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_policy) resource, configure the allowed formats in each direction. Use `text` for text clipboard and `file` for file transfer. For example, the following policy allows text clipboard in both directions but only allows file uploads (local to remote).

```tf
resource "cloudflare_zero_trust_access_policy" "rdp-policy" {
	account_id = var.cloudflare_account_id
	name       = "Allow engineers with clipboard and upload"
	decision   = "allow"

	include = [
		{
			email_domain = {
				domain = "example.com"
			}
		}
	]

	connection_rules = {
		rdp = {
			allowed_clipboard_local_to_remote_formats = ["text", "file"]
			allowed_clipboard_remote_to_local_formats = ["text"]
		}
	}
}
```

</TabItem>
</Tabs>

### Transfer files <Badge text="Beta" variant="caution" size="small" />

To manage transfers, select the settings gear icon on the left side of the RDP session. You can drag this icon along the left edge to reposition it.

File transfer has the following limits:

- **Maximum file size:** 2 GB per file (upload and download)
- **Maximum files per upload:** 1,000 files

#### Upload files (local to remote)

To transfer files from your local machine to the remote Windows session, drag files onto the browser window or use the control panel. Drag and drop supports individual files and folders (including subfolders, up to 1,000 total entries). The control panel file picker selects individual files only. Files land on the active element of the remote desktop. For example, if you have a folder open in File Explorer, the file lands in that folder.

#### Download files (remote to local)

To transfer files from the remote Windows session to your local machine:

1. In the remote Windows session, copy the file you want to download. Right-click the file and select **Copy**, or select the file and press **Ctrl+C**.
2. The control panel icon does a small hop to indicate that a file is available. Open the control panel to view the file.
3. Select one of the following options:
   - **Download**: Download the file to your local machine.
   - **Download zip**: Download multiple files at once as a zipped folder to your local machine.
   - **Print**: [Print PDF files](#print-pdfs) to a local printer on your network.

#### Print PDFs

You can print PDF files from the clipboard panel to a local printer. To print a single file, select the print icon next to the PDF. To print multiple files at once, copy the files together into the clipboard on the remote machine, then select **Print all PDFs** in the clipboard panel. The files are combined into a single PDF and sent to your browser as one print job.

:::note
Printing to a local printer is available for PDF files in Chromium-based browsers and Firefox. In Safari, download the PDF and print from your system viewer.
:::

#### Limitations

- Transfer history is discarded when the RDP session ends.
- The remote Windows server must support file transfer via the RDP clipboard virtual channel. Windows Server 2012 and later support this by default.
- If the remote server does not support file transfer, text clipboard continues to work normally.

## Compatibility

### RDP server operating systems

Browser-based RDP supports connecting to Windows machines that run the following operating systems:

- Windows 11 Pro
- Windows 11 Enterprise
- Windows 10 Pro
- Windows 10 Enterprise
- Windows Server 2025
- Windows Server 2022
- Windows Server 2019
- Windows Server 2016

### Browsers

| Browser                                      | Compatibility |
| -------------------------------------------- | ------------- |
| Google Chrome                                | ✅            |
| Mozilla Firefox                              | ✅            |
| Safari                                       | ✅            |
| Microsoft Edge (Chromium-based)              | ✅            |
| Other Chromium-based browsers (Opera, Brave) | ✅            |
| Internet Explorer 11 and below               | ❌            |

### Powershell

Run Powershell 7 or higher to mitigate a prior Microsoft issue where keystrokes are not recorded.

### User identifier formats

Browser-based RDP supports connecting to Windows machines using the following login credentials:

#### Security Account Manager (SAM)

SAM-formatted user identifiers are supported with and without spaces.

Examples:

- `DOMAIN\username`
- `DOMAIN\username with spaces`
- `.\username`
- `.\username with spaces`
- `username`
- `username with spaces`

<Details header = "Character limits" open= {true}>
Identifiers which specify a domain, such as `DOMAIN\username`, can have a maximum of 20 characters for the domain and 15 characters for the username.

Identifiers without a domain, such as `.\username`, will use the default domain. The username can have a maximum of 20 characters.

</Details>

#### User Principal Name (UPN)

UPN-formatted user identifiers are supported with spaces, with and without quotes.

Examples:

- `"username with spaces"@domain.org`
- `username with spaces@domain.org`
- `username@domain.org`

:::note
Cloudflare will not configure user identifiers on the RDP target. Any user identifier used to authenticate must be pre-configured on the server.
:::

#### Microsoft Entra ID

User identifiers that are bound to Microsoft Entra ID domains must enter their username as `AzureAD\user@example.com` or `AzureAD\user`. The `AzureAD\` prefix is case-insensitive.
The login flow differs slightly when using an Microsoft Entra ID-bound username:

1. Enter your username in one of the formats outlined above.
2. Once the username is entered, the password box will disappear and the RDP connection will initiate.
3. The RDP server will then prompt for the password before granting access to the RDP server.

### Cloudflare products

<Render
	file="access/self-hosted-app/product-compatibility"
	product="cloudflare-one"
/>

## Known limitations

- **TLS certificate verification**: Cloudflare uses TLS to connect to the RDP target but does not verify the origin TLS certificate.
- **Device authentication identity**: Since browser-based RDP traffic does not go through the Cloudflare One Client, users cannot use their [Cloudflare One Client session identity](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/#configure-warp-sessions-in-access) to authenticate.
- **Audio over RDP**: Users cannot use their microphone and speaker to interact with the remote machine.
- **Clipboard size limit**: Data copied between the local machine and the browser-based RDP session may not exceed 500 KB.
- **Clipboard data types**: Text clipboard controls only support text data. Image clipboard transfers are not supported.
- **File transfer availability**: File transfer is in beta. Refer to [Transfer files](#transfer-files) for supported functionality and limitations.
- **Print to local printer**: Local printing from a browser-based RDP session is only supported for PDF files through the [file transfer control panel](#print-pdfs).
- **Network Level Authentication for Entra-joined accounts**: Browser-based RDP does not support PKU2U authentication which is required for [Network Level Authentication (NLA)](https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/remotepc/remote-desktop-allow-access#why-allow-connections-only-with-network-level-authentication) with Entra-joined accounts. Connecting to Entra-joined accounts requires disabling enforcement of NLA on the remote Windows machine. You can disable NLA from **Settings** > **System** > **Remote Desktop**, or use the Local Group Policy Editor to disable **Require user authentication for remote connections by using Network Level Authentication**. When disabling NLA, only turn off NLA itself — do not switch the security layer to the legacy **RDP** option, because browser-based RDP still requires TLS (refer to the **RDP security layer must allow TLS** limitation).
- **RDP security layer must allow TLS**: Browser-based RDP connects to the remote machine over TLS, so the machine's RDP security layer must be set to at least **Negotiate** (or **SSL**). If the server is set to use the legacy **RDP** security layer, connections will fail. You can configure this in the Local Group Policy Editor by setting **Require use of specific security layer for remote (RDP) connections** to **Negotiate** or **SSL**.
- **Clipboard browser compatibility**: Automatic clipboard sharing between the local and remote machine is only available in Chromium-based browsers by default (Google Chrome, Microsoft Edge, Opera, Brave). To enable this functionality in Firefox:
    1. Type `about:config` into the browser address bar and press **Enter**.
    2. Accept the warning prompt if displayed.
    3. Search for `dom.events.testing.asyncClipboard` and set it to `true`.
    4. Search for `dom.events.asyncClipboard.clipboardItem` and set it to `true`.
    5. Search for `dom.events.asyncClipboard.readText` and set it to `true`.

# Connect to RDP with client-side cloudflared

import { DashButton, Render } from "~/components";

End users can connect to an RDP server without the Cloudflare One Client by authenticating through `cloudflared` in their native terminal. This method requires having `cloudflared` installed on both the server machine and on the client machine, as well as an active zone on Cloudflare. The traffic is proxied over this connection, and the user logs in to the server with their Cloudflare Access credentials.

Client-side `cloudflared` can be used in conjunction with [the Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-device-client/) and [Browser-based RDP](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-browser/) so that there are multiple ways to connect to the server. You can reuse the same Cloudflare Tunnel when configuring each connection method.

## 1. Connect the server to Cloudflare

1. Create a Cloudflare Tunnel by following our [dashboard setup guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).

2. In the Cloudflare dashboard, go to **Networking** > **Tunnels** and select your tunnel.

   <DashButton url="/?to=/:account/tunnels" />

3. On the **Routes** tab, select **Add route**, then select **Published application**.

4. Choose a domain from the drop-down menu and specify any subdomain (for example, `rdp.example.com`).

5. For **Service**, select _RDP_ and enter the [RDP listening port](https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/change-listening-port) of your server (for example, `localhost:3389`). It will likely be port `3389`.

6. Select **Add route**.

## 2. (Recommended) Create an Access application

By default, anyone on the Internet can connect to the server using the hostname of the published application. To allow or block specific users, create a [self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) in Cloudflare Access.

## 3. Connect as a user

1. [Install `cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) on the client machine.
2. Run this command to open an RDP listening port:

   ```sh
   cloudflared access rdp --hostname rdp.example.com --url rdp://localhost:3389
   ```

   This process will need to be configured to stay alive and autostart. If the process is killed, users will not be able to connect.

:::note

If the client machine is running Windows, port `3389` may already be consumed locally. Select an alternative port to `3389` that is not being used.
:::

3. While `cloudflared access` is running, connect from an RDP client such as Microsoft Remote Desktop:
   1. Open Microsoft Remote Desktop and select **Add a PC**.
   2. For **PC name**, enter `localhost:3389`.
   3. For **User account**, enter your RDP server username and password.
   4. Double-click the newly added PC.
   5. When asked if you want to continue, select **Continue**.

When the client launches, a browser window will open and prompt the user to authenticate with Cloudflare Access.

# Connect to RDP using the Cloudflare One Client

import { DashButton, Render } from "~/components";

The [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) allows users to connect to RDP servers using their preferred RDP client. Cloudflare Tunnel creates a secure, outbound-only connection from your RDP server to Cloudflare's global network; this requires running the `cloudflared` daemon on the server (or any other host machine within the private network). Users install the Cloudflare One Client on their device and enroll in your Zero Trust organization. Remote devices will be able to connect as if they were on your private network. By default, all devices enrolled in your organization can connect to the RDP server unless you build policies to allow or block specific users.

This example walks through how to set up an RDP server on a Google Cloud Platform (GCP) virtual machine (VM), but you can use any machine that supports RDP connections.

## 1. Set up an RDP server in GCP

1. In your [Google Cloud Console](https://console.cloud.google.com/), [create a new project](https://developers.google.com/workspace/guides/create-project).
2. Go to **Compute Engine** > **VM instances**.
3. Select **Create instance**.
4. Name your VM instance, for example `windows-rdp-server`.
5. Configure your VM instance:
   1. Scroll down to **Boot Disk** and select **Change**.
   2. For **Operating system**, select _Windows Server_.
   3. Choose a **Version** with Desktop Experience, for example _Windows Server 2016 Datacenter_.
6. Once your VM is running, open the dropdown next to **RDP** and select _View gcloud command to reset password_.
7. Select **Run in Cloud Shell**.
8. Run the command in the Cloud Shell terminal. You will be asked to confirm the password reset.
9. Copy the auto-generated password and username to a safe place.

## 2. Install Microsoft Remote Desktop

You can use any RDP client to access and configure the RDP server.

To access the server through Microsoft Remote Desktop:

1. Download and install [Microsoft Remote Desktop](https://apps.microsoft.com/store/detail/microsoft-remote-desktop/9WZDNCRFJ3PS).
2. Once downloaded, open Microsoft Remote Desktop and select **Add a PC**.
3. For **PC name**, enter the public IP address of your RDP server. In GCP, this is the **External IP** of the VM instance.
4. For **User account**, select **Add User Account** and enter your auto-generated password and username.
5. Select **Add**. The PC will display in Microsoft Remote Desktop.
6. To test basic connectivity, double-click the newly added PC.
7. When asked if you want to continue, select **Continue**.

You can now remotely access the RDP server using its public IP. The next steps will configure access to the server using its private IP.

:::note

By default, Internet Explorer will be installed and configured in [Enhanced Security mode](https://learn.microsoft.com/troubleshoot/developer/browsers/security-privacy/enhanced-security-configuration-faq#internet-explorer-enhanced-security-configuration). If the browser is slow or unable to load, you can turn off Enhanced Security and install an alternate browser such as Google Chrome.
:::

## 3. Connect the server to Cloudflare

<Render file="tunnel/connect-private-network" product="cloudflare-one" />

3. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

4. Select **Create route** > **Tunnel CIDR**. Select the tunnel you just created, enter the private IP or CIDR address of your server (in GCP, the server IP is the **Internal IP** of the VM instance), and select **Create route**.

5. (Optional) [Set up Zero Trust policies](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/#4-recommended-filter-network-traffic-with-gateway) to fine-tune access to your server.

## 4. Set up the client

<Render file="tunnel/warp-to-tunnel-client" product="cloudflare-one" />

## 5. Route private network IPs through the Cloudflare One Client

<Render
	file="tunnel/warp-to-tunnel-route-ips"
	product="cloudflare-one"
	params={{ one: "private network" }}
/>

## 6. Connect as a user

Once the Cloudflare One Client is configured, you can use your RDP client to connect to the server's private IP address (instead of the public IP address used initially).

To connect in Microsoft Remote Desktop:

1. Open Microsoft Remote Desktop and select **Add a PC**.
2. For **PC name**, enter the private IP address of your RDP server. In GCP, this is the **Internal IP** of the VM instance.
3. For **User account**, enter your RDP server username and password.
4. To test Zero Trust connectivity, double-click the newly added PC.
5. When asked if you want to continue, select **Continue**.

You now have secure, remote access to the RDP server.

# SSH

import { CardGrid, LinkTitleCard } from "~/components";

The Secure Shell Protocol (SSH) enables users to remotely access devices through the command line. With Cloudflare One, you can make your SSH server available over the Internet without the risk of opening inbound ports on the server.

Cloudflare offers four ways to secure SSH:

<CardGrid>

<LinkTitleCard
	title="SSH with client-side cloudflared"
	href="/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-cloudflared-authentication/"
	icon="seti:shell"
>

**Setup time:** 15-30 minutes

**Required products:** [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) (`cloudflared` on server and client), [Access](/cloudflare-one/access-controls/)

**Best for:** Seamless SSH access with identity-based authentication using native terminal

**Key differentiator:** No Cloudflare One Client required — works with just `cloudflared` on both ends

</LinkTitleCard>

<LinkTitleCard
	title="SSH with Access for Infrastructure"
	href="/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/"
	icon="seti:lock"
>

**Setup time:** 45-60 minutes

**Required products:** [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) (`cloudflared` on server), [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) or [Cloudflare WAN](/cloudflare-wan/) (client on-ramp), [Gateway](/cloudflare-one/traffic-controls/), [Access](/cloudflare-one/access-controls/)

**Best for:** Advanced SSH certificate-based authentication with short-lived credentials

**Key differentiator:** SSH certificates with Access policies and command logging

</LinkTitleCard>

<LinkTitleCard
	title="Self-managed SSH keys"
	href="/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-device-client/"
	icon="key"
>

**Setup time:** 30-45 minutes

**Required products:** [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) (`cloudflared` on server), [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) or [Cloudflare WAN](/cloudflare-wan/) (client on-ramp), [Gateway](/cloudflare-one/traffic-controls/)

**Best for:** Traditional SSH key management with network-level policy enforcement

**Key differentiator:** Keep your existing SSH key infrastructure with no client-side `cloudflared` or SSH config changes needed

</LinkTitleCard>

<LinkTitleCard
	title="Browser-rendered SSH terminal"
	href="/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-browser-rendering/"
	icon="laptop"
>

**Setup time:** 20-30 minutes

**Required products:** [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) (`cloudflared` on server), [Access](/cloudflare-one/access-controls/)

**Best for:** Browser-based SSH access for quick administrative tasks

**Key differentiator:** No SSH client or Cloudflare One Client required — connect directly from a browser

</LinkTitleCard>

</CardGrid>

# Connect to SSH in the browser

import { Render } from "~/components";

Cloudflare's browser-based terminal allows end users to connect to an SSH server without managing SSH keys or installing the Cloudflare One Client.

This method requires routing SSH access to the server through a public hostname. The traffic is proxied over this connection, and the user logs in to the server with their Cloudflare Access credentials.

The browser-based terminal can be used in conjunction with [the Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-device-client/) and [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) so that there are multiple ways to connect to the server. You can reuse the same Cloudflare Tunnel when configuring each connection method.

## 1. Connect the server to Cloudflare

<Render file="ssh/tunnel-public-hostname" product="cloudflare-one" />

## 2. Connect as a user

To enable browser-rendering for SSH, refer to [Browser-rendered terminal](/cloudflare-one/access-controls/applications/non-http/browser-rendering/).

When users visit the public hostname URL (for example, `https://ssh.example.com`) and log in with their Access credentials, Cloudflare will render a terminal in their browser.

# Connect to SSH with client-side cloudflared

import { Render } from "~/components";

End users can connect to an SSH server without the Cloudflare One Client by authenticating through `cloudflared` in their native terminal. This method requires having `cloudflared` installed on both the server machine and on the client machine, as well as an active zone on Cloudflare. The traffic is proxied over this connection, and the user logs in to the server with their Cloudflare Access credentials.

Client-side `cloudflared` can be used in conjunction with [the Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-device-client/) and [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) so that there are multiple ways to connect to the server. You can reuse the same Cloudflare Tunnel when configuring each connection method.

## 1. Connect the server to Cloudflare

<Render file="ssh/tunnel-public-hostname" product="cloudflare-one" />

## 2. Connect as a user

1. [Install `cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) on the client machine.

2. Make a one-time change to your SSH configuration file:

   ```sh
   vim ~/.ssh/config
   ```

3. Input the following values; replacing `ssh.example.com` with the hostname you created.

   ```txt
   Host ssh.example.com
   ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
   ```

   The `cloudflared` path may be different depending on your OS and package manager. For example, if you installed `cloudflared` on macOS with Homebrew, check its path by running `brew --prefix cloudflared`.

4. You can now test the connection by running a command to reach the service:

   ```sh
   ssh <username>@ssh.example.com
   ```

   When the command is run, `cloudflared` will launch a browser window to prompt you to authenticate with your identity provider before establishing the connection from your terminal.

# Connect with self-managed SSH keys

import { Render, Details, GlossaryTooltip } from "~/components";

If you want to manage your own SSH keys, you can use Cloudflare Tunnel to create a secure, outbound-only connection from your server to Cloudflare's global network. This requires running the `cloudflared` daemon on the server (or any other host machine within the private network). Users with SSH keys that are trusted by the SSH server can access the server by installing the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on their device and enrolling in your Zero Trust organization. Users can SSH directly to the server's private hostname (for example, `ssh.internal.local`). You control access to the server using network-level Gateway policies instead of application-level Access policies.

:::note

If you want to create more granular policies, allow Cloudflare to manage SSH keys for you, or to obtain command logs, consider using [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/) instead.
:::

## Prerequisites

- A [Cloudflare Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization)
- [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) installed on user devices.
- Devices [enrolled](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) in your Zero Trust organization

## 1. Create an example SSH server

This example walks through how to set up an SSH server on a Google Cloud Platform (GCP) virtual machine (VM), but you can use any machine that supports SSH connections. If you already have an SSH server configured, you can skip to [Step 2](#2-connect-the-server-to-cloudflare).

### 1.1 Create an SSH key pair

Before creating your VM instance you will need to create an SSH key pair.

1. Open a terminal and type the following command:

   ```sh
   ssh-keygen -t rsa -f ~/.ssh/gcp_ssh -C <username in GCP>
   ```

2. Enter your passphrase when prompted. It will need to be entered twice.

   Two files will be generated: `gcp_ssh` which contains the private key, and `gcp_ssh.pub` which contains the public key.

3. In the command line, enter:

   ```sh
   cat ~/.ssh/gcp_ssh.pub
   ```

4. Copy the output. This will be used when creating the VM instance in GCP.

### 1.2 Create a VM instance in GCP

Now that the SSH key pair has been created, you can create a VM instance.

1. In your [Google Cloud Console](https://console.cloud.google.com/), [create a new project](https://developers.google.com/workspace/guides/create-project).
2. Go to **Compute Engine** > **VM instances**.
3. Select **Create instance**.
4. Name your VM instance, for example `ssh-server`.
5. Scroll down to **Advanced options** > **Security** > **Manage Access**.
6. Under **Add manually generated SSH keys**, select **Add item** and paste the public key that you have created.
7. Select **Create**.
8. Once your VM instance is running, open the dropdown next to **SSH** and select _Open in browser window_.

:::note

In order to be able to establish an SSH connection, do not enable [OS Login](https://cloud.google.com/compute/docs/oslogin) on the VM instance.
:::

## 2. Connect the server to Cloudflare

This section covers how to create a new Cloudflare Tunnel for your SSH server. You can reuse the same tunnel for all services on a private network that are reachable from the `cloudflared` host.

<Render file="tunnel/create-tunnel" product="cloudflare-one" />

## 3. Use hostname routes

Hostname routes allow you to SSH directly to `ssh.internal.local` without managing static IP routes.  Hostname routes are especially useful when your SSH server has an unknown or ephemeral IP address, such as dynamic infrastructure provisioned by cloud providers.

   <Details header="How hostname routing works">

	When you create a hostname route in Cloudflare Tunnel:

	1. Users SSH to your private hostname (for example, `ssh user@ssh.internal.local`).
	2. Gateway resolves the hostname to an <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>.
	3. Traffic routes through the WARP tunnel to Cloudflare.
	4. Gateway network policies evaluate the connection.
	5. Cloudflared proxies the connection to your SSH server's private IP.

	</Details>

If you do not have a private DNS resolver configured or would rather SSH to an IP address, skip to [Step 4](#4-optional-use-ip-routes).

### 3.1 Add a hostname route

To add a hostname route to your tunnel:

1. Go to **Networking** > **Tunnels** and select your tunnel.
2. On the **Routes** tab, select **Add route**, then select **Private hostname**.
3. Enter the hostname of your SSH server (for example, `ssh.internal.local`).

    <Render
      file="tunnel/hostname-format-restrictions"
      product="cloudflare-one"
    />

4. Select **Save**.

### 3.2 Configure DNS resolution

When Gateway receives a request for your private hostname, it must resolve the hostname to your SSH server's private IP address.

#### Scenario A: Use the system resolver (Default)

By default, `cloudflared` uses the private DNS resolver configured on its host machine (for example, in `/etc/resolv.conf` on Linux). If the machine running `cloudflared` can already resolve `ssh.internal.local` to its private IP using the local system resolver, no further configuration is required. You can skip to [Step 3.3](#33-configure-cloudflare-one-clients).

<Details header="Verify local DNS resolution">
To check if `cloudflared` can successfully resolve `ssh.internal.local`, run the following command from the `cloudflared` host:

```sh
nslookup ssh.internal.local
```

```sh output
Server:		127.0.2.2
Address:	127.0.2.2#53

Non-authoritative answer:
Name:	ssh.internal.local
Address: 10.2.0.3
```

The output should contain the server's private IP address (the **Internal IP** of the GCP VM). If the hostname fails to resolve:
- Make sure that your private DNS resolver has a record that points `ssh.internal.local` to the server's private IP.
- In GCP, you may need to [add a private zone to Cloud DNS](https://docs.cloud.google.com/dns/docs/zones#create-private-zone) so that `internal.local` resolves using your private DNS resolver.

</Details>

#### Scenario B: Use a specific private DNS server (Advanced)

<Render
  file="tunnel/hostname-route-dns-advanced"
  product="cloudflare-one"
  params={{ hostname: "ssh.internal.local" }}
/>

### 3.3 Configure Cloudflare One Clients

To connect to private hostnames, Cloudflare One Clients must be configured to forward the following traffic to Cloudflare:

- Initial resolved IPs:
  <Render file="gateway/egress-selector-cgnat-ips" product="cloudflare-one" />
- DNS queries for your private hostname

#### 3.3.1 Configure Split Tunnels

<Render file="gateway/egress-selector-split-tunnels" product="cloudflare-one" />

#### 3.3.2 Configure Local Domain Fallback

In [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/), delete the top-level domain for your private hostname. This configures WARP to send the DNS query to Cloudflare Gateway for resolution.

For example, if your SSH hostname is `ssh.internal.local`, remove `internal.local` from Local Domain Fallback.

## 4. (Optional) Use IP routes

### 4.1 Add an IP route

To connect to the SSH server using its IP address (instead of a [hostname](#3-use-hostname-routes)), [add a CIDR route](/cloudflare-one/networks/routes/add-routes/#add-a-cidr-route) that includes the server's private IP address.

### 4.2 Configure Cloudflare One Clients

<Render
	file="tunnel/warp-to-tunnel-route-ips"
	product="cloudflare-one"
	params={{ one: "private network" }}
/>

## 5. (Optional) Create Gateway network policies

By default, all devices enrolled in your organization can SSH to the server unless you build Gateway network policies to allow or block specific users. You can create policies based on user identity, device posture, location, and other criteria.

<Render file="tunnel/enable-gateway-proxy" product="cloudflare-one" />

### Example policies

The following example consists of two policies: the first allows specific users to reach your SSH server, and the second blocks all other traffic.

#### Policy 1: Allow authorized users

1. Go to **Traffic policies** > **Firewall policies** > **Network**.
2. Select **Create a policy**.
3. Name your policy (for example, `Allow SSH to internal server`).
4. Create an expression to match your SSH hostname and authorized users:

   | Selector   | Operator | Value                                     |
   | ---------- | -------- | ----------------------------------------- |
   | SNI        | in       | `ssh.internal.local`                      |
   | User Email | in       | `admin@example.com`, `devops@example.com` |

5. In **Action**, select **Allow**.
6. Select **Create policy**.

#### Policy 2: Catch-all block

<Render file="tunnel/catch-all-policy" product="cloudflare-one" />

### Additional security with DNS policies

For an additional layer of protection, create a Gateway DNS policy to control DNS resolution:

1. Go to **Traffic policies** > **Firewall Policies** > **DNS**.
2. Select **Create a policy**.
3. Name your policy (for example, `Allow SSH hostname resolution`).
4. Create an expression:

   | Selector   | Operator | Value                                     |
   | ---------- | -------- | ----------------------------------------- |
   | Host       | in       | `ssh.internal.local`                      |
   | User Email | in       | `admin@example.com`, `devops@example.com` |

5. In **Action**, select **Allow**.
6. Select **Create policy**.

:::note[SNI selector limitations]

<Render file="gateway/selectors/sni-443" product="cloudflare-one" />

Additionally, SNI selectors will only apply to Cloudflare One Client traffic.
:::

## 6. Connect as a user

Once you have set up the tunnel route and the user device, the user can now SSH into the machine. If your SSH server requires an SSH key, the key should be included in the SSH command.

```sh
ssh -i ~/.ssh/gcp_ssh <username>@ssh.internal.local
```

The Cloudflare One Client must be connected to your Zero Trust organization. Users will be able to connect if they match the Gateway network policies you created.

### Troubleshooting

If you cannot connect, verify the following:

1. **Confirm DNS resolution** - From the device, confirm that you can successfully resolve the private hostname:

   ```sh
   nslookup ssh.internal.local
   ```

   ```sh output
   Server:		127.0.2.2
   Address:	127.0.2.2#53

   Non-authoritative answer:
   Name:	ssh.internal.local
   Address: 172.64.128.48
   ```

   The query should resolve using [WARP's DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) and return a Gateway <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>. If the query fails to resolve or returns a different IP, check your [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) configuration and [Gateway resolver policies](/cloudflare-one/traffic-controls/resolver-policies/).

2. **Check Gateway logs** - Review your [Gateway network logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) to see if the connection is being blocked by a policy.

3. **Verify tunnel status** - Confirm that your tunnel is healthy and connected by checking [tunnel status](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/).

4. **Test connectivity to initial resolved IP** - When you connect to the SSH server using its private hostname, the device should make a connection to the <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>:

		```sh
		ssh -v <username>@ssh.internal.local
		```

		```sh output
		...
		Authenticated to ssh.internal.local ([172.64.128.48]:22) using "publickey".
		...
		```

		Look for a line showing connection to an IP in your account's [initial resolved IP range](/cloudflare-one/networks/routes/reserved-ips/#gateway-initial-resolved-ips). If the request fails, confirm that the initial resolved IP [routes through the WARP tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/). You can also check your [tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/) to confirm that requests are routing to the server's private IP.

# SSH with Access for Infrastructure

import { Tabs, TabItem, Render, APIRequest, DashButton } from "~/components";

[Access for Infrastructure](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/) provides granular control over how users can connect to your SSH servers. Like the [self-managed SSH keys](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-device-client/) method, it uses the Cloudflare One Client on user devices and Cloudflare Tunnel on the server to create a secure, private connection through Cloudflare's network. Access for Infrastructure adds application-level policies with per-target and per-username controls, as well as SSH command logging.

<Render
	file="access/short-lived-certs-intro"
	product="cloudflare-one"
	params={{ intro: "Furthermore, Access for Infrastructure replaces" }}
/>

## 1. Connect the server to Cloudflare

<Render file="tunnel/connect-private-network" product="cloudflare-one" />

<Render
	file="tunnel/connect-private-network-infra-access"
	product="cloudflare-one"
/>

## 2. Set up the client

To connect your devices to Cloudflare:

1. [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on your devices in Traffic and DNS mode.
2. [Enable the Gateway proxy for TCP](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy).
3. [Create device enrollment rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) to determine which devices can enroll to your Zero Trust organization.

## 3. Route server IPs through the Cloudflare One Client

<Render
	file="tunnel/warp-to-tunnel-route-ips"
	params={{ one: "SSH server" }}
	product="cloudflare-one"
/>

## 4. Add a target

<Render
	file="access/add-target-generic"
	product="cloudflare-one"
/>

## 5. Add an infrastructure application

<Render file="access/add-infrastructure-app" product="cloudflare-one" />

## 6. (Recommended) Configure network policies

Traffic from the Cloudflare One Client to your infrastructure targets is filtered by both [Gateway network policies](/cloudflare-one/traffic-controls/get-started/network/) and the application-specific Access policies.

### Catch-all block policy

<Render file="tunnel/catch-all-policy" product="cloudflare-one" />

### Allow Access infrastructure targets

<Render
	file="access/modify-gateway-policy-precedence-infra"
	product="cloudflare-one"
	params={{ selector: "Access Infrastructure Target" }}
/>

## 7. Configure SSH server

Next, configure your SSH server to trust the Cloudflare SSH CA. This allows Access to authenticate using short-lived certificates instead of traditional SSH keys.

### Generate a Cloudflare SSH CA

<Render file="ssh/ssh-proxy-ca-note" product="cloudflare-one" />

To generate a Cloudflare SSH CA and get its public key:

<Render file="ssh/ssh-proxy-ca" product="cloudflare-one" />

### Save the public key

<Render file="ssh/public-key" product="cloudflare-one" />

### Modify your `sshd_config` file

<Render file="ssh/modify-sshd" product="cloudflare-one" />

:::note
For certain distributions, such as Amazon Linux 1 (based on RHEL), the certificate file permissions must be set to `600`. You can set file permissions with the following command:

```sh
chmod 600 /etc/ssh/ca.pub
```

:::

### Reload your SSH server

<Render file="ssh/restart-server" product="cloudflare-one" />

## 8. (Optional) Require independent MFA for SSH

You can require users to authenticate with a PIV key or FIDO2 key before connecting to SSH servers. When configuring custom MFA, select **PIV key**, **FIDO2 key**, or both.

To configure independent MFA for SSH, refer to [Enforce MFA for infrastructure applications](/cloudflare-one/access-controls/policies/mfa-requirements/#infrastructure-applications).

Before users can connect with MFA turned on, they must enroll their key and configure their SSH client.

- For PIV keys, [enroll the key](/cloudflare-one/access-controls/access-settings/independent-mfa/#enroll-a-piv-key-for-infrastructure-apps) and [configure the SSH client](/cloudflare-one/access-controls/access-settings/independent-mfa/#configure-your-ssh-client).
- For FIDO2 keys, [enroll the key](/cloudflare-one/access-controls/access-settings/independent-mfa/#enroll-a-fido2-key-for-infrastructure-apps) and [configure the SSH client](/cloudflare-one/access-controls/access-settings/independent-mfa/#configure-your-ssh-client-for-a-fido2-key).

When the user runs `ssh <username>@<target IP>`, the SSH proxy checks if MFA is required for the matching policy. If required, the user authenticates with their hardware key. Whether the key prompts for a touch, a PIN, or both depends on the PIV key policies you configured or on the FIDO2 key's own configuration. The proxy then completes the connection.

## 9. Connect as a user

Users can use any SSH client while logged in to the Cloudflare One Client. If the target uses a virtual network, [connect to that virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/#connect-to-a-virtual-network) first.

```sh
ssh <username>@<target IP>
```

Access for Infrastructure also supports `scp`, `sftp`, and `rsync` commands. Refer to [Known limitations](#known-limitations) for a list of unsupported SSH commands and features.

To learn more about user connections, refer to the [Access for Infrastructure documentation](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/#4-connect-as-a-user).

## SSH command logs

SSH command logs contain the actual SSH commands that a user ran on the target. Customers on all plans can store SSH logs on Cloudflare and download the logs from the dashboard. [Downloadable logs](#download-encrypted-ssh-logs) are encrypted using a public key provided by the customer and are not visible to Cloudflare. Delivery of downloadable SSH logs is best effort; for guaranteed delivery, Enterprise customers can [configure a Logpush job](#export-ssh-logs-with-logpush) to send SSH logs to storage destinations. Logpush payloads are not encrypted with a customer-provided public key.

### Download encrypted SSH logs

Follow these instructions to encrypt and download SSH command logs from Zero Trust.

#### Enable SSH command logging

<Render
	file="ssh/upload-ssh-key"
	params={{ note: "" }}
	product="cloudflare-one"
/>

#### Disable SSH command logging

To turn off SSH command logging, delete your uploaded public key:

<Tabs>
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings** > **SSH log encryption public key**.
2. Select **Remove**.
3. Select **Remove key** to confirm.

Cloudflare will stop logging SSH commands to your targets.

</TabItem>
<TabItem label="API">

To delete the SSH encryption public key using the [API](/api/resources/zero_trust/subresources/gateway/subresources/audit_ssh_settings/methods/update/):

<APIRequest
	path="/accounts/{account_id}/gateway/audit_ssh_settings"
	method="PUT"
	json={{
		public_key: "",
	}}
/>

</TabItem>
</Tabs>

#### View SSH logs

SSH command logs are not visible from the dashboard itself and must be exported and decrypted.

To manually retrieve logs:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Logs**.
2. Select **SSH command logs**.
3. Filter the logs using the name of your [SSH application](#5-add-an-infrastructure-application).
4. Select the SSH session for which you want to export command logs.
5. In the side panel, scroll down to **SSH logs** and select **Download**.
6. <Render file="ssh/decrypt-ssh-log" product="cloudflare-one" />

### Export SSH logs with Logpush

:::note[Availability]
Only available on Enterprise plans.
:::

Cloudflare allows you to send SSH command logs to storage destinations configured in [Logpush](/logs/logpush/), including third-party destinations. For a list of available data fields, refer to the [SSH logs dataset](/logs/logpush/logpush-job/datasets/account/ssh_logs/).

To set up the Logpush job, refer to [Logpush integration](/cloudflare-one/insights/logs/logpush/).

## Known limitations

### SSH features

The following SSH features are not supported:

- Local and remote port forwarding
- `ProxyJump`
- SSH agent forwarding
- X11 forwarding

### Session duration

Infrastructure applications enforce a configurable session duration, which defaults to 24 hours. When the session duration elapses, any active SSH connections through that application are closed and users must reconnect. When a target matches multiple infrastructure applications, the shortest session duration applies.

To configure the session duration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select an existing infrastructure application and choose **Configure**, or create a new infrastructure application.
3. Select a **Session duration** from the dropdown menu.
4. Save the application.

You can also set `session_duration` via the [API](/api/resources/zero_trust/subresources/access/subresources/applications/methods/create/) when creating or updating an infrastructure application. The value must be a non-negative duration in whole seconds (for example, `30m`, `8h`, or `2h45m`).

Separately, all connections proxied through Cloudflare Gateway have a maximum guaranteed duration of 10 hours due to periodic service releases. For more information, refer to [Troubleshoot Access](/cloudflare-one/access-controls/troubleshooting/#long-lived-ssh-sessions-disconnect).

## Troubleshooting

Failure to connect to your SSH endpoint could be the result of multiple variables. Use the following steps to investigate and resolve the source of your connection failure.

1. [Verify that your Access policies](#1-review-access-policies) allow the user to access the target.
2. [Check the user's device profile](#2-check-client-routing) routes the target through the Cloudflare One Client.
3. [Check Cloudflare Tunnel](#3-check-target-connection) health.
4. [Confirm user existence](#4-confirm-user-existence-on-the-server) on the server.
5. [Check your `sshd_config` file](#5-debug-sshd_config-file-misconfiguration) for misconfiguration.

### 1. Review Access policies

A user may be blocked by an Access policy from reaching your server because no explicit allow Access policy exists and Access is set to deny the user by default.

:::note[Access policies and infrastructure applications]

The Access infrastructure application (created in [step 5](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#5-add-an-infrastructure-application)) is the policy container for your SSH server. Cloudflare refers to your server that you connect to with SSH as a [target](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#4-add-a-target).

[Access policies](/cloudflare-one/access-controls/policies/policy-management/) are the rules attached to this Access infrastructure application, determining who can connect and what UNIX usernames they can log in as on the server. Cloudflare will not create new users on the target. UNIX users must already be present on the server.

You were guided to create an Access policy for your target in [substep 9 of step 5: Add an infrastructure application](#5-add-an-infrastructure-application).

:::

#### End users

As an end user, run [`warp-cli target list`](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/#display-available-targets) to verify that you have access to the target.

<Render file="tunnel/warp-cli-target-list" product="cloudflare-one" />

- If the target appears in the list, confirm that the username you are attempting to connect with is shown in the output. If the username is not shown, an administrator must find the Access policy associated with the target and add that username to the Access policy. An administrator should have created an Access policy in [substep 9 of step 5: Add an infrastructure application](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#5-add-an-infrastructure-application). If the username is shown, that means the Access policy should be granting access. Continue to [check client routing](#2-check-client-routing).

- If the target does not appear in the list, an administrator must audit the Access policies for the target in Cloudflare One for potential misconfiguration that may be blocking connection.

#### Administrators

As an admin, instead of running `warp-cli target list` on the end user device, you can use the Access logs to review if an Access policy is causing connection issues. Reviewing logs is useful when troubleshooting connection issues on behalf of the end user.

:::note

You will need Cloudflare dashboard access and log view [permissions](/cloudflare-one/roles-permissions/) to proceed with this step.

:::

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Logs**.

2. Select **Access authentication logs**.

3. Select the application you are testing or filter _Infrastructure_ as the App Type.

4. Review the **Decision**. If the **Decision** is `Access denied`, select the application and copy the name under App.

   If the decision is `Access granted`, continue to [check client routing](#2-check-client-routing).

5. Go to **Access controls** > **Applications**.

6. Input the app name in the search bar and select the application.

7. Select **Configure**.

8. Go to [**Policies**](/cloudflare-one/access-controls/policies/policy-management/#test-your-policies) to review what criteria may be blocking the user.

By adding an Access [policy](/cloudflare-one/access-controls/policies/) to allow the user, the connection issue should be resolved. After saving your policy changes, attempt to connect to the server.

If you are still having connection issues after auditing your Access policies, check the client routing configuration in the following step.

### 2. Check client routing

The target's IP address must route through the Cloudflare One Client. A device can receive a different Split Tunnel configuration than expected if it matches another device profile.

To check the active profile and its Split Tunnel configuration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**.
2. Find the user's device and note its **Last active device profile**.
3. Go to **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.
4. Edit the active profile and open **Split Tunnels**.
5. Confirm that the target's IP address or CIDR routes through the Cloudflare One Client. Refer to [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#change-split-tunnels-mode) for configuration instructions.
6. If the profile uses an `identity.email` selector, confirm that its email matches the identity allowed by the infrastructure application policy.

If the connection attempt does not appear in Access authentication logs, correct the client routing configuration before reviewing the tunnel or SSH server.

### 3. Check target connection

If the end user cannot connect to the target, the tunnel you set up in [step 1: Connect the server to Cloudflare](#1-connect-the-server-to-cloudflare) may be down or inactive.

To check the status of your tunnel:

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. Search your IP to find the route and its associated tunnel.

   This IP will be visible in the `warp-cli target list` output in [the previous step](#1-review-access-policies). If you are an admin, you can also go to **Networks** > **Targets** and find the IP next to your Hostname.

3. Select the tunnel name in the **Connector** column to open the tunnel detail page.

4. Review that the [Tunnel status](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/notifications/#available-notifications) says `Active`, and not `Down`, `Degraded`, or `Inactive`.

<Render file="tunnel/tunnel-status" product="cloudflare-one" />

For detailed steps on troubleshooting, refer to the [Troubleshooting Tunnel documentation](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/). Review the [Tunnel with Firewall documentation](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/#test-connectivity) to ensure your network is correctly configured to allow `cloudflared` connections.

After you have verified that there are no issues with your tunnel's health, confirm the user's existence on the server in the following step.

### 4. Confirm user existence on the server

To verify the existence of a user on a UNIX server, run the `id <USERNAME>` command on the server to verify that the username exists. If the username does not exist, you must add the user to the server.

If the user exists on the server, debug your `sshd_config` file in the following step.

### 5. Debug `sshd_config` file misconfiguration

One reason a user is failing to connect to your SSH endpoint might be the result of a misconfigured `sshd_config` file. Follow the steps below to audit your `sshd_config` file for misconfigurations.

#### Review your `sshd` logs

`sshd` logs can confirm whether or not the user is making it to the server. The location of your `sshd` logs is defined in your `sshd_config`. The logs location is likely at `journalctl -u ssh` on Ubuntu and `tail /var/log/auth.log` for Red Hat.

Using your `sshd` logs, validate that SSH connection attempts are arriving to the server.

#### Review your `sshd_config` file for misconfigurations

To rule out any issues in your `sshd_config` file, compare your existing `sshd_config` file with the example below to verify if any directives are causing authentication issues. The following example `sshd_config` file will result in successful authentication:

<details>
<summary>Example `sshd_config` file</summary>

```
# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

PubkeyAuthentication yes
TrustedUserCAKeys /etc/ssh/ca.pub

Include /etc/ssh/sshd_config.d/*.conf

# When systemd socket activation is used (the default), the socket
# configuration must be re-generated after changing Port, AddressFamily, or
# ListenAddress.
#
# For changes to take effect, run:
#
#   systemctl daemon-reload
#   systemctl restart ssh.socket
#
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
LogLevel DEBUG3

# Authentication:

#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10



# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile    .ssh/authorized_keys .ssh/authorized_keys2

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
KbdInteractiveAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the KbdInteractiveAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via KbdInteractiveAuthentication may bypass
# the setting of "PermitRootLogin yes
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and KbdInteractiveAuthentication to 'no'.
UsePAM yes

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

# no default banner path
#Banner none

# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

# override default of no subsystems
Subsystem    sftp    /usr/lib/openssh/sftp-server

# Example of overriding settings on a per-user basis
#Match User anoncvs
#    X11Forwarding no
#    AllowTcpForwarding no
#    PermitTTY no
#    ForceCommand cvs server
```

</details>

#### Confirm the account authorizes the certificate principal

If your `sshd` logs show that the certificate was accepted as CA-signed but the connection still fails, the account may not authorize the certificate's principal. `sshd` reports this as:

```txt
Certificate invalid: name is not a listed principal
```

Check the effective configuration for the account you are connecting as, because these directives are often set within a `Match` block:

```sh
sudo sshd -T -C user=<USERNAME> | grep -i principals
```

If `authorizedprincipalsfile` or `authorizedprincipalscommand` is set to any value other than `none`, confirm that the SSH username appears in that file or in the command's output. If it is missing, add it, validate your configuration with `sudo sshd -t`, then [reload](#reload-your-ssh-server) your SSH server.

#### Replace and test with example configuration

The next steps will walk you through a troubleshooting regimen. You will temporarily replace your existing `sshd_config` file with the provided example to rule out configuration issues. Before proceeding, carefully [review and compare both files](#review-your-sshd_config-file-for-misconfigurations) to identify any conflicting directives.

:::caution[You may lose access to your server]

These troubleshooting steps could result in you being locked out of your SSH server because your current SSH session may rely on existing configuration that is not in the [example file](#review-your-sshd_config-file-for-misconfigurations). Proceed with utmost caution.

:::

1. Back up the existing `sshd_config` file.

   ```sh
   mv /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
   ```

2. Create a new `sshd_config` file.

   ```sh
   vi /etc/ssh/sshd_config
   ```

3. Enter insert mode by pressing the `i` key on your keyboard.

4. Paste in the [example file](#review-your-sshd_config-file-for-misconfigurations).

5. Exit insert mode by pressing the escape (`esc`) key.
6. Enter `:x` to save and exit.
7. [Reload](#reload-your-ssh-server) your SSH server.

   :::caution[Do not restart]
   Restarting your `sshd` service will result in the termination of your current SSH connection. Make sure to reload instead of restarting to avoid terminating all currently open SSH sessions.
   :::

   <Render file="ssh/restart-server" product="cloudflare-one" />

After completing these troubleshooting steps, retry the connection. If issues persist, [recheck `sshd` logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#review-your-sshd-logs). The example [`sshd_config` shared above](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#review-your-sshd_config-file-for-misconfigurations) enables debug logging and may expose more specific issues.

### 6. Get help

For the fastest possible troubleshooting, ensure your support ticket includes comprehensive details. The more context you provide, the faster your issue can be identified and resolved.

To ensure efficient resolution when [contacting support](/support/contacting-cloudflare-support/), include as much relevant detail as possible in your ticket:

<ul>
	<li>
		<label>
			<input type="checkbox" /> Context: Briefly describe the scenario or use
			case (for example, where the user was, what they were trying to do).
		</label>
	</li>
	<li>
		<label>
			<input type="checkbox" /> Reproduction steps: Describe the steps you took
			to reproduce the issue during troubleshhooting.
		</label>
	</li>
	<li>
		<label>
			<input type="checkbox" /> Timestamps: Be specific and include the exact
			time and time zone when the issue occurred.
		</label>
	</li>
	<li>
		<label>
			<input type="checkbox" /> Troubleshooting attempts: Outline any
			troubleshooting steps or changes already attempted to resolve the issue.
		</label>
	</li>
	<li>
		<label>
			<input type="checkbox" /> <code>sshd</code> debug-level logs: Attach
			the <code>sshd</code> logs you collected in <a href="/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/#review-your-sshd-logs">step 4: Debug <code>sshd_config</code> file misconfiguration</a>.
		</label>
	</li>
	<li>
		<label>
			<input type="checkbox" /> <code>sshd_config</code> file: Include a
			copy of your server's <code>sshd_config</code> to help identify any
			misconfigurations or conflicting directives.
		</label>
	</li>
	<li>
		<label>
			<input type="checkbox" /> Client-side SSH output: Run the failing SSH
			command with verbose flags (<code>-vvv</code>) and include the full
			terminal output to show connection and authentication attempts from
			the client side.
		</label>
	</li>
</ul>

:::tip[Write a detailed ticket to resolve your issue faster]
Avoid vague descriptions and include scenario, timestamps, and steps taken to troubleshoot the issue. Refer to the following example:

<p>On October 30, 2025, at approximately 3:45 PM UTC, Alice attempted to SSH into 10.116.0.3 (target hostname: prod-db-01) using Access for Infrastructure. The SSH client returned <code>Permission denied (none)</code> despite her email being included in the Access policy.</p>

<p>The <code>sshd</code> logs (captured with LogLevel DEBUG3) are attached and show the connection reaching the server but failing at the certificate validation step. The user exists on the server (<code>id alice</code> verified).</p>

<p>The <code>sshd_config</code> file and <code>ssh -vvv alice@10.116.0.3</code> output are attached. The tunnel status is Healthy in the Cloudflare dashboard, and Access authentication logs show a successful <code>Access granted</code> decision.</p>
:::

# Get started

import { GlossaryTooltip } from "~/components";

Cloudflare WAN (formerly Magic WAN) allows you to achieve any-to-any connectivity across branch and retail sites and data centers, with the Cloudflare connectivity cloud.

If you are migrating from MPLS or a traditional WAN, refer to [WAN transformation](/cloudflare-one/networks/connectors/cloudflare-wan/wan-transformation/) to compare approaches and plan an incremental migration.

## Before you begin

Cloudflare WAN is an Enterprise-only product. [Contact Cloudflare](https://www.cloudflare.com/magic-wan/) to acquire Cloudflare WAN. If you plan on using Cloudflare One Appliance to automatically onboard your locations to Cloudflare, you will need to purchase Cloudflare WAN first.

## Set up method

Cloudflare WAN supports an automatic setup and a manual setup. The automatic setup through Cloudflare One Appliance is the preferred method.

### Automatic setup

Setting up Cloudflare WAN automatically is done through Cloudflare One Appliance, and is the preferred method. You can choose between the hardware version and the virtual version of Cloudflare One Appliance. The virtual version can be installed on your own machines.

If you plan on using Cloudflare One Appliance, you can skip the prerequisites below, and refer to [Configure with Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/) for more information on how to continue.

### Manual setup

Setting up Cloudflare WAN manually is done through a combination of third-party devices in your premises and the Cloudflare dashboard. To be successful, you need to:

1. Read the [Prerequisites](#prerequisites) below.
2. Follow the steps in [Manual configuration](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).

## Prerequisites

:::note
The list of prerequisites below is only for customers planning to connect manually to Cloudflare with a third-party device. If you plan on using Cloudflare One Appliance, skip this section and refer to [Configure with Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/).
:::

### Use compatible tunnel endpoint routers

Cloudflare WAN relies on [GRE](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/) and [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#ipsec-tunnels) to transmit [packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) from Cloudflare's global network to your origin network. To ensure compatibility with Cloudflare WAN, the routers at your tunnel endpoints must:

- Allow configuration of at least one tunnel per Internet service provider (ISP).
- Support <GlossaryTooltip term="maximum segment size (MSS)">maximum segment size (MSS)</GlossaryTooltip> clamping.
- Support the configuration parameters for IPsec mentioned in [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters).

### Set maximum segment size

Before enabling Cloudflare WAN, you must make sure that you set up the maximum segment size on your network. Cloudflare Cloudflare WAN uses tunnels to deliver [packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) from our global network to your data centers. Cloudflare encapsulates these packets adding new headers. You must account for the space consumed by these headers when configuring the maximum transmission unit (MTU) and maximum segment size (MSS) values for your network.

#### MSS clamping recommendations

##### GRE tunnels as off-ramp

The MSS value depends on how your network is set up.

- **On your edge router**: Apply the clamp to the GRE tunnel internal interface (meaning where the egress traffic will traverse). Set the MSS clamp to 1,436 bytes. Your devices may do this automatically once the tunnel is configured, but it depends on your devices.

##### IPsec tunnels

For IPsec tunnels, the value you need to specify depends on how your network is set up. The MSS clamping value is lower than for GRE tunnels because the physical interface sees IPsec-encrypted packets, not TCP packets, and MSS clamping does not apply to those.

- **On your edge router**: Apply this on your IPsec tunnel internal interface (meaning where the egress traffic will traverse). Your devices may do this automatically once the tunnel is configured, but it depends on your devices. Set the TCP MSS clamp to 1,360 bytes maximum.

:::caution[Important]
Refer to your device documentation to check if it sets IPsec MSS clamping automatically. If that is not the case and you are using IPsec inside GRE, you have to set MSS clamp manually.
:::

Refer to [Maximum transmission unit and maximum segment size](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/) for more details.

### Follow router vendor guidelines

Instructions to adjust MSS by applying MSS clamps vary depending on the vendor of your router.

The following table lists several commonly used router vendors with links to MSS clamping instructions:

| Router device | URL |
| ---					  | --- |
| Cisco         | [TCP IP Adjust MSS](https://www.cisco.com/en/US/docs/ios-xml/ios/ipapp/command/ip_tcp_adjust-mss_through_ip_wccp_web-cache_accelerated.html#GUID-68044D35-A53E-42C1-A7AB-9236333DA8C4) |
| Juniper | [TCP MSS - Edit System](https://www.juniper.net/documentation/en_US/junos/topics/reference/configuration-statement/tcp-mss-edit-system.html) |

# Cloudflare WAN

import {
	CardGrid,
	Description,
	Feature,
	GlossaryTooltip,
	LinkTitleCard,
	Plan,
	RelatedProduct,
	Stream,
} from "~/components";

<Description>
	Connect and secure your entire corporate network through Cloudflare, replacing
	MPLS circuits and hub-and-spoke routing with cloud-native networking.
</Description>

<Plan type="enterprise" />

Cloudflare WAN (formerly Magic WAN) connects your data centers, offices, and cloud resources through Cloudflare's global network. Instead of backhauling traffic through a central data center or maintaining dedicated MPLS circuits at every site, your traffic routes through the nearest Cloudflare data center where security policies apply inline.

Cloudflare WAN provides secure, performant [routing](https://www.cloudflare.com/learning/network-layer/what-is-routing/) for your entire corporate network. [Cloudflare Network Firewall](/cloudflare-network-firewall/) integrates with Cloudflare WAN, enabling you to enforce network firewall policies at Cloudflare's global network, across traffic from any entity within your network.

You connect your sites to Cloudflare through <GlossaryTooltip term="on-ramp">on-ramps</GlossaryTooltip> — tunnels or direct connections from your network to Cloudflare. Cloudflare WAN supports any device that uses <GlossaryTooltip term="anycast">anycast</GlossaryTooltip> <GlossaryTooltip term="GRE tunnel">GRE</GlossaryTooltip> or <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnels. Refer to [On-ramps](/cloudflare-one/networks/connectors/cloudflare-wan/on-ramps/) for a full list of supported on-ramps.

Refer to [WAN transformation](/cloudflare-one/networks/connectors/cloudflare-wan/wan-transformation/) to compare approaches and plan your migration, or go straight to [get started](/cloudflare-one/networks/connectors/cloudflare-wan/get-started/).

<Stream
	id="86f22d1f760b77cdc349f89b25b63c3e"
	title="SASE - Connect and secure from any network to anywhere"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/e71b5fcd-6de8-4ec5-28b2-4667c34c3900/public"
/>

---

## Features

<Feature
	header="Connect your network automatically"
	href="/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/"
	cta="Use Cloudflare One Appliance"
>
	Use Cloudflare One Appliance to automatically connect and steer any IP
	traffic.
</Feature>

<Feature
	header="Connect your network manually"
	href="/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/"
	cta="Use a third-party device"
>
	Set up Cloudflare WAN with your existing routers and firewalls. If you do
	not have Cloudflare One Appliance, start here to configure IPsec or GRE tunnels
	from a third-party device.
</Feature>

<Feature
	header="Zero Trust integration"
	href="/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/"
	cta="Integrate with other Zero Trust products"
>
	Learn how you can use Cloudflare WAN with other Cloudflare Zero Trust
	products.
</Feature>

<Feature header="BGP peering" href="/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#configure-bgp-routes">
	Use Border Gateway Protocol (BGP) peering between your networks and Cloudflare
	to automatically announce and withdraw routes as your network changes,
	rather than managing static routes manually.
</Feature>

<Feature
	header="WAN transformation"
	href="/cloudflare-one/networks/connectors/cloudflare-wan/wan-transformation/"
	cta="Plan your migration"
>
	Replace MPLS circuits and hub-and-spoke routing with cloud-native networking.
	Compare WAN approaches and plan an incremental migration.
</Feature>

<Feature
	header="Virtual networks"
	href="/cloudflare-one/networks/virtual-networks/"
	cta="Learn about virtual networks"
>
	Understand how virtual networks provide routing isolation within your
	Cloudflare account, keeping traffic separated between environments,
	partners, or applications.
</Feature>

---

## Related products

<RelatedProduct
	header="Cloudflare Network Firewall"
	href="/cloudflare-network-firewall/"
	product="cloudflare-network-firewall"
>
	Cloudflare Network Firewall is a firewall-as-a-service (FWaaS) that filters
	traffic at layers 3 and 4 across Cloudflare's global network. Included with
	Cloudflare WAN.
</RelatedProduct>

<RelatedProduct
	header="Cloudflare Network Interconnect"
	href="/network-interconnect/"
	product="network-interconnect"
>
	Cloudflare Network Interconnect (CNI) provides a private, dedicated connection
	between your network and Cloudflare instead of routing over the public
	Internet. Use CNI when you need lower latency or more consistent performance
	than tunnel-based connectivity.
</RelatedProduct>

<RelatedProduct
	header="Load Balancing"
	href="/load-balancing/"
	product="load-balancing"
>
	Cloudflare Load Balancing distributes traffic across your endpoints, which
	reduces endpoint strain and latency and improves the experience for end users.
</RelatedProduct>

---

## More resources

<CardGrid>

<LinkTitleCard title="Reference Architecture" href="/reference-architecture/architectures/sase/" icon="pen">

Explore the architecture of Cloudflare One as a SASE platform, including how Cloudflare WAN handles connectivity, routing, and security.

</LinkTitleCard>

</CardGrid>

# Load Balancing

import { Render } from "~/components";

<Render file="cloudflare-wan/load-balancing" product="networking-services" />

# Network Interconnect (CNI)

Cloudflare WAN (formerly Magic WAN) typically connects to Cloudflare through IPsec or GRE tunnels over the public Internet. Cloudflare Network Interconnect (CNI) is an alternative that provides a private, dedicated link — useful when you need lower latency, more consistent throughput, or want to avoid public Internet transit entirely.

Cloudflare Network Interconnect (CNI) provides a private, dedicated connection between your network and Cloudflare — bypassing the public Internet entirely. This is useful when you need consistent latency, higher throughput, or an additional layer of security that public Internet paths cannot guarantee.

With CNI, you get the same Cloudflare network services (firewall, routing, traffic management) applied to your traffic, but over a connection that does not traverse shared Internet infrastructure.

For more information about Network Interconnect, refer to the [Cloudflare Network Interconnect documentation](/network-interconnect/).

:::note[Run <code>traceroute</code>]
If you connect through [GRE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [CNI](/network-interconnect/), or [WARP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) and want to run `traceroute` to an endpoint behind a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), you need to change some settings.

Refer to [Run `traceroute`](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/traceroute/) for more information.
:::

# On-ramps

To on-ramp your network traffic to Cloudflare WAN (formerly Magic WAN), you can use [Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/), a lightweight software package you can install in corporate network locations to automatically connect and steer any IP traffic.

You can also use any device that supports [GRE or IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/) tunnels with the supported configuration parameters.

Additional compatible on-ramps include:

- [Cloudflare Network Interconnect (CNI)](/cloudflare-one/networks/connectors/cloudflare-wan/network-interconnect/): Connect your network infrastructure directly with Cloudflare - rather than using the public Internet - for a more reliable and secure experience.
- [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-tunnel/): Cloudflare WAN can be used together with Cloudflare Tunnel for easy access between your networks and applications.
- [WARP](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-one-client/): Protect corporate devices by securely and privately sending traffic from those devices to Cloudflare's global network, where Cloudflare Gateway can apply advanced web filtering.

- [Network on-ramp partnerships](https://www.cloudflare.com/network-onramp-partners/): Refer to our [third-party integration tutorials](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/) for guidance on configuring the most asked for third-party products.

# Security filters

Once your traffic flows through Cloudflare's network, you can apply security policies to it without deploying additional hardware. Cloudflare WAN (formerly Magic WAN) integrates with two primary security services, each operating at different layers of the network stack.

**[Cloudflare Network Firewall](/cloudflare-network-firewall/)** filters traffic at layers 3 and 4 of the [OSI model](https://www.cloudflare.com/learning/ddos/glossary/open-systems-interconnection-model-osi/) — the network and transport layers. You can allow or block traffic based on packet characteristics such as source and destination IP addresses, ports, protocols, and packet length. All Cloudflare WAN customers have [automatic access to Cloudflare Network Firewall](/cloudflare-network-firewall/plans/).

**[Cloudflare Gateway](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-gateway/)** inspects traffic at higher layers, including DNS queries, network sessions, and HTTP requests. Use Gateway to set up policies that control Internet-bound traffic and access to your private network infrastructure. Refer to [Connect to Cloudflare Gateway with Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-gateway/) to learn how to filter Cloudflare WAN traffic with Gateway policies.

# WAN transformation

import { GlossaryTooltip } from "~/components";

Traditional wide area networks (WANs) were designed for a world where applications ran in corporate data centers and employees worked from offices. These architectures rely on private circuits like Multiprotocol Label Switching (MPLS), hub-and-spoke routing through central data centers, and dedicated hardware at every branch.

As organizations adopt cloud services and support remote work, this model creates bottlenecks. Backhauling traffic to a central data center adds latency for cloud-bound traffic, and branch hardware requires ongoing maintenance and capital investment. WAN transformation replaces this architecture with cloud-native networking — routing traffic through a distributed global network instead of private circuits, and applying security inline rather than at a central chokepoint.

With Cloudflare One, your corporate WAN runs over Cloudflare's global network. You connect sites through <GlossaryTooltip term="anycast">anycast</GlossaryTooltip> <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> or <GlossaryTooltip term="GRE tunnel">GRE</GlossaryTooltip> tunnels, and Cloudflare handles routing, security inspection, and traffic optimization at the nearest point of presence.

## Why transform your WAN

### Reduce cost and rigidity

MPLS circuits require multi-year contracts and take weeks or months to provision. Adding a new site means ordering a new circuit. Cloudflare One uses standard Internet circuits with anycast tunnels — you can connect a new site in minutes using any Internet connection and any device that supports IPsec or GRE.

### Eliminate Internet breakout tradeoffs

With traditional WANs, you have two options for Internet-bound traffic: backhaul it to a central data center for security inspection (adding latency), or break out directly at the branch (bypassing security controls). Cloudflare One eliminates this tradeoff. Traffic from every site reaches the nearest Cloudflare data center, where security policies are applied without the backhaul penalty.

### Avoid vendor lock-in

Proprietary SD-WAN appliances create dependency on a single vendor's hardware and software ecosystem. Cloudflare One uses open standards — IPsec, GRE, and BGP — and works with your existing third-party routers and firewalls. You can also use the [Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/) for zero-touch provisioning at branch sites.

### Simplify operations

On-premises network and security appliances require manual firmware updates, patching, and capacity planning at every location. With Cloudflare One, networking and security services run in the cloud. Cloudflare manages updates and scaling globally, reducing the operational burden on your team.

## Compare WAN approaches

| | Traditional WAN (MPLS) | SD-WAN | Cloudflare One |
|---|---|---|---|
| **Performance** | Predictable but limited to circuit capacity. High latency for cloud-bound traffic due to backhauling. | Improved path selection across multiple links. Still relies on branch appliances for processing. | Traffic routed to the nearest Cloudflare data center. Cloud-bound traffic egresses locally without backhauling. |
| **Cost model** | High fixed costs. Multi-year contracts for private circuits. Per-site hardware investment. | Lower circuit costs (uses Internet links). Per-site appliance licensing and hardware costs remain. | Internet circuit costs only. No per-site hardware required (optional). Pay-as-you-grow model. |
| **Agility** | Weeks to months to provision new circuits. Rigid topology changes. | Faster site deployment over Internet circuits. Still requires appliance staging and configuration. | Connect a new site in minutes. Tunnels auto-establish from any Internet connection. |
| **Security** | Security applied at central data center or per-site firewalls. | Varies by vendor. Some offer integrated security, others require separate appliances. | Integrated security at every data center — firewall, secure web gateway, and Zero Trust policies applied inline. |
| **Management** | Separate management for WAN circuits, routers, and security appliances. | Single console for WAN, but security often managed separately. | Single dashboard for network connectivity, routing, firewall rules, and security policies. |

## Plan your migration

WAN transformation is not an all-or-nothing change. Most organizations follow an incremental approach, adding capabilities over time while decommissioning legacy infrastructure as each phase proves out.

### 1. Secure user access

Start by replacing VPN concentrators with Zero Trust Network Access (ZTNA). Deploy the Cloudflare One Client on user devices and use Cloudflare Access to enforce identity-based policies for application access. This step secures remote and hybrid workers without changing your existing network infrastructure.

For more information, refer to [Cloudflare One](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/).

### 2. Connect your networks

Set up site-to-site connectivity by establishing IPsec or GRE tunnels from your existing routers, deploying the Cloudflare One Appliance at branch locations, or using Cloudflare Network Interconnect for private connectivity. Your sites communicate through Cloudflare's network, and you manage routing through the dashboard or API.

- [Get started](/cloudflare-one/networks/connectors/cloudflare-wan/get-started/) with Cloudflare WAN
- Review [connectivity options](/cloudflare-one/networks/connectivity-options/) to choose the right on-ramp
- Explore all available [on-ramps](/cloudflare-one/networks/connectors/cloudflare-wan/on-ramps/)

### 3. Secure Internet egress

Enable Cloudflare Gateway to apply secure web gateway (SWG) policies to Internet-bound traffic from your sites. Add Cloudflare Network Firewall rules to enforce packet-level filtering. Traffic from every site is inspected at the nearest Cloudflare data center — no backhaul required.

For a complete overview of which security services apply to WAN traffic, refer to [Secure WAN traffic](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/security-services/). For configuration details, refer to [Cloudflare Gateway](/cloudflare-one/traffic-controls/) and [Cloudflare Network Firewall](/cloudflare-network-firewall/).

### 4. Reduce infrastructure

As Cloudflare handles routing and security in the cloud, you can begin decommissioning branch firewalls, VPN concentrators, and MPLS circuits. The end state is what some call "coffee shop networking" — every location, whether a corporate office, a home office, or a coffee shop, provides the same secure, performant experience. The network is managed centrally through Cloudflare, and local infrastructure is minimal.

Organizations that start with Cloudflare WAN for site-to-site connectivity and packet-level security can follow this same incremental path. Cloudflare One builds on the same network infrastructure, so you can add identity-based access controls, secure web gateway policies, and user-level security as your requirements grow — without re-architecting your deployment.

---

## Next steps

- [Get started](/cloudflare-one/networks/connectors/cloudflare-wan/get-started/): Set up Cloudflare WAN with the Cloudflare One Appliance or a third-party device.
- [Connectivity options](/cloudflare-one/networks/connectivity-options/): Compare all Cloudflare One connectivity options and choose the right combination for your deployment.
- [On-ramps](/cloudflare-one/networks/connectors/cloudflare-wan/on-ramps/): Review the full list of supported on-ramps for connecting your networks.
- [SASE reference architecture](/reference-architecture/architectures/sase/): Explore the architecture of Cloudflare One as a SASE platform.

# Analytics

import { AnchorHeading } from "~/components";

Use Cloudflare WAN (formerly Magic WAN) analytics to monitor site performance and troubleshoot issues.

Use these options to gather information at the start of your troubleshooting workflow. Then, use more detailed network data collection and analysis to identify the root cause.

- View your entire network at a glance in [Network overview](#network-overview)
- Analyze network traffic over time in [Network Analytics](#network-analytics)
- Perform more detailed troubleshooting with:
  - [Traceroutes](#traceroutes)
  - [Packet captures](#packet-captures)

<AnchorHeading title="Network overview" depth={2} slug="network-overview" />

Network overview shows the connectivity status and traffic analytics for all Cloudflare WAN sites. Use it when you receive an alert, start troubleshooting, or perform routine monitoring.

For details, refer to [Network health](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/site-analytics/).

<AnchorHeading depth={2} title="Network Analytics" />

Network Analytics provides detailed analytics on your Cloudflare WAN traffic over time. You can filter data by traffic characteristics and review traffic trends over time.

For details, refer to [Cloudflare WAN Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/).

## Traceroutes

Traceroutes provide a hop-by-hop breakdown of the Internet path network traffic follows from Cloudflare's network to your network.

For details, refer to [Traceroutes](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/traceroutes/).

## Packet captures

Packet captures allow you to analyze the raw packet data your network sends to and receives from Cloudflare's network.

For details, refer to [packet captures](/cloudflare-network-firewall/packet-captures/).

## Query analytics with GraphQL

GraphQL Analytics provides a GraphQL API to query raw JSON data for your Cloudflare WAN traffic analytics. You can ingest this data into a Security Information and Event Management (SIEM) tool or another platform for further analysis.

- [Querying Cloudflare WAN tunnel bandwidth analytics with GraphQL](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/query-bandwidth/)
- [Querying Cloudflare WAN tunnel health check results with GraphQL](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/query-tunnel-health/)

# NetFlow statistics

import { CURL } from "~/components";

## NetFlow exports from Cloudflare One Appliance to Network Flow

You can configure your Cloudflare One Appliance (formerly Magic WAN Connector) to export Netflow statistics for [local breakout traffic](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/application-based-policies/breakout-traffic/) to [Network Flow](/network-flow) (formerly Magic Network Monitoring). This provides insights into traffic that leaves your site directly, bypassing the Cloudflare network.

The Cloudflare One Appliance uses NetFlow v9 to export flow data for breakout traffic only. You can enable and configure this export by setting the Netflow configuration for the associated site via the Cloudflare API.

### Enable NetFlow exports

:::note
To export NetFlow statistics, you will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/), as well as the `site_id` associated with your Cloudflare One Appliance.
:::

1. Send a `PUT` request to the Netflow configuration endpoint for your site.
2. In the JSON body request, you must include the `collector_ip` parameter. To export traffic statistics to Network Flow, use the IP address `162.159.65.1`. This is the only field required to enable the feature.

Minimal configuration example:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/magic/sites/$SITE_ID/netflow_config"
	method="PUT"
	json={{
		"collector_ip": "162.159.65.1"
	}}
/>

3. You can customize the configuration by adding optional fields to the JSON payload. These fields include:
- `collector_port`: The UDP port for the collector. The default is `2055`.
- `sampling_rate`: The rate at which packets are sampled.
- `active_timeout`: The timeout for active flows in seconds.
- `inactive_timeout`: The timeout for inactive flows in seconds.

Full configuration example:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/magic/sites/$SITE_ID/netflow_config"
	method="PUT"
	json={{
		"collector_ip": "162.159.65.1",
		"collector_port": 2055,
		"sampling_rate": 100,
		"active_timeout": 60,
		"inactive_timeout": 30
	}}
/>

Your Cloudflare One Appliance will now begin exporting Netflow data for its breakout traffic, which will be ingested and displayed within your Network Flow dashboard. You can retrieve the current settings by sending a `GET` request, or disable the export by sending a `DELETE` request to the same endpoint.

# Network analytics

import { GlossaryTooltip, DashButton } from "~/components";

You can access real-time and historical network data in Network Analytics. Explore Cloudflare WAN traffic (in packets or bytes) over time in a time series, and filter the data by different [packet](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) characteristics.

Data is aggregated into time intervals that vary based on the selected zoom level. For example, a daily view shows 24-hour averages, which can flatten short-term traffic spikes. As a result, longer time intervals display lower peak bandwidth values compared to more granular views like five-minute intervals.

For details, refer to the [Network Analytics](/analytics/network-analytics/) documentation.

## Network traffic data filters

With Cloudflare WAN, you have increased insight into traffic flows across Cloudflare One products, including:

- Traffic entering Cloudflare's network via the Cloudflare One Client
- Traffic leaving Cloudflare's network via the Cloudflare One Client
- Traffic leaving Cloudflare's network via Cloudflare Tunnel (`cloudflared`)

The complete list of filters includes:

- A list of your top tunnels by traffic volume.
- Traffic source and destination by traffic type, on-ramps and off-ramps, <GlossaryTooltip term="IP address">IP addresses</GlossaryTooltip>, and ports.
- Destination IP ranges and ASNs.
- Protocols and packet sizes.
- Samples of all GRE or IPsec tunnel traffic entering or leaving Cloudflare's network.
- Mitigations applied (such as DDoS and Cloudflare Network Firewall) to traffic entering Cloudflare's network.

For instructions, refer to [Access tunnel traffic analytics](#access-tunnel-traffic-analytics).

## Access tunnel traffic analytics

1. Go to the **Network Analytics** page.

<DashButton url="/?to=/:account/networking-insights/analytics/network-analytics/transport-analytics" />

2. In the **All Traffic** tab, scroll to **Top Insights** to access network traffic filters. By default, the dashboard displays five items, but you can display up to 25 items at once. To change the number of items, select the drop-down menu.
3. (Optional) Hover over a traffic type. You can then filter for that traffic or exclude it from the results.
4. To adjust the scope of information, scroll to **All traffic** > **Add filter**.
5. In the **New filter** popover, select the data type from the left drop-down menu, an operator from the middle drop-down menu, and an action from the right drop-down menu. For example:

   ```txt
   <DESTINATION_TUNNELS> | _equals_ | <NAME_OF_YOUR_TUNNEL>
   ```

   This lets you examine traffic from specific Source tunnels and/or Destination tunnels.

## Feature notes

- For Cloudflare WAN, `Non-Tunnel traffic` refers to traffic outside GRE or IPsec tunnels. This can include traffic from:
  - [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/)
  - [CNIs](/network-interconnect/)
  - Traffic destined for the public Internet via [Gateway](/cloudflare-one/traffic-controls/)
  - Traffic destined for applications behind [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/)

The label `Non-Tunnel traffic` is a placeholder, and Cloudflare will apply more specific labels to this category of traffic in the future.

# Querying Cloudflare WAN IPsec/GRE tunnel bandwidth analytics with GraphQL

This example uses the GraphQL Analytics API to query Cloudflare WAN ingress tunnel traffic over a specified time period.

The following API call requests Cloudflare WAN ingress tunnel traffic over a one-hour period and outputs the requested fields. Replace `<CLOUDFLARE_ACCOUNT_TAG>` with your account ID, `<EMAIL>`, `<API_KEY>`[^1] (legacy), or `<API_TOKEN>`[^2] (preferred) with your API credentials, and adjust the `datetime_geq` and `datetime_leq` values as needed.

The example queries for ingress traffic. To query for egress traffic, change the value in the `direction` filter.

## API Call

```bash
PAYLOAD='{ "query":
  "query GetTunnelHealthCheckResults($accountTag: string, $datetimeStart: string, $datetimeEnd: string) {
      viewer {
        accounts(filter: {accountTag: $accountTag}) {
          magicTransitTunnelTrafficAdaptiveGroups(
            limit: 100,
            filter: {
              datetime_geq: $datetimeStart,
              datetime_lt:  $datetimeEnd,
              direction: $direction
            }
          ) {
            avg {
              bitRateFiveMinutes
            }
            dimensions {
              tunnelName
              datetimeFiveMinutes
            }
          }
        }
      }
  }",
    "variables": {
      "accountTag": "<CLOUDFLARE_ACCOUNT_TAG>",
      "direction": "ingress",
      "datetimeStart": "2022-05-04T11:00:00.000Z",
      "datetimeEnd": "2022-05-04T12:00:00.000Z"
    }
  }
}'

# curl with Legacy API Key
curl https://api.cloudflare.com/client/v4/graphql \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data "$(echo $PAYLOAD)"

# curl with API Token
curl https://api.cloudflare.com/client/v4/graphql \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data "$(echo $PAYLOAD)"
```

The returned values represent the total bandwidth in bits per second during the five-minute interval for a particular tunnel. To use aggregations other than five minutes, use the same time window for both your metric and datetime. For example, to analyze hourly groups, use `bitRateHour` and `datetimeHour`.

The result is in JSON (as requested), so piping the output to `jq` formats it for easier parsing, as in the following example:

```bash
curl https://api.cloudflare.com/client/v4/graphql \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data "$(echo $PAYLOAD)" | jq .

## Example response:
#=> {
#=>   "data": {
#=>     "viewer": {
#=>       "accounts": [
#=>         {
#=>           "magicTransitTunnelTrafficAdaptiveGroups": [
#=>             {
#=>               avg: { bitRateFiveMinutes:  327680 },
#=>               dimensions: {
#=>                 datetimeFiveMinute: '2021-05-12T22:00-00:00',
#=>                 tunnelName: 'tunnel_name'
#=>               }
#=>             },
#=>             {
#=>               avg: { bitRateFiveMinutes:  627213680 },
#=>               dimensions: {
#=>                 datetimeFiveMinute: '2021-05-12T22:05-00:00',
#=>                 tunnelName: 'another_tunnel'
#=>              }
#=>             }
#=>           ]
#=>         }
#=>       ]
#=>     }
#=>   },
#=>   "errors": null
#=> }
```

[^1]: For details, refer to [Authenticate with a Cloudflare API key](/analytics/graphql-api/getting-started/authentication/api-key-auth/).
[^2]: For details, refer to [Configure an Analytics API token](/analytics/graphql-api/getting-started/authentication/api-token-auth/).

# Querying Cloudflare WAN IPsec/GRE tunnel health check results with GraphQL

This example uses the GraphQL Analytics API to query Cloudflare WAN tunnel health check results. These results are aggregated from individual health checks that Cloudflare servers perform against the tunnels you configured in your account. You can query up to one week of data for dates up to three months in the past.

The following API call requests tunnel health checks for a specific account over a one-day period for a specific Cloudflare data center and outputs the requested fields. Replace `<CLOUDFLARE_ACCOUNT_TAG>` and `<API_TOKEN>`[^1] with your API credentials, and adjust the `datetimeStart` and `datetimeEnd` variables as needed.

The API call returns tunnel health check results by Cloudflare data center. Cloudflare aggregates each data center's result from health checks conducted on individual servers. The `tunnelState` field represents the state of the tunnel. Cloudflare WAN uses these states for routing. A `tunnelState` value of `0` represents a down tunnel, `0.5` represents a degraded tunnel, and `1` represents a healthy tunnel.

## API Call

```bash
echo '{ "query":
  "query GetTunnelHealthCheckResults($accountTag: string, $datetimeStart: string, $datetimeEnd: string) {
    viewer {
      accounts(filter: {accountTag: $accountTag}) {
        magicTransitTunnelHealthChecksAdaptiveGroups(
          limit: 100,
          filter: {
            datetime_geq: $datetimeStart,
            datetime_lt:  $datetimeEnd,
          }
        ) {
          avg {
            tunnelState
          }
          dimensions {
            tunnelName
            edgeColoName
          }
        }
      }
    }
  }",
  "variables": {
    "accountTag": "<CLOUDFLARE_ACCOUNT_TAG>",
    "datetimeStart": "2022-08-04T00:00:00.000Z",
    "datetimeEnd": "2022-08-04T01:00:00.000Z"
  }
}' | tr -d '\n' | curl --silent \
https://api.cloudflare.com/client/v4/graphql \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data @-
```

The results are returned in JSON (as requested), so piping the output to `jq` formats them for easier parsing, as in the following example:

```bash
... | curl --silent \
https://api.cloudflare.com/client/v4/graphql \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data @- | jq .

## Example response:
#=> {
#=>   "data": {
#=>     "viewer": {
#=>       "accounts": [
#=>         {
#=>           "conduitEdgeTunnelHealthChecks": [
#=>             {
#=>               {
#=>                 "avg": {
#=>                   "tunnelState": 1
#=>                 },
#=>                 "dimensions": {
#=>                   "edgeColoName": "mel01",
#=>                   "tunnelName": "tunnel_01",
#=>                   "tunnelState": 0.5
#=>                 }
#=>               },
#=>               {
#=>                 "avg": {
#=>                   "tunnelState": 0.5
#=>                 },
#=>                 "count": 310,
#=>                 "dimensions": {
#=>                   "edgeColoName": "mel01",
#=>                   "tunnelName": "tunnel_02",
#=>                   "tunnelState": 0.5
#=>                 }
#=>               }
#=>           ]
#=>         }
#=>       ]
#=>     }
#=>   },
#=>   "errors": null
#=> }
```

[^1]: For details, refer to [Configure an Analytics API token](/analytics/graphql-api/getting-started/authentication/api-token-auth/).

# Network visibility

import { Details } from "~/components"

After adding your sites, the Network visibility section of the dashboard provides a summary of the connectivity status and traffic analytics for all your sites. This is a great place to start if you receive a Cloudflare WAN alert, need to begin the troubleshooting process, or are performing routine monitoring. Refer to [Set up a site](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/sites/) for more information on how to set up a site.

Network visibility has the following data types available:

<Details header="Geographic map summary">
	- [Aggregate Cloudflare WAN site health](#site-health)
	- [Cloudflare WAN availability status for sites](#no-status-available)
	- [Cloudflare WAN site geographic location](#no-location-set)
</Details>

<Details header="Cloudflare WAN site data table">
	- Site Name
	- Site Health
	- Site Tunnel Names
	- Site Tunnel Statuses
	- Site Traffic Sent
	- Site Traffic Received
</Details>

<Details header="Cloudflare WAN site data">
	- Traffic Sent by Tunnel
	- Traffic Received by Tunnel
</Details>

To start using network overview:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Go to **Insights** > **Network visibility**.

You will have access to an overview map with all your active sites, and any alerts for sites that are unhealthy or have no status available to them.

Review the following topics to learn more about the options available to you.

### Network map and traffic overview

The network map section shows all the sites configured with Cloudflare WAN. At a glance, you can check:

- How many active sites you have
- Location for sites in a map (if you set up their geographic location)
- Sites that are healthy or unhealthy
- Sites that have no status available
- Sites that have no location set

The Traffic overview section displays a more granular list of your sites and their status.

#### Site health

Sites can be healthy or unhealthy, and Cloudflare WAN uses this information to route traffic. Refer to [Set thresholds for site health](#set-thresholds-for-site-health) to learn more about this topic.

#### No status available

The status of a site refers to its health. If your sites show a **No status available** message, this means you did not configure your alert settings when creating your site. For instructions, refer to [Configure Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/configure-tunnel-health-alerts/).

#### No location set

The dashboard displays the number of sites with no location set, meaning sites for which you did not set up a geographic location. To add a location to a site, find the site you want to add location to, and select **no location set** to edit its location settings. Refer to [Set geographic coordinates](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/sites/#set-geographic-coordinates) for more information.


### Traffic overview

Traffic overview aggregates all Cloudflare WAN sites configured in your account. Here, you can check summary information about each site like:

- Site status
- Traffic sent and received

Select one of your sites to have access to a more detailed view of its traffic, including traffic by tunnel.

### Set thresholds for site health

When you set up an alert for your site, you will be notified when there is an issue with one or more on-ramps. These alerts are sent when the percentage of successful health checks for a Cloudflare WAN on-ramp drops below the selected service-level objective (SLO). Setting health alerts will also display unhealthy tunnels in the Network map and in the Traffic overview sections.

To set up health alerts:

1. Configure [Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/configure-tunnel-health-alerts/) across all of the tunnels associated with each Cloudflare WAN site.
2. After configuring Tunnel health alerts, any Cloudflare WAN site with a tunnel (on-ramp) that is outside of its SLO threshold will be labeled unhealthy in Network map and Traffic overview.

# Traceroutes

You can run traceroutes to analyze the hop-by-hop Internet path and latency between Cloudflare's network and your network.

To run a traceroute from a specific Cloudflare data center to your network:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Insights**.
2. Go to **Network health** > **WAN connector health**.
3. Find the tunnel for the traceroute.
4. Select the three dots > **Traceroute details**.

You can access detailed data from the traceroute, including:

- Time to live (TTL) and host
- Autonomous system (AS) number
- [Packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) sent in the traceroute
- Average, minimum, and maximum latency
- Standard deviation of latency

# Configuration

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Configure Virtual Appliance

import {
	Aside,
	Tabs,
	TabItem,
	Card,
	Code,
	GlossaryTooltip,
} from "~/components";

Virtual Appliance is a virtual device alternative to the hardware based Cloudflare One Appliance. These two versions of Cloudflare One Appliance are identical otherwise.

Currently, you can set up Virtual Appliance on VMware ESXi and Proxmox Virtual Environment. Support for Proxmox is in beta.

In this page you will find instructions on how to configure Cloudflare One Appliance. This guide provides a step-by-step guide for Cloudflare One Appliance initial setup. You can either return here after setting up your Cloudflare One Appliance, or refer to the [Maintenance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/) section where you will find instructions on how to update your settings.

## Prerequisites

Before you can install Virtual Appliance, you need an Enterprise account with Cloudflare WAN. Additionally, you need to have a VMware or Proxmox host with sufficient compute, memory, and storage to run the virtual machine with Virtual Appliance. This includes:

- Intel x86 CPU architecture
- ESXi hypervisor 7.0U1 or higher
- 4 virtual CPUs per virtual appliance (We recommend deployment with a 1:1 virtual CPU to physical core allocation to avoid CPU over contention which will cause packet loss.)
- 8 GB of RAM per virtual appliance
- 8 GB of disk per virtual appliance
- One vSwitch port group or VLAN with access to the Internet (for example, through a WAN)
- One or more vSwitch port group or VLAN that will be the internal LAN

For details on installing ESXi and configuring a virtual machine, refer to [VMware's documentation](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.esxi.install.doc/GUID-B2F01BF5-078A-4C7E-B505-5DFFED0B8C38.html).

For details on installing Virtual environment and configuring a virtual machine, refer to [Proxmox documentation](https://www.proxmox.com/en/products/proxmox-virtual-environment/get-started).

---

## Before you begin

There are a couple of decisions you need to make when installing your Virtual Appliance. Review the following topics for more information.

### Determine the need for a high availability configuration

You can install up to two instances of Virtual Appliance for redundancy at each of your sites. If one of your devices fails, traffic will fail over to the other, ensuring that you never lose connectivity to that site.

In this type of high availability (HA) configuration, you will choose a reliable LAN interface as the HA link which will be used to monitor the health of the peer connector. HA links can be dedicated links or can be shared with other LAN traffic.

You must decide the type of configuration you want for your site from the beginning: no redundancy or with redundancy. You cannot add redundancy after finishing the configuration of your dashboard settings. If, at a later stage, you decide to enable redundancy, you will need to delete your Virtual Appliance device in the Cloudflare dashboard, and start again.

<Card title="Do you need a high availability configuration?" icon="information">

- If you need a high availability configuration for your premises, refer to
  [About high availability configurations](#about-high-availability-configurations) for details
  and learn how to configure your Virtual Appliance device in this mode.
- If you do not need a high availability configuration for you premises, check if you need a [DHCP or a static IP setup](#decide-on-dhcp-vs-static-ip-connections) before proceeding to [Set up Cloudflare dashboard](#set-up-cloudflare-dashboard).

</Card>

:::caution
You cannot enable high availability for an existing Virtual Appliance on-ramp. To add high availability to an existing Virtual Appliance on-ramp in the Cloudflare dashboard, you need to delete the on-ramp and start again. Plan accordingly to create a high availability configuration from the start if needed.
:::

### Decide on DHCP vs static IP connections

Virtual Appliance uses a DHCP connection at first boot to download your settings and go through the activation process. However, if you need to use a static IP in your Virtual Appliance, and this is a fresh install:

1. Connect the machine with your Virtual Appliance VM to a DHCP port with access to the Internet.
2. Follow the [setup flow](#set-up-cloudflare-dashboard) and activate your Virtual Appliance device.
3. Refer to [WAN with a static IP address](#wan-with-a-static-ip-address).

---

## Configure a virtual machine

Select the appropriate tab to configure Virtual Appliance on VMware ESXi or Proxmox Virtual Environment.

<Tabs> <TabItem label="VMware ESXi">

**1. Obtain the VMware image**

Download the Virtual Appliance OVA image from [https://assets.magic-wan-connector.cloudflare.com/stable/mconn.ova](https://assets.magic-wan-connector.cloudflare.com/stable/mconn.ova). You can also download it from the dashboard when you register a virtual appliance, in the **Add an appliance** > **Virtual appliance** step. The OVA image includes the files required to install and configure the virtual machine (VM) for Virtual Appliance with the appropriate settings. For details, refer to [VMware VMs documentation](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-AE61948B-C2EE-436E-BAFB-3C7209088552.html).

This image can be deployed multiple times to create several instances of a Virtual Appliance, in different locations or on the same ESXi host.

You consume one license key for each instance created. Generate a license key for each instance from the [Connectors page](/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#register-a-virtual-appliance-and-generate-a-license-key). For example, to deploy 10 Virtual Appliances, generate 10 license keys.

**2. Deploy the Virtual Appliance on VMware**

The following instructions assume you already have VMware ESXi hypervisor installed with sufficient resources. For details, refer to [Prerequisites](#prerequisites).

1. When setting up your VMware ESXi, you need to create port groups for Virtual Appliance. Go to **Networking** > **Port groups**, and prepare your vSwitch port groups and/or VLANs for your desired network topology. For example, a simple deployment typically has:
	- A WAN port group where the Virtual Appliance will get an IP address (static or DHCP) that has access to the Internet.
	- A LAN port group, where the Virtual Appliance will act as default router, and possibly DHCP server.
	- A null, or unused, port group for allocating unused virtual interfaces in the Virtual Appliance. You can, for example, create a null port group with the name of <code>Null port group</code>, and a **VLAN ID** of <code>999</code>.

:::note[VLAN tagging]
Virtual Appliance supports creating subinterfaces through the use of [802.1Q VLAN tagging](https://en.wikipedia.org/wiki/IEEE_802.1Q).

Use VLAN ID <code>0</code> when:

- Connected to a Port Group or Distributed Port Group that is associated with a specific VLAN.
- Connected to a Port Group or Distributed Port Group that is configured as a trunk that requires untagged packets.

You can also configure subinterfaces on the Virtual Appliance by associating the network interface with a Port Group or Distributed Port Group trunk and specifying a VLAN ID in addition to the port associated with the network interface (VLAN ID <code>1</code>-<code>4094</code>).

Refer to [VMware's documentation](https://kb.vmware.com/s/article/1003825) for more information.
:::

2. Extract the files in the OVA image you downloaded. For example:

<Code lang="sh" code="tar -xvf mconn.ova" />

Take note of the folder where you are extracting the files to, as you will need to refer to that folder when creating the VM.

3. Go to **Virtual Machines** > **Create/Register VM** wizard to start deploying the Virtual Appliance.

4. Select **Deploy a virtual machine from an OVF or OVA file** > **Next**.

5. Choose a descriptive name for your virtual machine.

6. Upload the files you have extracted from the OVA image. These include <code>mconn.ovf</code>, <code>mconn.nvram</code>, and <code>mconn.vmdk</code>.

7. Select where you want to save the files extracted from the OVA image > **Next**.

8. In **Networking mappings**, select assignments for your desired topology according to the port groups you set up previously:
	1. For example, map <code>eno1</code> port to <code>VM Network</code> to create your WAN, and <code>eno2</code> to <code>LAN0</code> to act as your LAN port.
	2. Allocate any unused ports to the <code>null</code> port group.
	3. Take note of your configuration. You will need this information to configure your network in the Cloudflare dashboard.

9. In **Disk provisioning**, select **Thin**.

10. Before completing the deployment wizard, disable **Power on automatically**. This is important so that you can configure the license key prior to boot.

11. Configure the virtual machine with the license key you generated:
	1. Select the Virtual Appliance's VM > **Settings**.
	2. Go to **VM Options** > **Advanced** > **Edit Configuration**.
	3. Select **Add parameter** to add your license key. Scroll down to the last entry (this is where VMware adds the new parameter), and add the following two new entries:
		- **Key**: <code>guestinfo.cloudflare.identity</code>
		- **Value** `<YOUR_LICENSE_KEY>`

:::note
You cannot use the same license key twice, or reuse a key once the virtual machine has been registered with Cloudflare. Generate a new key for every new Virtual Appliance from the [Connectors page](/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#register-a-virtual-appliance-and-generate-a-license-key).
:::

12. Select **Save** to finish configuring your Virtual Appliance.
13. Continue setup in your <a href="#set-up-cloudflare-dashboard">Cloudflare dashboard.</a>

</TabItem> <TabItem label="Proxmox Virtual Environment (beta)">

**1. Obtain the Virtual Appliance script**

Download the Virtual Appliance helper script for Proxmox to your Proxmox server. The script sets up and configures a Proxmox virtual machine with the appropriate settings for Virtual Appliance. For details on system requirements, refer to [Prerequisites](#prerequisites).

The script can be deployed multiple times to create several instances of a Virtual Appliance, in different locations or on the same Proxmox host. You consume one license key for each instance created. Generate a license key for each instance from the [Connectors page](/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#register-a-virtual-appliance-and-generate-a-license-key). For example, to deploy 10 Virtual Appliances, generate 10 license keys.

<Code lang="sh" code="curl -OL https://assets.magic-wan-connector.cloudflare.com/stable/proxmox_mconn_vm.sh" />

**2. Deploy the Virtual Appliance on Proxmox**

The following instructions assume you already have Proxmox Virtual Environment installed with sufficient resources. For details, refer to [Prerequisites](#prerequisites).

1. In the terminal prompt of your Proxmox server, run the script with elevated privileges:

<Code lang="sh" code="bash ./proxmox_mconn_vm.sh" />

2. You will be prompted to create a new Virtual Appliance. Select **yes** to proceed.
3. Enter your license key.

:::note
You cannot use the same license key twice, or reuse a key once the virtual machine has been registered with Cloudflare. Generate a new key for every new Virtual Appliance from the [Connectors page](/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#register-a-virtual-appliance-and-generate-a-license-key).
:::

The script applies default settings and configures the virtual machine for Virtual Appliance. After the script finishes, adjust the VM for your environment. The new VM has all network interface cards (NICs) on the same bridge, and only the first NIC has an active link.

4. In the **Hardware settings** for the new VM, select the NIC you want to use with Virtual Appliance.
5. Select the network bridge that corresponds to the physical NIC on your host machine. This bridge allows the network adapter in the virtual machine to communicate through the NIC in the host, as if it were directly connected to the physical network.
6. (Optional) Configure your VLAN setting if needed.

:::note[VLAN tagging]
Virtual Appliance supports creating subinterfaces through the use of [802.1Q VLAN tagging](https://en.wikipedia.org/wiki/IEEE_802.1Q).

Use VLAN ID <code>0</code> when:

- Connected to a Port Group or Distributed Port Group that is associated with a specific VLAN.
- Connected to a Port Group or Distributed Port Group that is configured as a trunk that requires untagged packets.

You can also configure subinterfaces on the Virtual Appliance by associating the network interface with a Port Group or Distributed Port Group trunk and specifying a VLAN ID in addition to the port associated with the network interface (VLAN ID <code>1</code>-<code>4094</code>).

Refer to [Proxmox documentation](https://www.proxmox.com/en/products/proxmox-virtual-environment/get-started) for more information.
:::

7. Make sure the hardware settings match the minimum requirements for running Virtual Appliance. Make changes to the RAM and CPU if needed.
8. Continue setup in your [Cloudflare dashboard](#set-up-cloudflare-dashboard).

</TabItem> <TabItem label="libvirt/KVM (beta)">

**1. Obtain the Virtual Appliance script**

Download the Virtual Appliance helper script for libvirt/KVM to your host. The script deploys the virtual machine with the appropriate settings for Virtual Appliance. For details on system requirements, refer to [Prerequisites](#prerequisites).

The deployment assumes the use of libvirt. The virtual machine manager graphical interface is not required. The VM uses the `OVMF_CODE.fd` file from the `ovmf` package on Debian or Ubuntu.

The script can be deployed multiple times to create several instances of a Virtual Appliance, in different locations or on the same host. You consume one license key for each instance created. Generate a license key for each instance from the [Connectors page](/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#register-a-virtual-appliance-and-generate-a-license-key). For example, to deploy 10 Virtual Appliances, generate 10 license keys.

<Code lang="sh" code="curl -OL https://assets.magic-wan-connector.cloudflare.com/stable/libvirt_mconn_vm.sh" />

**2. Deploy the Virtual Appliance on libvirt/KVM**

The following instructions assume you already have libvirt installed with sufficient resources. For details, refer to [Prerequisites](#prerequisites).

1. In the terminal prompt of your host, run the script with elevated privileges. The script takes one optional argument, the name of the VM. If you do not provide a name, the VM defaults to `vmconn`:

<Code lang="sh" code="sudo bash ./libvirt_mconn_vm.sh" />

2. When prompted, enter a name for the VM. This name is used for the VM definition, the disk name, and the nvram file name. If the name already exists, the script exits with an error and you must use a different name.
3. Select the storage pool for the VM disk.
4. Choose whether to enable the graphics console in addition to the serial console.
5. Enter your license key.

:::note
You cannot use the same license key twice, or reuse a key once the virtual machine has been registered with Cloudflare. Generate a new key for every new Virtual Appliance from the [Connectors page](/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#register-a-virtual-appliance-and-generate-a-license-key).
:::

The script deploys the virtual machine with the appropriate settings. NICs are named based on the defined PCI path of each NIC. Changing the PCI path of a NIC breaks functionality. NICs are configured in an inactive state with the `default` network source.

6. Configure the NICs for your environment. Configure the WAN-associated NICs as macvtap devices to avoid potential connectivity issues.
7. Continue setup in your [Cloudflare dashboard](#set-up-cloudflare-dashboard).

</TabItem> </Tabs>

---

## Set up Cloudflare dashboard

### Create a new profile

You need to create a profile for your appliance before connecting it to the Internet.

To create a profile:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Create a profile**.
3. In **Name**, enter a descriptive name for your Virtual Appliance. Optionally, you can also add a description for it.
4. You need to decide if you want to turn on high availability for the Virtual Appliance. For details, refer to [About high availability configurations](#about-high-availability-configurations).
5. Select **Create and continue**.
6. Select **Add Appliance**. This will display a list of devices associated with your account. For a Virtual Appliance to show up you need to: <br /><ul><li><strong>VMware:</strong> Have already obtained your OVA package and license keys if you are installing on VMware.</li><li><strong>Proxmox:</strong> Have already obtained your Virtual Appliance Script and license keys if you are installing on Proxmox.</li></ul> For more information, refer to [Configure a virtual machine](#configure-a-virtual-machine) and select the appropriate tab.
7. If you have more than one Virtual Appliance, choose the one that corresponds to the on-ramp you are creating. Virtual Appliance devices are identified by a serial number, also known as a service tag. Use this information to choose the right Virtual Appliance. <br /> Select **Add Appliance** when you are ready to proceed.
8. Virtual Appliance will be added to your account with an **Interrupt window** defined. The interrupt window is the time period when the Virtual Appliance software can update, which may result in interruption to existing connections. You can change this later. Refer to [Interrupt window](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/interrupt-service-window/) for more details on how to define when the Virtual Appliance can update its systems.
9. Select **Continue** to proceed to creating your WAN and LAN networks.

### Create a WAN

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

When you have more than one anycast IP configured in your account (set up during your Cloudflare WAN (formerly Magic WAN) onboarding), Virtual Appliance will automatically create at most two tunnels per WAN port. This improves reliability and performance, and requires no additional configuration on your part.

1. In **WAN configuration**, select **Create**. You can create one or more [wide area networks (WANs)](https://www.cloudflare.com/learning/network-layer/what-is-a-wan/). Configuring multiple WANs will create multiple <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnels (one IPsec tunnel per WAN port). This allows Virtual Appliance to load balance traffic over WANs of equal priority. It also allows Virtual Appliance to failover between circuits according to their <GlossaryTooltip term="tunnel health-check" link="/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/">health</GlossaryTooltip>. Refer to [WAN settings](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#wan-settings) for more details.
   :::note
   This is not the same as a high availability (HA) configuration. HA configurations need two Virtual Appliance devices to work. For details, refer to [About high availability configurations](#about-high-availability-configurations).
   :::
2. In **Interface name**, enter a descriptive name for your WAN.
3. **Interface number** needs to correspond to the virtual network interface on the Virtual Appliance instance you have set up in VMware. Following our example from the previous steps, you need to choose port `1` since that is what corresponds to the `eno1` port we set up in VMware.
4. In **VLAN ID**, enter a number between `0` and `4094` to specify a [VLAN ID](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#vlan-id).
5. In **Priority**, choose the priority for your WAN. Lower numbers have higher priority. For details on how Cloudflare calculates priorities, refer to [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/).
6. In **Health check rate** configure the health check frequency for your site. Options are `low`, `mid`, and `high`. For details, refer to [Update tunnel health checks frequency](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/update-tunnel-health-checks-frequency/).
7. **Addressing**: Select **DHCP**. This is needed the first time you set up your Virtual Appliance to successfully download all settings to the machine and activate it. If you need a static IP address in your network environment:
	1. Continue the set up flow to activate your Virtual Appliance.
	2. Refer to [WAN with a static IP address](#wan-with-a-static-ip-address). If you choose a static IP, you also need to specify the static IP and gateway addresses.
8. Select **Save** when you are finished.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Make a `POST` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/wans/methods/create/) to create a WAN.

The `static_addressing` object is optional. Omit it if you are using DHCP. If you are using static addressing, add the `secondary_address` parameter when your site is in high availability (HA) mode.

Example:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/sites/{site_id}/wans \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
	"name": "<YOUR_WAN_NAME>",
	"physport": 1,
	"priority": 0,
	"vlan_tag": 0
}'
```

</TabItem> </Tabs>

### Create a LAN

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In **LAN configuration**, select **Create**.
2. Enter a descriptive name for your LAN in **Interface name**.
3. **Interface number** needs to correspond to the virtual LAN interface on the Virtual Appliance instance you have set up in VMware. Following our example from the previous steps, you need to choose port `2` since that is what corresponds to the `eno2` port we set up in VMware.
4. In **VLAN ID**, specify a [VLAN ID](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#vlan-id) to create virtual LANs.
5. In **Static addressing** > **Static address** give your Virtual Appliance's LAN interface its IP address. You can also enable the following options if they suit your use case:
   - **This is a DHCP server**: If your Virtual Appliance is a [DHCP server](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-server/).
   - **This is a DHCP relay**: If your Virtual Appliance is a [DHCP relay](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-relay/).
6. (Optional) In **Directly attached subnet** > **Static NAT prefix**, enter a CIDR prefix to enable NAT (network address translation). The prefix you enter here should be the same size as the prefix entered in **Static addressing**. For example, both networks have a subnet mask of `/24`: `192.168.100.0/24` and `10.10.100.0/24`.
7. (Optional) If your LAN contains additional subnets behind a layer 3 router, select **Add routed subnet** under **Routed subnets** to add them:
   - **Prefix**: The CIDR prefix for the subnet behind the L3 router.
   - **Next hop**: The address of the L3 router to which the Virtual Appliance should forward packets for this subnet.
   - **Static NAT prefix**: Optional setting. If you want to enable NAT for a routed subnet, supply an "external" prefix for the overlay-facing side of the NAT to use. It must be the same size as **Prefix**. <br /> For details, refer to [Routed subnets](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/routed-subnets/).
8. Select **Save**.
9. Select **Done** to finish your configuration. Tunnels and <GlossaryTooltip term="static route">static routes</GlossaryTooltip> will be automatically created for your Virtual Appliance, once it boots up.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Make a `POST` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/lans/methods/create/) to create a LAN.

Example:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/sites/{site_id}/lans \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
	"name": "<YOUR_LAN_NAME>",
	"physport": 2,
	"static_addressing": {
		"address": "172.16.14.0/24"
	},
	"vlan_tag": 0
}'
```

</TabItem> </Tabs>

#### Network segmentation

After setting up your LANs, you can configure your Virtual Appliance to enable communication between them without traffic leaving your premises. For details, refer to [Network segmentation](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/network-segmentation/).

#### DHCP options

Virtual Appliance supports different types of DHCP configurations. Virtual Appliance can:

- Connect to a DHCP server or use a static IP address instead of connecting to a DHCP server.
- Act as a [DHCP server](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-server/).
- Use [DHCP relay](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-relay/) to connect to a DHCP server outside the location your Virtual Appliance is in.
- [Reserve IP addresses](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-static-address-reservation/) for specific devices on your network.

### Add your Virtual Appliance to a site

After finishing your Virtual Appliance configuration, you need to add it to a site. Sites represent the local network of a data center, office, or other physical location, and combine all on-ramps available there. Sites also allow you to check, at a glance, the state of your on-ramps and set up health alert settings so that Cloudflare notifies you when there are issues with the site's on-ramps.

Refer to [Set up a site](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/sites/) for more information.

## Activate appliance

Virtual Appliance is deactivated after you install it, and will only establish a connection to the Cloudflare network when it is activated. Cloudflare recommends leaving it deactivated until you finish [setting it up in the dashboard](#set-up-cloudflare-dashboard).

When the Virtual Appliance is first activated, one of the ports must be connected to the Internet through a device that supports DHCP. This is required so that the Virtual Appliance can reach the Cloudflare global network and download the required configurations that you <a href="#set-up-cloudflare-dashboard">set up</a>.

<Aside type="caution">
	Remember to connect Virtual Appliance through a route that supports DHCP for
	its first connection to the Internet. Otherwise, Virtual Appliance will not
	work.
</Aside>

When you are ready to connect your Virtual Appliance to the Cloudflare network:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances**.
3. Find the Virtual Appliance you want to activate, select the three dots next to it > **Edit**. Make sure you verify the serial number to choose the right Virtual Appliance you want to activate.
4. In the new window, the **Status** dropdown will show as **Deactivated**. Select it to change the status to **Activated**.
5. The **Interrupt window** is the time period when the Virtual Appliance software can update, which may result in interruption to existing connections. Choose a time period to minimize disruption to your sites. For details on defining when the Virtual Appliance can update its systems, refer to [Interrupt window](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/interrupt-service-window/).
6. Select **Update**.

## Boot your Virtual Appliance

### Default password to access Virtual Appliance

Your Virtual Appliance's default password is the last seven characters of your license key, all uppercase, plus an <code>!</code> (exclamation mark).

For example, if your license key is <code>mconn-abcdefghijklmnopqrstuvwxyz</code>, your default password will be <code>TUVWXYZ!</code>.

---

## WAN with a static IP address

After activating your device, you can use it in a network configuration with the WAN interface set to a static IP address - that is, an Internet configuration that is not automatically set by DHCP. To use your Virtual Appliance on a network configuration with a static IP, follow these steps:

<Aside type="caution">
	Make sure you complete the setup workflow and activate your Virtual Appliance
	before changing the WAN settings to a static IP.
</Aside>

1. Connect the machine where you installed the VM with Virtual Appliance to a DHCP port with access to the Internet.
2. [Create a new profile](#create-a-new-profile) in the dashboard.
3. Create a [DHCP WAN](#create-a-wan).
4. [Activate](#activate-appliance) and boot your Virtual Appliance.
5. Wait 60 seconds.
6. Make changes to the [WAN settings](#create-a-wan) in the dashboard to a static IP set up.
7. Wait 60 seconds again.
8. Modify your [Port Groups](#configure-a-virtual-machine) as needed to change the source from which the WAN port obtains its IP address.
9. Reboot your virtual machine.

---

## About high availability configurations

You need to install two Virtual Appliances before you can set up a site in high availability. When you set up a site in high availability, the WANs and LANs in your Virtual Appliance have the same configuration but are replicated on two nodes. In case of failure of one of the devices, the other device becomes the active node, taking over the configuration of the LAN gateway IP and allowing traffic to continue without disruption.

Because Virtual Appliances in high availability configurations share a single site, you need to set up:

- **Static address**: The IP for the primary node in your site.
- **Secondary static address**: The IP for the secondary node in your site.
- **Virtual static address**: The IP that the LAN south of the Virtual Appliance device will forward traffic to, which is the LAN's gateway IP.

Make sure all IPs are part of the same subnet.

For detailed information about the expected behavior of high availability configurations, refer to the [High availability configurations](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#high-availability-configurations) reference page.

### Create a high availability configuration

You cannot enable high availability for an existing site. To add high availability to an existing site in the Cloudflare dashboard, you need to delete the site and start again.

To set up a high availability configuration:

1. Follow the steps in [Create a new profile](#create-a-new-profile) up until step 4.
2. After naming your site, select **Turn on high availability**.
3. Select **Create and continue**.
4. Select **Add Appliance**.
5. From the list, choose your first Virtual Appliance > **Add Appliance**.
6. Back on the previous screen, select **Add secondary appliance**.
7. From the list, choose your second Virtual Appliance > **Add Appliance**.
8. Select **Continue** to create a WAN. If you are configuring a static IP, configure the IP for the primary node as the static address, and the IP for the secondary node as the secondary static address.
9. To create a LAN, follow the steps in [Create a LAN](#create-a-lan) up until step 4.
10. In **Static address**, enter the IP for the primary node in your site. For example, `192.168.10.1/24`.
11. In **Secondary static address**, enter the IP for the secondary node in your site. For example, `192.168.10.2/24`.
12. In **Virtual static address**, enter the IP that the LAN south of the Virtual Appliance device will forward traffic to. For example, `192.168.10.3/24`.
13. Select **Save**.
14. From the **High availability probing link** drop-down menu, select the port that should be used to monitor the node's health. Cloudflare recommends you choose a reliable interface as the HA probing link. The primary and secondary node's probing link should be connected over a switch, and cannot be a direct connection.
15. Follow the instructions in [Activate appliance](#activate-appliance) to finish setting up your Appliances.

---

## IPsec tunnels and static routes

Virtual Appliance automatically creates [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#ipsec-tunnels) and [static routes](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/) for you. You cannot configure these manually.

To check the IPsec tunnels and static routes created by your Virtual Appliance:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Connectors**.
2. In **Cloudflare WAN** you can inspect the IPsec tunnels created by your Virtual Appliance.
3. In **Routes** you can inspect the static routes created by your Virtual Appliance.

---

## Next steps

- [Network options](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/)
- [Maintenance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/)
- [Reference information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/)
- [Troubleshooting](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/troubleshooting/)

# Device metrics

Cloudflare customers can inspect metrics for a specific Cloudflare One Appliance (formerly Magic WAN Connector) in the Cloudflare dashboard. These metrics help you troubleshoot potential issues with your Cloudflare One Appliance. For details, refer to [Troubleshooting](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/troubleshooting/).

## Query metrics with GraphQL

Customers can query Cloudflare's GraphQL API to fetch their Cloudflare One Appliance device metrics. The Cloudflare dashboard displays Cloudflare One Appliance device metrics over the past one hour. Via the GraphQL API, customers can query for up to 30 days of historical Cloudflare One Appliance device metrics.

For example:

```graphql graphql-api-explorer
query telemetry(
  $accountTag: string
  $snapshotsFilter: AccountMconnTelemetrySnapshotsAdaptiveGroupsFilter_InputObject!
  $snapshotMountsFilter: AccountMconnTelemetrySnapshotMountsAdaptiveGroupsFilter_InputObject!
  $snapshotThermalsFilter: AccountMconnTelemetrySnapshotThermalsAdaptiveGroupsFilter_InputObject!
  $limit: int64!
) {
  viewer {
    accounts(filter: { accountTag: $accountTag }) {
      snapshots: mconnTelemetrySnapshots(
        filter: $snapshotsFilter
        limit: $limit
        orderBy: [datetimeFiveMinutes_DESC]
      ) {
        max {
          cpuCount
          loadAverage1m
          memoryFreeBytes
          memoryTotalBytes
        }
        dimensions {
          connectorId
          datetimeFiveMinutes
        }
      }
      snapshotMounts: mconnTelemetrySnapshotMounts(
        filter: $snapshotMountsFilter
        limit: $limit
        orderBy: [datetimeFiveMinutes_DESC]
      ) {
        max {
          availableBytes
          totalBytes
        }
        dimensions {
          connectorId
          datetimeFiveMinutes
        }
      }
      snapshotThermals: mconnTelemetrySnapshotThermals(
        filter: $snapshotThermalsFilter
        limit: $limit
        orderBy: [datetimeFiveMinutes_DESC, connectorId_DESC]
      ) {
        max {
          currentCelsius
        }
        dimensions {
          connectorId
          datetimeFiveMinutes
        }
      }
    }
  }
}
```

### Average CPU load explained

The metric `average CPU load` is unique and distinctly different from `CPU utilization` which is another common CPU metric. The Cloudflare One Appliance uses a [Unix-style CPU load calculation](<https://en.wikipedia.org/wiki/Load_(computing)>).

CPU load is a measure of the number of processes that are currently running and that are waiting to be run on the CPU. Cloudflare collects the one minute load average from the device and converts that into a percentage based on the total number of cores in the CPU. If the Cloudflare One Appliance CPU has eight cores, and a one minute load average of two, then the average CPU load is 25%. If the average CPU load is above 100%, then there are processes in the queue that are waiting to be executed on the CPU.

Cloudflare is still evaluating the typical CPU load operating range on the Cloudflare One Appliance. In general, a healthy range for average CPU load on any device is between 30% and 70%. Customers may experience decreased Cloudflare One Appliance performance if the average CPU load is consistently above 100%.

# Configure with Connector

import { DirectoryListing, GlossaryTooltip } from "~/components";

Cloudflare One Appliance is a lightweight appliance you can install in corporate network locations to automatically connect and <GlossaryTooltip term="traffic steering">steer</GlossaryTooltip> any IP traffic through [secure IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#security-and-other-information). Cloudflare One Appliance is the easiest way to onboard your network locations to Cloudflare One. It is managed remotely through the Cloudflare dashboard, so you do not require an onsite IT team.

You can [purchase Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/) software pre-installed on a Cloudflare-certified device, or download and deploy [Virtual Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/) in your own infrastructure.

Either option ensures the best possible connectivity to the closest Cloudflare network location, where Cloudflare will apply security controls and send traffic on an optimized route to its destination.

Cloudflare One Appliance has the same type of support process as other Cloudflare Enterprise products. Contact your team account manager to learn more.

Review this section to learn how to configure and deploy Cloudflare One Appliance.

<DirectoryListing />

# Reference

import { GlossaryTooltip } from "~/components";

The Cloudflare One Appliance (formerly Magic WAN Connector) software is certified for use on the [Dell Networking Virtual Edge Platform](https://www.dell.com/support/home/en-us/product-support/product/dell-emc-networking-vep1445-vep1485/docs). It can be purchased with software pre-installed through our partner network for plug-and-play connectivity to Cloudflare One.

## Security and other information

- Cloudflare ensures the Cloudflare One Appliance device is secure and is not altered via TPM/Secure boot (does not apply to Virtual Appliance).
- Connectivity to the Cloudflare global network is secure and all traffic is encrypted through <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunneling. The Cloudflare One Appliance uses ESP-in-UDP with GCM-AES-256 encryption. Cloudflare uses a non-IKE keying protocol built into our control plane, secured with TLS, that establishes the keys used to encrypt dataplane traffic in the IPsec ESP protocol. From Appliance version 2026.2.0, the control plane provides post-quantum protection for traffic with hybrid ML-KEM (X25519MLKEM768) over TLS 1.3 to establish the dataplane keys used in IPsec ESP.
- The Cloudflare One Appliance does not support fail open.
- Customers have the ability to layer on additional security features/policies that are enforced at the Cloudflare network.

---

## ICMP traffic

<GlossaryTooltip term="ICMP">ICMP traffic</GlossaryTooltip> is routed through the Internet and bypasses [Cloudflare Gateway](/cloudflare-one/traffic-controls/). This enables you to ping resources on the Internet from the Cloudflare One Appliance directly, which can be useful for debugging.

---

## VLAN ID

This feature allows you to have multiple [virtual LANs](https://www.cloudflare.com/learning/network-layer/what-is-a-lan/) (VLANs) configured over the same physical port on your Cloudflare One Appliance. VLAN tagging adds an extra header to [packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) in order to identify which VLAN the packet belongs to and to route it appropriately. This effectively allows you to run multiple networks over the same physical port.

A non-zero value set up for the VLAN ID field in your WAN/LAN is used to handle VLAN-tagged traffic. Cloudflare uses the VLAN ID to handle traffic coming into your Cloudflare One Appliance device, and applies a VLAN tag with the configured VLAN ID for traffic going out of your Cloudflare One Appliance through WAN/LAN.

You can setup VLAN IDs both for WAN and LAN. For instructions on setting up VLAN IDs, refer to [Configure hardware Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/) or [Configure Virtual Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/).

## High availability configurations

### Terminology

- **Primary/Secondary**: Used to identify the two nodes which are part of a high availability (HA) configuration pair of Cloudflare One Appliances. This identity allows the node to identify which configuration is attributed to it — for example, specifying a primary and secondary IP in a LAN configuration. This identity is configured by the user on the Cloudflare dashboard.
- **Active/Standby**: These are states that the two nodes in a HA pair will dynamically assume based on an election process. Only one node at any time is expected to be active.

### High availability

A site set up in high availability (HA) mode has two Cloudflare One Appliances with the same configuration but replicated in two nodes. In case of failure of one Cloudflare One Appliance, the other Cloudflare One Appliance becomes the active node, taking over configuration of the LAN gateway IP and allowing traffic to continue without disruption.

### Active/Standby Election

During the LAN configuration, one of the LAN links is configured as a HA link, which is used to exchange heartbeats, resulting in the active / standby election of nodes.

The state election uses a `PRIORITY` parameter where the node with the higher priority becomes active and the other assumes the standby state. If the priority is the same, the state machine automatically picks one of the nodes as active.

The HA pair is configured in non-preemptive mode, meaning that once a node becomes active, it will remain active unless its priority drops below that of the other node.

### Configuration

The two Cloudflare One Appliances of a high availability (HA) pair are part of a single site. You designate the Cloudflare One Appliance [as primary and secondary](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#create-a-high-availability-configuration) in the Cloudflare dashboard.

:::note
The HA link cannot be connected back-to-back. It has to be connected over a switch. This is because, in a direct connection, if the link is unplugged on one end, the other end also detects a link failure. Since we have configured the system to enter a `FAULT` state when the HA link goes down, the affected node will be unable to function as the active node.
:::

### Failure detection and failover

The Cloudflare One Appliance's health can be in one of three states:

- **Good** : All health parameters are good
- **Degraded** : One of the following is true:
  - Health of at least one configured tunnel is `DOWN`
  - At least one of the LAN links is disconnected (physically unplugged)
- **Down** : If one of the following is true:
  - Health of all tunnels is `DOWN`
  - All LAN interfaces are disconnected
  - Cloudflare One Appliance's software is not healthy

A failover happens when the active node's health declines to a level lower than that of the standby node. For example, from `GOOD` to `DEGRADED`, or from `DEGRADED` to `DOWN`. In the case of a failover where one Cloudflare One Appliance is acting as a DHCP server, DHCP leases will be synchronized.

When a failover occurs, traffic is moved to the new active node. It could take up to 30 seconds for traffic to be fully restored over the new active node.

## WAN settings

This is where you add and configure your WAN connections. Each configured WAN will create one IPsec tunnel, unless you have more than one anycast IP configured in your account.

When you have more than one anycast IP configured in your account (set up during your Cloudflare WAN (formerly Magic WAN) onboarding), Cloudflare One Appliance will automatically create at most two tunnels per WAN port. This improves reliability and performance, and requires no additional configuration on your part.

When you have multiple WANs you can attribute different priorities to each one. Lower values mean a higher priority. This translates in Cloudflare One Appliance routing traffic through the higher priority WANs or, more precisely, over the IPsec tunnels established over that interface. On the other hand, if you configure multiple WANs of equal priority, traffic will be distributed over those links through [Equal-Cost Multi-Path (ECMP routing)](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#equal-cost-multi-path-routing).

Creating several WAN connections also means Cloudflare One Appliance can failover between circuits according to their health.

### High-capacity use cases

For high-capacity use cases, multiple tunnels can be established with equal priority. Outgoing traffic is then distributed across all available connections using an [ECMP routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#equal-cost-multi-path-routing) algorithm, which balances the load base.

### Configure multiple tunnels in the same WAN profile

If you do not have more than one anycast IP configured in your account, and you need to configure multiple tunnels for the same WAN profile, [set up multiple WAN connections](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#create-a-wan). Each WAN is assigned one IPsec tunnel.

### WAN settings

- **Interface number:** When using the hardware version of Cloudflare One Appliance, this refers to the Ethernet port that you are using for your WAN. If you need a throughput higher than 1 Gbps, you can use one of the SFP+ ports. For details on supported hardware, refer to [SFP+ port information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/sfp-port-information/). <br />  If you are using Virtual Appliance, this needs to correspond to the virtual network interface on the Virtual Appliance instance you have set up in your virtual machine.
- **VLAN ID**: Allows you to have multiple virtual WANs configured over the same port on your Cloudflare One Appliance. Refer to [VLAN ID](#vlan-id) for more information.
- **Priority**: Assigns a priority to the WAN interface. Lower numbers have higher priority. For details on how Cloudflare calculates priorities, refer to [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/).
- **Health check rate:** Configures the health check frequency for your WAN. Options are low, mid, and high. For details, refer to [Update tunnel health checks frequency](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/update-tunnel-health-checks-frequency/).
- **Addressing:** Configures the Cloudflare One Appliance to work in a DHCP or static IP environment.

## LAN settings

- **Interface number:** When using the hardware version of Cloudflare One Appliance, this refers to the Ethernet port that you are using for your LAN. If you need a throughput higher than 1 Gbps, you can use one of the SFP+ ports. For details on supported hardware, refer to [SFP+ port information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/sfp-port-information/). <br />  If you are using the Virtual Appliance, this needs to correspond to the virtual LAN interface on the Virtual Appliance instance you have set up in your virtual machine.
- **VLAN ID**: Allows you to have multiple virtual LANs configured over the same port on your Cloudflare One Appliance. Refer to [VLAN ID](#vlan-id) for more information.
- **Static addressing:** Configures the type of IP addressing for your Appliance. Depending on your use case, this is where you configure your LAN interface IP address, or enable  DHCP server or DHCP relay. For details, refer to [DHCP options](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/).
- **Static NAT prefix**: Enable NAT (network address translation). This is an optional setting.
- **Routed subnets:** Configures additional subnets behind a layer 3 router. For details, refer to [Routed subnets](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/routed-subnets/).

### Restrict traffic to your premises

Depending on your use case, you can define policies in your Cloudflare One Appliance to either allow traffic to flow between your LANs without it leaving your local premises or to forward it via the Cloudflare network where you can add additional security features. The default behavior is to drop all LAN-to-LAN traffic. These policies can be created for specific subnets, and link two LANs.

For details, refer to [Network segmentation](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/network-segmentation/).

# Troubleshooting

Cloudflare customers can inspect metrics for a specific Cloudflare One Appliance (formerly Magic WAN Connector) in the Cloudflare dashboard. These metrics help you troubleshoot potential issues with your device. The information spans categories such as:

- Performance analytics
- Port analytics
- Event logs
- DHCP leasing information

To find the information above and start troubleshooting your Cloudflare One Appliance:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Connectors**.
2. Go to **Appliances** > **Profiles**.
3. Select your Appliance > **View analytics**.

### Performance analytics

In Performance analytics you can review your Cloudflare One Appliance's performance over time including:

- Kernel boot time (how long it has been running and if it is activated or not)
- Last device snapshot (this also shows the frequency with which your device captures the snapshots that are used in several troubleshooting procedures)
- CPU temperature
- CPU load over time
- Used RAM over time

To access performance analytics:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Connectors**.
2. Go to **Appliances** > **Profiles**.
3. Select your Appliance > **View analytics**.
4. Select **Performance analytics**.

### Port analytics

Port analytics gives you access to information related to the packets sent and received through the ports in your Cloudflare One Appliance. You can adjust the time range for the information displayed in the dashboard regarding to:

- Rate for packets sent and received
- Rate for data sent and received

The dashboard provides this information for all active ports in your Cloudflare One Appliance. To access port analytics:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Connectors**.
2. Go to **Appliances** > **Profiles**.
3. Select your Appliance > **View analytics**.
4. Select **Port analytics**.

### Event logs

Use Event logs to identify general patterns and changes over time. This is useful to find correlations with other data and gather deeper insights into your Cloudflare One Appliance. The following event logs are available:

- `Init`: Initialized `mcon-agent` process. This process manages the Appliance.
- `Leave`: Stopped `mcon-agent` process.
- `StartAttestation`: Started attestation to verify the integrity of the Appliance before allowing the device to connect to your account.
- `FinishAttestationSuccess`: Finished attestation successfully.
- `FinishAttestationFailure`: Failed attestation.
- `StartRotateCryptKey`: Started cryptography key rotation.
- `FinishRotateCryptKeySuccess`: Finished cryptography key rotation.
- `FinishRotateCryptKeyFailure`: Failed cryptography key rotation.
- `StartRotatePki`: Started public key infrastructure (PKI) rotation.
- `FinishRotatePkiSuccess`: Finished PKI rotation.
- `FinishRotatePkiFailure`: Failed PKI rotation.
- `StartUpgrade`: Began Appliance's operating system upgrade.
- `FinishUpgradeSuccess`: Finished operating system upgrade.
- `FinishUpgradeFailure`: Failed operating system upgrade.
- `Reconcile`: Cloudflare is comparing the system's current state against its desired state.
- `ConfigureCloudflaredTunnel`: Configured Cloudflare Tunnel to debug device.

To access event logs:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Connectors**.
2. Go to **Appliances** > **Profiles**.
3. Select your Appliance > **View analytics**.
4. Select **Events**.
5. You can filter results by specific events, and by time.

### DHCP leasing

The DHCP leasing section identifies DHCP assigned leases and their expiration dates. To access DHCP leasing:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Connectors**.
2. Go to **Appliances** > **Profiles**.
3. Select your Appliance > **View analytics**.
4. Select **DHCP leasing**.

## Troubleshooting tips

If you are experiencing difficulties with your Cloudflare One Appliance, refer to the following tips to troubleshoot what might be happening.

## I have set up a site, but my Cloudflare One Appliance is not working

Make sure that you have [activated your Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#activate-appliance). Cloudflare ships the Cloudflare One Appliance deactivated, and the it will only establish a connection to the Cloudflare network when it is activated.

## I have tried to activate Cloudflare One Appliance, but it is still not working

Check if your Cloudflare One Appliance is connected to the Internet via a port that can serve DHCP. This is required the first time a Cloudflare One Appliance boots up so that it can reach the Cloudflare global network and download the required configurations that you set up in the Site configuration step. For details, refer to [Activate Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#activate-appliance).

If you have a firewall deployed upstream of the Cloudflare One Appliance, [check your firewall settings](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#firewall-settings-required). You might need to configure your firewall to allow traffic in specific ports for the Cloudflare One Appliance to work properly.

## I can access Cloudflare One Appliance's health checks, but there is no traffic

If you have a firewall deployed upstream of the Cloudflare One Appliance, make sure you review your [firewall settings](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#firewall-settings-required). You might need to configure your firewall to allow traffic in specific ports for the Cloudflare One Appliance to work properly.

## Devices I have behind Cloudflare One Appliance cannot connect to the Internet

If you have other routing appliances behind Cloudflare One Appliance, make sure you create policy-based routing policies to send traffic from your devices through Cloudflare One Appliance, instead of these other routing devices.

## How do I know if my device is contacting Cloudflare?

Cloudflare One Appliance sends a heartbeat periodically to Cloudflare. You can [access the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/heartbeat/), and check for the heartbeat status of your Appliance device.

## What do I do in the event of hardware issues with Cloudflare One Appliance?

Cloudflare is the single point of contact for any issues related to Cloudflare One Appliance, including issues with hardware. When required, Cloudflare Support will work with our partner, TD Synnex, to resolve any issues with the physical device.

# Configure hardware Connector

import { Aside, Card, GlossaryTooltip, Tabs, TabItem } from "~/components";

In this page you will find instructions on how to configure Cloudflare One Appliance. This guide provides a step-by-step guide for Cloudflare One Appliance initial setup. You can either return here after setting up your Cloudflare One Appliance, or refer to the [Maintenance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/) section where you will find instructions on how to update your settings.

## Prerequisites

You need to purchase [Cloudflare WAN](https://www.cloudflare.com/magic-wan/) before you can purchase and use Cloudflare One Appliance. Cloudflare One Appliance can function as your primary edge device for your network, or be deployed in-line with existing network gear.

You also need to purchase Cloudflare One Appliance before you can start configuring your settings in the Cloudflare dashboard. Contact your account representative to learn more about purchasing options for Cloudflare One Appliance.

---

## Before you begin

There are a couple of decisions you need to make when installing your Cloudflare One Appliance. Review the following topics for more information.

### Determine the need for a high availability configuration

You can install up to two instances of Cloudflare One Appliance for redundancy at each of your sites. If one of your devices fails, traffic will fail over to the other, ensuring that you never lose connectivity to that site.

In this type of high availability (HA) configuration, you will choose a reliable LAN interface as the HA link which will be used to monitor the health of the peer connector. HA links can be dedicated links or can be shared with other LAN traffic.

You must decide the type of configuration you want for your site from the beginning: no redundancy or with redundancy. You cannot add redundancy after finishing the configuration of your dashboard settings. If, at a later stage, you decide to enable redundancy, you will need to delete your Cloudflare One Appliance device in the Cloudflare dashboard, and start again.

<Card title="Do you need a high availability configuration?" icon="information">

- If you need a high availability configuration for your premises, refer to
  [About high availability configurations](#about-high-availability-configurations) for details
  and learn how to configure your Cloudflare One Appliance device in this mode.

- If you do not need a high availability configuration for you premises, check if you need a [DHCP or a static IP setup](#decide-on-dhcp-vs-static-ip-connections) before proceeding to [Set up Cloudflare dashboard](#set-up-cloudflare-dashboard).

</Card>

:::caution
You cannot enable high availability for an existing Cloudflare One Appliance on-ramp. To add high availability to an existing Cloudflare One Appliance on-ramp in the Cloudflare dashboard, you need to delete the on-ramp and start again. Plan accordingly to create a high availability configuration from the start if needed.
:::

### Decide on DHCP vs static IP connections

You can use Cloudflare One Appliance in both DHCP networks and networks that require a static IP configuration. At first boot, however, Cloudflare One Appliance needs to reach out to Cloudflare to download your settings and go through the activation process. If any of the networks plugged into your Cloudflare One Appliance device are DHCP enabled, do not use a VLAN, and have an Internet connection, that process is handled automatically. However, if all of the networks require more information to utilize, (such as a network with static IPs, or tagged VLAN networks) your Cloudflare One Appliance might need some more information to proceed.

There are couple of ways to provide this information. Choose the one that fits your workflow:

#### Option one - Activate on a DHCP Network

1. Connect Cloudflare One Appliance to a DHCP port with access to the Internet.
2. Follow the [setup flow](#set-up-cloudflare-dashboard) and activate your Cloudflare One Appliance device.
3. Refer to [WAN with a static IP address](#wan-with-a-static-ip-address).

#### Option two - Bootstrap via Serial Console

Refer to the [Bootstrap workflow](#bootstrap-via-serial-console).

---

## Port speeds

The hardware version of the Cloudflare One Appliance includes two [SFP+ ports](https://en.wikipedia.org/wiki/Small_Form-factor_Pluggable) that support 10G throughput, as well as six RJ45 ports that support 1G throughput.

Refer to [SFP+ port information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/sfp-port-information/) for details on this topic.

---

## Set up Cloudflare dashboard

### Register your Appliance

To set up and use the hardware version of Cloudflare One Appliance (formerly Magic WAN Connector), you first need to register it with your account. This is not applicable to Virtual Cloudflare One Appliance.

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances**, and select **Register an appliance**.
3. In **Appliance details** > **Serial number**, insert the serial number for your device. You can optionally add notes about the Cloudflare One Appliance you are adding to the dashboard.
4. (Optional) Select **Add** under **Serial number** to add multiple Cloudflare One Appliances at once to your account.
5. Select **Register appliance**.

Your device is now registered with your account.

### Create a new profile

You need to create a profile for your appliance before connecting it to the Internet.

To create a profile:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Create a profile**.
3. In **Name**, enter a descriptive name for your Cloudflare One Appliance. Optionally, you can also add a description for it.
4. You need to decide if you want to turn on high availability for the Cloudflare One Appliance. For details, refer to [About high availability configurations](#about-high-availability-configurations).
5. Select **Create and continue**.
6. Select **Add Appliance**. This will display a list of devices associated with your account. You need to have bought a Connector already for it to show up here. Refer to [Prerequisites](#prerequisites) if no Connector shows up in this list.
7. If you have more than one Cloudflare One Appliance, choose the one that corresponds to the on-ramp you are creating. Cloudflare One Appliance devices are identified by a serial number, also known as a service tag. Use this information to choose the right Cloudflare One Appliance. <br /> Select **Add Appliance** when you are ready to proceed.
8. Cloudflare One Appliance will be added to your account with an **Interrupt window** defined. The interrupt window is the time period when the Cloudflare One Appliance software can update, which may result in interruption to existing connections. You can change this later. Refer to [Interrupt window](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/interrupt-service-window/) for more details on how to define when the Cloudflare One Appliance can update its systems.
9. Select **Continue** to proceed to creating your WAN and LAN networks.

### Create a WAN

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

When you have more than one anycast IP configured in your account (set up during your Cloudflare WAN (formerly Magic WAN) onboarding), Cloudflare One Appliance will automatically create at most two tunnels per WAN port. This improves reliability and performance, and requires no additional configuration on your part.

1. In **WAN configuration**, select **Create**. You can create one or more [wide area networks (WANs)](https://www.cloudflare.com/learning/network-layer/what-is-a-wan/). Configuring multiple WANs will create multiple <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnels (one IPsec tunnel per WAN port). This allows Cloudflare One Appliance to load balance traffic over WANs of equal priority. It also allows Cloudflare One Appliance to failover between circuits according to their <GlossaryTooltip term="tunnel health-check" link="/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/">health</GlossaryTooltip>. Refer to [WAN settings](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#wan-settings) for more details.
   :::note
   This is not the same as a high availability (HA) configuration. HA configurations need two Cloudflare One Appliance devices to work. For details, refer to [About high availability configurations](#about-high-availability-configurations).
   :::
2. In **Interface name**, enter a descriptive name for your WAN.
3. **Interface number** refers to the physical Connector Ethernet port that you are using for your WAN. The ports are labeled `GE1`, `GE2`, `GE3`, `GE4`, `GE5`, and `GE6`. Choose the number corresponding to the port that you are using in Connector. <br/> If you need a throughput higher than 1 Gbps, you can use one of the SFP+ ports. Refer to [SFP+ port information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/sfp-port-information/) for more information on the hardware supported.
4. In **VLAN ID**, enter a number between `0` and `4094` to specify a [VLAN ID](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#vlan-id).
5. In **Priority**, choose the priority for your WAN. Lower numbers have higher priority. For details on how Cloudflare calculates priorities, refer to [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/).
6. In **Health check rate** configure the health check frequency for your site. Options are `low`, `mid`, and `high`. For details, refer to [Update tunnel health checks frequency](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/update-tunnel-health-checks-frequency/).
7. **Addressing**: Select **DHCP**. This is needed the first time you set up your Cloudflare One Appliance to successfully download all settings to the machine and activate it. If you need a static IP address in your network environment:
	1. Continue the set up flow to activate your Cloudflare One Appliance.
	2. Refer to [WAN with a static IP address](#wan-with-a-static-ip-address). If you choose a static IP, you also need to specify the static IP and gateway addresses.
8. Select **Save** when you are finished.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Make a `POST` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/wans/methods/create/) to create a WAN.

The `static_addressing` object is optional. Omit it if you are using DHCP. If you are using static addressing, add the `secondary_address` parameter when your site is in high availability (HA) mode.

Example:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/sites/{site_id}/wans \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
	"name": "<YOUR_WAN_NAME>",
	"physport": 1,
	"priority": 0,
	"vlan_tag": 0
}'
```

</TabItem> </Tabs>

### Create a LAN

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In **LAN configuration**, select **Create**.
2. Enter a descriptive name for your LAN in **Interface name**.
3. **Interface number** refers to the physical Connector Ethernet port that you are using for your LAN. The ports are labeled `GE1`, `GE2`, `GE3`, `GE4`, `GE5`, and `GE6`. Choose a number corresponding to the port that you are using in Connector. <br/> If you need a throughput higher than 1 Gbps, you can use one of the SFP+ ports. Refer to [SFP+ port information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/sfp-port-information/) for more information on the hardware supported.
4. In **VLAN ID**, specify a [VLAN ID](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#vlan-id) to create virtual LANs.
5. In **Static addressing** > **Static address** give your Cloudflare One Appliance's LAN interface its IP address. You can also enable the following options if they suit your use case:
   - **This is a DHCP server**: If your Cloudflare One Appliance is a [DHCP server](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-server/).
   - **This is a DHCP relay**: If your Cloudflare One Appliance is a [DHCP relay](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-relay/).
6. (Optional) In **Directly attached subnet** > **Static NAT prefix**, enter a CIDR prefix to enable NAT (network address translation). The prefix you enter here should be the same size as the prefix entered in **Static addressing**. For example, both networks have a subnet mask of `/24`: `192.168.100.0/24` and `10.10.100.0/24`.
7. (Optional) If your LAN contains additional subnets behind a layer 3 router, select **Add routed subnet** under **Routed subnets** to add them:
   - **Prefix**: The CIDR prefix for the subnet behind the L3 router.
   - **Next hop**: The address of the L3 router to which the Cloudflare One Appliance should forward packets for this subnet.
   - **Static NAT prefix**: Optional setting. If you want to enable NAT for a routed subnet, supply an "external" prefix for the overlay-facing side of the NAT to use. It must be the same size as **Prefix**. <br /> For details, refer to [Routed subnets](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/routed-subnets/).
8. Select **Save**.
9. Select **Done** to finish your configuration. Tunnels and <GlossaryTooltip term="static route">static routes</GlossaryTooltip> will be automatically created for your Cloudflare One Appliance, once it boots up.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Make a `POST` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/lans/methods/create/) to create a LAN.

Example:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/sites/{site_id}/lans \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
	"name": "<YOUR_LAN_NAME>",
	"physport": 2,
	"static_addressing": {
		"address": "172.16.14.0/24"
	},
	"vlan_tag": 0
}'
```

</TabItem> </Tabs>

#### Network segmentation

After setting up your LANs, you can configure your Cloudflare One Appliance to enable communication between them without traffic leaving your premises. For details, refer to [Network segmentation](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/network-segmentation/).

#### DHCP options

Cloudflare One Appliance supports different types of DHCP configurations. Cloudflare One Appliance can:

- Connect to a DHCP server or use a static IP address instead of connecting to a DHCP server.
- Act as a [DHCP server](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-server/).
- Use [DHCP relay](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-relay/) to connect to a DHCP server outside the location your Cloudflare One Appliance is in.
- [Reserve IP addresses](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-static-address-reservation/) for specific devices on your network.

### Add your Cloudflare One Appliance to a site

After finishing your Cloudflare One Appliance configuration, you need to add it to a site. Sites represent the local network of a data center, office, or other physical location, and combine all on-ramps available there. Sites also allow you to check, at a glance, the state of your on-ramps and set up health alert settings so that Cloudflare notifies you when there are issues with the site's on-ramps.

Refer to [Set up a site](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/sites/) for more information.

## Set up your Cloudflare One Appliance

### Device installation

There are several deployment options for Cloudflare One Appliance. Cloudflare One Appliance can act like a DHCP server for your local network, or integrate with your local setup and have static IP addresses assigned to it.

When Cloudflare One Appliance acts like the WAN router for your site, deployment will be something like this:

<pre class="mermaid">
{`flowchart LR
	accTitle: Appliance as WAN router
	accDescr: Cloudflare One Appliance set up as a DHCP server, and connecting to the Internet.
	a(Cloudflare One Appliance)--> b(Internet) --> c(Cloudflare)

	subgraph Customer site
	d[LAN 1] --> a
	e[LAN 2] --> a
	end

	classDef orange fill:#f48120,color: black
	class a,c orange`}
</pre>

_Cloudflare One Appliance set up as a DHCP server, and connecting to the Internet._

In the following example, the Cloudflare One Appliance device sits behind the WAN router in your site, and on-ramps only some of the existing LANs to Cloudflare.

<pre class="mermaid">
{`flowchart LR
	accTitle: Appliance behind site router
	accDescr: Cloudflare One Appliance connects to the router in the site, and only some of the LANs connect to Appliance.
	a(Cloudflare One Appliance)--> b((Site's router)) --> c(Internet) --> i(Cloudflare)

	subgraph Customer site
	d[LAN 1] --> a
	e[LAN 2] --> a
	g(LAN 3) --> b
	h(LAN 4) --> b
	end

	classDef orange fill:#f48120,color: black
	class a,i orange`}
</pre>

_Cloudflare One Appliance connects to the router in the site, and only some of the LANs connect to Appliance._

Refer to [Cloudflare One Appliance deployment options](/reference-architecture/diagrams/sase/cloudflare-one-appliance-deployment/) for a high-level explanation of the deployment options that make sense to most environments, as well as a few advanced use cases.

#### Firewall settings required

If there is a firewall deployed upstream of Cloudflare One Appliance, configure the firewall to allow the following traffic:

| Protocol/port           | Destination IP/URL                          | Purpose                                                                                                                                       |
| ----------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `UDP/53`                | DNS destination IP `1.1.1.1`                | Needed to allow DNS traffic to Cloudflare DNS servers. Cloudflare uses this port for DNS lookups of control plane API.                       |
| `TCP/443`               | -                                           | Cloudflare One Appliance will open outbound HTTPS connections over this port for control plane operations.                                    |
| `UDP/4500`              | Destination IP `162.159.64.1`               | Needed for Cloudflare One Appliance initialization and discovery through outbound connections.                                                |
| `UDP/4500`              | Destination IP - Cloudflare anycast IPs     | Needed for the Cloudflare anycast IPs assigned to your account for tunnel outbound connections. This traffic is tunnel traffic.               |
| `TCP/7844`, `UDP/7844`  | Outbound connections                        | Used to support debugging features in Cloudflare One Appliance.                                                                               |
| `UDP/123`               | `http://time.cloudflare.com/`               | Needed for Cloudflare One Appliance to periodically contact Cloudflare's Time Services.                                                       |

## Activate appliance

The Connector is shipped to you deactivated, and will only establish a connection to the Cloudflare network when it is activated. Cloudflare recommends leaving it deactivated until you finish [setting it up in the dashboard](#set-up-cloudflare-dashboard).

When Cloudflare One Appliance is first activated, you need to have Internet connection. If you chose to set up your Cloudflare One Appliance with DHCP you will need to have one of the Cloudflare One Appliance ports connected to the Internet through a device that supports DHCP. This is required so that the Cloudflare One Appliance can reach the Cloudflare global network and download the required configurations that you [set up](#set-up-cloudflare-dashboard).

If you set up your Cloudflare One Appliance with a static IP through the bootstrap method, you do not need a DHCP port. For details, refer to [DHCP vs static IP connections](#decide-on-dhcp-vs-static-ip-connections).

<Aside type="caution">Remember that if you chose the DHCP method you have to connect Cloudflare One Appliance through a route that supports DHCP for its first connection to the Internet. Otherwise, Cloudflare One Appliance will not work.</Aside>

When you are ready to connect your Cloudflare One Appliance to the Cloudflare network:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances**.
3. Find the Cloudflare One Appliance you want to activate, select the three dots next to it > **Edit**. Make sure you verify the serial number to choose the right Cloudflare One Appliance you want to activate.
4. In the new window, the **Status** dropdown will show as **Deactivated**. Select it to change the status to **Activated**.
5. The **Interrupt window** is the time period when the Cloudflare One Appliance software can update, which may result in interruption to existing connections. Choose a time period to minimize disruption to your sites. For details on defining when the Cloudflare One Appliance can update its systems, refer to [Interrupt window](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/interrupt-service-window/).
6. Select **Update**.

---

## WAN with a static IP address

After activating your device, you can use it in a network configuration with the WAN interface set to a static IP address — that is, an Internet configuration that is not automatically set by DHCP. To use your Cloudflare One Appliance on a network configuration with a static IP, follow these steps:

<Aside type="caution">Make sure you complete the setup workflow and activate your Cloudflare One Appliance before changing the WAN settings to a static IP.</Aside>

1. Connect Cloudflare One Appliance to a DHCP port with access to the Internet.
2. [Create a new profile](#create-a-new-profile) in the dashboard.
3. Create a [DHCP WAN](#create-a-wan).
4. [Activate](#activate-appliance) and power on your Cloudflare One Appliance.
5. Wait 60 seconds.
6. Make changes to the [WAN settings](#create-a-wan) in the dashboard to a static IP set up.
7. Wait 60 seconds again.
8. Cloudflare One Appliance will go offline. This is normal and expected behavior.
9. Adjust your physical connections as required to match the static configuration.
10. Cloudflare One Appliance comes back online.

## Bootstrap via Serial Console

Advanced users can locally configure their Cloudflare One Appliance to work in a static IP configuration. This local method does not require having access to a DHCP Internet connection. However, it does require being comfortable with using tools to access the serial port on Cloudflare One Appliance as well as using a serial terminal client to access the environment in your Cloudflare One Appliance.

The following is a detailed description of how to use the serial port to configure your Cloudflare One Appliance locally.

<Aside type="note">The <code>reset device</code> option in your Cloudflare One Appliance clears most of the configuration that is locally cached, resets the password to the default, and reboots.</Aside>

### Equipment required

To access the serial port on Cloudflare One Appliance you will need the following equipment:

- The Cloudflare One Appliance device
- A Phillips-head screwdriver
- A micro-USB to USB-A cable (there should be one included in the packaging of your Cloudflare One Appliance device)
- A computer with an available USB port
- A serial terminal client
- Optional: if needed, a USB-A to USB-C converter dongle if your computer requires it

### 1. Access the device's serial port

1. Using the Phillips screwdriver, loosen the screw covering the serial console panel on the back of the Cloudflare One Appliance and turn the panel out of the way.
	- Pictures and more instructions can be found on [Dell's Technical Documents](https://www.dell.com/support/kbdoc/en-us/000134440/how-to-access-console-port-of-dell-emc-networking-virtual-edge-platform-1405-series).
2. Connect your computer to your Cloudflare One Appliance device using the USB cable.

#### Default password

The default password for your Cloudflare One Appliance device is the serial number (also known as a Service Tag for Dell devices), all uppercase followed by an <code>!</code> (for example, <code>A1B2C3D!</code>)

### 2. Install a serial terminal client

To access the Cloudflare One Appliance device environment you need a serial terminal client. Follow these instructions to install one, based on your operating system.

#### Windows

Cloudflare recommends using PuTTY for Windows. Download PuTTY from the [official website](https://www.putty.org/) and then install it.

1. Check the COM port of the USB to UART device in the Windows Device Manager. It should appear as something similar to <code>Silicon Labs CP210x USB to UART Bridge (COMX)</code>.
2. Take note of the value in the parentheses (COMX).
	- For details on creating a serial console connection, refer to the [Dell Documentation Page](https://infohub.delltechnologies.com/l/virtual-edge-platform-vep-1405-series-diag-os-and-tools-release-notes/bios-installation-and-configuration).
3. Launch PuTTY.
4. Under **Category**, make sure that **Session** (the first item) is selected.
5. Under **Connection type**, select **Serial**.
6. In the **Serial Line**, type in the COM port found in step 2 (for example, <code>COM1</code>).
7. In the **Speed**, enter <code>115200</code>.
8. Select Open on the bottom of the dialog box. A terminal window should pop up.
9. The screen may need to be manually refreshed when a new device is connected. You can do that by pressing <code>CTRL + C</code>.

#### macOS

Cloudflare recommends installing Screen for macOS. You can install Screen via <code>brew install screen</code>. If you do not have <code>brew</code> installed, follow the instructions on [Brew's Official Website](https://brew.sh/) to install it.

1. Open the macOS Terminal.
2. Run <code>ls /dev/cu.*</code> to list the connected serial devices.
3. The command should return an output similar to <code>/dev/cu.usbserial-0001</code>. Copy this output to the clipboard or note this down somewhere else.
4. Run `sudo screen -adRUS mconn <PATH_FROM_STEP_3> 115200`.
5. The screen may need to be manually refreshed when a new device is connected. You can do that by pressing <code>CMD + C</code>.

#### Linux

Cloudflare recommends installing Screen for Linux. You can install Screen via your package manager of choice. For example, for Debian/Ubuntu, install by running <code>sudo apt update && sudo apt install screen</code>

1. Open Terminal.
2. List the connected serial devices by running <code>ls /dev/serial/by-id/*</code>.
3. The command should return an output similar to <code>/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0</code>. Copy this to the clipboard or note this down.
4. Run `sudo screen -adRUS mconn <PATH_FROM_STEP_3> 115200`.
5. The screen may need to be manually refreshed when a new device is connected. You can do that by pressing <code>CTRL + C</code>.

### 3. Configure a static IP

The <code>reset device</code> option in your Cloudflare One Appliance clears most of the configuration that is locally cached, resets the password to the default, and reboots.

1. Log into your Cloudflare One Appliance device. You will be prompted to change your password if you attempt to log in with the default password.
2. From the menu, go to **Bootstrap** with the arrow keys and select it with the Enter key.
3. Select the jack (physical port) you want to configure for the initialization of the appliance.
4. Enter the VLAN tag (if applicable) of the network. Leave it blank if untagged.
5. Select the <code>static</code> option as your network type.

<Aside>The main reason to use the bootstrapper is if every network your Cloudflare One Appliance device is plugged into is either static, behind a VLAN, or both. If you find yourself here and configuring a network with DHCP and no VLAN, you are probably not in the right place. See the section on configuring your Cloudflare One Appliance <a href="#set-up-cloudflare-dashboard">via the dashboard</a>.</Aside>

6. Enter the IP address you would like the appliance to have in CIDR form (for example, <code>10.0.0.2/24</code>).
7. Enter the IP address of the Internet gateway (this must be in the same subnet as the previous IP address you entered and must not be the same address).
8. Select **Save** and confirm that you want to use the new settings.
9. The Cloudflare One Appliance will download the rest of the settings from Cloudflare. The last heartbeat of the Cloudflare One Appliance should update once it has made contact with Cloudflare.

---

## About high availability configurations

You need to deploy two Connectors in your premises before you can set up a site in high availability. When you set up a site in high availability, the WANs and LANs in your Cloudflare One Appliance have the same configuration but are replicated on two nodes. In case of failure of one of the devices, the other device becomes the active node, taking over the configuration of the LAN gateway IP and allowing traffic to continue without disruption.

Because Cloudflare One Appliances in high availability configurations share a single site, you need to set up:

- **Static address**: The IP for the primary node in your site.
- **Secondary static address**: The IP for the secondary node in your site.
- **Virtual static address**: The IP that the LAN south of the Cloudflare One Appliance device will forward traffic to, which is the LAN's gateway IP.

Make sure all IPs are part of the same subnet.

For detailed information about the expected behavior of high availability configurations, refer to the [High availability configurations](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/#high-availability-configurations) reference page.

### Create a high availability configuration

You cannot enable high availability for an existing site. To add high availability to an existing site in the Cloudflare dashboard, you need to delete the site and start again.

To set up a high availability configuration:

1. Follow the steps in [Create a new profile](#create-a-new-profile) up until step 4.
2. After naming your site, select **Turn on high availability**.
3. Select **Create and continue**.
4. Select **Add Appliance**.
5. From the list, choose your first Cloudflare One Appliance > **Add Appliance**.
6. Back on the previous screen, select **Add secondary appliance**.
7. From the list, choose your second Cloudflare One Appliance > **Add Appliance**.
8. Select **Continue** to create a WAN. If you are configuring a static IP, configure the IP for the primary node as the static address, and the IP for the secondary node as the secondary static address.
9. To create a LAN, follow the steps in [Create a LAN](#create-a-lan) up until step 4.
10. In **Static address**, enter the IP for the primary node in your site. For example, `192.168.10.1/24`.
11. In **Secondary static address**, enter the IP for the secondary node in your site. For example, `192.168.10.2/24`.
12. In **Virtual static address**, enter the IP that the LAN south of the Cloudflare One Appliance device will forward traffic to. For example, `192.168.10.3/24`.
13. Select **Save**.
14. From the **High availability probing link** drop-down menu, select the port that should be used to monitor the node's health. Cloudflare recommends you choose a reliable interface as the HA probing link. The primary and secondary node's probing link should be connected over a switch, and cannot be a direct connection.
15. Follow the instructions in [Set up your Cloudflare One Appliance](#set-up-your-cloudflare-one-appliance) and [Activate appliance](#activate-appliance) to finish setting up your Appliances.

---

## IPsec tunnels and static routes

Cloudflare One Appliance automatically creates [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#ipsec-tunnels) and [static routes](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/) for you. You cannot configure these manually.

To check the IPsec tunnels and static routes created by your Cloudflare One Appliance:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Connectors**.
2. In **Cloudflare WAN** you can inspect the IPsec tunnels created by your Cloudflare One Appliance.
3. In **Routes** you can inspect the static routes created by your Cloudflare One Appliance.

---

## Next steps

- [Network options](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/)
- [Maintenance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/)
- [Reference information](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/reference/)
- [Troubleshooting](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/troubleshooting/)

# SFP+ port information

import { Render } from "~/components";

<Render file="mconn/sfp-port-info"
	product="networking-services" />

# Activate Connector

Before you can activate your Cloudflare One Appliance (formerly Magic WAN Connector), you need to follow Cloudflare's instructions regarding DHCP. For instructions, refer to:

- The [hardware version of Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#activate-appliance)
- The [virtual version of Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#activate-appliance)

# Appliance operations

import { Render } from "~/components";

<Render
	file="mconn/maintenance/appliance-operations"
	product="networking-services"
/>

# Deactivate Connector

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Appliances**.
3. Find the Cloudflare One Appliance you want to deactivate, select the three dots next to it > **Edit**.
4. In **Status**, select _Deactivated_ from the drop-down menu.
5. Select **Update**.

# Default password

import { Render } from "~/components";

<Render
	file="mconn/maintenance/default-password"
	product="networking-services"
/>

# Edit basic information

In **Basic information**, you can change the name and description of your Cloudflare One Appliance (formerly Magic WAN Connector).

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Appliances**.
3. Find the Cloudflare One Appliance that you want to edit > select the three dots next to it > **Edit**.
4. In **Basic information** make the necessary changes.
5. Select **Save**.

# Edit network settings

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.
3. Find the Appliance that you want to edit > select the three dots next to it > **Edit**.
4. Go to **Network configuration** > **WAN configuration** or **LAN configuration**.
5. Find the WAN/LAN you want to edit > select the three dots next to it > **Edit**.
6. Make the necessary changes.
7. Select **Save**.

# Edit sites

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Insights**.
2. Go to **Network visibility** > **Traffic overview**, and find the site you want to make changes on.
3. Select the three dots next to it > **Edit**.
4. In **Basic information**, make changes to the site's name, description, and geographic coordinates.
5. In **On-ramps**, add new on-ramps to your site. You can also remove existing ones.

# Edit traffic steering settings

You can only add or remove applications to Breakout traffic and Prioritized traffic. To add or remove applications:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.
3. Find the Appliance that you want to edit > select the three dots next to it > **Edit**.
4. Go to **Traffic steering** > **Breakout traffic** or **Prioritized traffic**.
5. Select **Add** to add a new application.
6. To delete an application, find the one you want to delete from **Breakout traffic** or **Prioritized traffic** > select the three dots next to it > **Remove**.

# Heartbeat

Cloudflare One Appliance (formerly Magic WAN Connector) communicates periodically with Cloudflare via HTTPS. This is also known as a heartbeat, and lets Cloudflare know that the Cloudflare One Appliance in question is connected to the Internet and reachable.

The heartbeat calls are made to `api.cloudflare.com`. Each Cloudflare One Appliance has a heartbeat frequency of 10 seconds, independently of the number of WAN interfaces you have running on your device.

There are three symbols for the heartbeat signal that allow you to quickly check the status of Cloudflare One Appliance:

- **Blue `i`**: Cloudflare One Appliance is contacting Cloudflare as expected.
- **Yellow triangle**: Cloudflare One Appliance has not yet connected to Cloudflare.
- **Red triangle**: There is a potential problem with Cloudflare One Appliance.

### Access Cloudflare One Appliance's heartbeat

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Appliances**.
3. Find your Cloudflare One Appliance, and place your cursor over the icon on the **Status** column to check the timestamp. The timestamp displays the last time Cloudflare One Appliance successfully contacted Cloudflare.

# Maintenance

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Interrupt window

The Interrupt window defines when Cloudflare One Appliance (formerly Magic WAN Connector) can update its systems. When Cloudflare One Appliance is updating, this may result in an interruption to existing connections. Set up a time window that minimizes disruption to your sites.

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Connectors**.
2. In **Appliances** > **Appliances**, select the Cloudflare One Appliance for which you want to set up the update window > **Edit**.
3. In **Interrupt window**, select the most appropriate time for the Cloudflare One Appliance to update its systems:
   - **Timezone**: Select the time zone for the Cloudflare One Appliance to update.
   - **Start time**: Choose an hour for the Cloudflare One Appliance to start updating. Cloudflare recommends you choose an hour when there is minimal activity in your network, to avoid potential disruptions.
   - **Duration**: Duration indicates the time window during which the Cloudflare One Appliance is scheduled to update. For example, if you configure your Cloudflare One Appliance to update at `22:00` and specify a **Duration** of `4 hours`, the Cloudflare One Appliance will attempt to update within the four-hour period following `22:00`.

# Register a hardware Connector

To set up and use the hardware version of Cloudflare One Appliance (formerly Magic WAN Connector), you first need to register it with your account. This is not applicable to Virtual Cloudflare One Appliance.

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances**, and select **Register an appliance**.
3. In **Appliance details** > **Serial number**, insert the serial number for your device. You can optionally add notes about the Cloudflare One Appliance you are adding to the dashboard.
4. (Optional) Select **Add** under **Serial number** to add multiple Cloudflare One Appliances at once to your account.
5. Select **Register appliance**.

Your device is now registered with your account.

# Remove connectors

When adding or removing Cloudflare One Appliances (formerly Magic WAN Connectors), you need to be aware of the difference between the physical device and its profile.
- The physical device is the hardware at your site.
- The profile contains the configuration that allows the device to connect to Cloudflare, including your WANs, LANs, traffic steering, and LAN policies.

You can have more than one Cloudflare One Appliance in one profile if you initially enabled high availability during the configuration of the profile. If you did not enable high availability, you need to delete the profile associated with a site before adding a new Cloudflare One Appliance.

## Remove a profile

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.
3. Find the profile that you want to edit > select the three dots next to it > **Delete**.

## Remove a physical device

To remove a Cloudflare One Appliance from your account:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Appliances**.
3. Find the Cloudflare One Appliance that you want to delete > select the three dots next to it > **Delete**.

# Network options

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Configure link aggregation groups

import { DashButton } from "~/components";

You can bundle multiple physical LAN ports on a Cloudflare One Appliance into a single logical port called a Link Aggregation Group (LAG). This increases LAN bandwidth and provides redundancy. If a member port fails, traffic automatically shifts to the remaining ports in under one second.

:::note
Your appliance must be running OS version 2026.2.0 or later. This version deploys automatically.
:::

The following guide assumes you have already created a site and configured your Cloudflare One Appliance. For instructions, refer to [Configure hardware Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/) or [Configure virtual Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/).

## Create a LAG

1. Go to the **Connectors** page.

<DashButton url="/?to=/:account/magic-networks/connections" />

2. Go to the **Appliances** tab > **Profiles**.
3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Go to the **Appliances** tab.
5. In **Link aggregation groups (LAGs)**, select **Create A LAG**.
6. Select the LAN ports you want to bundle. You can add up to six ports per LAG. All ports must be the same type and speed.
7. Select **Save**.

## Assign a LAN to a LAG

1. Go to the **Connectors** page.

<DashButton url="/?to=/:account/magic-networks/connections" />

2. Go to the **Appliances** tab > **Profiles**.
3. Select the Cloudflare One Appliance you want to edit > **Edit**.
4. Go to **Network Configuration** > **LAN configuration**.
5. Select or create a LAN > **Edit**.
6. In **Interface** > **Interface type**, select **Aggregate** as your LAG instead of a single port.
7. Select **Save**.

## Monitor LAG status

1. Go to the **Connectors** page.

<DashButton url="/?to=/:account/magic-networks/connections" />

2. Go to the **Appliances** tab > **Profiles**.
3. Select the Cloudflare One Appliance > **Edit**.
4. Go to the **Appliances** tab.

The page displays each configured LAG and the status of its member ports.

## Delete a LAG

1. Go to the **Connectors** page.

<DashButton url="/?to=/:account/magic-networks/connections" />

2. Go to the **Appliances** tab > **Profiles**.
3. Select the Cloudflare One Appliance > **Edit**.
4. Go to the **Appliances** tab.
5. Next to the LAG you want to delete, select the three-dot menu > **Delete**.
6. Select **Delete**.

# Enable NAT for a subnet

Every subnet in the Cloudflare WAN (formerly Magic WAN) overlay must have a unique address space — otherwise, Cloudflare cannot determine which site should receive traffic for a given IP address. In practice, many organizations reuse the same private address ranges (for example, `192.168.1.0/24`) at multiple sites. Rather than renumbering those subnets, you can enable static network address translation (NAT) for a subnet on a Cloudflare One Appliance (formerly Magic WAN Connector). NAT assigns each site a unique overlay-facing prefix while preserving the existing local addressing.

With subnet NAT, the Appliance performs a static, 1:1 translation between:

- The **local prefix** used inside the site.
- A **NAT prefix** that is advertised into the Cloudflare WAN overlay.

Because the mapping is static, the Appliance supports both outbound connections from the site and inbound connections from Cloudflare WAN to the site. Connections do not have to be initiated by hosts behind the Cloudflare One Appliance.

## How subnet NAT works in Cloudflare WAN

NAT is static and 1:1 between equal-sized prefixes. When you enable NAT for a subnet on an Appliance:

- The **local prefix** is the subnet on the LAN side of the Appliance.
- The **NAT prefix** is a WAN-facing prefix of the same size.
- The Appliance translates addresses 1:1 between the two prefixes:
  - For traffic leaving the site towards Cloudflare WAN, it replaces local addresses with the corresponding NAT addresses.
  - For traffic arriving at the site from Cloudflare WAN, it replaces NAT addresses with the corresponding local addresses.

## Addressing rules

To avoid overlapping addresses in the overlay, Cloudflare WAN enforces the following rules:

- **Uniqueness within a LAN**
  - The local prefix for each subnet must be unique within that LAN on the Appliance.
  - You can reuse the same local prefix on a different LAN or on a different site.

- **Uniqueness in the Cloudflare WAN overlay**
  - Every **overlay-facing prefix** must be unique across all sites in your Cloudflare WAN deployment.
  - For a subnet **with NAT enabled**, the overlay-facing prefix is the **NAT prefix**.
  - For a subnet **without NAT**, the overlay-facing prefix is the **local prefix**.

These rules allow you to reuse local space at multiple sites, as long as each subnet in the Cloudflare WAN overlay has a unique overlay-facing prefix.

## Example

Consider a subnet that uses the following prefixes:

- **Local prefix**: `192.168.100.0/24`
- **NAT prefix**: `10.10.100.0/24`

In this case:

- When a host inside the site with address `192.168.100.13` sends traffic into the Cloudflare WAN overlay, the Appliance translates the address to `10.10.100.13`.
- When traffic from another site, or from the Internet via Cloudflare WAN, targets `10.10.100.13`, the Appliance translates the address back to `192.168.100.13`.

## Configure NAT for subnets

You configure subnet NAT when you create or edit a LAN on a Cloudflare One Appliance. In the Appliance configuration:

- You define the **local prefix** for the subnet on the LAN side.
- You optionally define a **static NAT prefix** of the same size. When present, this prefix becomes the overlay-facing prefix for that subnet.

For step-by-step instructions to configure a LAN and supply a static NAT prefix, refer to:

- [Configure hardware Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#create-a-lan)
- [Configure Virtual Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#create-a-lan)

# Network segmentation

import { APIRequest, TabItem, Tabs } from "~/components";

You can define policies in your Cloudflare One Appliance (formerly Magic WAN Connector) to either allow traffic to flow between your LANs without it leaving your local premises or to forward it via the Cloudflare network where you can add additional security features. The default behavior is to drop all LAN-to-LAN traffic. These policies can be created for specific subnets, and link two LANs.

<pre class="mermaid">
	{`
		flowchart LR
		accTitle: LAN-to-LAN traffic flow
		accDescr: In this example, the red path shows traffic that stays in the customer's premises (allowing direct communication between LAN 3 and LAN 4), and the orange path shows traffic that goes to Cloudflare before returning to the customer's premises (processing traffic between LAN 1 and LAN 2 in Cloudflare).
				a(Cloudflare One Appliance) <---> b(Internet) <---> c(Cloudflare)

				subgraph Customer site
				d[LAN 1] <---> a
				e[LAN 2] <---> a
				g[LAN 3] <---> a
				h[LAN 4] <---> a
				end
				classDef orange fill:#f48120,color: black
				class a,c orange

				linkStyle 0,1,2,3 stroke:#f48120,stroke-width:3px
				linkStyle 4,5 stroke:red,stroke-width:3px
	`}
</pre>

_In this example, the red path shows traffic that stays in the customer's premises (allowing direct communication between LAN 3 and LAN 4), and the orange path shows traffic that goes to Cloudflare before returning to the customer's premises (processing traffic between LAN 1 and LAN 2 in Cloudflare)._

<br />

As a best practice for security, we recommend sending all traffic through Cloudflare's network for Zero Trust security filtering. Use these policies with care and only for scenarios where you have a hard requirement for LAN-to-LAN traffic flows.

If you enable LAN to LAN traffic flows, communications can only be initiated from origin to destination — for example, LAN 1 to LAN 2 — and not the other way around. This is by design and prevents potential exfiltration of information. This does not mean bidirectional communication on TCP is not possible. It only means that the origin is the only one authorized to initiate communications.

Unidirectional communication can be enabled for UDP and ICMP, but it is not available for TCP, as it would break that protocol.

The following guide assumes you have already created a site and configured your Cloudflare One Appliance. For instructions to create a site and configure your Cloudflare One Appliance, refer to [Configure hardware Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/) or [Configure Virtual Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/), depending on the type of Cloudflare One Appliance you have on your premises.

## Create a policy

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

Follow these steps to create a new LAN policy to segment your network. Only the fields marked **required** are mandatory.

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Go to **Network Configuration** > **LAN configuration**.
5. Select **LAN policies** > **Create**.
6. In **Policy name**, enter a descriptive name for the policy you are creating.
7. From the drop-down menu **Origin (required)**, select your origin LAN.
8. Specify a subnet for your first LAN in **Subnets**.
9. In **Ports** specify the TCP/UDP ports you want to use. Valid ports range from `1` to `65535`. Zero (`0`) is not a valid port number. Add a comma to separate each of the ports or add a port range. For example, `2,5,6,9-14`.
10. In **Destination (required)**, select the destination LAN and repeat the above process to configure it.
11. In **Protocols**, select the type of traffic you want to allow. You can choose **TCP**, **UDP**, and **ICMP**. You can also select **Any** to choose all types of traffic.
12. In **Traffic direction** you can choose between bidirectional traffic (the default) and unidirectional traffic. What you can choose depends on the protocol that you chose for the policy:
    - **Any**: If **Any** is selected and you choose **Unidirectional**, the system will alert you that this will break TCP traffic.
    - **TCP**: You can only select **Bidirectional**.
    - **UDP**: The system defaults to **Bidirectional** but you can choose **Unidirectional**.
    - **ICMP**: The system defaults to **Bidirectional** but you can choose **Unidirectional**.
13. In **Traffic path**, select **Forwarded via Cloudflare** if you want traffic to be forwarded to Cloudflare to be processed. If you do not select this option, traffic will flow locally in your premises, without passing through Cloudflare.
14. Select **Save**.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a `POST` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/acls/methods/create/) to create a network policy.

Example:

<APIRequest
  path="/accounts/{account_id}/magic/sites/{site_id}/acls"
  method="POST"
	json={{
    "description": "<POLICY_DESCRIPTION>",
    "forward_locally": true,
    "lan_1": {
      "lan_id": "<LAN_ID>",
      "lan_name": "<LAN_NAME>",
      "ports": [
        1
      ],
      "subnets": [
        "192.0.2.1"
      ]
    },
    "lan_2": {
      "lan_id": "<LAN_ID>",
      "lan_name": "<LAN_NAME",
      "ports": [
        1
      ],
      "subnets": [
        "192.0.2.1"
      ]
    },
    "name": "<POLICY_NAME>",
    "protocols": [
      "tcp"
    ]
  }}
/>

```json output
{
  "errors": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "result": {
    "id": "023e105f4ecef8ad9ca31a8372d0c353",
    "description": "Allows local traffic between PIN pads and cash register.",
    "forward_locally": true,
    "lan_1": {
      "lan_id": "lan_id",
      "lan_name": "lan_name",
      "port_ranges": [
        "8080-9000"
      ],
      "ports": [
        1
      ],
      "subnets": [
        "192.0.2.1"
      ]
    },
    "lan_2": {
      "lan_id": "lan_id",
      "lan_name": "lan_name",
      "port_ranges": [
        "8080-9000"
      ],
      "ports": [
        1
      ],
      "subnets": [
        "192.0.2.1"
      ]
    },
    "name": "PIN Pad - Cash Register",
    "protocols": [
      "tcp"
    ],
    "unidirectional": true
  },
  "success": true
}
```

Take note of the `id` parameter, as you will need it to edit or delete network policies.

</TabItem> </Tabs>

The new policy will ensure that traffic between the specified LANs flows locally, bypassing Cloudflare.

## Edit a policy

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Go to **Network Configuration** > **LAN configuration**.
5. Select **LAN policies**.
6. Select the policy you need to edit > **Edit**.
7. Make your changes, and select **Update policy**.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a `PUT` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/acls/methods/update/) to edit a network policy.

Example:

<APIRequest
  path="/accounts/{account_id}/magic/sites/{site_id}/acls/{acl_id}"
  method="PUT"
  json={{
    "description": "<POLICY_DESCRIPTION>",
    "forward_locally": true,
    "lan_1": {
      "lan_id": "<LAN_ID>",
      "lan_name": "<LAN_NAME>",
      "ports": [
        1
      ],
      "subnets": [
        "192.0.2.1"
      ]
    },
    "lan_2": {
      "lan_id": "<LAN_ID>",
      "lan_name": "<LAN_NAME>",
      "ports": [
        1
      ],
      "subnets": [
        "192.0.2.1"
      ]
    },
    "name": "<POLICY_NAME>",
    "protocols": [
      "tcp"
    ]
  }}
/>

```json output
{
  "errors": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "result": {
    "id": "023e105f4ecef8ad9ca31a8372d0c353",
    "connector_id": "ac60d3d0435248289d446cedd870bcf4",
    "description": "description",
    "ha_mode": true,
    "location": {
      "lat": "37.6192",
      "lon": "122.3816"
    },
    "name": "site_1",
    "secondary_connector_id": "8d67040d3835dbcf46ce29da440dc482"
  },
  "success": true
}
```

</TabItem> </Tabs>

## Delete a policy

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Go to **Network Configuration** > **LAN configuration**.
5. Select **LAN policies**.
6. Select the policy you need to edit > **Edit**.
7. Select **Delete**.
8. Select **I understand that deleting a policy is permanent** in the dialog box > **Delete**.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a `DELETE` request [using the API](/api/resources/magic_transit/subresources/sites/subresources/acls/methods/delete/) to delete a network policy.

Example:

<APIRequest
  path="/accounts/{account_id}/magic/sites/{site_id}/acls/{acl_id}"
  method="DELETE"
/>

</TabItem> </Tabs>

# Routed subnets

Each LAN interface (physical port + VLAN tag) on Cloudflare One Appliance (formerly Magic WAN Connector) is part of a _directly-attached subnet_ — a subnet that the Appliance connects to directly. When you specify a static address for the LAN interface, you indicate both the interface's address and the subnet it attaches to. For example, `192.168.100.13/24` means the LAN interface has the IP address `192.168.100.13`, and is part of the subnet `192.168.100.0/24`.

Some LANs have additional subnets behind Layer 3 routers that sit between those subnets and the Cloudflare One Appliance. These are routed subnets — the Appliance does not connect to them directly but can reach them through a next-hop router. You need to configure routed subnets so that Cloudflare installs the correct routes to forward traffic to the right Appliance and LAN interface.

Refer to the following diagram for an example of how this might work:

:::note
Blue represents directly-attached subnets, and red represents routed subnets.
:::

<pre class="mermaid">
	{`
		flowchart TB
		accTitle: Routed subnets
		accDescr: Some LANs are complex, and might have additional subnets behind L3 routers.

		a((WAN)) --> b

		subgraph b [Cloudflare One Appliance]
		direction TB
		c(LAN 1)
		d(LAN n)
		end

		c --- e(subnet x):::blue
		d --- f(subnet 192.168.100.0/24):::blue

		f---|192.168.100.10|g(Layer 3 router)

		g --- h(routed subnet y):::red
		g --- i(192.168.200.0/24):::red
		g --- j(layer 3 router)
		j --- k(routed subnet z):::red

		classDef blue fill:#add8e6,color: black
		classDef red fill:#ff6900,color: black
	`}
</pre>

<br/>

To add a routed subnet to your LAN, you need:

- **A prefix**: The subnet's CIDR prefix; Cloudflare will automatically install static routes to this prefix in our global network (to forward [packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) for this subnet to the right Cloudflare One Appliance), and in your Cloudflare One Appliance (to forward packets for this subnet to the right LAN interface). In the figure above, the routed subnet in the center has the prefix `192.168.200.0/24`.
- **A next-hop address**: The address of the L3 router to which the Cloudflare One Appliance should forward packets for this subnet. In the figure, the routed subnet in the center has the next-hop address `192.168.100.10`.

Optionally, you can also [enable NAT for a subnet](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/nat-subnet/) by providing a static overlay prefix.

## Create routed subnets

For instructions on creating routed subnets, refer to **Create a LAN** in either [Configure hardware Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-hardware-appliance/#create-a-lan) or [Configure Virtual Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/configure-virtual-appliance/#create-a-lan).

# Breakout traffic

import { APIRequest, Aside, AnchorHeading, CURL, TabItem, Tabs } from "~/components";

Breakout traffic allows you to define which applications should bypass Cloudflare's security filtering, and go directly to the Internet. It works via DNS requests inspection. This means that if your network is caching DNS requests, Breakout traffic will only take effect after you cache entries expire and your client issues a new DNS request that Cloudflare One Appliance (formerly Magic WAN Connector) can detect. This can take several minutes.

<Aside type="caution">Breakout traffic will not work for applications that use DNS-over-HTTPS.</Aside>

<pre class="mermaid">
	{`
	flowchart LR
	accTitle: Breakout traffic flow
	accDescr: Applications 1 and 2 are configured to bypass Cloudflare's security filtering, and go straight to the Internet.
	a(Cloudflare One Appliance) --> b(Cloudflare) -->|Filtered traffic|c(Internet)

	a-- Breakout traffic ---d(Application1) & e(Application2) --> c

	classDef orange fill:#f48120,color: black
	class a,b orange
	`}
</pre>

_In the graph above, Applications 1 and 2 are configured to bypass Cloudflare's security filtering, and go straight to the Internet._

<Aside type="note" title="A note on security">

We recommend [routing](https://www.cloudflare.com/learning/network-layer/what-is-routing/) all traffic through our global network for comprehensive security filtering and access controls. However, there may be specific cases where you want a subset of traffic to bypass Cloudflare's security filtering and route it directly to the Internet. You can scope this breakout traffic to specific applications from the Cloudflare dashboard.

For details on how Cloudflare routes traffic, refer to [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/).

</Aside>

## Add an application to your account

Before you can add or remove Breakout traffic applications to your Cloudflare One Appliance, you need to create an account-level list with the applications that you want to configure. Currently, adding to or modifying this list is only possible via API, through the [`managed_app_id`](/api/resources/magic_transit/subresources/apps/methods/create/) endpoint.

To add applications to your account:

Send a `POST` request to add new apps to your account.

<APIRequest
	path="/accounts/{account_id}/magic/apps"
	method="POST"
	json={{
    "managed_app_id": "<APP_ID>",
    "name": "<APP_NAME>",
    "type": "<APP_TYPE>",
	}}
	/>

```json output
{
	"result": {
		"account_app_id": "eb09v665c0784618a3e4ba9809258fd4",
		"name": "<APP_NAME>",
		"type": "<APP_TYPE>",
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

You can now add this new app to the Breakout traffic list in your Cloudflare One Appliance.

### Add an application to Cloudflare One Appliance

You need to configure Breakout traffic applications for each of your existing sites, as this is a per-site configuration.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to the [Cloudflare One dashboard](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Select **Traffic Steering**.
5. In **Breakout traffic**, select **Create**.
6. Select one or more applications that should bypass Cloudflare filtering from the list. You can also use the search box.

7. (Optional) You can also pin an application to a WAN port. In **Preferred breakout port**, select the WAN you want to assign your applications to. Refer to [Designate WAN ports for breakout apps](#designate-wan-ports-for-breakout-apps) for more information.

8. Select **Save**.

The traffic for the application you chose will now go directly to the Internet and bypass Cloudflare's filtering.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

1. Send a `GET` [request](/api/resources/magic_transit/subresources/apps/methods/list/) to list the applications associated with an account.

	<APIRequest
	path="/accounts/{account_id}/magic/apps"
	method="GET"
	/>

	```json output
		{
			"result": [
				{
					"managed_app_id": "<APP_ID>",
					"name": "<APP_NAME>",
					"type": "File Sharing",
					"hostnames": [
						"<app_name.com>",
						"<app-name.info>"
					]
				}
			]
		}
	```

	Take note of the `"managed_app_id"` value for any application you want to add.

2. Send a `POST` request to add new apps to the Breakout traffic policy.

	<APIRequest
	path="/accounts/{account_id}/magic/sites/{site_id}/app_configs"
	method="POST"
	json={{
		"managed_app_id": "<MANAGED_APP_ID>",
		"breakout": true
	}}
	/>

	```json output
	{
		"result": {
			"account_app_id": "<APP_ID>",
			"name": "<APP_NAME>",
			"type": "<BREAKOUT_OR_PRIORITY>"
		},
		"success": true,
		"errors": [],
		"messages": []
	}
	```

</TabItem> </Tabs>

### Delete an application from Cloudflare One Appliance

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to the [Cloudflare One dashboard](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Appliance you want to configure > **Edit**.
4. Select **Traffic Steering**.
5. In **Breakout traffic**, find the application you want to delete > select the **three dots** next to it > **Remove application traffic**.
6. (Optional) If you have several pages of applications, you can use the search box to quickly find the application you are looking for.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

You need to delete Breakout traffic applications for each of your existing sites, as this is a per-site configuration.

1. Send a [`GET` request](/api/resources/magic_transit/subresources/apps/methods/list/) to list the applications associated with a site.

	<APIRequest
	path="/accounts/{account_id}/magic/sites/{site_id}/app_configs"
	method="GET"
	/>

	```json output
		{
			"result": [
				{
					"id": "<APP_ID>",
					"site_id": "<SITE_ID>",
					"managed_app_id": "<APP_NAME>",
					"breakout": true
				}
			]
		}
	```

	Take note of the `"id"` value for the application that you want to delete.

2. Send a `DELETE` request to delete an application from the Breakout traffic policy.

	<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/sites/{site_id}/app_configs/{id}"
	method="DELETE"
	/>

	```json output
	{
			"result": {
					"id": "<APP_ID>",
					"site_id": "<SITE_ID>",
					"managed_app_id": "<APP_NAME>",
					"breakout": true
			},
			"success": true,
			"errors": [],
			"messages": []
	}
	```

</TabItem> </Tabs>

<AnchorHeading title="Designate WAN ports for breakout apps" depth={2} />

You can pin applications to a specific WAN port in Cloudflare One Appliance when you need control over which WAN port your applications egress from the device. In case your preferred WAN port goes down, Cloudflare One Appliance automatically fails over to a standard configured WAN port priority.

With this preferred breakout port, customers have direct control over their local Internet breakout traffic. You can designate a specific WAN uplink as the primary path for your critical applications configured to bypass the Cloudflare network. This provides the predictability and control needed for performance-sensitive applications, ensuring your critical traffic always takes the path you choose.

To pin applications to a WAN port:

1. Log in to the [Cloudflare One dashboard](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.
3. Select the Cloudflare One Appliance you want to configure > **Edit**.

4. In **Traffic steering** > **Breakout Traffic** find the application you want to pin to a WAN port.
5. Select the three dots next to it > **Edit application traffic**.
6. From the **Preferred breakout port** drop-down menu, select the WAN port you want to assign to the applications.
7. Select **Save**.

## NetFlow exports from Cloudflare One Appliance to Network Flow

You can configure your Cloudflare One Appliance (formerly Magic WAN Connector) to export Netflow statistics for local breakout traffic to [Network Flow](/network-flow) (formerly Magic Network Monitoring). This provides insights into traffic that leaves your site directly, bypassing the Cloudflare network.

The Cloudflare One Appliance uses NetFlow v9 to export flow data for breakout traffic only. You can enable and configure this export by setting the Netflow configuration for the associated site via the Cloudflare API.

### Enable NetFlow exports

:::note
To export NetFlow statistics, you will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/), as well as the `site_id` associated with your Cloudflare One Appliance.
:::

1. Send a `PUT` request to the Netflow configuration endpoint for your site.
2. In the JSON body request, you must include the `collector_ip` parameter. To export traffic statistics to Network Flow, use the IP address `162.159.65.1`. This is the only field required to enable the feature.

Minimal configuration example:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/magic/sites/$SITE_ID/netflow_config"
	method="PUT"
	json={{
		"collector_ip": "162.159.65.1"
	}}
/>

3. You can customize the configuration by adding optional fields to the JSON payload. These fields include:

- `collector_port`: The UDP port for the collector. The default is `2055`.
- `sampling_rate`: The rate at which packets are sampled.
- `active_timeout`: The timeout for active flows in seconds.
- `inactive_timeout`: The timeout for inactive flows in seconds.

Full configuration example:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/magic/sites/$SITE_ID/netflow_config"
	method="PUT"
	json={{
		"collector_ip": "162.159.65.1",
		"collector_port": 2055,
		"sampling_rate": 100,
		"active_timeout": 60,
		"inactive_timeout": 30
	}}
/>

Your Cloudflare One Appliance will now begin exporting Netflow data for its breakout traffic, which will be ingested and displayed within your Network Flow dashboard. You can retrieve the current settings by sending a `GET` request, or disable the export by sending a `DELETE` request to the same endpoint.

## Cloudflare One Client traffic

If you have Cloudflare One Appliance (formerly Magic WAN Connector) and Cloudflare One Clients deployed in your premises, Cloudflare One Appliance automatically routes Cloudflare One Client traffic to the Internet rather than Cloudflare WAN IPsec tunnels. This prevents traffic from being encapsulated twice.

You may need to configure your firewall to allow this new traffic. Make sure to allow the following IPs and ports:

- **Destination IPs**: `162.159.193.0/24`, `162.159.197.0/24`
- **Destination ports**: `443`, `500`, `1701`, `2408`, `4443`, `4500`, `8095`, `8443`

Refer to [Cloudflare One Client with firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/) for more information on this topic.

# Application-aware policies

import { DirectoryListing } from "~/components";

Standard traffic policies match on network-layer attributes like IP addresses and port ranges. Application-aware policies go further — they identify traffic by the application generating it, so you can make routing and security decisions based on what the traffic is, not just where it is going.

Cloudflare One Appliance (formerly Magic WAN Connector) classifies traffic using the same application categories used across Cloudflare's [Secure Web Gateway](/cloudflare-one/traffic-controls/). This means routing decisions on the Appliance and security policies in Gateway use the same application definitions.

For the full list of recognized applications and categories, refer to [Applications and app types](/cloudflare-one/traffic-controls/application-app-types/).

With application-aware policies, you can:

- **Break out traffic directly to the Internet** — route specific applications directly to the Internet from the Appliance, bypassing Cloudflare's security filtering.
- **Prioritize traffic** — assign higher priority to specific applications so the Appliance processes them first when the network is congested.

For details, refer to the following pages:

<DirectoryListing />

# Prioritized traffic

import { APIRequest, Aside, CURL, TabItem, Tabs } from "~/components";

Prioritized traffic allows you to define which applications Cloudflare One Appliance (formerly Magic WAN Connector) should process first. Applications not in the list will be queued behind prioritized traffic.

Similarly to breakout traffic, prioritized traffic also works via DNS requests inspection.

<Aside type="caution">Prioritized traffic will not work for applications that use DNS-over-HTTPS.</Aside>

## Add an application to your account

Before you can add or remove Prioritized traffic applications to your Cloudflare One Appliance, you need to create an account-level list with the applications that you want to configure. Currently, adding to or modifying this list is only possible via API, through the [`managed_app_id`](/api/resources/magic_transit/subresources/apps/methods/create/) endpoint.

To add applications to your account:

Send a `POST` request to add new apps to your account.

<APIRequest
	path="/accounts/{account_id}/magic/apps"
	method="POST"
	json={{
    "managed_app_id": "<APP_ID>",
    "name": "<APP_NAME>",
    "type": "<APP_TYPE>",
	}}
	/>

```json output
{
	"result": {
		"account_app_id": "eb09v665c0784618a3e4ba9809258fd4",
		"name": "<APP_NAME>",
		"type": "<APP_TYPE>",
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

You can now add this new app to the Prioritized traffic list in your Cloudflare One Appliance.

### Add an application to Cloudflare One Appliance

You need to configure Prioritized traffic applications for each of your existing sites, as this is a per-site configuration.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to the [Cloudflare One dashboard](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Select **Traffic Steering**.
5. In **Prioritized traffic**, select **Create**.
6. Select one or more applications that should bypass Cloudflare filtering from the list. You can also use the search box.

7. Select **Save**.

The traffic for the application you chose is now processed first by Connector.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

1. Send a `GET` [request](/api/resources/magic_transit/subresources/apps/methods/list/) to list the applications associated with an account.

	<APIRequest
	path="/accounts/{account_id}/magic/apps"
	method="GET"
	/>

	```json output
		{
			"result": [
				{
					"managed_app_id": "<APP_ID>",
					"name": "<APP_NAME>",
					"type": "File Sharing",
					"hostnames": [
						"<app_name.com>",
						"<app-name.info>"
					]
				}
			]
		}
	```

	Take note of the `"managed_app_id"` value for any application you want to add.

2. Send a `POST` request to add new apps to the Prioritized traffic policy.

	<APIRequest
	path="/accounts/{account_id}/magic/sites/{site_id}/app_configs"
	method="POST"
	json={{
		"managed_app_id": "<MANAGED_APP_ID>",
		"breakout": true
	}}
	/>

	```json output
	{
		"result": {
			"account_app_id": "<APP_ID>",
			"name": "<APP_NAME>",
			"type": "<BREAKOUT_OR_PRIORITY>"
		},
		"success": true,
		"errors": [],
		"messages": []
	}
	```

</TabItem> </Tabs>

### Delete an application from Cloudflare One Appliance

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to the [Cloudflare One dashboard](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.

3. Select the Appliance you want to configure > **Edit**.
4. Select **Traffic Steering**.
5. In **Prioritized traffic**, find the application you want to delete > select the **three dots** next to it > **Remove application traffic**.
6. (Optional) If you have several pages of applications, you can use the search box to quickly find the application you are looking for.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

You need to delete Prioritized traffic applications for each of your existing sites, as this is a per-site configuration.

1. Send a [`GET` request](/api/resources/magic_transit/subresources/apps/methods/list/) to list the applications associated with a site.

	<APIRequest
	path="/accounts/{account_id}/magic/sites/{site_id}/app_configs"
	method="GET"
	/>

	```json output
		{
			"result": [
				{
					"id": "<APP_ID>",
					"site_id": "<SITE_ID>",
					"managed_app_id": "<APP_NAME>",
					"breakout": true
				}
			]
		}
	```

	Take note of the `"id"` value for the application that you want to delete.

2. Send a `DELETE` request to delete an application from the Prioritized traffic policy.

	<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/sites/{site_id}/app_configs/{id}"
	method="DELETE"
	/>

	```json output
	{
			"result": {
					"id": "<APP_ID>",
					"site_id": "<SITE_ID>",
					"managed_app_id": "<APP_NAME>",
					"breakout": true
			},
			"success": true,
			"errors": [],
			"messages": []
	}
	```

</TabItem> </Tabs>

# DHCP relay

import { APIRequest, TabItem, Tabs } from "~/components";

DHCP Relay provides a way for DHCP clients to communicate with DHCP servers that are not available on the same local subnet/broadcast domain. When you enable DHCP Relay, Cloudflare One Appliance (formerly Magic WAN Connector) forwards DHCP discover messages to a predefined DHCP server, and routes the responses back to the original device that sent the discover message.

<pre class="mermaid">
	{`
		flowchart LR
		accTitle: DHCP Relay diagram
		accDescr: The graph shows Cloudflare One Appliance sending DHCP discover messages to a DHCP server offsite.
				a(Cloudflare One Appliance) <--> b(Cloudflare/Cloudflare WAN) <--> c(DHCP server)

				subgraph Site A
				d[LAN 1] <--> a
				e[LAN 2] <--> a
				end

				subgraph Site B
				c
				end
				classDef orange fill:#f48120,color: black
				class a,b,c orange
	`}
</pre>

_The graph shows Cloudflare One Appliance sending DHCP discover messages to a DHCP server offsite._

:::caution
DHCP relay will not work if your DHCP server is behind a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/). To enable DHCP relay functionality, use either a Cloudflare WAN IPsec/GRE tunnel or a CNI connection.
:::

To configure DHCP relay:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.
3. Select your Cloudflare One Appliance > **Edit**.
4. Select **Network Configuration**.
5. In **LAN configuration**, select the LAN where you need to configure DHCP relay.
6. Select **Edit**.
7. Select **This is a DHCP Relay**.
8. In **Upstream DHCP server addresses**, enter the IP address of your DHCP server.
9. (Optional) If you need to add more DHCP server addresses, select **Add upstream DHCP server address** as many times as needed, and enter the new values.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a [`PUT` request](/api/resources/magic_transit/subresources/sites/subresources/lans/methods/update/) to update the LAN where you want to enable DHCP relay:

Example:

<APIRequest
  path="/accounts/{account_id}/magic/sites/{site_id}/lans/{lan_id}"
  method="PUT"
  json={{
    "lan": {
    "static_addressing": {
      "dhcp_relay": {
        "server_addresses": [
          "192.0.2.1"
        ]
      }
    }
  }
  }}
/>

</TabItem> </Tabs>

# DHCP server

import { APIRequest, TabItem, Tabs } from "~/components";

When you use a static IP address, Cloudflare One Appliance (formerly Magic WAN Connector) can also act as a DHCP server in your network. To enable this feature:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Appliances** > **Profiles**.
3. Select the Cloudflare One Appliance you want to configure > **Edit**.
4. Select **Network Configuration** > **LAN configuration**.
5. In **LAN configuration**, select the LAN where you want to enable DHCP server.
6. Select **Edit**.
7. Under **Static addressing**, select **This is a DHCP Server**. You also have to specify:
   - The DNS server address. You can have more than one IP address. Select **Add DNS Server** for each server you want to add.
   - The DHCP pool start
   - The DHCP pool end

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a [`PUT` request](/api/resources/magic_transit/subresources/sites/subresources/lans/methods/update/) to update the LAN where you want to enable DHCP server:

Example:

<APIRequest
  path="/accounts/{account_id}/magic/sites/{site_id}/lans/{lan_id}"
  method="PUT"
  json={{
    "lan": {
    "static_addressing": {
      "dhcp_server": {
        "dhcp_pool_end": "<IP_ADDRESS>",
        "dhcp_pool_start": "<IP_ADDRESS>",
        "dns_server": "<IP_ADDRESS>"
      }
    }
  }
  }}
/>

</TabItem> </Tabs>

# DHCP static address reservation

import { APIRequest, TabItem, Tabs } from "~/components";

If you configure your Cloudflare One Appliance (formerly Magic WAN Connector) to be a DHCP server, you can also assign IP addresses to specific devices on your network. To reserve IP addresses:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Configure your Cloudflare One Appliance to be a [DHCP server](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/network-options/dhcp/dhcp-server/).
2. Select **Add DHCP Reservation**.
3. In **Hardware Address** enter the [MAC address](https://en.wikipedia.org/wiki/MAC_address) for the device you want a specific IP address for.
4. In **IP Address**, enter the IP address for that device.
5. (Optional) If you need to reserve more IP addresses, select **Add DHCP Reservation** as many times as needed, and enter the new values.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a [`PUT` request](/api/resources/magic_transit/subresources/sites/subresources/lans/methods/update/) to update the LAN where you want to reserve addresses:

Example:

<APIRequest
  path="/accounts/{account_id}/magic/sites/{site_id}/lans/{lan_id}"
  method="PUT"
  json={{
    "lan": {
    "static_addressing": {
      "dhcp_server": {
        "reservations": {
          "<HARDWARE_MAC_ADDRESS>": "<IP_ADDRESS>",
          "<HARDWARE_MAC_ADDRESS_2>": "<IP_ADDRESS>"
        }
      }
    }
  }
  }}
/>

</TabItem> </Tabs>

# DHCP options

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Check tunnel health in the dashboard

import { GlossaryTooltip } from "~/components";

The Cloudflare dashboard monitors the health of all <GlossaryTooltip term="anycast">anycast</GlossaryTooltip> tunnels on your account that route traffic from Cloudflare to your origin network.

The dashboard shows the view of tunnel health as measured from each Cloudflare location where your traffic is likely to land. If the tunnels are healthy on your side, you will see the majority of servers reporting an **up** status. It is normal for a subset of these locations to report tunnel status as degraded or unhealthy, since the Internet is not homogeneous and intermediary path issues between Cloudflare and your network can cause interruptions for specific paths.

:::note
To access more than one hour of tunnel health data, you should use the [GraphQL API](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/query-tunnel-health/).
:::

Not all data centers are relevant to you at all times. You can refer to the **Traffic volume (1 hour)** column to understand if a given data center is receiving traffic for your network, and if its health status is relevant to you.

## Check tunnel health

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) and go to **Insights**.
2. Go to **Network health** > **WAN connector health**.
3. In this view you can access a list of your tunnels and their current health status. You can also check the amount of health checks passed in the last hour as well as traffic volume for each tunnel.
4. Find the tunnel you want to inspect, select the three dots next to it, and choose:
	- **Create alert**: Opens the [notifications wizard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/configure-tunnel-health-alerts/) so you can create specific alerts for that tunnel when specific conditions are met.
	- **Network Analytics**: Opens the Analytics section of the dash, prefiltered with the tunnel you want to inspect.
5. Alternatively, from the list of tunnels, select the tunnel you want to inspect to access details about it.

## Check tunnel health for a specific tunnel

You can drill down into a specific tunnel to check its health status and other information.

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) and go to **Insights**.
2. Go to **Network health** > **WAN connector health**.

3. Find and select the tunnel you want to inspect.

The next view displays detailed information about the tunnel, including:
- Status information
	- Up: More than 80% of health checks pass.
	- Degraded: More than 40% of health checks pass.
	- Down: Less than 40% of health checks pass.
- Health checks passed in the last hour
- Traffic volume in the last hour

If you select the three dots in front of the tunnel you want to inspect, you have access to the following tools:
- Packet captures: Collect [packet level data for your traffic](/cloudflare-network-firewall/packet-captures/)
- Network Analytics: Leverage real-time insights into [network analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/).

:::note
Cloudflare WAN customers with [Customer Metadata Boundary](/data-localization/metadata-boundary/) enabled for the European Union can access GRE, IPsec, and CNI (Cloudflare Network Interconnect) health check and traffic volume data in the Cloudflare dashboard and through the API. This ensures that customers who need to be General Data Protection Regulation (GDPR) compliant can access all Cloudflare WAN features.
:::

## Connectors

Cloudflare One Appliance (formerly Magic WAN Connector) also includes a heartbeat function, an additional way of communicating its health status which does not depend on successfully setting up any tunnels. The heartbeat function communicates periodically with Cloudflare through HTTPS and lets Cloudflare know that the Cloudflare One Appliance in question is connected to the Internet and reachable.

Refer to [Heartbeat](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/maintenance/heartbeat/) to learn more.

## Troubleshooting

If you received a tunnel health alert but are unsure whether it affects your traffic, refer to [Troubleshoot connectivity](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/connectivity/) to determine whether the alert is relevant.

If your tunnels show as unhealthy or degraded, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/) for common issues and solutions.

# Configure tunnel health alerts

import {
	APIRequest,
	Code,
	Details,
	GlossaryTooltip,
	Tabs,
	TabItem,
	DashButton,
} from "~/components";

You can configure Tunnel Health Alerts (formerly Magic Tunnel health alerts) to receive email, webhook, and PagerDuty notifications when the percentage of successful <GlossaryTooltip term="tunnel health-check">health checks</GlossaryTooltip> for an IPsec/GRE tunnel drops below the selected [service-level objective (SLO)](/cloudflare-one/networks/connectors/cloudflare-wan/reference/how-cloudflare-calculates-tunnel-health-alerts/).

Tunnel health alerts monitor the health check success rate of each IPsec/GRE tunnel included in the alert that has actively transferred customer traffic (excluding health check traffic) over the past six hours. You can define an SLO threshold for the percentage of health checks that must be successful for each IPsec/GRE tunnel. If the number of successful health checks for the IPsec/GRE tunnel(s) included in the alert drops below the SLO threshold, an alert fires.

## Alert data

When a Tunnel health alert fires, you receive the following data in the email, webhook, and PagerDuty notification:

- Cloudflare account name
- Cloudflare account ID
- Alert type
- Tunnel name
- Tunnel ID
- Tunnel status
- Alert SLO
- Timestamp

## SLO thresholds

Currently, there are seven SLO threshold values that you can configure through the Cloudflare dashboard. For a more granular approach, use the [API](#set-up-tunnel-health-alerts).

The SLO threshold for Tunnel health alerts is the percentage of successful health checks for each IPsec/GRE tunnel in the alert:

| Alert Sensitivity Level | SLO threshold |
| --- 										| --- 					|
| Minimum 								| 95.0 					|
| Very low 								| 96.0					|
| Low 										| 97.0 					|
| Medium 									| 98.0 					|
| High 										| 99.0 					|
| Very high 							| 99.5					|
| Maximum 								| 99.9					|

The time it takes to receive alerts depends on the sensitivity level you configure for your SLO thresholds. Higher sensitivity levels notify you faster when a tunnel's health degrades, but they may also trigger alerts for brief or minor disruptions. Lower sensitivity levels reduce the chance of false alarms but may delay notifications for less severe issues.

While the underlying detection timing remains consistent across sensitivity levels, the speed of notification depends on how significantly the tunnel's health has dropped and the sensitivity you have chosen. Cloudflare recommends that you [test SLO thresholds](#test-slos) to determine which one better serves your use case.

For details, refer to [How Cloudflare calculates Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/reference/how-cloudflare-calculates-tunnel-health-alerts/).


## Set up Tunnel Health Alerts

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Go to the **Notifications** page.

	 <DashButton url="/?to=/:account/notifications" />

2. Select **Add**.
3. From the **Product** drop-down menu, select **Cloudflare WAN**.
4. Select **Tunnel Health Check Alert** > **Select** to add a notification. You can add alerts by tunnel or by data center (beta).

<Details header="Alert by tunnel">

5. Select **Alert by tunnel**.
6. Enter a name and description for the notification.
7. Add webhooks or an email address for the person who should receive the notification, and select **Next**.
8. Select the **Alert Sensitivity Level** threshold from the drop-down menu. The threshold defaults to _Medium (98.0)_. You can choose from options between _Minimum (95.0)_ and _Maximum (99.9)_. For details, refer to [How Cloudflare calculates Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/reference/how-cloudflare-calculates-tunnel-health-alerts/).
9. From the **Alert interval** drop-down menu, set the minimum amount of time that must pass before Cloudflare sends you a duplicate alert. Options range from five minutes to seven days.
10. Enable **Set as default alert for any new tunnels created in the future** if you want the alert sensitivity level you chose to be automatically applied to all new tunnels you create.
11. Select **Next**.
12. Choose the tunnels you want to receive alerts for. You can search by specific tunnel names, or filter them by type (Generic Routing Encapsulation (GRE), Internet Protocol Security (IPsec), and CNI (Cloudflare Network Interconnect)). Select **Next**.
13. Review the details of your alert. If these details are correct, select **Create alert**.

</Details>

<Details header="Alert by data center (beta)">

5. Select **Alert by data center**.
6. Enter a name and description for the notification.
7. Add webhooks or an email address for the person who should receive the notification, and select **Next**.
8. Select the **Alert Sensitivity Level** threshold from the drop-down menu. The threshold defaults to _Medium (98.0)_. You can choose from options between _Minimum (95.0)_ and _Maximum (99.9)_. For details, refer to [How Cloudflare calculates Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/reference/how-cloudflare-calculates-tunnel-health-alerts/).
9. From the **Alert interval** drop-down menu, set the minimum amount of time that must pass before Cloudflare sends you a duplicate alert. Options range from five minutes to seven days.
10. Choose the data centers you want to receive alerts for, and select **Next**.
11. Choose the tunnels you want to receive alerts for. You can search by specific tunnel names, or filter them by type (GRE, IPsec, and CNI (Cloudflare Network Interconnect)). Select **Next**.
12. Review the details of your alert. If these details are correct, select **Create alert**.

</Details>

</TabItem> <TabItem label="API">

:::note
For details on specific permissions, refer to the [documentation for Notifications](/notifications/get-started/).
:::

Send a [`POST` request](/api/resources/alerting/subresources/policies/methods/create/) to create a tunnel health alert. You can set tunnel health alerts with any SLO value between `0` and `99.99`.


	<APIRequest
		path="/accounts/{account_id}/alerting/v3/policies"
		method="POST"
		json={{
			"alert_type": "magic_wan_tunnel_health",
			"description": "<DESCRIBE_POLICY>",
			"enabled": true,
			"filters": {
				"slo": [
					"99.9"
				]
			},
			"mechanisms": {
				"email": [
					{
						"id": "EMAIL_ADDRESS"
					}
				]},
			"name": "<DESCRIBE_ALERT>"
		}}
	/>

	<Code lang="json" code={`
		{
			"result": [
				{
					"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
					"name": "<POLICY_NAME>",
					"description": "<POLICY_DESCRIPTION>",
					"enabled": true,
					"alert_type": "magic_wan_tunnel_health",
					"mechanisms": {
						"email": [
							{
								"id": "<YOUR_EMAIL>"
							}
						]
					},
					"created": "2024-09-11T14:13:29.585658Z",
					"modified": "2024-09-11T14:13:29.585658Z",
					"conditions": {
						"and": [
							{
								"or": [
									{
										"<=": [
											{
												"var": "slo"
											},
											"99.9"
										]
									}
								]
							}
						]
					},
					"filters": {
						"slo": ["99.9"]
					}
				}
			],
			"success": true,
			"errors": [],
			"messages": []
		}
	`} />

</TabItem> </Tabs>

## Test SLOs

To test whether a specific alert sensitivity level works for your use case:

1. [Create an alert](#set-up-tunnel-health-alerts) with a specific sensitivity level for a tunnel with active traffic within the past six hours. If you are unsure which tunnels to choose, refer to [Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/) for real-time and historical data about your network.
2. Disable the tunnel you are testing, so there is 100% [health check failure](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/).
3. The time it takes for Cloudflare to send you an alert depends on the sensitivity you chose for your alerts.

# Custom IKE ID for IPsec

import { Render } from "~/components";

<Render file="cloudflare-wan/custom-ike-id-ipsec" product="networking-services" />

# Enable Magic user roles

import { Render } from "~/components";

<Render file="magic-user-role" product="networking-services" />

# Common settings

import { DirectoryListing } from "~/components";

Review this section to learn about the common settings that apply to both the Cloudflare One Appliance (formerly Magic WAN Connector) setup process and the manual setup process for Cloudflare WAN.

<DirectoryListing />

# Set up a site

import { Details } from "~/components";

Sites represent the local network of a data center, office, or other physical location, and combine all on-ramps available there. Sites also allow you to quickly check the state of your on-ramps and set up health alert settings so that you get notified when there are issues with the site's on-ramps.

To use a site, start by setting up your on-ramps. On-ramps can be:

- [GRE or IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/)
- [Cloudflare One Appliance](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/appliance/)
- Direct [CNI link](/cloudflare-one/networks/connectors/cloudflare-wan/network-interconnect/)

Before creating a site, ensure you have set up at least one on-ramp. Then, follow these steps:

## Add a site

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Go to **Insights** > **Network visibility**.
3. In **Network overview**, select **Add site**.
4. Add a name and description for your new site. Optionally, you can also add the geographical coordinates for your site in **Latitude** and **Longitude**. If you add geographical coordinates, your site's location will appear in the map once created.
5. Select **Create and continue**.
6. Choose one or more on-ramps for your site from the list. Remember to only choose the on-ramps available to that particular site, as the list might include on-ramps available on other locations.
7. Select **Continue**.
8. In **Define alert settings** you set up alerts to notify you when there are issues with your site's on-ramps. If you want to set up alerts later, select **Skip this for now** to complete your setup. Otherwise, continue reading.
9. In **Tunnel Health Check Alert** > **Notification name**, enter a name for the site's alert.
10. Under **Alert settings**, choose how you want to be notified when there is an issue. You can add webhooks as well as email addresses.
11. In **Alert sensitivity level** define the threshold for Tunnel health alerts to be fired. For details, refer to [How Cloudflare calculates Tunnel health alerts](/cloudflare-wan/reference/how-cloudflare-calculates-tunnel-health-alerts/).
12. Select **Complete setup** to finish setting up your site.

Your site is now set up. If you have other sites you need to set up, repeat the steps above. If you did not set up alerts, we strongly recommend that you do it. Otherwise you will not be notified when there is a problem with one of your on-ramps.

---

## Network overview

After adding your sites, the Network visibility section of the dashboard provides a summary of the connectivity status and traffic analytics for all your sites. This is a great place to start if you receive a Cloudflare WAN alert, need to begin the troubleshooting process, or are performing routine monitoring.

Network visibility has the following data types available:

<Details header="Geographic map summary">
	- [Aggregate Cloudflare WAN site health](#site-health)
	- [Cloudflare WAN availability status for sites](#no-status-available)
	- [Cloudflare WAN site geographic location](#no-location-set)
</Details>

<Details header="Cloudflare WAN site data table">
	- Site Name
	- Site Health
	- Site Tunnel Names
	- Site Tunnel Statuses
	- Site Traffic Sent
	- Site Traffic Received
</Details>

<Details header="Cloudflare WAN site data">
	- Traffic Sent by Tunnel
	- Traffic Received by Tunnel
</Details>

To start using network overview:

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Go to **Insights** > **Network visibility**.

You will have access to an overview map with all your active sites, and any alerts for sites that are unhealthy or have no status available to them.

Review the following topics to learn more about the options available to you.

### Network map and traffic overview

The network map section shows all the sites configured with Cloudflare WAN. At a glance, you can check:

- How many active sites you have
- Location for sites in a map (if you set up their geographic location)
- Sites that are healthy or unhealthy
- Sites that have no status available
- Sites that have no location set

The Traffic overview section displays a more granular list of your sites and their status.

#### Site health

Sites can be healthy or unhealthy, and Cloudflare WAN uses this information to route traffic. Refer to [Set thresholds for site health](#set-thresholds-for-site-health) to learn more about this topic.

#### No status available

The status of a site refers to its health. If your sites show a **No status available** message, this means you did not configure your alert settings when creating your site. For instructions, refer to [Configure Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/configure-tunnel-health-alerts/).

#### No location set

The dashboard displays the number of sites with no location set, meaning sites for which you did not set up a geographic location. To add a location to a site, find the site you want to add location to, and select **no location set** to edit its location settings. Refer to [Set geographic coordinates](#set-geographic-coordinates) for more information.

### Traffic overview

Traffic overview aggregates all Cloudflare WAN sites configured in your account. Here, you can check summary information about each site like:

- Site status
- Traffic sent and received

Select one of your sites to have access to a more detailed view of its traffic, including traffic by tunnel.

---

## Edit a site

### Add or remove on-ramps

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/).
2. Go to **Insights** > **Network visibility**.
3. In **Network overview** > **Traffic overview**, find your site > select the three dots in front of it > **Edit**.
4. Select **On-ramps**.
5. Select **Add** to add a new on-ramp.
6. If you want to remove an on-ramp, select the three dots in front of your on-ramp > **Remove**.

### Set geographic coordinates

If you add geographic coordinates to your site, it will appear in the Network map. To set up or edit geographic coordinates to an existing site:

1. Go to **Insights** > **Network visibility**.
2. In **Network overview** > **Traffic overview**, find your site > select the three dots in front of it > **Edit**.

3. In **Basic information**, edit your site's **Latitude** and **Longitude** coordinates.
4. Select **Save**.

### Set thresholds for site health

When you set up an alert for your site, you will be notified when there is an issue with one or more on-ramps. These alerts are sent when the percentage of successful health checks for a Cloudflare WAN on-ramp drops below the selected service-level objective (SLO). Setting health alerts will also display unhealthy tunnels in the Network map and in the Traffic overview sections.

To set up health alerts:

1. Configure [Tunnel health alerts](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/configure-tunnel-health-alerts/) across all of the tunnels associated with each Cloudflare WAN site.
2. After configuring Tunnel health alerts, any Cloudflare WAN site with a tunnel (on-ramp) that is outside of its SLO threshold will be labeled unhealthy in Network map and Traffic overview.

# Update tunnel health checks frequency

import { CURL, AnchorHeading, GlossaryTooltip, TabItem, Tabs } from "~/components";

By default, Cloudflare servers send <GlossaryTooltip term="tunnel health-check" link="/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/">health checks</GlossaryTooltip> to each <GlossaryTooltip term="GRE tunnel">GRE</GlossaryTooltip>, Cloudflare Network Interconnect (CNI), or <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnel endpoint you configure to receive traffic from Cloudflare WAN.

For Cloudflare One Appliance (formerly Magic WAN Connector), Cloudflare sends health checks to IPsec tunnel endpoints.

You can configure the health check frequency through the dashboard or [the API](/api/resources/magic_transit/subresources/gre_tunnels/methods/update/) to suit your use case. For example, if you are connecting a lower-traffic site that does not need immediate failover and you prefer a lower volume of health check traffic, set the frequency to `low`. On the other hand, if you are connecting a site that is extremely sensitive to any issues and you want proactive failover at the earliest sign of a potential problem, set this to `high`.

Available options are `low`, `mid`, and `high`.

To configure health checks frequency in Cloudflare One Appliance, refer to [Configure Connector](#configure-connector)

## Manual configuration

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. To create or edit your tunnel, refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels).
2. Change the **Health check rate** to your desired rate. For example, _Low_.
3. Save your changes.

</TabItem> <TabItem label="API">

You can adjust the health check frequency by updating your [GRE](/api/resources/magic_transit/subresources/gre_tunnels/methods/update/), [IPsec](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/update/), or [CNI](/api/resources/magic_transit/subresources/cf_interconnects/methods/update/) tunnels.

The following example adjusts tunnel health check frequency to `low`. Note that this command applies to GRE, IPsec and CNI tunnels:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{ipsec_tunnel_id}"
	method="PUT"
	json={{
		health_check: {
			rate: "low",
		},
	}}
/>

</TabItem> </Tabs>

## Configure Connector

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/) > **Networks**.
2. Go to **Connectors** > **Appliances**.
3. In **Profiles**, find the Connector profile you want to edit > select the three dots > **Edit**.
4. In **Network Configuration** > **WAN configuration** > select your WAN > **Edit**.

5. Change the **Health check rate** to your desired rate.
6. Select **Save**.

:::note
Cloudflare WAN customers with [Customer Metadata Boundary](/data-localization/metadata-boundary/) enabled for the European Union can access GRE, IPsec, and CNI (Cloudflare Network Interconnect) health check and traffic volume data in the Cloudflare dashboard and through the API. This ensures that customers who need to be General Data Protection Regulation (GDPR) compliant can access all Cloudflare WAN features.
:::

# Configure Cloudflare source IPs

import { APIRequest, DashButton, TabItem, Tabs } from "~/components";

You can configure the source IP address range used by Cloudflare whenever a Cloudflare service, such as Cloudflare Load Balancing, sends traffic to a Cloudflare One private network. This address range is referred to as the Cloudflare Source IP Prefix (or `cloudflare_source` subnet type in the API).
- IPv4 traffic is sourced from `100.64.0.0/12`. This range is configurable.
- IPv6 traffic is sourced from `2606:4700:cf1:5000::/64`. This range is not configurable.

When Cloudflare services send traffic to your private network, the source IP address determines how return traffic is routed. It also determines whether on-premises security devices can properly inspect the traffic. In legacy routing mode, traffic to private networks is sourced from public Cloudflare IPs, which can cause routing and security issues.

For customers using [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing), traffic to private networks is sourced from a dedicated, non-internet-routable private IPv4 range by default. This ensures:

- **Symmetric routing** — Return traffic stays on your private network connection instead of taking an asymmetric path over the public Internet.
- **Firewall state preservation** — On-premises stateful firewalls can track connections end-to-end because they see both request and response traffic.
- **Security and compliance** — Private traffic stays on secure private paths.

Customers may wish to change the default allocated range to avoid IP conflicts or fit with an existing IP Address Management plan.

You must configure routes in your network so that response traffic for these source ranges is sent back to Cloudflare over your Cloudflare One connections.

## Prerequisites

Before you begin, ensure that:
- You have Cloudflare One [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). If your account uses Legacy Routing, follow [How to upgrade to Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#how-to-upgrade-to-unified-routing).
- You have [Cloudflare One Networks Write](/fundamentals/api/reference/permissions/) permission.
- Your desired new network range meets the following requirements:
  - Your network must be defined as a single CIDR with a prefix length of `/12`.
  - Cloudflare One subnets in the same account cannot overlap. Default allocations include:
    - Cloudflare Source IPs (`100.64.0.0/12`)
    - Hostname Route Token IPs (`172.64.128.0/20` by default; configurable)
    - Cloudflare One Clients (`100.96.0.0/12`)
    - Private Load Balancers (`100.112.0.0/16`)
  - The source subnet cannot match or contain any existing route in your Cloudflare One routing table. The source subnet can be within a supernet route.

## Affected connectors and services

### Connectors

Cloudflare One supports multiple [connectivity options](/cloudflare-one/networks/connectivity-options/). The following connectors will receive traffic from the `cloudflare_source` subnet when a Cloudflare service initiates a request to the connected network or endpoint as an offramp:

- **Anycast tunnels:** GRE, IPsec, and CNI
- **Software connectors:** Cloudflare One Client and Cloudflare Mesh

Networks or endpoints connected via Cloudflare Tunnel will not receive traffic from the Cloudflare source IP subnet. Instead, the source IP address will be that of the host running the `cloudflared` software.

### Services that originate or proxy connections

All Cloudflare services that originate or proxy connections will send traffic from a Cloudflare source IP.

This includes traffic that is proxied from a private network or endpoint onramp.

For example, traffic onramped from a Cloudflare One Client through Cloudflare Load Balancer or Gateway DNS Resolver will present a Cloudflare source IP to the destination offramp.

## Configure source IPs

:::note
You need Unified Routing to configure source IPs. If your account uses Legacy Routing, follow [How to upgrade to Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#how-to-upgrade-to-unified-routing).
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Go to the **Address space** page.

   <DashButton url="/?to=/:account/ip-addresses/address-space" />

2. Select the **Custom IPs** tab.
3. Find the prefix you want to update. This is your new `/12` range.
4. Select the three dots to the right of the prefix > **Edit**.
5. Enter a new prefix in the **IP address** field.
6. Select **Save**.

</TabItem> <TabItem label="API">

To set up your source IPs, send a `PATCH` request to the [Update Cloudflare Source Subnet endpoint](/api/resources/zero_trust/subresources/networks/subresources/subnets/subresources/cloudflare_source/) with your desired network range. The payload must include the network (your new `/12` range), and may include a name and comment.

Example:

<APIRequest
	path="/accounts/{account_id}/zerotrust/subnets/cloudflare_source/{address_family}"
	method="PATCH"
	json={{
		"comment": "example_comment",
		"name": "IPv4 Cloudflare Source IPs",
		"network": "100.64.0.0/12"
	}}
/>

</TabItem> </Tabs>

# Configure routes

import { Aside, APIRequest, DashButton, Steps, TabItem, Tabs } from "~/components";

Cloudflare Virtual Network uses a routing table to steer your traffic from Cloudflare's global network to your connected networks via next-hop. You can add entries to the Cloudflare Virtual Network routing table through static route configuration or routes learned from BGP peering over IPsec and GRE tunnels or CNI with Dataplane v2 (closed beta).

Refer to [Traffic Steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/) for more information about all the technical aspects related to:

- Routes' priorities and weights
- Regional scoping of traffic to reduce latency
- BGP peering
- [Automatic Return Routing (ARR)](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#automatic-return-routing)

<Aside type="note" title="One page for all your routes">
The dashboard **Routes** page shows the routes for all of your connectors — including Cloudflare Tunnel and Cloudflare Mesh — in a single table, not just Cloudflare WAN static routes. The following steps cover Cloudflare WAN static routes. Routes for other connector types appear in the same table and can be filtered by type.
</Aside>

## Configure static routes

The following IPv4 address ranges are allowed in the Cloudflare Virtual Network routing table:

- [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) address space, specifically `10.0.0.0/8`, `172.16.0.0/12`, and `192.168.0.0/16`.

When using Cloudflare WAN and Cloudflare Tunnel together, consider the IP ranges utilized in the static routes of Cloudflare Tunnel when selecting static routes for Cloudflare WAN. For more information, refer to [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-tunnel/).

For prefixes outside RFC 1918, contact your Cloudflare customer service manager.

### Create a static route

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Routes** > **WAN Routes**, and select **Create** to add a new route.
3. Enter a descriptive name for your route in **Description**.
4. In **Prefix**, enter your range of IP addresses. For example, `10.10.10.100/24`.
5. In **Tunnel/Next hop**, select a tunnel for your route from the tunnels you created in [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).
6. Choose the **Priority** for your route. Lower numbers have higher priorities.
	:::note
	Cloudflare routing applies longest-prefix match. A more specific static route (like `/30`) always takes precedence over a less specific one (like `/29`), regardless of tunnel priority — unless you remove the more specific route. Keep this in mind when configuring priorities for your routes. Refer to [Route prioritization](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#route-prioritization) for more information.
	:::
7. (Optional) Choose a **Weight** for your route. Refer to [Set priority and weights for static routes](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#set-priority-and-weights-for-static-routes) for examples.
8. (Optional) If you need to scope your route to a specific region, you can do it in **Region code**.
9. (Optional) We highly recommend testing your route before adding it by selecting **Test routes**.
10. Select **Add routes**.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a `POST` request [using the API](/api/resources/magic_transit/subresources/routes/methods/create/) to create one or more static routes.

Example:

<APIRequest
  path="/accounts/{account_id}/magic/routes"
  method="POST"
  json={{
		"nexthop": "<IP_NEXT_HOP>",
		"prefix": "<YOUR_IP_PREFIX>",
		"priority": 0,
		"id": "023e105f4ecef8ad9ca31a8372d0c353",
		"description": "<ROUTE_DESCRIPTION>",
		"scope": {
			"colo_names": [
				"den01"
			],
			"colo_regions": [
				"APAC"
			]
		},
		"weight": 0
  }}
/>

```json output
{
  "errors": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "result": {
    "routes": [
      {
        "nexthop": "203.0.113.1",
        "prefix": "192.0.2.0/24",
        "priority": 0,
        "id": "023e105f4ecef8ad9ca31a8372d0c353",
        "description": "New route for new prefix 203.0.113.1",
        "scope": {
          "colo_names": [
            "den01"
          ],
          "colo_regions": [
            "APAC"
          ]
        },
        "weight": 0
      }
    ]
  },
  "success": true
}
```

</TabItem> </Tabs>

### Edit a static route

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In **Routes** > **WAN Routes**, locate the route to modify.
2. Select the three dots next to it > **Edit**.
3. Enter the updated route information.
4. (Optional) We highly recommend testing your route before adding it by selecting **Test routes**.
5. Select **Edit routes**.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a `PUT` request [using the API](/api/resources/magic_transit/subresources/routes/methods/update/) to update one or more static routes.

Example:

<APIRequest
  path="/accounts/{account_id}/magic/routes/{route_id}"
  method="PUT"
  json={{
		"nexthop": "<IP_NEXT_HOP>",
		"prefix": "<YOUR_IP_PREFIX>",
		"priority": 0,
		"id": "023e105f4ecef8ad9ca31a8372d0c353",
		"description": "<ROUTE_DESCRIPTION>",
		"scope": {
			"colo_names": [
				"den01"
			],
			"colo_regions": [
				"APAC"
			]
		},
		"weight": 0
  }}
/>

```json output
{
  "errors": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "result": {
    "modified": true,
    "modified_route": {
      "nexthop": "203.0.113.1",
      "prefix": "192.0.2.0/24",
      "priority": 0,
      "id": "023e105f4ecef8ad9ca31a8372d0c353",
      "description": "New route for new prefix 203.0.113.1",
      "scope": {
        "colo_names": [
          "den01"
        ],
        "colo_regions": [
          "APAC"
        ]
      },
      "weight": 0
    }
  },
  "success": true
}
```

</TabItem> </Tabs>

### Delete static route

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In **Routes** > **WAN Routes**, locate the static route to delete.
2. Select the three dots next to it > **Delete**.
3. Confirm the action by selecting the checkbox and select **Delete**.

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

Create a `DELETE` request [using the API](/api/resources/magic_transit/subresources/routes/methods/delete/) to delete a static route.

Example:

<APIRequest
  path="/accounts/{account_id}/magic/routes/{route_id}"
  method="DELETE"
/>

```json output
{
  "errors": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "result": {
    "deleted": true,
    "deleted_route": {
      "nexthop": "203.0.113.1",
      "prefix": "192.0.2.0/24",
      "priority": 0,
      "id": "023e105f4ecef8ad9ca31a8372d0c353",
      "description": "New route for new prefix 203.0.113.1",
      "scope": {
        "colo_names": [
          "den01"
        ],
        "colo_regions": [
          "APAC"
        ]
      },
      "weight": 0
    }
  },
  "success": true
}
```

</TabItem> </Tabs>

## Configure Automatic Return Routing

[Automatic Return Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#automatic-return-routing) allows Cloudflare to track network flows from your Cloudflare WAN (formerly Magic WAN) connected locations, ensuring return traffic is routed back to the connection where it was received without requiring static or dynamic routes. This functionality requires [Unified Routing mode](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing).

To enable ARR:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Follow the [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) information to learn how to create an IPsec or GRE tunnel.
2. On the tunnel's options, select **Automatic return routing**.
3. Select **Add tunnels** to save your changes.

</TabItem> <TabItem label="API">

Create a `POST` request to create an [IPsec](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/create/) or [GRE](/api/resources/magic_transit/subresources/gre_tunnels/methods/create/) tunnel with ARR enabled. For example:

<APIRequest
	path="/accounts/{account_id}/magic/ipsec_tunnels"
	method="POST"
	json={{
		"cloudflare_endpoint": "<CLOUDFLARE_ENDPOINT>",
		"interface_address": "<INTERFACE_ADDRESS>",
		"name": "IPsec_1",
		"customer_endpoint": "<CUSTOMER_ENDPOINT>",
		"description": "Tunnel for ISP X",
		"psk": "<PSK>",
		"automatic_return_routing": "true"
	}}
/>

</TabItem> </Tabs>

## Configure BGP routes

BGP peering is available when using the following on-ramps:

- [IPsec and GRE tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/). Requires [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing).
- [CNI with Dataplane v2](/network-interconnect/) (closed beta and not available to new customers).

### Choose an ASN for BGP peering

The Cloudflare Virtual Network routing table is managed by the customer. You can select both the Cloudflare-side ASN (Autonomous System Number) and the ASN for your customer device. The customer device ASN can be 2-byte or 4-byte.

By default, each BGP peering session uses the same Cloudflare-side ASN to represent peering with the Cloudflare Virtual Network routing table. This ASN is called the **CF Account ASN** and is set to `13335`. You can configure this to a private 2-byte ASN (any value between `64512` and `65534`, such as `65000`).

The CF Account ASN applies to all BGP sessions in your account, over both IPsec/GRE tunnels and CNI. It must be different from the customer device ASN on each tunnel.

To set this ASN:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Routes** > **WAN configuration**.
3. In **Border Gateway Protocol (BGP) configuration**, select **Edit** and enter your ASN.
4. Select **Save**.

</TabItem> <TabItem label="API">

Create a `PUT` request to update the BGP settings for your account. The following example sets the CF Account ASN to `65000`:

<APIRequest
  path="/accounts/{account_id}/magic/bgp/settings"
  method="PUT"
  json={{
		"cloudflare_asn": 65000
  }}
/>

</TabItem> </Tabs>

Cloudflare WAN customers should also be aware of the following:

- The customer chooses their device ASN, which must be different from the Cloudflare-side ASN.
- The Cloudflare side ASN will be included in the `AS_PATH` of announced routes to any BGP enabled on-ramp (interconnect, IPsec or GRE tunnel).
- The customer-announced `AS_PATH` is transitive between on-ramps — meaning the origin (customer) ASN is visible in the `AS_PATH` of routes received from Cloudflare via BGP. Due to default BGP loop prevention mechanisms, a router will reject any route that contains its own ASN in the `AS_PATH`. For example, if two Cloudflare WAN-connected sites both use `ASN 65000`, site A will not accept routes from site B, and vice versa, because each site sees its own ASN in the advertised `AS_PATH`. <br />
	To enable routing between private networks over Cloudflare WAN, you should either:
	- Assign a unique ASN to each site/network, or
	- Configure your edge CPE to accept BGP routes that include its own ASN in the `AS_PATH`.

### Set up BGP peering

You need to configure two ASNs:

- The Cloudflare [account-scoped ASN](#choose-an-asn-for-bgp-peering) named **CF Account ASN**.
- One ASN for each on-ramp you want to configure with BGP.

If you have already set up your Cloudflare account ASN, skip steps two and three below.

#### Set up BGP for an interconnect

:::note
BGP over CNI is in closed beta and is not currently available to new customers. If you are interested in BGP peering over CNI, contact your account team.
:::

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Routes** > **WAN configuration**.
3. In **Border Gateway Protocol (BGP) configuration**, select **Edit ASN** and enter your ASN.
4. Go to **Networks** > **Connectors** > **Interconnects**.
5. Locate the CNI interconnect with Dataplane v2 to configure with BGP > select the **three dots** next to it > **Configure BGP**.
6. In **Customer device ASN**, enter the ASN for your network.

	<Aside type="note">Multiple tunnels or interconnects with the same ASN will not exchange routes if standard BGP loop prevention is enabled. Consider using a different ASN per session, or enabling duplicate ASNs (like Cisco's <code>allowas-in</code> feature) to exchange routes between networks. </Aside>

7. In **MD5 key**, you can optionally enter the key for your network. Note that this is meant to prevent accidental misconfigurations and is not a security mechanism.
8. (Optional) In **Additional Advertised prefix list**, input any additional prefixes you want to advertise alongside your existing routes. Leave this blank if you do not want to advertise extra routes. Typical prefixes to configure here include:
	- A route to `0.0.0.0/0`, the default route — to attract all Internet-bound traffic if using Cloudflare WAN with Gateway.
	- A route to `100.96.0.0/12`, the portion of CGNAT space [used by default with Cloudflare One Clients](/mesh/features/routes/#return-traffic-routing).
	- A route to `100.64.0.0/12`, the portion of CGNAT space [used by default for Cloudflare Source IPs](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-cloudflare-source-ips/).
9. Select **Save**.

#### Set up BGP for IPsec/GRE tunnels

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Routes** > **WAN configuration**.
3. In **Border Gateway Protocol (BGP) configuration**, select **Edit ASN** and enter your ASN.
4. Go to **Networks** > **Connectors** > **Cloudflare WAN**.
5. In **IPsec/GRE tunnels**, locate the tunnel you want to configure with BGP > select the **three dots** next to it > **Configure BGP**.
6. In **Customer device ASN**, enter the ASN for your network.

	<Aside type="note">Multiple tunnels or interconnects with the same ASN will not exchange routes if standard BGP loop prevention is enabled. Consider using a different ASN per session, or enabling duplicate ASNs (like Cisco's <code>allowas-in</code> feature) to exchange routes between networks. </Aside>

7. In **MD5 key**, you can optionally enter the key for your network. Note that this is meant to prevent accidental misconfigurations and is not a security mechanism.
8. (Optional) In **Additional Advertised prefix list**, input any additional prefixes you want to advertise alongside your existing routes. Leave this blank if you do not want to advertise extra routes. Typical prefixes to configure here include:
	- A route to `0.0.0.0/0`, the default route — to attract all Internet-bound traffic if using Cloudflare WAN with Gateway.
	- A route to `100.96.0.0/12`, the portion of CGNAT space [used by default with Cloudflare One Clients](/mesh/features/routes/#return-traffic-routing).
	- A route to `100.64.0.0/12`, the portion of CGNAT space [used by default for Cloudflare Source IPs](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-cloudflare-source-ips/).
9. Select **Save**.

### Important remarks for GRE/IPsec tunnels

If you are configuring BGP peering for a tunnel (GRE or IPsec) you must be aware of the following:

- Your Customer Premises Equipment (CPE) must initiate the BGP peering session. Cloudflare will not initiate.
- Your BGP speaker must peer with the tunnel's IPv4 interface address. Your CPE may use any IPv4 address for its side of the peering connection; it does not need to use the other address from the `/31` or `/30` interface subnet.
  :::caution
	If the tunnel is to an Azure VPN gateway, the tunnel interface address must not be in the link-local range. Azure will not initiate BGP sessions to peers using link-local addresses. Use an RFC 1918 address for your tunnel interface address instead.
  :::
- Hold time must be 30 seconds or greater. Cloudflare recommends at least 90 seconds. Cloudflare advertises a hold time of 240 seconds, so if you set a value greater than 240 seconds, the negotiated hold time will be 240 seconds.
- Connect retry time should be low (for example, five or 10 seconds).
- Your CPE may advertise up to 5,000 prefixes on one BGP session.
- MD5 authentication is optional. You can use a maximum of 80 characters. Supported characters include ``a-zA-Z0-9'!@#$%^&*()+[]{}<>/.,;:_-~`= \\|``
  :::caution
	MD5 authentication is not a valid security mechanism. The MD5 key is not treated as a secret value. This is only supported for preventing misconfiguration, not for defending against malicious attacks.

	When MD5 is enabled, you cannot use Telnet to test BGP connectivity (Telnet does not support TCP MD5 authentication).
  :::

### Filter BGP routes

BGP filter profiles control routes exchanged over your peering session with Cloudflare. They apply to BGP over GRE or IPsec tunnels.

Each account-scoped profile requires a unique name, one match action, and up to 1,000 Classless Inter-Domain Routing (CIDR) targets.

Match actions have the following behavior:

| Match action | Behavior                                           |
| ------------ | -------------------------------------------------- |
| `allow`      | Accept matching routes and reject everything else. |
| `deny`       | Reject matching routes and accept everything else. |

Each CIDR target has a matching mode that determines whether the prefix and its subnets match:

| Matching mode                   | Syntax              | Matches                                             |
| ------------------------------- | ------------------- | --------------------------------------------------- |
| Exact prefix                    | `10.0.0.0/8`        | Only `10.0.0.0/8`.                                  |
| Prefix and more-specific routes | `10.0.0.0/8+`       | `10.0.0.0/8` and every more-specific subnet.        |
| Exact length                    | `10.0.0.0/8{16}`    | Every `/16` subnet within `10.0.0.0/8`.             |
| Length range                    | `10.0.0.0/8{16,24}` | Every `/16` through `/24` subnet within the prefix. |

Each tunnel supports one import filter and one export filter.

Import filters apply to routes Cloudflare receives from the peer. Rejected routes do not enter the Magic routing table.
Rejected routes remain in the local routing information base (RIB-in). Making the filter more permissive adds them to the Magic routing table without a route refresh.

Export filters apply to routes selected for peer advertisement. Prefixes in the tunnel's `extra_prefixes` field bypass export filters.

#### Create profiles

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>

1. Go to the **Routes** page.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. From the **BGP filter profiles** tab, select **Create filter profile**.
3. Enter a descriptive **Name** for your filter profile and optionally add a **Description**.
4. Choose **Allow** or **Deny** as the **Action** to take when a route matches a target.
5. Add a **Target** by entering a CIDR prefix in **CIDR prefix**. Choose which **Match** mode this target should use.
6. If you want to add more targets, choose **Add target**.
7. Select **Create**.

</Steps>

</TabItem> <TabItem label="API">

You need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.

The following request creates an allow profile for corporate subnets:

<APIRequest
	path="/accounts/{account_id}/magic/bgp/filter_profiles"
	method="POST"
	json={{
		name: "Allowed on-premises imports",
		description: "Corporate subnets accepted from the customer router",
		match_action: "allow",
		targets: ["10.0.0.0/8+", "192.168.0.0/16", "2001:db8::/32+"],
	}}
/>

</TabItem> </Tabs>

Profile targets have these requirements:

- Targets must use valid CIDR network addresses without whitespace. Cloudflare rejects duplicate or equivalent targets.
- Profiles support IPv4 and IPv6 targets, but not IPv4-mapped IPv6 targets.

#### Assign profiles

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Steps>

1. Go to the **Connectors** page and choose **IPsec/GRE tunnels**.

   <DashButton url="/?to=/:account/magic-networks/connections" />

2. Choose **Create a tunnel** to make a new tunnel or choose **Configure BGP** from the three-dot menu next to an existing tunnel.
3. Select a profile from **Import filter profile**. To remove it, select **X** next to the selected profile.
4. Repeat for **Export filter profile**.
5. Choose **Add tunnels** or **Save**.

</Steps>

</TabItem> <TabItem label="API">

The following request assigns import and export profiles to a GRE tunnel. Set each field to a profile ID from your account.

<APIRequest
	path="/accounts/{account_id}/magic/gre_tunnels/{gre_tunnel_id}"
	method="PUT"
	json={{
		bgp: {
			import_filter_id: "a1b2c3d4e5f647890a1b2c3d4e5f6789",
			export_filter_id: "b2c3d4e5f6a78901b2c3d4e5f6a78901",
		},
	}}
/>

To remove an assigned profile, set its corresponding field to `null`.

</TabItem> </Tabs>

Profile assignments have these behaviors and limits:

- Changing a profile or a tunnel's profile assignments does not reset BGP sessions.
- You cannot delete an in-use profile. Detach the profile from all tunnels first.
- Cloudflare enforces the 5,000-route RIB-in limit before import filtering. Rejected routes still count toward this limit.

## Next steps

Now that you have configured your tunnels and routes, the next step is to create a site. Sites represent the local network of a data center, office, or other physical location, and combine all on-ramps available there. Sites also allow you to check, at a glance, the state of your on-ramps and set up health alert settings so that Cloudflare notifies you when there are issues with the site's on-ramps.

Refer to [Set up a site](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/sites/) for more information.

# Configure tunnel endpoints

import { APIRequest, CURL, Details, GlossaryTooltip, TabItem, Tabs } from "~/components";

Cloudflare assigns an IPv4 anycast address to your account for use as the tunnel destination for your network's routers. You can find this address in the Cloudflare dashboard under **Address Space** > [**Leased IPs**](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space). To request additional endpoint addresses, contact your account team.

Cloudflare handles failures on its network automatically by advertising your endpoint IP from multiple nodes across many globally distributed data centers. To handle failures on your network, configure two tunnels from separate routers.

## Before you begin

Before creating a tunnel, make sure you have the following information:

- **Cloudflare endpoint address**: The anycast IP address assigned to your account. You can find it in the Cloudflare dashboard under **Address Space** > [**Leased IPs**](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
- **Customer endpoint IP**: A public Internet routable IP address outside of the prefixes Cloudflare will advertise on your behalf (typically provided by your ISP). Not required if using [Cloudflare Network Interconnect](/network-interconnect/) or for <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnels (unless your router uses an <GlossaryTooltip term="Internet key exchange (IKE)">IKE</GlossaryTooltip> ID of type `ID_IPV4_ADDR`).
- **Interface address**: A `/31` (recommended) or `/30` subnet from RFC 1918 private IP space (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) or `169.254.240.0/20`(this address space is also a link-local address).

:::caution
Make sure the interface address prefixes are always within the allowed Cloudflare ranges, especially for cloud service providers that might automatically generate prefixes for you. Otherwise, the tunnel will not work.
:::

## Ways to onboard traffic to Cloudflare

### GRE and IPsec tunnels

You can use GRE or IPsec tunnels to onboard your traffic to Cloudflare WAN, and set them up through the Cloudflare dashboard or the API. If you use the API, you need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API key](/fundamentals/api/get-started/keys/#view-your-global-api-key).

#### Choose between GRE and IPsec

| Feature | GRE | IPsec |
|---------|-----|-------|
| Encryption | No | Yes |
| Authentication | No | Pre-shared key (PSK) |
| Setup complexity | Simpler | Requires PSK exchange |
| Best for | Trusted networks, CNI connections | Internet-facing connections requiring encryption |

Refer to [Tunnels and encapsulation](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/) to learn more about the technical requirements for both tunnel types.

#### IPsec supported ciphers

Refer to [supported ciphers for IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters) for a complete list. IPsec tunnels only support Internet Key Exchange version 2 (IKEv2).

#### Anti-replay protection

If you use Cloudflare WAN and <GlossaryTooltip term="anycast">anycast</GlossaryTooltip> IPsec tunnels, we recommend disabling anti-replay protection. Cloudflare disables this setting by default. However, you can enable it through the API or the Cloudflare dashboard for devices that do not support disabling it, including Cisco Meraki, Velocloud, and AWS VPN Gateway.

Refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/) for more information on this topic, or [Add IPsec tunnels](#add-ipsec-tunnel) to learn how to enable this feature.

### Network Interconnect (CNI)

Beyond GRE and IPsec tunnels, you can also use Network Interconnect (CNI) to onboard your traffic to Cloudflare WAN. Refer to [Network Interconnect (CNI)](/cloudflare-one/networks/connectors/cloudflare-wan/network-interconnect/) for more information.

## Add tunnels

:::caution
Cloudflare Network Firewall rules apply to Internet Control Message Protocol (ICMP) traffic. If you enable Cloudflare Network Firewall, ensure your rules allow ICMP traffic sourced from Cloudflare public IPs. Otherwise, <GlossaryTooltip term="tunnel health-check">health checks</GlossaryTooltip> will fail. Refer to [Cloudflare Network Firewall rules](/cloudflare-network-firewall/about/ruleset-logic/#cloudflare-network-firewall-rules-and-magic-transit-endpoint-health-checks) for more information.
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Cloudflare WAN**, and select **Create**.
3. On the **Add Tunnel** page, choose either a **GRE tunnel** or **IPsec tunnel**.
4. In **Name**, give your tunnel a descriptive name. This name must be unique, cannot contain spaces or special characters, and cannot be shared with other tunnels.
5. _(Optional)_ Give your tunnel a description in **Description**.
6. In **IPv4 Interface address**, enter the internal IP address for your tunnel along with the interface's prefix length (`/31` or `/30`). This is used to route traffic through the tunnel on the Cloudflare side. We recommend using a `/31` subnet, as it provides the most efficient use of IP address space.

Expand the section below for your tunnel type to complete the configuration:

<span id="add-gre-tunnel"></span>
<Details header="GRE tunnel">

7. In **Customer GRE endpoint**, enter your router's public IP address. You do not need this value if you use a physical or virtual connection like Cloudflare Network Interconnect because Cloudflare provides it.
8. In **Cloudflare GRE endpoint**, enter one of the anycast addresses assigned to your account. You can find them in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
9. _(Optional)_ Leave the default values for **TTL** and **MTU**, or customize them for your network.
10. _(Optional)_ Configure health check settings. Expand the following to learn more about each option:

    <Details header="Health check options">

    - **Tunnel health checks**: Enabled by default. If you disable tunnel health checks, your tunnels appear 100% down in your [tunnel health dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) even when working. Cloudflare keeps sending traffic through the tunnel without the means to detect if the tunnel goes down. You must set up your own system to detect down tunnels, as Cloudflare cannot warn you about down tunnels. Refer to [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) for more information.
    - **Health check rate**: If you keep tunnel health checks enabled, choose a [health check rate](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/update-tunnel-health-checks-frequency/) for your tunnel. Available options are _Low_, _Medium_, and _High_.
    - **Health check type**: Defaults to _Reply_ and to creating an ICMP (Internet Control Message Protocol) reply. If your firewall drops this type of packet because it assumes the packet is an attack, change this option to _Request_ which creates an ICMP request. Refer to [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) for more information.
    - **Health check direction**: Defaults to **bidirectional** for Cloudflare WAN. Refer to [Bidirectional vs unidirectional health checks](#bidirectional-vs-unidirectional-health-checks) for more details.
    - **Health check target**: The customer end of the tunnel. This field is only visible when **Health check direction** is set to _Unidirectional_.

    </Details>

11. _(Optional)_ We recommend you test your tunnel before officially adding it. To test the tunnel, select **Test tunnels**.
12. (_Optional_) Select **Automatic return routing** if you are setting up this tunnel for a site that only needs to send traffic to and receive responses from Cloudflare, and does not need to receive traffic from other sites in your WAN. This feature requires [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). Refer to [Configure Automatic Return Routing](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#configure-automatic-return-routing) for more information.
13. To add multiple tunnels, select **Add GRE tunnel** for each new tunnel.
14. After adding your tunnel information, select **Add tunnels**.
15. (_Optional_) Select **Allow BGP (Border Gateway Protocol) peering** if you want to dynamically exchange routes between your network and Cloudflare. This feature requires [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). <br /> BGP is recommended for environments with frequently changing routes or when you need automatic failover. Refer to [Configure BGP routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#configure-bgp-routes) for more information.

</Details>

<span id="add-ipsec-tunnel"></span>
<Details header="IPsec tunnel">

7. _(Optional)_ In **Customer endpoint**, enter your router's public IP address. This value is only required if your router uses an IKE ID of type `ID_IPV4_ADDR`.
8. In **Cloudflare endpoint**, enter one of the anycast addresses assigned to your account. You can find them in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
9. _(Optional)_ Configure health check settings. Expand the following to learn more about each option:

    <Details header="Health check options">

    - **Tunnel health checks**: Enabled by default. If you disable tunnel health checks, your tunnels appear 100% down in your [tunnel health dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) even when working. Cloudflare keeps sending traffic through the tunnel without the means to detect if the tunnel goes down. You must set up your own system to detect down tunnels, as Cloudflare cannot warn you about down tunnels. Refer to [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) for more information.
    - **Health check rate**: If you keep tunnel health checks enabled, choose a [health check rate](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/update-tunnel-health-checks-frequency/) for your tunnel. Available options are _Low_, _Medium_, and _High_.
    - **Health check type**: Defaults to _Reply_ and to creating an ICMP (Internet Control Message Protocol) reply. If your firewall drops this type of packet because it assumes the packet is an attack, change this option to _Request_ which creates an ICMP request. Refer to [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) for more information.
    - **Health check direction**: Defaults to **bidirectional** for Cloudflare WAN. Refer to [Bidirectional vs unidirectional health checks](#bidirectional-vs-unidirectional-health-checks) for more details.
    - **Health check target**: The customer end of the tunnel. This field is only visible when **Health check direction** is set to _Unidirectional_.

    </Details>

    :::note
    IPsec tunnels will not function without a pre-shared key (PSK).
    :::

10. If you do not have a pre-shared key yet:

    1. Select **Add pre-shared key later**.
    2. _(Optional)_ We recommend you test your tunnel configuration before officially adding it. To test the tunnel, select **Test tunnels**.
    3. Select **Add tunnels**.
    4. The Cloudflare dashboard loads the list of tunnels you have configured. The IPsec tunnel you just created displays a warning triangle icon to indicate it is not yet functional. Select **Edit**.
    5. Choose **Generate a new pre-shared key** > **Update and generate a pre-shared key**. Save the key to a safe place, and select **Done**.

11. If you already have a pre-shared key:

    1. Select **Use my own pre-shared key**.
    2. Paste your key in **Your pre-shared key**.
    3. _(Optional)_ We recommend you test your tunnel before officially adding it. To test the tunnel, select **Test tunnels**.
    4. Select **Add tunnels**.

12. _(Optional)_ Enable **Replay protection** if you have devices that do not support disabling it. Refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/) for more information.
13. (_Optional_) Select **Automatic return routing** if you are setting up this tunnel for a site that only needs to send traffic to and receive responses from Cloudflare, and does not need to receive traffic from other sites in your WAN. This feature requires [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). Refer to [Configure Automatic Return Routing](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#configure-automatic-return-routing) for more information.
14. To add multiple tunnels, select **Add IPsec tunnel** for each new tunnel.
15. After adding your tunnel information, select **Add tunnels**.
16. (_Optional_) Select **Allow BGP (Border Gateway Protocol) peering** if you want to dynamically exchange routes between your network and Cloudflare. This feature requires [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). <br /> BGP is recommended for environments with frequently changing routes or when you need automatic failover. Refer to [Configure BGP routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#configure-bgp-routes) for more information.

</Details>

</TabItem> <TabItem label="API">

:::note
You will need your [account ID](/fundamentals/account/find-account-and-zone-ids/) and [API token](/fundamentals/api/get-started/account-owned-tokens/) to use the API.
:::

<Details header="GRE tunnel">

Create a `POST` request [using the API](/api/resources/magic_transit/subresources/gre_tunnels/methods/create/) to create a GRE tunnel.

<APIRequest
  path="/accounts/{account_id}/magic/gre_tunnels"
  method="POST"
  json={{
		"name": "<TUNNEL_NAME>",
		"description": "<TUNNEL_DESCRIPTION>",
		"interface_address": "<INTERFACE_ADDRESS>",
		"cloudflare_gre_endpoint": "<CLOUDFLARE_ENDPOINT>",
		"customer_gre_endpoint": "<CUSTOMER_ENDPOINT>"
  }}
/>

```json output
{
  "errors": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "messages": [
    {
      "code": 1000,
      "message": "message"
    }
  ],
  "result": {
    "gre_tunnels": [
      {
        "cloudflare_gre_endpoint": "<IP_ADDRESS>",
        "customer_gre_endpoint": "<IP_ADDRESS>",
        "interface_address": "<INTERFACE_CIDR>",
        "name": "<TUNNEL_NAME>",
        "description": "<TUNNEL_DESCRIPTION>",
        "health_check": {
          "direction": "unidirectional",
          "enabled": true,
          "rate": "low",
          "type": "reply"
        },
        "mtu": 0,
        "ttl": 0
      }
    ]
  },
  "success": true
}
```

</Details>

<Details header="IPsec tunnel">

1. Create a `POST` request [using the API](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/create/) to create an IPsec tunnel.

	Note that in the example, replay protection is disabled by default. You can enable it with the flag `"replay_protection": true` for each IPsec tunnel, if the devices you use do not support disabling this feature. If you have already created IPsec tunnels, update them with a [`PUT` request](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/update/). Refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/) for more information on this topic.

	<APIRequest
		path="/accounts/{account_id}/magic/ipsec_tunnels"
		method="POST"
		json={{
			"name": "<TUNNEL_NAME>",
			"description": "<TUNNEL_DESCRIPTION>",
			"interface_address": "<INTERFACE_ADDRESS>",
			"cloudflare_endpoint": "<CLOUDFLARE_ENDPOINT>",
			"customer_endpoint": "<CUSTOMER_ENDPOINT>"
		}}
	/>

	```json output
	{
		"errors": [
			{
				"code": 1000,
				"message": "message"
			}
		],
		"messages": [
			{
				"code": 1000,
				"message": "message"
			}
		],
		"result": {
			"ipsec_tunnels": [
				{
					"id": "<IPSEC_TUNNEL_ID>",
					"interface_address": "<INTERFACE_CIDR>",
					"name": "<TUNNEL_NAME>",
					"cloudflare_endpoint": "<IP_ADDRESS>",
					"customer_endpoint": "<IP_ADDRESS>",
					"description": "<TUNNEL_DESCRIPTION>",
					"health_check": {
						"direction": "unidirectional",
						"enabled": true,
						"rate": "low",
						"type": "reply"
					},
					"psk_metadata": {},
					"replay_protection": false
				}
			]
		},
		"success": true
	}
	```

	Take note of the tunnel `id` value. We will use it to generate a pre-shared key (PSK).

2. Create a `POST` [request](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/psk_generate/) to generate a PSK. Use the tunnel `id` value you received from the previous command.

	<APIRequest
		path="/accounts/{account_id}/magic/ipsec_tunnels/{ipsec_tunnel_id}/psk_generate"
		method="POST"
	/>

	```json output
	{
		"result": {
			"ipsec_id": "<IPSEC_ID>",
			"ipsec_tunnel_id": "<IPSEC_TUNNEL_ID>",
			"psk": "<PSK_CODE>",
			"psk_metadata": {
				"last_generated_on": "2025-03-13T14:28:47.054317925Z"
			}
		},
		"success": true,
		"errors": [],
		"messages": []
	}
	```

	Take note of your `psk` value.

3. Create a `PUT` [request](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/update/) to update your IPsec tunnel with the PSK.

	<CURL
		url="https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{ipsec_tunnel_id}"
		method="PUT"
		json={{
			"psk": "<PSK_VALUE>"
		}}
	/>

```json output
{
  "result": {
    "modified": true,
    "modified_ipsec_tunnel": {
      "id": "<IPSEC_ID>",
      "interface_address": "<IPSEC_CIDR>",
      "created_on": "2025-03-13T14:28:21.139535Z",
      "modified_on": "2025-03-13T14:33:26.09683Z",
      "name": "<TUNNEL_NAME>",
      "cloudflare_endpoint": "<IP_ADDRESS>",
      "customer_endpoint": "<IP_ADDRESS>",
      "remote_identities": {
        "hex_id": "",
        "fqdn_id": "",
        "user_id": ""
      },
      "psk_metadata": {
        "last_generated_on": "2025-03-13T14:28:47.054318Z"
      },
      "description": "<TUNNEL_DESCRIPTION>",
      "health_check": {
        "enabled": true,
        "target": "",
        "type": "reply",
        "rate": "mid",
        "direction": "unidirectional"
      }
    }
  },
  "success": true,
  "errors": [],
  "messages": []
}
```

4. Use the `psk` value from step 3 to configure the IPsec tunnel on your equipment as well.

</Details>

<Details header="Configure bidirectional health checks">

Bidirectional health checks are available for GRE and IPsec tunnels. For Cloudflare WAN this option defaults to bidirectional.

You can change this setting via the API with `"bidirectional"` or `"unidirectional"`:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{ipsec_tunnel_id}"
	method="PUT"
	json={{
		"health_check":
			{
				"direction": "bidirectional"
			}
	}}
/>

```json output
{
  "result": {
    "modified": true,
    "modified_ipsec_tunnel": {
      "id": "<IPSEC_ID>",
      "interface_address": "<IPSEC_CIDR>",
      "created_on": "2025-03-13T14:28:21.139535Z",
      "modified_on": "2025-03-13T14:33:26.09683Z",
      "name": "<TUNNEL_NAME>",
      "cloudflare_endpoint": "<IP_ADDRESS>",
      "customer_endpoint": "<IP_ADDRESS>",
      "remote_identities": {
        "hex_id": "",
        "fqdn_id": "",
        "user_id": ""
      },
      "psk_metadata": {
        "last_generated_on": "2025-03-13T14:28:47.054318Z"
      },
      "description": "<TUNNEL_DESCRIPTION>",
      "health_check": {
        "enabled": true,
        "target": "",
        "type": "reply",
        "rate": "mid",
        "direction": "bidirectional"
      }
    }
  },
  "success": true,
  "errors": [],
  "messages": []
}
```

</Details>

</TabItem> </Tabs>

## Bidirectional vs unidirectional health checks

To check for tunnel health, Cloudflare sends a [health check probe](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) consisting of ICMP (Internet Control Message Protocol) reply [packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) to your network. Cloudflare needs to receive these probes to know if your tunnel is healthy.

Cloudflare defaults to bidirectional health checks for Cloudflare WAN, and unidirectional health checks for Magic Transit (direct server return). However, routing unidirectional ICMP reply packets over the Internet to Cloudflare is sometimes subject to drops by intermediate network devices, such as stateful firewalls. Magic Transit customers with egress traffic can modify this setting to bidirectional.

### Legacy bidirectional health checks

For customers using the legacy health check system with a public IP range, Cloudflare recommends:

- Configuring the tunnel health check target IP address to one within the `172.64.240.252/30` prefix range.
- Applying a policy-based route that matches [packets](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) with a source IP address equal to the configured tunnel health check target (for example `172.64.240.253/32`), and route them over the tunnel back to Cloudflare.

## Next steps

Now that you have set up your tunnel endpoints, you need to configure routes to direct your traffic through Cloudflare. You have two routing options:

- **Static routes**: Best for simple, stable networks where routes rarely change. You manually define each route.
- **BGP peering**: Best for dynamic environments with frequently changing routes, multiple prefixes, or when you need automatic failover. Requires enabling BGP on your tunnel during creation.

Refer to [Configure routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/) for detailed instructions on both options.

After configuring your routes, you need to [set up a site](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/sites/).

## Troubleshooting

If you experience issues with your tunnels:

- For tunnel health check problems, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/).
- For IPsec tunnel establishment issues, refer to [Troubleshoot with IPsec logs](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/ipsec-troubleshoot/).

# How to

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Run traceroute

If you have a Cloudflare WAN (formerly Magic WAN) client connected through [GRE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [CNI](/network-interconnect/) or [WARP](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-one-client/) and want to perform a `traceroute` to an endpoint behind a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-tunnel/), the following settings must be applied for the command to return useful information.

## Inherited TTL value

On the machine where the `traceroute` client is executed, make sure the tunnel device does not inherit the TTL value of the inner packet. This is the default behavior on Linux and can result in unhelpful `traceroute` results:

```sh
sudo traceroute -s 10.1.0.100 -I 10.3.0.100
```

```sh output
traceroute to 10.3.0.100 (10.3.0.100), 30 hops max, 60 byte packets
 1  * * *
 2  * * *
 3  * * *
 4  * * *
 5  * * *
 6  * * *
 7  * * *
 8  * * *
 9  * * *
10  10.3.0.100 (10.3.0.100)  420.505 ms  420.779 ms  420.776 ms
```

Setting the TTL explicitly returns much better results:

```sh
sudo ip link set cf_gre type gre ttl 64
sudo traceroute -s 10.1.0.100 -I 10.3.0.100
```

```sh output
traceroute to 10.3.0.100 (10.3.0.100), 30 hops max, 60 byte packets
 1  10.0.0.11 (10.0.0.11)  58.947 ms  58.933 ms  58.930 ms
 2  173.245.60.175 (173.245.60.175)  61.138 ms  61.316 ms  61.313 ms
 3  172.68.145.21 (172.68.145.21)  367.448 ms  367.532 ms  367.530 ms
 4  mplat-e2e-vm3.c.magic-transit.internal (10.152.0.20)  370.362 ms  370.440 ms  370.522 ms
 5  10.3.0.100 (10.3.0.100)  370.519 ms  370.541 ms  518.152 ms
```

## Cloudflare One Client

Some Linux distributions default to a very strict setting for [reverse path filtering](https://sysctl-explorer.net/net/ipv4/rp_filter/). This strict setting attempts to drop fake traffic as a security measure. Performing a `traceroute` with this setting on can unintentionally drop `traceroute` packets. If you use the Cloudflare One Client on Linux, set a less strict policy before attempting to perform a `traceroute`:

```sh
sudo sysctl -w net.ipv4.conf.CloudflareWARP.rp_filter=2
```

```sh output
net.ipv4.conf.CloudflareWARP.rp_filter = 2
```

```sh
sudo traceroute -s 172.16.0.2 -I 10.3.0.100
```

```sh output
traceroute to 10.3.0.100 (10.3.0.100), 30 hops max, 60 byte packets
 1  169.254.21.171 (169.254.21.171)  48.887 ms  48.894 ms  48.620 ms
 2  173.245.60.175 (173.245.60.175)  49.403 ms  49.519 ms  49.603 ms
 3  172.68.65.7 (172.68.65.7)  357.499 ms  357.519 ms  357.520 ms
 4  mplat-e2e-vm3.c.magic-transit.internal (10.152.0.20)  360.024 ms  360.086 ms  360.078 ms
 5  10.3.0.100 (10.3.0.100)  360.283 ms  360.297 ms  360.489 ms
```

# Alibaba Cloud VPN Gateway

This tutorial shows you how to connect Alibaba Cloud infrastructure to Cloudflare WAN (formerly Magic WAN) through IPsec tunnels. For more information regarding Alibaba Cloud technology, refer to [Alibaba's documentation](https://www.alibabacloud.com/help/en/vpn-gateway).

## Alibaba Cloud

### 1. Create a VPC

1. Log in to your Alibaba Cloud account.
2. Go to **VPC** > **VPN Gateways**, and select **Create VPC** to create a new Virtual Private Cloud (VPC).
3. Give your VPC a descriptive name. For example, `Cloudflare-Magic-WAN`.
4. Choose the **Region** that aligns with where your servers are located.
5. In **IPv4 CIDR block**, choose from one of the recommended Internet Protocol (IP) blocks in Classless Inter-Domain Routing (CIDR) notation. For example, `192.168.20.0/24`. Take note of the IP block you choose, as you will need it to create a static route in Cloudflare WAN.

### 2. Create a VPN gateway

1. Still in your Alibaba Cloud account, go to **VPC** > **VPN Gateway**, and select **Create VPN Gateway**.
2. Give your VPN Gateway a descriptive name. For example, `VPN-Gateway-Magic-WAN`.
3. In **Region**, choose the server that is best for your geographic region. For example, **US (Silicon Valley)**.
4. For **Gateway Type**, choose **Standard**.
5. In **Network Type**, choose **Public**.
6. For **Tunnels**, select **Single-tunnel**.
7. In the **VPC** dropdown menu, choose the name of the VPC you created before for Cloudflare WAN. For example, `Cloudflare-Magic-WAN`.
8. In the **VSwitch** drop-down menu, choose the VSwitch you created previously. For example, `VSwitch-CF`.
9. For options such as **Maximum Bandwidth**, **Traffic**, and **Duration**, select the options that best suit your use case.
10. In **IPsec-VPN**, select **Enable**.
11. For **SSL-VPN**, select **Disable**.
12. When you are finished configuring your VPN gateway, return to the main VPN Gateway window.
13. Select the VPN gateway you have just created, and then select **Destination-based Routing**.
14. Select **Add Route Entry**, and enter the subnets needed to reach the required destinations. For example, you can add a default route to send all traffic through your IPsec tunnel.
15. When you are finished, return to the main window.
16. Select **Publish** > **OK** to publish the route.

### 3. Create IPsec connections

1. Go to **VPC** > **Customer Gateways** > **Create Customer Gateway**.
2. Create a customer gateway with one of the Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space). This typically starts with `162.xx.xx.xx`.
3. Now, go to **VPC** > **IPsec Connections** > **Create IPsec Connection**.
4. Create an IPsec connection with the following settings:
   1. **Name**: give it a descriptive name, like `CF-Magic-WAN-IPsec`.
   2. **Associate Resource**: **VPN Gateway**.
   3. **VPN Gateway**: From the dropdown menu, choose the VPN gateway you created previously. In our example, `VPN-Gateway-Magic-WAN`.
   4. **Customer Gateway**: Select the customer gateway you created above for Cloudflare WAN.
   5. **Routing Mode**: **Destination Routing Mode**.
   6. **Effective Immediately**: **Yes**.
   7. **Pre-Shared Key**: This is the pre-shared key (PSK) you will have to use in the Cloudflare WAN IPsec tunnel. If you do not specify one here, the Alibaba system will generate a random pre-shared key for you.
5. Go to **Advanced Settings**, and expand the **Encryption Configuration** settings.
6. In **IKE Configurations**, select the following settings to configure the IPsec connection. These settings have to match the supported configuration parameters for [Cloudflare WAN IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters):
   1. **Version**: _ikev2_
   2. **Negotiation Mode**: _main_
   3. **Encryption Algorithm**: _aes256_
   4. **Authentication Algorithm**: _sha256_
   5. **DH Group**: _group20_
   6. **Localid**: This is the customer endpoint. These are generally IP addresses provided by your ISP. For example, `47.xxx.xxx.xxx`.

## Cloudflare WAN

### 1. IPsec tunnels

1. Follow the [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) instructions to create the required IPsec tunnels with the following options:
   1. **Tunnel name**: Give your tunnel a descriptive name, like `Alibaba`.
   2. **Interface address**: Choose from the subnet in your Alibaba Cloud configuration. For example, if your Alibaba default configuration is `169.xx.xx.1/30`, you might want to choose `169.xx.xx.2/30` for your Cloudflare WAN side of the IPsec tunnel.
   3. **Customer endpoint**: This is the IP address you entered for **Localid** in Alibaba's IPsec connection. For example, `47.xxx.xxx.xxx`.
   4. **Cloudflare endpoint**: Enter the same anycast IP address provided by Cloudflare you have entered for Alibaba's Customer Gateway. Typically starts with `162.xx.xx.xx`.
   5. **Pre-shared key**: Select **Use my own pre-shared key**, and enter the PSK key from your Alibaba Cloud IPsec tunnel.
   6. **Replay protection**: **Enabled**.
2. Select **Add tunnels** when you are done.

### 2. Static route

1. Follow the [Configure static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) instructions to create a static route.
2. In **Prefix**, enter the IP CIDR you used to create your virtual private cloud in the Alibaba Cloud interface. In our example we used `192.168.20.0/24`.

# Aruba EdgeConnect Enterprise

import { Details } from "~/components";

Cloudflare partners with Aruba's EdgeConnect SD-WAN solution to provide users with an integrated solution. The EdgeConnect appliances manage subnets associated with branch offices or retail locations. Anycast tunnels are set up between the EdgeConnect appliances and Cloudflare to securely route traffic.

This tutorial describes how to configure the EdgeConnect device for both east-west (branch to branch) and north-south (Internet-bound) use cases.

:::caution
Note that north-south traffic routed through Cloudflare's Secure Web Gateway is an optional add-on feature set and requires a Cloudflare Zero Trust account.
:::

### Prerequisites

Before setting up a connection between EdgeConnect and Cloudflare, you must have:

- A contract that includes Cloudflare WAN (formerly Magic WAN) and Secure Web Gateway.
- Received two Cloudflare endpoints (anycast IP addresses), available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
- Determined a private static /31 IP pair to use with each tunnel. The /31 pairs should be from a different private subnet, separate from the private subnets used behind each EdgeConnect appliance.
- The EdgeConnect devices used in this tutorial and on v9.0.

## Example scenario

<Details header="GRE tunnel configuration">

For the purpose of this tutorial, the integration will refer to a scenario with two branch offices, each with distinct subnets.

There are 2 branch offices each with distinct subnets.

- The east branch office has a `10.3.0.0/16` network with an EdgeConnect terminating the anycast GRE tunnel.
- The west branch office has a `10.30.0.0/16` network with an EdgeConnect terminating the anycast GRE tunnel.

![Table of branch subnet information](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/branch-subnets.png)

_Note: Labels in this image may reflect a previous product name._

The following example shows the **east_branch** deployment on the Orchestrator.

![GCP East deployment configuration](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/east-branch-deployment.png)

The Deployment screenshot displays several different IP addresses and interfaces. From left to right:

- **Next Hop 10.3.0.1** - This example uses Google Cloud. This IP defines the default gateway IP for the subnet and is built into GCP.
- **IP/Mask (LAN) 10.3.0.2/24** - This defines the LAN0 interface IP of the EdgeConnect appliance.
- **IP/Mask (WAN) 10.2.0.2/24** - This defines the WAN0 interface IP of the EdgeConnect appliance.
- **Next Hop 10.2.0.1** - This example uses Google Cloud. This IP defines the default gateway IP for the subnet and is built into GCP.

</Details>

<Details header="IPsec tunnel configuration">

For the purpose of this tutorial, the integration will refer to a scenario with two branch offices, each with distinct subnets.

The central branch office has a `10.22.0.0/24` network with an EdgeConnect terminating the anycast IPsec tunnel.

The west branch office has a `10.77.0.0/24` network with an EdgeConnect terminating the anycast IPsec tunnel.

![IPsec tunnel values for east and west branches](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/central-west-branch-ipsec.png)

_Note: Labels in this image may reflect a previous product name._

The following example shows the **central_branch** deployment on the Orchestrator.

![Values for central branch configuration within Orchestrator](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/orchestrator-ipsec.png)

The Deployment screenshot displays several different IP addresses and interfaces. From left to right:

- **Next Hop 10.22.0.1** - This example uses Google Cloud. This IP defines the default gateway IP for the subnet and is built into GCP.
- **IP/Mask (LAN) 10.22.0.2/24** - This defines the LAN0 interface IP of the EdgeConnect appliance.
- **IP/Mask (WAN) 10.32.0.2/24** - This defines the WAN0 interface IP of the EdgeConnect appliance.
- **Next Hop 10.32.0.1** - This example uses Google Cloud. This IP defines the default gateway IP for the subnet and is built into GCP.

</Details>

## 1. Define a common site on the Orchestrator

For all EdgeConnect devices using Cloudflare, modify the devices to put them on the same site. This disables automatic IPsec tunnel creation between the EdgeConnect devices using the same labels for the WAN interfaces in use.

This step is only required if Cloudflare is used for east-west traffic routing.

## 2. Configure overlay policies

Aruba Orchestrator's Business Intent Overlays create intuitive policies which automatically identify and steer application traffic to Cloudflare. This example creates two Business Intent Overlay (BIO) policies.

<Details header="GRE tunnel configuration">

Cloudflare's [tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) are ping reply packets encapsulated in GRE packets. The source IP is the EdgeConnect WAN interface used to establish a tunnel, and the destination IP is Cloudflare servers. These packets need to be sent directly from the WAN interface and not through the established tunnels.

To create the overlay policy:

1. Create a compound application, which is a combination of all [Cloudflare public IPs](https://www.cloudflare.com/ips/) and ICMP packets.

![Application definition screen with IP values](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/app-definition.png)

2. Create a breakout Business Intent Overlay (BIO) to bypass the GRE tunnel as the first policy and use this newly created application as the match criteria.

3. Define at least one additional overlay policy and the traffic you want to send to Cloudflare over the GRE tunnels.

The service name used to send traffic through the tunnel created in the next step is **Cloudflare_GRE**. The example uses **Match Everything** to send all other traffic through the established tunnel (both private east-west traffic & Internet bound north-south traffic through Cloudflare's Secure Web Gateway).

![Business Intent Overlay screen with breakout and CF overlays](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/biz-intent-overlay.png)

_Note: Labels in this image may reflect a previous product name._

</Details>

<Details header="IPsec tunnel configuration">

Cloudflare's [tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) are ping reply packets encapsulated in IPsec packets. The source IP is the EdgeConnect WAN interface used to establish a tunnel, and the destination IP is Cloudflare servers. These packets need to be sent directly from the WAN interface and not through the established tunnels.

To create the overlay policy:

1. Create a compound application, which is a combination of all [Cloudflare public IPs](https://www.cloudflare.com/ips/) and ICMP packets.

![Application definition screen with IP values](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/app-definition.png)

2. Create a breakout Business Intent Overlay (BIO) to bypass the IPsec tunnel as the first policy and use this newly created application as the match criteria.

3. Define at least one additional overlay policy and the traffic you want to send to Cloudflare over the IPsec tunnels.

The service name used to send traffic through the tunnel created in the next step is **Cloudflare_IPsec**. The example uses **Match Everything** to send all other traffic through the established tunnel (both private east-west traffic and Internet bound north-south traffic through Cloudflare's Secure Web Gateway).

![Business Intent Overlay screen with breakout and CF overlays for IPsec](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/biz-intent-overlay-ipsec.png)

_Note: Labels in this image may reflect a previous product name._

</Details>

## 3. Create tunnels on Cloudflare and EdgeConnect

<Details header="GRE tunnel configuration">

![Diagram of GCP, Aruba Orchestratror, and Cloudflare products](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/gcp-edgeconnect-diagram.png)

_Note: Labels in this image may reflect a previous product name._

1. Create a tunnel on the EdgeConnect using Cloudflare's assigned public anycast IP and the service used in the overlay policy in the [previous step](#2-configure-overlay-policies).
2. Create a Virtual Tunnel Interface (VTI) using the private IP pair shared with CF GRE tunnel endpoint and the passthrough tunnel to match the newly created tunnel alias (**CF_GRE_east** in our example).

![Modify Passthrough Tunnel screen](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/modify-passthrough.png)

![Edit Virtual Tunnel Interface screen](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/edit-vti.png)

3. Define a GRE tunnel on the Cloudflare dashboard using the EdgeConnect appliance's public IP and the private IP pair /31 shared with the appliance.

![GRE tunnels information for each branch](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/gre-tunnels-edgeconnect.png)

</Details>

<Details header="IPsec tunnel configuration">

![Diagram of GCP, Aruba Orchestratror, and Cloudflare products for IPsec tunnels](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/gcp-edgeconnect-diagram-ipsec.png)

_Note: Labels in this image may reflect a previous product name._

For additional information on creating IPsec tunnels, refer to [API documentation for IPsec tunnels](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/create/).

- `X-Auth-Email`: Your Cloudflare email ID
- `X-Auth-Key`: Seen in the URL (`dash.cloudflare.com/<X-Auth-Key>/....`)
- `Account key`: Global API token in Cloudflare dashboard

1. Test new IPsec tunnel creation

```bash
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels?validate_only=true" \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "ipsec_tunnels": [
    {
      "name": "EdgeConnect_IPSEC_1",
      "customer_endpoint": "35.188.72.56",
      "cloudflare_endpoint": "172.64.241.205",
      "interface_address": "192.168.10.11/31",
      "description": "Tunnel for EdgeConnect - GCP Central"
    }
  ]
}'
```

2. Create a new IPsec tunnel

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "ipsec_tunnels": [
    {
      "name": "EdgeConnect_IPSEC_1",
      "customer_endpoint": "35.188.72.56",
      "cloudflare_endpoint": "172.64.241.205",
      "interface_address": "192.168.10.11/31",
      "description": "Tunnel for EdgeConnect - GCP Central"
    }
  ]
}'
```

```json output
{
	"result": {
		"ipsec_tunnels": [
			{
				"id": "tunnel_id",
				"interface_address": "192.168.10.11/31",
				"created_on": "2022-04-14T19:57:43.938376Z",
				"modified_on": "2022-04-14T19:57:43.938376Z",
				"name": "EdgeConnect_IPSEC_1",
				"cloudflare_endpoint": "172.64.241.205",
				"customer_endpoint": "35.188.72.56",
				"description": "Tunnel for EdgeConnect - GCP Central",
				"health_check": {
					"enabled": true,
					"target": "35.188.72.56",
					"type": "reply"
				}
			}
		]
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

3. Generate Pre Shared Key (PSK) for tunnel

Use the tunnel ID from the response in Step 2. Save the pre-shared key generated in this step as you will need it to set up tunnels on the Orchestrator.

```bash
curl --request POST \
"https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{tunnel_id}/psk_generate?validate_only=true" \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>"
```

```json output
{
	"result": {
		"ipsec_id": "<ipsec_id>",
		"ipsec_tunnel_id": "<tunnel_id>",
		"psk": "XXXXXXXXXXXXXXXXX",
		"psk_metadata": {
			"last_generated_on": "2022-04-14T20:05:29.756514071Z"
		}
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

**Create an IPsec tunnel on EdgeConnect**

You can create a tunnel after the Business Intent Overlay policies have been defined. Use the correct policy or service created in [configure overlay policy](#2-configure-overlay-policies). The local IP is the local WAN interface of the EdgeConnect device, and the remote IP is the Cloudflare public IP assigned as the tunnel endpoint.

![Modify Passthrough Tunnel dialog with General values](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/general-modify-passthrough.png)

![Modify Passthrough Tunnel dialog with IKE values](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/ike-modify-passthrough.png)

![Modify Passthrough Tunnel dialog with IPsec values](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/ipsec-modify-passthrough.png)

**Create a Virtual Tunnel Interface (VTI) on the EdgeConnect appliance**

![Values for Edit VTI Interface](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/vti-interface-ipsec.png)

</Details>

## 4. Create static routes on Cloudflare and EdgeConnect

<Details header="GRE tunnel configuration">

1. Define static routes on the Cloudflare dashboard for the LAN subnet(s) attached to the EdgeConnect appliance. Use the private IP pair for the EdgeConnect tunnel endpoint.

   In this example, the traffic to subnet `10.3.0.0/16` attached to the **east_branch** EdgeConnect appliance has a next hop of `10.40.8.10`.

![Static route information for each branch](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/static-routes-cf.png)

2. Define static routes on the Orchestrator so Cloudflare can route traffic between sites.

   This example creates a route for the subnet `10.30.0.0/24` on the **west_branch** to route via the established GRE tunnel between the EdgeConnect appliance and Cloudflare.

![Static route information for each branch](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/static-routes-edgeconnect.png)

</Details>

<Details header="IPsec tunnel configuration">

![Static route values from Cloudflare dashboard](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/static-routes-ipsec.png)

**Static routes for central branch on EdgeConnect**

![Static route values from EdgeConnect for central branch](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/static-routes-central-ipsec.png)

**Static routes for west branch on EdgeConnect**

![Static route values from EdgeConnect for west branch](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/static-routes-west-ipsec.png)

</Details>

## 5. Validate traffic flow

<Details header="GRE tunnel configuration">

**Validate Secure Web Gateway**

To validate traffic flow from the local subnet through Cloudflare's Secure Web Gateway, perform a cURL as shown in this example.

![Curl example for validating Secure Web Gateway](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/validate-swg-curl.png)

You can validate the request went through Gateway with the presence of the `Cf-Team` response header, or by looking at the logs in the dashboard under **Logs** > **Gateway** > **HTTP**.

![Dashboard example for validating Secure Web Gateway](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/dash-validate-swg.png)

**Validate east-west traffic**

To validate east-west traffic flow, perform a traceroute as shown in the example.

![Traceroute example for verifying east-west traffic](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/validate-traceroute.png)

The example shows a client in GCP East (`10.3.0.3`), which can ping the private IP of a client in GCP West (`10.30.0.4`).

The traceroute shows the path going from the client (`10.3.0.3`) to:

- the GCP East lan0 IP on the EdgeConnect (`10.3.0.2`)
- the Cloudflare private GRE endpoint IP (`10.4.8.11`)
- the GCP West lan0 IP on the West EdgeConnect (`10.30.0.3`)
- the GCP West client (`10.30.0.4`)

This validates the east-west traffic flow through Cloudflare WAN.

</Details>

<Details header="IPsec tunnel configuration">

**Validate Secure Web Gateway**

To validate traffic flow from the local subnet through Cloudflare's Secure Web Gateway, perform a cURL as shown in this example.

![cURL example for validating traffic](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/static-routes-west-ipsec.png)

You can validate the request went through Secure Web Gateway with the presence of the `Cf-Team` response header or by looking at the logs in the dashboard under **Logs** > **Gateway** > **HTTP**.

![Dashboard example for validating Secure Web Gateway](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/dash-validation-ipsec.png)

**Validate east-west traffic**

To validate east-west traffic flow, perform a traceroute as shown in the example.

![Traceroute example for IPsec validation](~/assets/images/cloudflare-wan/third-party/aruba-edge-connect/traceroute-ipsec.png)

The example shows a client in GCP Central (`10.22.0.9`), which can ping the private IP of a client in GCP West (`10.77.0.10`).

The traceroute shows the path going from the client (`10.22.0.9`) to:

- the GCP Central lan0 IP on the EdgeConnect (`10.22.0.2`)
- the Cloudflare private IPsec endpoint IP (`192.168.10.11`)
- the GCP West EdgeConnect private IPsec endpoint IP (`192.168.15.10`)
- the GCP West client (`10.77.0.10`)

This validates the east-west traffic flow through Cloudflare WAN.

</Details>

## 6. Cloudflare policies

At this point, the GRE or IPsec tunnels should be connected from the EdgeConnect appliances to Cloudflare's global network, and traffic is scoped to route over the tunnels using the EdgeConnect Business Intent Overlays.

To begin filtering traffic and gathering analytics, refer to the [Cloudflare Network Firewall documentation](/cloudflare-network-firewall/) to learn how to create filters for east-west inter-branch traffic and the [Secure Web Gateway documentation](/cloudflare-one/traffic-controls/) to learn how to configure Gateway policies if you decide to send traffic from your local private subnets to the Internet through Cloudflare Gateway.

# Amazon AWS Transit Gateway

This guide provides step-by-step instructions for connecting an Amazon Web Services (AWS) Transit Gateway to Cloudflare WAN with redundant, route-based IPsec tunnels and Border Gateway Protocol (BGP). The primary topology uses:

- One Cloudflare anycast IP
- One AWS customer gateway
- One AWS Site-to-Site VPN connection with two IPsec tunnels
- Two BGP sessions between AWS Transit Gateway and Cloudflare WAN

Cloudflare assigns an IPv4 anycast IP to each Cloudflare WAN account, and additional anycast IPs are available from your account team. Because the anycast IP is served from Cloudflare data centers globally, a tunnel is not tied to a single Cloudflare server or data center. This guide uses one anycast IP for the baseline and supports a second anycast IP for customers who have one. For more information, refer to [Anycast](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#anycast) and [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).

AWS Transit Gateway uses equal-cost multipath (ECMP) to distribute traffic across equal BGP paths. BGP dynamically exchanges routes and withdraws a path when its session fails. This design avoids the active/standby limitation of an AWS static-routing Site-to-Site VPN connection.

AWS requires a `/30` inside tunnel network for each tunnel and supports a maximum Phase 1 lifetime of `28800` seconds. This guide uses `/30` tunnel addressing and a `28800`-second Phase 1 lifetime for that reason.

:::note[Support scope]
This configuration is a reference example demonstrating known compatibility with Cloudflare services. It is not an officially supported AWS configuration. The steps and settings were tested in the [test environment](#test-environment) listed below. Adjustments may be required for other Regions or environments.
:::

:::note[Terraform is recommended]
Terraform is the recommended deployment method for this integration because it consistently applies the interdependent AWS, tunnel, routing, and Cloudflare WAN settings described in this guide. The [Terraform reference deployment](#terraform-reference-deployment) has been tested against the reference environment, and testing has not identified any issues.
:::

## Architecture

The general design uses the following resources:

- One AWS Transit Gateway with VPN ECMP support enabled
- One dedicated Transit Gateway route table for the workload VPC and AWS VPN attachments for Cloudflare WAN
- One or more workload VPCs attached to the Transit Gateway
- One AWS customer gateway for each configured Cloudflare anycast IP
- One dynamic-routing AWS Site-to-Site VPN connection for each customer gateway
- Two AWS-managed IPsec tunnels and two corresponding Cloudflare WAN IPsec tunnels per VPN connection
- One BGP session per tunnel between AWS Transit Gateway and Cloudflare WAN

The Terraform proof of concept creates:

- One workload VPC with exactly one public workload subnet
- Two Transit Gateway attachment subnets in separate Availability Zones
- One Internet Gateway for direct egress from the public workload subnet
- Selective routes that send only `vpn_routed_prefixes` destinations to the Transit Gateway

It does not create:

- Virtual machines or test hosts
- Standalone Elastic Network Interfaces (ENIs); AWS creates ENIs for the Transit Gateway VPC attachment in the attachment subnets
- Security groups
- Elastic IP addresses
- NAT services

The workload subnet assigns public IPv4 addresses on launch. Its route table sends only the more-specific destinations configured in `vpn_routed_prefixes` to the Transit Gateway and Site-to-Site VPNs; remaining IPv4 egress follows the `0.0.0.0/0` route to the Internet Gateway.

```txt
Terraform proof of concept: AWS workload VPC 192.168.0.0/16

Public workload subnet 192.168.125.0/24
  Workload route table
    +-- vpn_routed_prefixes (more-specific) --> AWS Transit Gateway ASN 65020
    |                                             |
    |                                VPN connection 1
    |                                        |
    |                                   +----+----+
    |                                   |         |
    |                                Tunnel 1  Tunnel 2
    |                                   |         |
    |                             Cloudflare WAN ASN 13335
    |                                  162.159.135.1
    |
    +-- 0.0.0.0/0 (remaining IPv4) --> Internet Gateway --> Internet

TGW attachment subnets: 192.168.124.0/28 and 192.168.124.16/28
```

The AWS Site-to-Site VPN connection terminates on one AWS customer gateway. Because an AWS customer gateway has one public IP address, both tunnels use the same Cloudflare anycast IP.

:::note[AWS tunnel model]
AWS requires every Site-to-Site VPN connection to contain two tunnel objects and does not provide an option to define a single-tunnel connection. Tunnels 01 and 02 therefore both terminate on `162.159.135.1`. Configure both tunnels for AWS redundancy, even though a connection can temporarily operate while only one tunnel is established.
:::

:::note[Secondary anycast IP customers]
Each leased anycast IP uses one AWS customer gateway and one Site-to-Site VPN connection. The first anycast IP uses tunnels 01 and 02, and the second uses tunnels 03 and 04. The Terraform configuration supports one or two distinct anycast IPs, with exactly two tunnel CIDRs and two PSKs for each anycast IP.
:::

## Test environment

| **Field**                     | **Value**                                    |
| ----------------------------- | -------------------------------------------- |
| Cloud provider                | Amazon Web Services                          |
| Services                      | AWS Transit Gateway and AWS Site-to-Site VPN |
| AWS Region                    | US East (Ohio), `us-east-2`                  |
| Routing mode                  | Dynamic routing with BGP                     |
| AWS Transit Gateway ASN       | `65020`                                      |
| Cloudflare ASN                | `13335`                                      |
| Number of anycast IPs         | Two                                          |
| Number of VPN connections     | Two                                          |
| Number of IPsec tunnels       | Four                                         |
| Terraform AWS provider        | `hashicorp/aws` `6.67.0`                     |
| Terraform Cloudflare provider | `cloudflare/cloudflare` `5.27.0`             |
| Date tested                   | October 2026                                 |

The test environment used:

- Two leased anycast IPs
- Four IPsec tunnels
- Four BGP sessions

AWS imported the expected Cloudflare WAN routes on every configured tunnel, and Cloudflare learned the complete AWS VPC prefix.

The baseline deployment in this guide uses one anycast IP, one VPN connection, and two tunnels. It uses the same settings as the first VPN connection in the test environment. The baseline was not deployed on its own; its Terraform plan is covered by the included `terraform test` suite, which uses mock providers.

## IKE/IPsec and BGP settings

| **Field**                    | **Value**               |
| ---------------------------- | ----------------------- |
| Traffic selection criteria   | Route-based VPN         |
| Routing                      | BGP                     |
| Tunnel forwarding            | Active/active with ECMP |
| IKE version                  | IKEv2                   |
| Authentication               | Pre-shared key          |
| Phase 1 encryption           | AES-256-CBC             |
| Phase 1 integrity            | SHA-256                 |
| Phase 1 DH group             | Group 20                |
| Phase 1 lifetime             | 28,800 seconds          |
| Phase 2 encryption           | AES-256-CBC             |
| Phase 2 integrity            | SHA-256                 |
| Phase 2 PFS group            | Group 20                |
| Phase 2 lifetime             | 3,600 seconds           |
| **AWS replay window**        | **2,048 packets**       |
| Cloudflare replay protection | Enabled                 |
| NAT traversal (NAT-T)        | Automatic               |
| Dead Peer Detection action   | Restart                 |
| Tunnel startup action        | Start                   |
| AWS Transit Gateway ASN      | `65020`                 |
| CF Account ASN               | `13335` (default)       |
| BGP authentication           | None                    |

AWS Site-to-Site VPN does not support disabling anti-replay protection. Enable replay protection on each corresponding Cloudflare WAN tunnel and use the maximum AWS replay window of `2048`. Replay protection can pin traffic for a tunnel to one Cloudflare server to preserve packet sequence, reducing some anycast performance and resilience benefits.

### Pre-shared key requirements

Configure a pre-shared key that meets the following AWS requirements:

- Contains 8 to 64 characters
- Contains only letters, numbers, periods (`.`), and underscores (`_`)
- Does not begin with `0`

Configure the same PSK on the matching AWS and Cloudflare WAN tunnel objects. In Cloudflare, select **Use my own pre-shared key** and enter the PSK configured on the AWS tunnel.

For more information, refer to [VpnTunnelOptionsSpecification](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VpnTunnelOptionsSpecification.html) in the AWS documentation.

For the AWS console workflow, configure a unique PSK for each tunnel or reuse the same PSK across all tunnels. The Terraform workflow requires a unique PSK for each tunnel.

:::caution[Use unique PSKs with Terraform]
The Terraform AWS provider identifies which AWS public IPv4 address belongs to tunnel 1 by matching the tunnel 1 PSK. If both tunnels in a VPN connection use the same PSK, the provider cannot make that match and can assign the wrong AWS public IPv4 address to a Cloudflare WAN tunnel. IKE can still establish because the PSKs match, but BGP and tunnel health checks fail because the inside addresses do not match. The Terraform configuration rejects duplicate PSKs.
:::

## Configuration settings

Cloudflare WAN accepts a `/31` or `/30` interface address for each tunnel and recommends `/31`. For more information, refer to [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).

AWS Site-to-Site VPN requires a unique `/30` inside tunnel network for each tunnel, so this guide uses `/30`. AWS assigns the first usable IP address in the `/30` to its side of the tunnel. Therefore, configure the second usable IP address on the Cloudflare WAN side. Cloudflare supports AWS inside tunnel networks selected from `169.254.240.0/20`.

AWS and Cloudflare WAN also require different input formats:

- AWS accepts the `/30` network address as the **Inside IPv4 CIDR**
- Cloudflare WAN requires its usable host address with the `/30` prefix length as the **IPv4 interface address**

Do not enter the `/30` network address as the Cloudflare interface address. In this example, AWS uses `169.254.240.45`, and Cloudflare WAN uses `169.254.240.46`. The baseline uses the following two `/30` networks.

| **Attribute**                | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ---------------------------- | --------------------- | --------------------- |
| Cloudflare tunnel name       | `AWS_TUN_01`          | `AWS_TUN_02`          |
| AWS VPN connection           | VPN 1                 | VPN 1                 |
| AWS public IPv4 address      | `<AWS_OUTSIDE_IP_01>` | `<AWS_OUTSIDE_IP_02>` |
| Cloudflare anycast IP        | `162.159.135.1`       | `162.159.135.1`       |
| AWS inside IPv4 CIDR input   | `169.254.240.44/30`   | `169.254.240.48/30`   |
| AWS inside IP                | `169.254.240.45`      | `169.254.240.49`      |
| Cloudflare interface address | `169.254.240.46/30`   | `169.254.240.50/30`   |
| BGP initiator                | AWS                   | AWS                   |
| AWS ASN                      | `65020`               | `65020`               |
| CF Account ASN               | `13335`               | `13335`               |
| Pre-shared key               | `<TUNNEL_01_PSK>`     | `<TUNNEL_02_PSK>`     |

:::note[Secondary anycast IP customers]
Use tunnels 03 and 04 with `169.254.240.52/30` and `169.254.240.56/30`, respectively. Their Cloudflare interface addresses are `169.254.240.54/30` and `169.254.240.58/30`.
:::

The example AWS VPC prefix is `192.168.0.0/16`. Replace it and all angle-bracketed values with values from your environment. AWS generates two public IPv4 addresses per VPN connection. These values are unavailable before AWS creates the connection.

## Assumptions

This guide assumes the following:

- Your Cloudflare WAN account has Unified Routing and BGP over IPsec enabled
- The Cloudflare WAN account has `162.159.135.1` available and, when using two anycast IPs, also has `172.64.135.1` available
- For the AWS console path, an AWS Transit Gateway exists and the workload VPC is attached in at least two Availability Zones; the Terraform path creates these resources
- The AWS VPC prefix is `192.168.0.0/16` for this example
- The AWS VPC prefix and prefixes learned from Cloudflare WAN do not overlap
- Security groups, network access control lists, host firewalls, and Cloudflare WAN network firewall policies allow the required application and test traffic
- You can create and modify AWS customer gateways, Site-to-Site VPN connections, Transit Gateway route tables, VPC route tables, and Cloudflare WAN resources
- IPv4 is used for tunnel interfaces and routed prefixes
- AWS Transit Gateway ASN `65020` is not used by another Cloudflare WAN-connected site whose routes AWS must learn; otherwise, BGP loop prevention can reject those routes

## Configuration workflow

1. Configure the AWS Transit Gateway for ASN `65020` and VPN ECMP.
2. Create a dedicated Transit Gateway route table.
3. Create one AWS customer gateway for the Cloudflare anycast IP.
4. Create one dynamic-routing AWS Site-to-Site VPN connection.
5. Configure the IKE version and Phase 1 and Phase 2 settings for each tunnel by using **Modify VPN tunnel options**.
6. Create two matching Cloudflare WAN IPsec tunnels.
7. Enable BGP on each Cloudflare tunnel.
8. Propagate the VPC and VPN attachments into the Transit Gateway route table.
9. Add VPC routes toward Cloudflare WAN.
10. Verify every configured tunnel, BGP session, and end-to-end traffic.

## Choose a deployment method

Use the Terraform workflow unless you have a specific requirement to configure existing resources manually. Review the generated plan for your account and network before applying it. Do not complete both workflows for the same deployment.

- To create the complete reference AWS and Cloudflare WAN environment, continue with [Terraform reference deployment](#terraform-reference-deployment)
- To configure an existing Transit Gateway and workload VPC manually, continue with [Gather Cloudflare WAN information](#gather-cloudflare-wan-information)

## Gather Cloudflare WAN information

Gather and verify the Cloudflare information before creating AWS resources. AWS customer gateways use the Cloudflare anycast IPs as their public IP addresses, so changing an anycast IP later requires replacing the corresponding AWS customer gateway and VPN connection.

For more information about Cloudflare anycast IPs and tunnel addressing, refer to [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).

1. Log in to the Cloudflare dashboard.
2. Go to **Address Space** > **Leased IPs**.
3. Confirm the account has the baseline IPv4 anycast IP `162.159.135.1`.
4. Go to **Networking** > **Routes** > **WAN configuration**.
5. Confirm Unified Routing and BGP over IPsec are available for the account.
6. Record the **CF Account ASN**. The default is `13335`.
7. Inventory the eligible routes in the Unified Routing dataplane that AWS should learn.
8. [Check existing tunnel names and interface addresses](#check-existing-tunnel-names-and-interface-addresses).

:::note[Secondary anycast IP customers]
In step 3, also confirm that `172.64.135.1` is leased to the account.
:::

Record the following values before continuing:

| **Item**                                       | **Reference value**              |
| ---------------------------------------------- | -------------------------------- |
| Cloudflare anycast IP 1                        | `162.159.135.1`                  |
| Cloudflare anycast IP 2                        | `172.64.135.1`                   |
| CF Account ASN                                 | `13335` (default)                |
| AWS Transit Gateway ASN                        | `65020`                          |
| AWS VPC prefix                                 | `192.168.0.0/16`                 |
| Expected RFC 1918 prefixes from Cloudflare WAN | `<EXPECTED_RFC1918_PREFIX_LIST>` |

By default, Cloudflare WAN routes RFC 1918 address space. Publicly routable address space can be enabled one prefix at a time. Contact Cloudflare for assistance with enabling public prefixes.

:::note[Create the AWS VPN connection first]
Do not create the Cloudflare tunnel objects yet. AWS generates two public IPv4 addresses for each VPN connection. Each generated address identifies the **Customer endpoint** for one Cloudflare tunnel. Record the VPN connection ID, inside `/30` CIDR, and public IPv4 address together so you can apply the generated address to the correct Cloudflare tunnel.
:::

### Check existing tunnel names and interface addresses

Cloudflare rejects a new tunnel whose name or interface address matches another tunnel in the same Cloudflare account. The AWS Site-to-Site VPN connections are created before the Cloudflare tunnels, so a conflict otherwise appears only after the AWS resources exist. Before you choose tunnel names and inside `/30` CIDRs, list the names and interface addresses already in use.

In the Cloudflare dashboard, go to the **Connectors** page and review the **IPsec/GRE tunnels** tab. Alternatively, run the following commands with an API token that can read Cloudflare WAN configuration:

```bash
curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "https://api.cloudflare.com/client/v4/accounts/<CLOUDFLARE_ACCOUNT_ID>/magic/ipsec_tunnels" | jq -r '.result.ipsec_tunnels[] | "\(.interface_address)\t\(.name)"' | sort
curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "https://api.cloudflare.com/client/v4/accounts/<CLOUDFLARE_ACCOUNT_ID>/magic/gre_tunnels" | jq -r '.result.gre_tunnels[] | "\(.interface_address)\t\(.name)"' | sort
```

Choose tunnel names that do not appear in the output. For each tunnel, choose an inside `/30` CIDR within `169.254.240.0/20` that does not contain any listed interface address. The reference values in this guide (`169.254.240.44/30` through `169.254.240.56/30`) are examples; replace them if any are already in use, for example by another deployment of this configuration.

## Configure the AWS Transit Gateway

### Verify the Transit Gateway options

1. Log in to the AWS Management Console and open **VPC**.
2. Go to **Transit gateways** > **Transit gateways**.
3. Select the Transit Gateway used by the workload VPC.
4. Confirm the following settings:

| **Transit Gateway option** | **Value** |
| -------------------------- | --------- |
| Amazon side ASN            | `65020`   |
| VPN ECMP support           | Enabled   |
| DNS support                | Enabled   |

This guide uses `65020` as an example private ASN. Choose an ASN that no other Cloudflare WAN-connected site uses, and avoid the AWS default of `64512`. For more information, refer to [AS path loop prevention](#as-path-loop-prevention).

AWS allows changing the Amazon-side ASN only when the Transit Gateway has no VPN, Direct Connect gateway, Connect, VPN Concentrator, or Client VPN attachments. To change it, run the following command:

```bash
aws ec2 modify-transit-gateway --transit-gateway-id <AWS_TGW_ID> --options AmazonSideAsn=65020
```

If the Transit Gateway already has one of those attachments, keep its current ASN, substitute that ASN throughout this guide, and configure the same value as **Customer device ASN** in Cloudflare. Confirm that no other Cloudflare WAN-connected site uses that ASN.

### Verify the workload VPC attachment

1. Go to **Transit gateways** > **Transit gateway attachments**.
2. Confirm that the workload VPC has an available VPC attachment to the Transit Gateway.
3. Confirm that attachment subnets exist in at least two Availability Zones.

### Create a dedicated Transit Gateway route table

The route table associated with an AWS VPN attachment determines which routes AWS advertises through that Site-to-Site VPN connection. Use a dedicated route table so unrelated VPC, Site-to-Site VPN, and Direct Connect routes are not exported to Cloudflare WAN.

1. Go to **Transit gateways** > **Transit gateway route tables**.
2. Select **Create transit gateway route table**.
3. For **Transit gateway ID**, select `<AWS_TGW_ID>`.
4. Add a descriptive Name tag, such as `cloudflare-wan-routing`.
5. Select **Create transit gateway route table**.
6. Record its ID as `<AWS_TGW_ROUTE_TABLE_ID>`.

:::caution[Prevent unintended route advertisement]
Do not enable propagation from unrelated attachments into this route table. If you use an existing dedicated table, audit every active route and propagation before attaching the VPNs.
:::

## Create the AWS customer gateway

1. Go to **Virtual private network (VPN)** > **Customer gateways**.
2. Select **Create customer gateway**.
3. Configure the following values:

   | **AWS field**   | **Value**                           |
   | --------------- | ----------------------------------- |
   | Name tag        | `cloudflare-wan-anycast-01`         |
   | BGP ASN         | CF Account ASN (`13335` by default) |
   | IP address      | `162.159.135.1`                     |
   | Certificate ARN | Leave blank                         |
   | Device          | `cloudflare-wan-anycast-01`         |

4. Select **Create customer gateway**.
5. Record the generated customer gateway ID as `<AWS_CUSTOMER_GATEWAY_01_ID>`.

:::note[Secondary anycast IP customers]
Repeat the preceding process with name and device `cloudflare-wan-anycast-02`, the same CF Account ASN, and IP address `172.64.135.1`. Record the generated ID as `<AWS_CUSTOMER_GATEWAY_02_ID>`.
:::

:::caution[Use a unique device name]
If a customer gateway with the same type, IP address, and BGP ASN already exists in the Region, AWS returns the existing customer gateway instead of creating a new one unless the **Device** value differs. Enter a device name that no other customer gateway in the Region uses, and confirm that the recorded customer gateway ID is new.
:::

:::note[Use tags for readability]
Record each generated customer gateway ID as the authoritative resource identifier. You can also apply descriptive AWS tags, including a `Name` tag, to make customer gateways easier to identify in the AWS console and operational tooling.
:::

## Create the AWS Site-to-Site VPN connection

1. Go to **Virtual private network (VPN)** > **Site-to-Site VPN connections**.
2. Select **Create VPN connection**.
3. Configure the following values:

   | **AWS field**            | **Value**                      |
   | ------------------------ | ------------------------------ |
   | Name tag                 | `cloudflare-wan-bgp-01`        |
   | Target gateway type      | Transit gateway                |
   | Transit gateway          | `<AWS_TGW_ID>`                 |
   | Customer gateway         | Existing                       |
   | Customer gateway ID      | `<AWS_CUSTOMER_GATEWAY_01_ID>` |
   | Routing options          | Dynamic (requires BGP)         |
   | Local IPv4 network CIDR  | `0.0.0.0/0`                    |
   | Remote IPv4 network CIDR | `0.0.0.0/0`                    |
   | Tunnel inside IP version | IPv4                           |

   The local and remote network CIDR fields are IKE traffic selectors. Route-based forwarding is controlled by the Transit Gateway route table and BGP, not by these broad selectors.

4. Select **Create VPN connection**.
5. Wait until the connection state is **Available**.
6. Record the VPN connection ID as `<AWS_VPN_CONNECTION_01_ID>`.

:::note[Secondary anycast IP customers]
Repeat the preceding process with these values:

| **AWS field**       | **Value**                      |
| ------------------- | ------------------------------ |
| Name tag            | `cloudflare-wan-bgp-02`        |
| Customer gateway ID | `<AWS_CUSTOMER_GATEWAY_02_ID>` |

Use the same Transit Gateway and dynamic-routing options. Record the VPN connection ID as `<AWS_VPN_CONNECTION_02_ID>`.
:::

### Record the AWS resource IDs

Keep a deployment record that maps the generated AWS resources to their Cloudflare role:

| **Resource**                          | **Recorded value**             |
| ------------------------------------- | ------------------------------ |
| Transit Gateway                       | `<AWS_TGW_ID>`                 |
| Dedicated Transit Gateway route table | `<AWS_TGW_ROUTE_TABLE_ID>`     |
| Workload VPC attachment               | `<AWS_VPC_ATTACHMENT_ID>`      |
| Customer gateway for `162.159.135.1`  | `<AWS_CUSTOMER_GATEWAY_01_ID>` |
| VPN connection for `162.159.135.1`    | `<AWS_VPN_CONNECTION_01_ID>`   |

:::note[Secondary anycast IP customers]
Also record the customer gateway and VPN connection IDs for `172.64.135.1` as `<AWS_CUSTOMER_GATEWAY_02_ID>` and `<AWS_VPN_CONNECTION_02_ID>`.
:::

Tag each AWS resource with a descriptive name, environment, owner, and change-management reference according to your organization's standards.

## Configure the AWS VPN tunnels

AWS assigns two public IPv4 addresses to each VPN connection. Configure both tunnels; do not leave the second tunnel at its default settings.

### Configure tunnel 01

1. Select `<AWS_VPN_CONNECTION_01_ID>`.
2. Open **Tunnel details** and record the first **Public IPv4 address** as `<AWS_OUTSIDE_IP_01>`.
3. Select **Actions** > **Modify VPN tunnel options**.
4. Select `<AWS_OUTSIDE_IP_01>` for **VPN tunnel outside IP address**.
5. Configure the following options. Remove other algorithm choices so AWS cannot negotiate an unexpected proposal.

| **AWS option**                | **Value**                                                                      |
| ----------------------------- | ------------------------------------------------------------------------------ |
| Inside IPv4 CIDR              | `169.254.240.44/30`                                                            |
| Pre-shared key                | Enter a PSK that meets the AWS requirements and record it as `<TUNNEL_01_PSK>` |
| Phase 1 encryption algorithms | `AES256`                                                                       |
| Phase 2 encryption algorithms | `AES256`                                                                       |
| Phase 1 integrity algorithms  | `SHA2-256`                                                                     |
| Phase 2 integrity algorithms  | `SHA2-256`                                                                     |
| Phase 1 DH group numbers      | `20`                                                                           |
| Phase 2 DH group numbers      | `20`                                                                           |
| IKE versions                  | `ikev2`                                                                        |
| **Replay window size**        | **`2048`**                                                                     |
| DPD timeout action            | `Restart`                                                                      |
| Startup action                | `Start`                                                                        |

Leave the Phase 1 lifetime at `28800` seconds and Phase 2 lifetime at `3600` seconds, then select **Save changes**.

AWS supports the **Start** startup action only with IKEv2. Cloudflare only responds to IKE and never initiates it, so AWS must initiate each tunnel.

### Configure tunnel 02

Repeat the preceding process for the second public IPv4 address and tunnel-specific values:

| **Tunnel** | **VPN connection**           | **Inside IPv4 CIDR** | **Pre-shared key** |
| ---------- | ---------------------------- | -------------------- | ------------------ |
| 02         | `<AWS_VPN_CONNECTION_01_ID>` | `169.254.240.48/30`  | `<TUNNEL_02_PSK>`  |

Record the public IPv4 address as `<AWS_OUTSIDE_IP_02>`. Use the same IKE, IPsec, DPD, replay-window, lifetime, and startup settings on both tunnels. Configure each tunnel with a PSK that meets the [pre-shared key requirements](#pre-shared-key-requirements). The tunnels can share a PSK.

:::note[Secondary anycast IP customers]
For the second VPN connection, configure tunnel 03 with `169.254.240.52/30` and `<TUNNEL_03_PSK>`, and tunnel 04 with `169.254.240.56/30` and `<TUNNEL_04_PSK>`. Record their public IPv4 addresses as `<AWS_OUTSIDE_IP_03>` and `<AWS_OUTSIDE_IP_04>`. Apply the same tunnel settings used for tunnels 01 and 02.
:::

:::caution[AWS connection changes restart tunnels]
Modifying an AWS Site-to-Site VPN connection triggers a tunnel restart and will most likely result in a brief loss of connectivity. Changes typically take approximately three to five minutes to complete. Make changes to an existing production connection during an approved maintenance window.
:::

## Create the Cloudflare WAN IPsec tunnels

AWS assigns a public IPv4 address to each VPN tunnel and automatically uses that address as the IKE ID. AWS does not allow the IKE ID to be changed. Cloudflare WAN refers to this AWS address as the **Customer endpoint**. Although **Customer endpoint** is generally optional in a Cloudflare WAN IPsec tunnel configuration, it is required for this integration so that it matches the IKE ID presented by AWS.

Create the AWS VPN connection first to obtain its two public IPv4 addresses. Because each AWS VPN connection contains two tunnels, the baseline requires two corresponding Cloudflare WAN IPsec tunnels.

For general Cloudflare WAN tunnel configuration guidance and field definitions, refer to [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).

:::caution[AWS assigns the Customer endpoint values]
Use the AWS **Public IPv4 address** generated for the corresponding VPN tunnel as the **Customer endpoint**. If you create a Cloudflare tunnel before adding the generated AWS address, the tunnel will remain down until you update and save its **Customer endpoint**.
:::

1. In the Cloudflare dashboard, go to the **Connectors** page.
2. From the **IPsec/GRE tunnels** tab, select **Create a tunnel**.
3. On the **Add tunnels** page, select **IPsec tunnel**.
4. Create two tunnels using the following values:

| **Attribute**            | **AWS_TUN_01**        | **AWS_TUN_02**        |
| ------------------------ | --------------------- | --------------------- |
| IPv4 interface address   | `169.254.240.46/30`   | `169.254.240.50/30`   |
| Customer endpoint        | `<AWS_OUTSIDE_IP_01>` | `<AWS_OUTSIDE_IP_02>` |
| Cloudflare anycast IP    | `162.159.135.1`       | `162.159.135.1`       |
| Pre-shared key           | `<TUNNEL_01_PSK>`     | `<TUNNEL_02_PSK>`     |
| Tunnel health checks     | Enabled               | Enabled               |
| Health check rate        | Medium                | Medium                |
| Health check type        | Request               | Request               |
| Health check direction   | Bidirectional         | Bidirectional         |
| Health check target      | Default (not shown)   | Default (not shown)   |
| Replay protection        | Enabled               | Enabled               |
| Automatic return routing | Disabled              | Disabled              |

:::note[Secondary anycast IP customers]
Create `AWS_TUN_03` and `AWS_TUN_04` using the corresponding values recorded earlier and Cloudflare anycast IP `172.64.135.1`.
:::

Tunnel names and interface addresses must be unique within the Cloudflare account. If another tunnel already uses one of these names or interface addresses, choose a different name or a different inside `/30` CIDR for the matching AWS tunnel.

Replace every `<AWS_OUTSIDE_IP_XX>` placeholder with the address shown for the matching AWS tunnel. Match tunnels by VPN connection and inside IPv4 CIDR rather than by comparing or sorting the generated public IP addresses.

Select **Use my own pre-shared key** for each tunnel and enter the PSK configured on the matching AWS tunnel. AWS uses IP-address IKE identities for this integration; do not use the Cloudflare-generated FQDN identity workflow. Cloudflare matches an IP-address IKE identity to a tunnel by its **Customer endpoint**, so each Cloudflare tunnel must use a unique **Customer endpoint** value.

:::note[Automatic return routing]
Automatic return routing is disabled because this design exchanges routes with BGP and supports traffic initiated from other Cloudflare WAN sites toward AWS.

For more information, refer to [Automatic return routing (ARR)](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-tunnel/#automatic-return-routing-arr).
:::

### Verify tunnel-to-VPN pairing

Before saving each tunnel, verify these tunnel-specific values as a set:

- AWS VPN connection ID
- AWS **Public IPv4 address** used as the Cloudflare **Customer endpoint**
- Inside `/30` CIDR and Cloudflare interface address
- Cloudflare anycast IP used by the AWS customer gateway
- Matching pre-shared key

:::note[Match tunnels by their AWS details]
Do not infer tunnel order from the AWS public IPv4 addresses. Match each Cloudflare tunnel to the **Public IPv4 address** and **Inside IPv4 CIDR** shown under the AWS VPN connection's **Tunnel details**.
:::

### Verify health check settings

Cloudflare health checks test reachability independently of IKE, IPsec, and BGP state. Configure every tunnel with exactly these values:

| **Health check field** | **Value**     |
| ---------------------- | ------------- |
| Rate                   | Medium        |
| Type                   | Request       |
| Direction              | Bidirectional |
| Target                 | Default       |

The dashboard shows **Health check target** only when **Health check direction** is **Unidirectional**. With **Bidirectional** selected, the field is hidden and the default target applies, so no action is required. Do not set a custom target through the API or Terraform.

The default target is the other usable address in the tunnel's `/30`, which is the AWS inside address. For example, for the Cloudflare interface address `169.254.240.46/30`, Cloudflare sends health checks to `169.254.240.45`. A healthy IPsec security association does not by itself prove that the health check or BGP session is working.

For more information, refer to [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/).

## Configure BGP in Cloudflare WAN

BGP over IPsec requires Unified Routing. AWS initiates each BGP session; Cloudflare does not initiate BGP peering.

### Record the CF Account ASN

1. Go to **Networking** > **Routes**.
2. Select **WAN configuration**.
3. Record the **CF Account ASN**.

The CF Account ASN defaults to `13335` and can be changed to a private ASN from `64512` through `65534`. It applies to every BGP session in the Cloudflare account and must differ from the AWS Transit Gateway ASN. This guide uses `13335` as the reference value. If your account uses a different CF Account ASN, use that value wherever this guide shows `13335`, including the BGP ASN of each AWS customer gateway.

For more information, refer to [Configure routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#choose-an-asn-for-bgp-peering).

### Enable BGP on each tunnel

For `AWS_TUN_01` and `AWS_TUN_02`:

1. In the Cloudflare dashboard, go to the **Connectors** page.
2. In **IPsec/GRE tunnels**, locate the tunnel.
3. Select the three-dot menu next to the tunnel, then select **Configure BGP**.
4. For **Customer device ASN**, enter `65020`.
5. Leave **MD5 key** blank. AWS Site-to-Site VPN does not provide a matching BGP MD5 configuration option.
6. Leave **Additional advertised prefix list** blank unless AWS must receive an additional route that is not already eligible in the Unified Routing dataplane.
7. Select **Save**.

:::note[Secondary anycast IP customers]
Repeat these steps for `AWS_TUN_03` and `AWS_TUN_04`.
:::

Leaving **Additional advertised prefix list** blank does not restrict advertisements to zero routes. Cloudflare still advertises eligible routes from the Unified Routing dataplane. Inventory those routes before enabling BGP, and block any prefix that AWS must not use as described in [Block unwanted prefixes](#block-unwanted-prefixes). In the split-egress design used throughout this guide, AWS must not learn `0.0.0.0/0`; advertise it only for the optional Cloudflare Gateway egress design described in [Prefix control](#prefix-control).

Cloudflare peers with the AWS inside IP through the tunnel. AWS peers with the corresponding Cloudflare interface address. The resulting sessions are:

| **Tunnel**   | **AWS BGP address** | **Cloudflare BGP address** |
| ------------ | ------------------- | -------------------------- |
| `AWS_TUN_01` | `169.254.240.45`    | `169.254.240.46`           |
| `AWS_TUN_02` | `169.254.240.49`    | `169.254.240.50`           |

:::note[Secondary anycast IP customers]
The sessions for the second anycast IP use `169.254.240.53` and `169.254.240.54` for tunnel 03, and `169.254.240.57` and `169.254.240.58` for tunnel 04.
:::

Do not create a Cloudflare WAN static route for `192.168.0.0/16`. Cloudflare learns the VPC prefix from AWS over BGP.

### Understand route exchange

The two route directions use different sources:

| **Direction**     | **Route source**                                                                                       | **Control point**                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
| AWS to Cloudflare | Routes in the Transit Gateway route table associated with each VPN attachment                          | Dedicated route table and attachment propagation                     |
| Cloudflare to AWS | Eligible routes from the Unified Routing dataplane plus tunnel-specific additional advertised prefixes | Cloudflare route inventory and **Additional advertised prefix list** |

AWS advertises the propagated VPC prefix over every established BGP session. Cloudflare can therefore retain equivalent paths and withdraw only the paths affected by a failed session. AWS learns RFC 1918 prefixes from Cloudflare WAN by default, along with any publicly routable prefixes explicitly enabled for the account. AWS can install equal-cost paths when their BGP attributes are equivalent.

## Configure AWS Transit Gateway routing

The example propagates and exports the complete `192.168.0.0/16` VPC CIDR. It also propagates all prefixes learned through the VPN attachments into the dedicated Transit Gateway route table.

### Associate the attachments

1. Go to **Transit gateways** > **Transit gateway route tables**.
2. Select `<AWS_TGW_ROUTE_TABLE_ID>`.
3. Open **Associations**.
4. Confirm the workload VPC attachment and every configured VPN attachment are associated with this route table.
5. If an attachment is not associated, select **Create association**, select the attachment, and create the association.
6. Confirm no unrelated attachment is associated with this route table.

An attachment can be associated with only one Transit Gateway route table. Moving an existing workload attachment can change its forwarding behavior; ensure the dedicated table contains every route the workload requires before changing the association.

### Enable VPC route propagation

1. Open **Propagations** for `<AWS_TGW_ROUTE_TABLE_ID>`.
2. Select **Create propagation**.
3. Select the workload VPC attachment and create the propagation.
4. Confirm `192.168.0.0/16` appears as a propagated route.

AWS advertises the propagated VPC CIDR to Cloudflare through every established BGP session.

### Enable VPN route propagation

1. Open **Propagations** for `<AWS_TGW_ROUTE_TABLE_ID>`.
2. Select **Create propagation**.
3. Select the VPN attachment and create the propagation.

:::note[Secondary anycast IP customers]
Repeat these steps for the second VPN attachment.
:::

The propagated routes are the prefixes that AWS learns from Cloudflare over BGP. When the same prefix arrives with equivalent BGP attributes over multiple tunnels, the Transit Gateway can install equal-cost paths and use ECMP.

### Configure workload subnet routes

For each VPC route table associated with a workload subnet:

1. Go to **Virtual private cloud** > **Route tables**.
2. Select the workload subnet route table.
3. Select **Routes** > **Edit routes**.
4. For each approved destination prefix that should traverse Cloudflare WAN, add a route with `<AWS_TGW_ID>` as the target.
5. Leave any existing `0.0.0.0/0` route unchanged.
6. Save the changes.

This produces the same split-egress design as the Terraform reference deployment: approved prefixes use the Transit Gateway, and the existing default route continues to carry Internet traffic through the Internet Gateway or NAT gateway. AWS longest-prefix matching selects the more-specific routes over the default route. Add only prefixes from the approved inventory that Cloudflare advertises; do not add a route that overlaps the VPC CIDR.

:::caution[Do not point the default route at the Transit Gateway by default]
A VPC route table holds one `0.0.0.0/0` route. Targeting it to `<AWS_TGW_ID>` replaces any existing Internet Gateway or NAT gateway route rather than adding a second path. If Cloudflare does not advertise an active default route over BGP, the Transit Gateway has no matching route and drops Internet-bound traffic from the subnet.

Change the default route only if you intend to send Internet traffic through Cloudflare Gateway, Cloudflare advertises `0.0.0.0/0`, and that route appears as active in `<AWS_TGW_ROUTE_TABLE_ID>`. Record the original target first so you can restore it. Refer to [Prefix control](#prefix-control).
:::

### Review security controls

Confirm that the following controls permit the intended traffic:

- Workload security groups
- Subnet network access control lists
- Host firewalls
- Cloudflare WAN network firewall policies

AWS manages UDP 500, UDP 4500, and ESP connectivity for the Site-to-Site VPN tunnels. These protocols do not require rules in workload security groups.

## BGP design considerations

This section covers only the BGP behavior specific to combining AWS Transit Gateway and Cloudflare WAN.

### BGP timers and convergence

AWS sets the BGP hold time for each tunnel, and it cannot be changed. Cloudflare advertises 240 seconds and uses the lower value, so the AWS value applies. Cloudflare requires a hold time of at least 30 seconds. Terraform reports the AWS value as `aws_bgp_holdtime` in the `cloudflare_tunnel_mapping` output.

After a session goes down, Cloudflare propagates the route withdrawal in no more than a few minutes. In Edge Resiliency Mode, a Cloudflare data center keeps forwarding on its last known routes while tunnel health checks pass, so a BGP failure on a healthy tunnel may not withdraw the path immediately.

For more information, refer to [BGP route distribution and convergence](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#bgp-route-distribution-and-convergence), [BGP timers and settings](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#bgp-timers-and-settings), and [Edge Resiliency Mode](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#edge-resiliency-mode-non-stop-forwarding).

### AS path loop prevention

Cloudflare preserves the original AS path between Cloudflare WAN sites, and AWS Transit Gateway has no `allowas-in` option. AWS silently rejects routes from any other Cloudflare WAN site that uses the Transit Gateway ASN. Give each site a unique ASN, and do not use the AWS defaults: `64512` for Transit Gateways and virtual private gateways, and `65000` for customer gateways.

### Prefix control

Cloudflare advertises every eligible route from the Unified Routing dataplane, plus any entries in **Additional advertised prefix list**. Leaving that list empty does not limit what Cloudflare advertises. AWS accepts up to 1,000 routes from each VPN connection and advertises up to 5,000; neither quota is adjustable.

Advertising `0.0.0.0/0` to AWS sends Internet traffic through Cloudflare Gateway only if the workload route table also sends `0.0.0.0/0` to the Transit Gateway. The Terraform reference deployment does not use this design.

### Block unwanted prefixes

To stop AWS from using a prefix, remove it from the Cloudflare advertisement (a static route under **Networking** > **Routes**, or an **Additional advertised prefix list** entry), or add a blackhole static route for the exact CIDR in `<AWS_TGW_ROUTE_TABLE_ID>`. A Transit Gateway static route takes precedence over a VPN-propagated route for the same CIDR, but not over a more-specific route. Routes that Cloudflare learns over BGP from other sites can only be withdrawn at the originating site or blocked in AWS. Remove any `0.0.0.0/0` blackhole route before adopting Cloudflare Gateway egress.

For more information, refer to [Route evaluation order](https://docs.aws.amazon.com/vpc/latest/tgw/how-transit-gateways-work.html#tgw-route-evaluation-overview) in the AWS documentation.

## MSS and MTU

For Cloudflare WAN guidance, refer to [Maximum transmission unit and maximum segment size](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/).

IPsec encapsulation reduces the usable packet size. Cloudflare recommends a maximum TCP MSS clamp of `1360` bytes for IPsec tunnels. This value is below the `1382`-byte MSS that AWS lists for AES-CBC with SHA-256 and NAT-T, so it is compatible with the cipher suite in this guide. AWS Site-to-Site VPN does not provide a per-VPN MSS-clamping control, so apply the clamp on a device or workload in the traffic path when necessary.

AWS Site-to-Site VPN does not support Path MTU Discovery. Validate large TCP flows with controlled packet-size and Don't Fragment (DF) tests. Where supported, fragment packets before encryption or apply MSS clamping rather than relying on Path MTU Discovery.

## Verification

### Verify AWS tunnel and BGP status

For each VPN connection:

1. In the AWS VPC console, go to **Site-to-Site VPN connections**.
2. Select the VPN connection.
3. Open **Tunnel details**.
4. Confirm both tunnels show **UP**.
5. Confirm the accepted route count matches the expected Cloudflare WAN prefix inventory.

An **UP** tunnel confirms IKE and IPsec state, not the BGP finite-state-machine state. Confirm each session is **Established** in the Cloudflare BGP status view. A positive accepted route count confirms route exchange, but an established session can legitimately have zero accepted routes.

You can also verify the configured tunnels with the AWS CLI:

```bash
aws ec2 describe-vpn-connections --vpn-connection-ids <AWS_VPN_CONNECTION_01_ID> --query 'VpnConnections[].{VpnId:VpnConnectionId,Tunnels:VgwTelemetry[].{OutsideIP:OutsideIpAddress,Status:Status,ImportedRoutes:AcceptedRouteCount,Message:StatusMessage}}' --output table --no-cli-pager
```

:::note[Secondary anycast IP customers]
Add `<AWS_VPN_CONNECTION_02_ID>` after `<AWS_VPN_CONNECTION_01_ID>` in the `--vpn-connection-ids` argument.
:::

Expected results:

- Every configured tunnel reports `UP`
- `AcceptedRouteCount` matches the expected Cloudflare WAN prefixes and is greater than zero when routes are being advertised

The command reports tunnel telemetry and accepted route counts; it does not expose an explicit BGP session state. Use the Cloudflare BGP status view to verify that every configured session is currently established.

### Verify the Transit Gateway routes

1. Go to **Transit gateways** > **Transit gateway route tables**.
2. Select `<AWS_TGW_ROUTE_TABLE_ID>` and open **Routes**.
3. Confirm `192.168.0.0/16` is an active propagated route to the workload VPC attachment.
4. Confirm the expected RFC 1918 prefixes and any explicitly enabled public prefixes appear as propagated routes through the VPN attachments.
5. Confirm equivalent Cloudflare WAN routes have multiple equal paths when ECMP is expected.

Enumerate the exact active Transit Gateway routes and compare every CIDR and attachment with the approved prefix inventory:

```bash
aws ec2 search-transit-gateway-routes --transit-gateway-route-table-id <AWS_TGW_ROUTE_TABLE_ID> --filters Name=state,Values=active --max-results 1000 --query '{AdditionalRoutesAvailable:AdditionalRoutesAvailable,Routes:Routes[].{CIDR:DestinationCidrBlock,Type:Type,State:State,Attachments:TransitGatewayAttachments[].TransitGatewayAttachmentId}}' --output json --no-cli-pager
```

If `AdditionalRoutesAvailable` is `true`, the result is incomplete. Run narrower searches for each approved prefix and investigate any route not covered by the approved inventory.

### Verify Cloudflare tunnel and route status

1. In the Cloudflare dashboard, go to **Networking** > **Insights** > **Network health** > **IPsec/GRE tunnels and interconnects**.
2. Confirm every configured tunnel is healthy.
3. Inspect the BGP status for each tunnel and confirm every configured session is **Established**.
4. Go to **Networking** > **Routes**.
5. Confirm `192.168.0.0/16` is learned through the AWS BGP sessions.
6. Inspect the available route paths and confirm the expected tunnels contribute paths.
7. Confirm the complete AWS VPC prefix is present and no unrelated AWS prefix is advertised.

### Test end-to-end connectivity

1. Start a packet capture or flow-log query where available.
2. From a permitted AWS workload in `192.168.125.0/24`, ping or connect to a host in a prefix learned from Cloudflare WAN.
3. Test the reverse direction from a Cloudflare WAN-connected network to the AWS workload.
4. Confirm the application flow is not translated unexpectedly.
5. During a maintenance window, disable one tunnel and confirm its BGP path is withdrawn while traffic continues over the other tunnel. Allow up to the negotiated BGP hold time for each side to declare the session down, plus up to a few minutes for Cloudflare to propagate the withdrawal. Refer to [BGP timers and convergence](#bgp-timers-and-convergence).

:::note[Secondary anycast IP customers]
Also test the loss of both tunnels in one VPN connection and confirm traffic continues through the second Cloudflare anycast IP. Allow the same convergence time.
:::

Also confirm that every approved destination prefix (`vpn_routed_prefixes` in Terraform) uses the Transit Gateway and that an unmatched IPv4 destination still uses the existing Internet Gateway or NAT gateway default route. AWS longest-prefix matching gives the configured VPN routes precedence over `0.0.0.0/0`. Testing direct Internet egress requires a workload with a public IPv4 address or a NAT gateway path; the Terraform proof of concept does not create a workload or NAT service.

## Troubleshooting

### All tunnels are down

- Confirm each AWS customer gateway IP matches its configured Cloudflare anycast IP
- Confirm **Startup action** is `Start`; Cloudflare only responds to IKE and never initiates it
- Verify IKEv2, AES-256-CBC, SHA-256, and DH group 20 are the enabled proposals
- Confirm each pre-shared key matches the corresponding AWS public IPv4 address and Cloudflare WAN tunnel
- Review Cloudflare tunnel health events

### One tunnel is down

- Confirm the Cloudflare **Customer endpoint** contains the AWS **Public IPv4 address** generated for that specific VPN tunnel
- Verify the inside `/30`, AWS public IPv4 address, and pre-shared key were not copied from another tunnel
- Confirm every inside CIDR is unique and within `169.254.240.0/20`
- Confirm replay protection is enabled in Cloudflare and the AWS replay window is `2048`
- Confirm **DPD timeout action** is `Restart`

### IPsec is up but BGP is down

- Confirm the AWS customer gateway BGP ASN matches the CF Account ASN
- Confirm **Customer device ASN** is `65020` on the corresponding Cloudflare WAN tunnel
- Confirm BGP is enabled on every Cloudflare WAN tunnel
- Confirm the Cloudflare interface address matches the second usable address in the AWS `/30`
- Confirm no BGP MD5 key is configured in Cloudflare
- Confirm AWS initiates TCP port 179 toward the Cloudflare interface address

Interpret the reported BGP state as follows:

| **State**   | **Meaning**                              | **Primary checks**                                             |
| ----------- | ---------------------------------------- | -------------------------------------------------------------- |
| Established | The session is exchanging routes         | Validate received and advertised prefix inventories            |
| Active      | The peer is retrying the connection      | Check the peer address, ASN, and TCP port 179 reachability     |
| Connect     | The TCP connection is in progress        | Check tunnel reachability and the Cloudflare interface address |
| Idle        | The session is not attempting to connect | Confirm BGP is enabled and AWS is configured to initiate       |

### BGP is up but routes are missing

- Confirm every configured VPN attachment has propagation enabled in the intended Transit Gateway route table
- Confirm the workload VPC attachment is associated with and propagated into that table
- Confirm `192.168.0.0/16` exists as a propagated Transit Gateway route to the VPC attachment
- Check whether BGP loop prevention rejects a route containing ASN `65020`
- Confirm the expected Cloudflare WAN routes are eligible in the Unified Routing dataplane
- Check for route filters or a more-specific route that changes the forwarding result

### Routes exist but traffic does not pass

- Check the workload subnet route table, Transit Gateway route table, and Cloudflare WAN route table in both directions
- Confirm security groups, network access control lists, host firewalls, and Cloudflare WAN network firewall policies allow the traffic
- Check for overlapping or more-specific routes
- Review VPC Flow Logs and Transit Gateway Flow Logs, if enabled
- Confirm source and destination prefixes are not translated unexpectedly

Trace the route at all three forwarding layers:

1. The workload subnet route table must send the destination prefix to the Transit Gateway.
2. The dedicated Transit Gateway route table must send the destination through the VPN attachments.
3. Cloudflare WAN must have a return route for `192.168.0.0/16` learned from the AWS BGP sessions.

If only one direction works, compare source and destination prefixes in the flow logs and confirm the return route is present rather than assuming the IPsec tunnel is the cause.

### Tunnel health is degraded but IPsec is up

- Confirm the health check rate is **Medium**, type is **Request**, and direction is **Bidirectional**
- Confirm no custom health check target is set; the target should be the AWS inside address in the same `/30`
- Verify the Cloudflare interface and AWS inside addresses belong to the same `/30`
- Confirm the corresponding AWS tunnel is using the expected inside CIDR
- Check whether a network policy blocks ICMP to the AWS tunnel inside address

### Traffic uses fewer tunnels than expected

- Confirm VPN ECMP support is enabled on the Transit Gateway
- Confirm every configured IPsec tunnel and BGP session is up
- Confirm each tunnel advertises the same prefix with equivalent BGP attributes
- Confirm every configured VPN attachment is associated with and propagated into the dedicated route table
- Remember that ECMP distributes flows, not individual packets; a small number of test flows may not use every tunnel

### Unexpected prefixes appear in AWS or Cloudflare

- Audit every route and propagation in the dedicated Transit Gateway route table
- Confirm the workload VPC attachment and every configured VPN attachment are propagated
- Confirm no unrelated attachment is propagated into the dedicated table
- Review the eligible route inventory in the Unified Routing dataplane and each tunnel's **Additional advertised prefix list**
- Remove an unintended route at its source rather than relying on a less-specific route to override it

### Small packets work but applications fail

- Do not rely on Path MTU Discovery, which AWS Site-to-Site VPN does not support
- Use controlled packet-size and DF-bit tests to identify the effective path MTU
- Apply a TCP MSS clamp of no more than `1360` bytes where the architecture permits
- Check whether ICMP fragmentation-needed messages are filtered

## Design notes

- Each AWS Site-to-Site VPN connection provides two tunnels in different AWS Availability Zones
- AWS does not support creating a single-tunnel Site-to-Site VPN connection; both tunnels use the same customer gateway and therefore the same Cloudflare anycast IP
- The baseline VPN connection provides two paths through one Cloudflare anycast IP; a second anycast IP adds a second VPN connection and two more paths
- AWS requires `/30` inside tunnel networks, so this integration cannot use the `/31` interface addresses that Cloudflare recommends for other devices
- AWS Transit Gateway can use ECMP across equal BGP paths when VPN ECMP support is enabled
- Cloudflare automatically withdraws a route learned from a failed BGP session while equivalent paths remain available through healthy sessions
- Standard AWS Site-to-Site VPN tunnels support the AWS-published bandwidth limit per tunnel; Transit Gateway also supports Large Bandwidth Tunnels in eligible Regions, but validate Cloudflare interoperability and commercial requirements before using that option
- Modifying an AWS VPN tunnel option can interrupt that tunnel for several minutes
- Configure every tunnel with an 8-64 character PSK containing only letters, numbers, periods, and underscores; a PSK cannot begin with `0`, and console-configured tunnels can use unique PSKs or reuse one PSK across all tunnels, while the Terraform workflow requires a unique PSK for each tunnel

## Summary

The primary topology connects AWS Transit Gateway through:

- One Cloudflare anycast IP
- One AWS customer gateway
- One AWS Site-to-Site VPN connection
- Two IPsec tunnels and two BGP sessions

:::note[Secondary anycast IP customers]
Add:

- One additional Cloudflare anycast IP
- One additional AWS customer gateway
- One additional AWS Site-to-Site VPN connection
- Two additional IPsec tunnels and two additional BGP sessions

:::

For each configured anycast IP, the Terraform workflow requires:

- Exactly two unique `/30` tunnel CIDRs
- Exactly two AWS-compatible pre-shared keys

## Configuration record

Use this table during implementation and handoff. Do not record pre-shared key values in the document.

| **Placeholder**                         | **Description**                                                        |
| --------------------------------------- | ---------------------------------------------------------------------- |
| `<AWS_TGW_ID>`                          | Transit Gateway ID                                                     |
| `<AWS_TGW_ROUTE_TABLE_ID>`              | Dedicated route table associated with the workload and VPN attachments |
| `<AWS_VPC_ATTACHMENT_ID>`               | Workload VPC attachment ID                                             |
| `<AWS_CUSTOMER_GATEWAY_01_ID>`          | Customer gateway representing `162.159.135.1`                          |
| `<AWS_VPN_CONNECTION_01_ID>`            | VPN connection using `162.159.135.1`                                   |
| `<AWS_OUTSIDE_IP_01>`                   | AWS public IPv4 address for VPN 1, tunnel 1                            |
| `<AWS_OUTSIDE_IP_02>`                   | AWS public IPv4 address for VPN 1, tunnel 2                            |
| `<TUNNEL_01_PSK>` and `<TUNNEL_02_PSK>` | Pre-shared keys for tunnels 01 and 02                                  |
| `<EXPECTED_RFC1918_PREFIX_LIST>`        | RFC 1918 prefixes AWS is expected to learn from Cloudflare WAN         |

:::note[Secondary anycast IP customers]
Also record:

- `<AWS_CUSTOMER_GATEWAY_02_ID>` for the customer gateway representing `172.64.135.1`
- `<AWS_VPN_CONNECTION_02_ID>` for the VPN connection using `172.64.135.1`
- `<AWS_OUTSIDE_IP_03>` for VPN 2, tunnel 1
- `<AWS_OUTSIDE_IP_04>` for VPN 2, tunnel 2
- `<TUNNEL_03_PSK>` for tunnel 03
- `<TUNNEL_04_PSK>` for tunnel 04

:::

The completed record should let an operator correlate an AWS tunnel alarm, Cloudflare tunnel-health event, BGP session, and route-table path without guessing which resources belong together.

## Terraform reference deployment

The self-contained Terraform configuration creates a minimal proof of concept with:

- One workload VPC
- One public workload subnet
- Two Transit Gateway attachment subnets in separate Availability Zones
- One Internet Gateway
- One workload route table with split-egress routes
- One Transit Gateway and one dedicated Transit Gateway route table
- One VPC attachment
- One customer gateway and one Site-to-Site VPN connection per configured anycast IP
- Two AWS IPsec/BGP tunnels per VPN connection
- Two matching Cloudflare WAN tunnels with BGP per VPN connection
- The required Transit Gateway route-table associations and propagations

The configuration does not create:

- Virtual machines or test hosts
- Standalone Elastic Network Interfaces (ENIs); AWS creates ENIs for the Transit Gateway VPC attachment in the attachment subnets
- Security groups
- Elastic IP addresses
- NAT gateways or other NAT services
- Cloudflare routes
- Cloudflare network firewall policies

AWS assigns two public IPv4 addresses per VPN connection for use as the Cloudflare **Customer endpoint** values.

The deployment propagates the complete VPC CIDR into the Transit Gateway route table for advertisement to Cloudflare and propagates all routes learned from Cloudflare into that table. In the VPC workload route table, each configured `vpn_routed_prefixes` destination is a more-specific route to the Transit Gateway and Site-to-Site VPNs, while `0.0.0.0/0` sends all remaining IPv4 egress directly through the Internet Gateway. AWS longest-prefix matching selects a configured VPN route over the Internet default when both match.

The workload subnet enables public IPv4 assignment on launch, but this configuration does not create a workload to receive an address. Direct Internet Gateway egress requires a public IPv4 address on the workload ENI and appropriate security controls; an Internet Gateway does not provide NAT for private-only hosts.

:::caution[Route availability for VPN-routed prefixes]
A destination matching `vpn_routed_prefixes` has no fallback to the Internet Gateway and will blackhole unless the Transit Gateway has an active matching route learned through the AWS VPN attachments for Cloudflare WAN.
:::

:::caution[Billable AWS resources]
Terraform creates billable AWS resources. Run this deployment in a non-production AWS account. Review the plan against your organization's security and tagging requirements. Remove the resources when testing is complete.
:::

### Reference deployment layout

| **Resource**                        | **Reference value** |
| ----------------------------------- | ------------------- |
| AWS Region                          | `us-east-2`         |
| VPC CIDR                            | `192.168.0.0/16`    |
| Public workload subnet              | `192.168.125.0/24`  |
| Transit Gateway attachment subnet A | `192.168.124.0/28`  |
| Transit Gateway attachment subnet B | `192.168.124.16/28` |
| VPN-routed prefix example           | `172.16.0.0/16`     |
| Remaining IPv4 egress               | Internet Gateway    |
| AWS Transit Gateway ASN             | `65020`             |
| CF Account ASN                      | `13335` (default)   |
| Cloudflare anycast IP 1             | `162.159.135.1`     |

The baseline reference layout uses one anycast IP.

:::note[Secondary anycast IP customers]
Use `172.64.135.1` as the second anycast IP.
:::

### Terraform prerequisites

Install Terraform `1.11.0` or later. The included tests use `override_during`, which requires Terraform `1.11.0`. Configure AWS credentials for the target account. Discover the active AWS account ID with the following command:

```bash
aws sts get-caller-identity --query Account --output text
```

Set the returned value as `expected_account_id`. The Terraform AWS provider verifies the active account against this value before creating resources.

Create a Cloudflare API token scoped to the target account with `Magic WAN Write` or `Magic Transit Write` permission. Make the token available to the Terraform process as `CLOUDFLARE_API_TOKEN`:

macOS or Linux:

```bash
export CLOUDFLARE_API_TOKEN="<API_TOKEN>"
```

PowerShell:

```powershell
$env:CLOUDFLARE_API_TOKEN = "<API_TOKEN>"
```

Run `terraform plan` and `terraform apply` from the same environment where the token is set. In the Cloudflare dashboard, select the target account and copy its Account ID from the account overview; avoid relying on a fixed navigation path because the dashboard can change. Set this 32-character value as `cloudflare_account_id` in `terraform.tfvars`. Set `cloudflare_asn` to the account's CF Account ASN, recorded under **Networking** > **Routes** > **WAN configuration**. Terraform accepts `13335` or a private ASN from `64512` through `65534` that differs from `aws_transit_gateway_asn`. Terraform does not change the CF Account ASN because Cloudflare provider v5.27 does not expose the account-wide BGP settings as a resource.

Confirm that every value in `cloudflare_anycast_ips` appears under **Address Space** > **Leased IPs** in the account identified by `cloudflare_account_id`. These values are the public Cloudflare anycast IPs, not Cloudflare WAN routed prefixes or link-local tunnel interface addresses.

Create an empty Terraform directory and add the following files from the complete listings in this guide:

| **File**                                   | **Purpose**                                                               |
| ------------------------------------------ | ------------------------------------------------------------------------- |
| `versions.tf`                              | Configures Terraform, the AWS and Cloudflare providers, and account guard |
| `variables.tf`                             | Declares and validates all deployment inputs                              |
| `main.tf`                                  | Creates the AWS network, IGW, selective routes, Transit Gateway, and VPNs |
| `cloudflare.tf`                            | Creates the Cloudflare WAN tunnels, health checks, and BGP sessions       |
| `outputs.tf`                               | Returns resource IDs, egress routes, tunnel mappings, and status          |
| `terraform.tfvars.example`                 | Provides the reference input values for one anycast IP                    |
| `terraform.tfvars.two-anycast-ips.example` | Provides the reference input values for two anycast IPs                   |
| `tests/reference.tftest.hcl`               | Tests reference invariants and invalid inputs with mock providers         |

Do not create `.terraform.lock.hcl` from a listing. `terraform init` generates it from the provider constraints, and you should commit the generated file to preserve the provider selections reviewed for your deployment. A `.gitignore` and `README.md` are optional repository support files and are not required to execute this configuration or its tests, so they are not included.

:::caution[Existing deployments]
Terraform does not automatically adopt AWS or Cloudflare resources created outside this configuration, such as by the console workflow in this guide. Import each existing resource into Terraform state, or remove the existing deployment before applying this configuration. Do not apply it over an existing deployment without first choosing one of those options.
:::

:::caution[Existing Cloudflare tunnels]
Terraform does not automatically adopt manually created Cloudflare tunnels. Import matching tunnels before applying this configuration, or remove them first.

Cloudflare rejects a tunnel whose name or interface address matches another tunnel in the same Cloudflare account, including tunnels from another deployment of this configuration. Before applying, review the existing tunnels on the **Connectors** page and choose:

- A `deployment_name` or `cloudflare_tunnel_names` value that produces names no other tunnel uses
- `tunnel_inside_cidrs` values whose Cloudflare interface addresses do not overlap any existing tunnel

To list the names and interface addresses already in use, refer to [Check existing tunnel names and interface addresses](#check-existing-tunnel-names-and-interface-addresses).
:::

### Configure the Terraform variables

After creating the files below, copy `terraform.tfvars.example` to `terraform.tfvars`. Replace both account IDs and the two PSK placeholders.

:::note[Secondary anycast IP customers]
Copy `terraform.tfvars.two-anycast-ips.example` to `terraform.tfvars` instead, and replace all four PSK placeholders.
:::

Terraform derives the deployment size from the number of entries in `cloudflare_anycast_ips`. One anycast IP creates one customer gateway, one Site-to-Site VPN connection, and two Cloudflare WAN tunnels. Two anycast IPs create two of each AWS resource and four Cloudflare WAN tunnels. Validation rejects zero or more than two anycast IPs, duplicate anycast IPs, and any tunnel CIDR or PSK count other than two per anycast IP.

Cloudflare tunnel names default to `<deployment_name>-tun-01`, `-02`, and so on. To choose the names, set `cloudflare_tunnel_names` to two unique names per anycast IP in tunnel order. Names can contain letters, numbers, hyphens, and underscores, and must be unique within the Cloudflare account.

Configure exactly two tunnel CIDRs and two PSKs per anycast IP. Every PSK must be unique. Review every CIDR, anycast IP, ASN, `vpn_routed_prefixes` destination, and additional advertised prefix. The VPN-routed list accepts unique canonical IPv4 prefixes more specific than `0.0.0.0/0` that do not overlap the VPC CIDR; use it only for destinations that should traverse Cloudflare WAN.

The anycast IP list order defines Terraform resource identity. Keep the first anycast IP unchanged. Append a second anycast IP to expand, or remove only the final anycast IP to shrink. Reordering anycast IPs or replacing the first anycast IP can replace every customer gateway and VPN path, causing an outage. Review those changes in a maintenance window.

The `resource_ids` output returns `vpn_connection_ids` and `vpn_attachment_ids` maps keyed by `"01"` and optional `"02"`.

Each customer gateway uses the device name `<deployment_name>-cgw-NN`, so AWS creates a distinct customer gateway instead of returning an existing gateway with the same IP address and ASN. Changing `deployment_name` replaces every customer gateway and briefly interrupts each VPN connection. Review those changes in a maintenance window.

The `aws_transit_gateway_asn` default is `65020`. AWS does not allow changing the ASN of a Transit Gateway that has VPN attachments, so set `aws_transit_gateway_asn` before the first apply and do not change it afterward.

The empty `cloudflare_additional_advertised_prefixes` list does not suppress routes already eligible for BGP advertisement from the Cloudflare routing table. Add only prefixes that Cloudflare must advertise in addition to those routes.

### Complete Terraform configuration

#### `versions.tf`

Create `versions.tf` to constrain Terraform and provider versions, configure provider authentication, guard the AWS account, and apply default tags.

```hcl
terraform {
  # terraform test uses override_during, which requires Terraform 1.11.0 or later.
  required_version = ">= 1.11.0"

  # Keep provider behavior within the minor versions tested by this guide.
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.67.0"
    }

    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 5.27.0"
    }
  }
}

# Authentication comes from CLOUDFLARE_API_TOKEN rather than a Terraform variable,
# which keeps the API token out of configuration and variable files.
provider "cloudflare" {}

provider "aws" {
  region = var.aws_region
  # Refuse to operate if the active credentials point at the wrong AWS account.
  allowed_account_ids = [var.expected_account_id]

  default_tags {
    tags = {
      Deployment = var.deployment_name
      ManagedBy  = "Terraform"
    }
  }
}
```

#### `variables.tf`

Create `variables.tf` to declare deployment inputs and reject invalid account IDs, CIDRs, anycast IPs, ASNs, and pre-shared keys.

```hcl
variable "aws_region" {
  description = "AWS Region for the reference deployment."
  type        = string
  default     = "us-east-2"
}

variable "expected_account_id" {
  description = "Twelve-digit AWS account ID allowed to receive the deployment."
  type        = string

  validation {
    condition     = can(regex("^[0-9]{12}$", var.expected_account_id))
    error_message = "expected_account_id must be a 12-digit AWS account ID."
  }
}

variable "cloudflare_account_id" {
  description = "Cloudflare account ID that owns the Cloudflare WAN configuration."
  type        = string

  validation {
    condition     = can(regex("^[0-9a-fA-F]{32}$", var.cloudflare_account_id))
    error_message = "cloudflare_account_id must be a 32-character hexadecimal Cloudflare account ID."
  }
}

variable "deployment_name" {
  description = "Name used to tag the reference deployment."
  type        = string
  default     = "cloudflare-wan-reference"

  validation {
    condition     = can(regex("^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$", var.deployment_name))
    error_message = "deployment_name may contain only letters, numbers, periods, underscores, and hyphens."
  }
}

variable "vpc_cidr" {
  description = "IPv4 CIDR for the workload VPC."
  type        = string
  default     = "192.168.0.0/16"

  validation {
    # Require the canonical network address so containment checks are unambiguous.
    condition = try(
      can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", var.vpc_cidr)) &&
      cidrhost(var.vpc_cidr, 0) == split("/", var.vpc_cidr)[0] &&
      tonumber(split("/", var.vpc_cidr)[1]) >= 16 &&
      tonumber(split("/", var.vpc_cidr)[1]) <= 28,
      false
    )
    error_message = "vpc_cidr must be an IPv4 network with an AWS-supported /16 through /28 prefix."
  }
}

variable "workload_subnet_cidr" {
  description = "Public subnet for workloads that use split Internet and Cloudflare WAN connectivity."
  type        = string
  default     = "192.168.125.0/24"

  validation {
    condition = try(
      can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", var.workload_subnet_cidr)) &&
      cidrhost(var.workload_subnet_cidr, 0) == split("/", var.workload_subnet_cidr)[0] &&
      tonumber(split("/", var.workload_subnet_cidr)[1]) >= 16 &&
      tonumber(split("/", var.workload_subnet_cidr)[1]) <= 28,
      false
    )
    error_message = "workload_subnet_cidr must be an IPv4 network with an AWS-supported /16 through /28 prefix."
  }
}

variable "vpn_routed_prefixes" {
  description = "IPv4 destination prefixes routed through the Transit Gateway and Site-to-Site VPNs instead of the Internet Gateway."
  type        = list(string)
  default     = []

  validation {
    # The default route belongs to the Internet Gateway; only more-specific
    # canonical networks can be selected for the VPN path.
    condition = length(distinct(var.vpn_routed_prefixes)) == length(var.vpn_routed_prefixes) && alltrue([
      for cidr in var.vpn_routed_prefixes : try(
        can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", cidr)) &&
        cidrhost(cidr, 0) == split("/", cidr)[0] &&
        tonumber(split("/", cidr)[1]) >= 1 &&
        tonumber(split("/", cidr)[1]) <= 32,
        false
      )
    ])
    error_message = "vpn_routed_prefixes must contain unique canonical IPv4 network CIDRs more specific than 0.0.0.0/0."
  }

  validation {
    # Overlapping local and remote address space cannot be routed unambiguously.
    condition = alltrue([
      for cidr in var.vpn_routed_prefixes : try(
        cidrhost("${cidrhost(cidr, 0)}/${split("/", var.vpc_cidr)[1]}", 0) != cidrhost(var.vpc_cidr, 0) &&
        cidrhost("${cidrhost(var.vpc_cidr, 0)}/${split("/", cidr)[1]}", 0) != cidrhost(cidr, 0),
        false
      )
    ])
    error_message = "vpn_routed_prefixes must not overlap vpc_cidr."
  }
}

variable "tgw_subnet_a_cidr" {
  description = "Transit Gateway attachment subnet in the first Availability Zone."
  type        = string
  default     = "192.168.124.0/28"

  validation {
    condition = try(
      can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", var.tgw_subnet_a_cidr)) &&
      cidrhost(var.tgw_subnet_a_cidr, 0) == split("/", var.tgw_subnet_a_cidr)[0] &&
      tonumber(split("/", var.tgw_subnet_a_cidr)[1]) >= 16 &&
      tonumber(split("/", var.tgw_subnet_a_cidr)[1]) <= 28,
      false
    )
    error_message = "tgw_subnet_a_cidr must be an IPv4 network with an AWS-supported /16 through /28 prefix."
  }
}

variable "tgw_subnet_b_cidr" {
  description = "Transit Gateway attachment subnet in the second Availability Zone."
  type        = string
  default     = "192.168.124.16/28"

  validation {
    condition = try(
      can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", var.tgw_subnet_b_cidr)) &&
      cidrhost(var.tgw_subnet_b_cidr, 0) == split("/", var.tgw_subnet_b_cidr)[0] &&
      tonumber(split("/", var.tgw_subnet_b_cidr)[1]) >= 16 &&
      tonumber(split("/", var.tgw_subnet_b_cidr)[1]) <= 28,
      false
    )
    error_message = "tgw_subnet_b_cidr must be an IPv4 network with an AWS-supported /16 through /28 prefix."
  }
}

variable "tunnel_inside_cidrs" {
  description = "Two unique AWS VPN tunnel /30 networks per Cloudflare anycast IP, ordered by VPN connection and tunnel."
  type        = list(string)
  default = [
    "169.254.240.44/30",
    "169.254.240.48/30",
  ]

  validation {
    # Every AWS VPN connection contains exactly two tunnels.
    condition = length(var.tunnel_inside_cidrs) == length(var.cloudflare_anycast_ips) * 2 && length(distinct(var.tunnel_inside_cidrs)) == length(var.tunnel_inside_cidrs) && alltrue([
      for cidr in var.tunnel_inside_cidrs : try(
        split("/", cidr)[1] == "30" &&
        cidrhost(cidr, 0) == split("/", cidr)[0] &&
        cidrhost("${cidrhost(cidr, 0)}/20", 0) == "169.254.240.0" &&
        cidrhost("${cidrhost(cidr, -1)}/20", 0) == "169.254.240.0",
        false
      )
    ])
    error_message = "tunnel_inside_cidrs must contain two unique /30 network addresses within 169.254.240.0/20 for each Cloudflare anycast IP."
  }
}

variable "vpn_preshared_keys" {
  description = "Two AWS-compatible pre-shared keys per Cloudflare anycast IP, in tunnel order."
  type        = list(string)
  # Sensitive suppresses CLI display, but the keys are still stored in state.
  sensitive = true

  validation {
    # List order matches tunnel_inside_cidrs and the generated tunnel numbering.
    # Key format matches the AWS Site-to-Site VPN PreSharedKey constraints.
    condition = length(var.vpn_preshared_keys) == length(var.cloudflare_anycast_ips) * 2 && alltrue([
      for key in var.vpn_preshared_keys : can(regex("^[A-Za-z1-9._][A-Za-z0-9._]{7,63}$", key))
    ])
    error_message = "vpn_preshared_keys must contain two keys per Cloudflare anycast IP; each key must contain 8-64 letters, numbers, periods, or underscores and cannot begin with 0."
  }

  validation {
    # The AWS provider identifies tunnel1_address by matching the tunnel 1 PSK.
    # Identical PSKs disable that match and can swap the AWS public IPv4
    # addresses assigned to the Cloudflare WAN tunnels.
    condition     = length(distinct(var.vpn_preshared_keys)) == length(var.vpn_preshared_keys)
    error_message = "vpn_preshared_keys must be unique for every tunnel."
  }
}

variable "cloudflare_anycast_ips" {
  description = "One or two Cloudflare anycast IPs leased to cloudflare_account_id."
  type        = list(string)
  default     = ["162.159.135.1"]

  validation {
    # Each anycast IP backs one AWS VPN connection containing two tunnels.
    condition = length(var.cloudflare_anycast_ips) >= 1 && length(var.cloudflare_anycast_ips) <= 2 && length(distinct(var.cloudflare_anycast_ips)) == length(var.cloudflare_anycast_ips) && alltrue([
      for address in var.cloudflare_anycast_ips : try(
        can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}$", address)) &&
        cidrhost("${address}/32", 0) == address,
        false
      )
    ])
    error_message = "cloudflare_anycast_ips must contain one or two distinct IPv4 addresses."
  }
}

variable "aws_transit_gateway_asn" {
  description = "Private ASN used by the AWS Transit Gateway."
  type        = number
  default     = 65020

  validation {
    condition     = floor(var.aws_transit_gateway_asn) == var.aws_transit_gateway_asn && ((var.aws_transit_gateway_asn >= 64512 && var.aws_transit_gateway_asn <= 65534) || (var.aws_transit_gateway_asn >= 4200000000 && var.aws_transit_gateway_asn <= 4294967294))
    error_message = "aws_transit_gateway_asn must be an AWS-supported private ASN."
  }
}

variable "cloudflare_asn" {
  description = "CF Account ASN configured in Cloudflare WAN and used by every AWS customer gateway."
  type        = number
  default     = 13335

  validation {
    # Cloudflare defaults to 13335 and permits a private 2-byte ASN instead.
    condition     = floor(var.cloudflare_asn) == var.cloudflare_asn && (var.cloudflare_asn == 13335 || (var.cloudflare_asn >= 64512 && var.cloudflare_asn <= 65534))
    error_message = "cloudflare_asn must match the CF Account ASN: 13335 or a private ASN from 64512 through 65534."
  }

  validation {
    # The CF Account ASN must differ from the customer device ASN on each tunnel.
    condition     = var.cloudflare_asn != var.aws_transit_gateway_asn
    error_message = "cloudflare_asn must differ from aws_transit_gateway_asn."
  }
}

variable "cloudflare_additional_advertised_prefixes" {
  description = "Additional prefixes for Cloudflare to advertise to AWS on every BGP session. Eligible Cloudflare WAN routes are advertised without listing them here."
  type        = list(string)
  default     = []

  validation {
    condition = length(distinct(var.cloudflare_additional_advertised_prefixes)) == length(var.cloudflare_additional_advertised_prefixes) && alltrue([
      for cidr in var.cloudflare_additional_advertised_prefixes : try(
        can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", cidr)) &&
        cidrhost(cidr, 0) == split("/", cidr)[0],
        false
      )
    ])
    error_message = "cloudflare_additional_advertised_prefixes must contain unique IPv4 network CIDRs."
  }
}

variable "cloudflare_tunnel_names" {
  description = "Optional Cloudflare WAN tunnel names in tunnel order, two per Cloudflare anycast IP. Leave empty to use <deployment_name>-tun-01, -02, and so on."
  type        = list(string)
  default     = []

  validation {
    # Cloudflare rejects a tunnel whose name matches any other tunnel in the account.
    condition = length(var.cloudflare_tunnel_names) == 0 || (
      length(var.cloudflare_tunnel_names) == length(var.cloudflare_anycast_ips) * 2 &&
      length(distinct(var.cloudflare_tunnel_names)) == length(var.cloudflare_tunnel_names) &&
      alltrue([for name in var.cloudflare_tunnel_names : can(regex("^[A-Za-z0-9_-]+$", name))])
    )
    error_message = "cloudflare_tunnel_names must be empty or contain two unique names per Cloudflare anycast IP, using only letters, numbers, hyphens, and underscores."
  }
}
```

#### `main.tf`

Create `main.tf` to build the AWS VPC, public workload subnet, Internet Gateway, split-egress routes, Transit Gateway, and one customer gateway and VPN connection per configured anycast IP. Each VPN connection contains two ordered tunnels.

```hcl
data "aws_availability_zones" "available" {
  state = "available"
}

locals {
  # Resources require two AZs; try() keeps expression evaluation safe until the
  # VPC precondition can return a clear error in a single-AZ Region.
  availability_zone_a = data.aws_availability_zones.available.names[0]
  availability_zone_b = try(data.aws_availability_zones.available.names[1], data.aws_availability_zones.available.names[0])
  subnet_cidrs        = [var.workload_subnet_cidr, var.tgw_subnet_a_cidr, var.tgw_subnet_b_cidr]
  subnet_pairs        = [[0, 1], [0, 2], [1, 2]]

  # Each anycast IP creates one AWS customer gateway and one two-tunnel VPN.
  customer_gateways = {
    for index, anycast_ip in var.cloudflare_anycast_ips : format("%02d", index + 1) => {
      cloudflare_anycast_ip = anycast_ip
      tunnel1_index         = index * 2
      tunnel2_index         = index * 2 + 1
    }
  }
}

resource "aws_vpc" "workload" {
  cidr_block           = var.vpc_cidr
  instance_tenancy     = "default"
  enable_dns_support   = true
  enable_dns_hostnames = false

  tags = {
    Name = "${var.deployment_name}-vpc"
  }

  lifecycle {
    precondition {
      condition     = length(data.aws_availability_zones.available.names) >= 2
      error_message = "The selected AWS Region must have at least two available Availability Zones."
    }

    precondition {
      # Check both ends of each subnet against the VPC to prevent partial overlap.
      condition = alltrue([
        for cidr in local.subnet_cidrs : try(
          cidrhost(cidr, 0) == split("/", cidr)[0] &&
          tonumber(split("/", cidr)[1]) >= tonumber(split("/", var.vpc_cidr)[1]) &&
          cidrhost("${cidrhost(cidr, 0)}/${split("/", var.vpc_cidr)[1]}", 0) == cidrhost(var.vpc_cidr, 0) &&
          cidrhost("${cidrhost(cidr, -1)}/${split("/", var.vpc_cidr)[1]}", 0) == cidrhost(var.vpc_cidr, 0),
          false
        )
      ])
      error_message = "Each subnet CIDR must be a valid network contained within vpc_cidr."
    }

    precondition {
      # Evaluate every pair so no workload or attachment subnet can overlap.
      condition = try(alltrue([
        for pair in local.subnet_pairs :
        cidrhost("${cidrhost(local.subnet_cidrs[pair[1]], 0)}/${split("/", local.subnet_cidrs[pair[0]])[1]}", 0) != cidrhost(local.subnet_cidrs[pair[0]], 0) &&
        cidrhost("${cidrhost(local.subnet_cidrs[pair[0]], 0)}/${split("/", local.subnet_cidrs[pair[1]])[1]}", 0) != cidrhost(local.subnet_cidrs[pair[1]], 0)
      ]), false)
      error_message = "The workload and Transit Gateway subnet CIDRs must not overlap."
    }
  }
}

resource "aws_subnet" "workload" {
  vpc_id                  = aws_vpc.workload.id
  cidr_block              = var.workload_subnet_cidr
  availability_zone       = local.availability_zone_a
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.deployment_name}-workload"
  }
}

# The workload subnet uses direct Internet Gateway egress for destinations that
# do not match a more-specific VPN route. Workloads still need a public IPv4
# address and security controls; this reference deployment does not create workloads.
resource "aws_internet_gateway" "workload" {
  vpc_id = aws_vpc.workload.id

  tags = {
    Name = "${var.deployment_name}-igw"
  }
}

resource "aws_subnet" "tgw_a" {
  vpc_id            = aws_vpc.workload.id
  cidr_block        = var.tgw_subnet_a_cidr
  availability_zone = local.availability_zone_a

  tags = {
    Name = "${var.deployment_name}-tgw-${local.availability_zone_a}"
  }
}

resource "aws_subnet" "tgw_b" {
  vpc_id            = aws_vpc.workload.id
  cidr_block        = var.tgw_subnet_b_cidr
  availability_zone = local.availability_zone_b

  tags = {
    Name = "${var.deployment_name}-tgw-${local.availability_zone_b}"
  }
}

resource "aws_route_table" "workload" {
  vpc_id = aws_vpc.workload.id

  tags = {
    Name = "${var.deployment_name}-workload-rt"
  }
}

resource "aws_route_table_association" "workload" {
  subnet_id      = aws_subnet.workload.id
  route_table_id = aws_route_table.workload.id
}

resource "aws_ec2_transit_gateway" "cloudflare" {
  amazon_side_asn                = var.aws_transit_gateway_asn
  auto_accept_shared_attachments = "disable"
  # Explicit associations and propagations below avoid implicit routing changes.
  default_route_table_association    = "disable"
  default_route_table_propagation    = "disable"
  dns_support                        = "enable"
  multicast_support                  = "disable"
  security_group_referencing_support = "disable"
  vpn_ecmp_support                   = "enable"

  tags = {
    Name = "${var.deployment_name}-tgw"
  }
}

resource "aws_ec2_transit_gateway_route_table" "cloudflare" {
  transit_gateway_id = aws_ec2_transit_gateway.cloudflare.id

  tags = {
    Name = "${var.deployment_name}-tgw-rt"
  }
}

resource "aws_ec2_transit_gateway_vpc_attachment" "workload" {
  subnet_ids                                      = [aws_subnet.tgw_a.id, aws_subnet.tgw_b.id]
  transit_gateway_id                              = aws_ec2_transit_gateway.cloudflare.id
  vpc_id                                          = aws_vpc.workload.id
  appliance_mode_support                          = "disable"
  dns_support                                     = "enable"
  ipv6_support                                    = "disable"
  security_group_referencing_support              = "disable"
  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false

  tags = {
    Name = "${var.deployment_name}-vpc-attachment"
  }
}

resource "aws_ec2_transit_gateway_route_table_association" "workload" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.workload.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.cloudflare.id
}

resource "aws_ec2_transit_gateway_route_table_propagation" "workload" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.workload.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.cloudflare.id
}

# Route only approved destination prefixes through the Transit Gateway. AWS
# longest-prefix matching gives these routes precedence over the Internet default.
resource "aws_route" "cloudflare" {
  for_each = toset(var.vpn_routed_prefixes)

  route_table_id         = aws_route_table.workload.id
  destination_cidr_block = each.value
  transit_gateway_id     = aws_ec2_transit_gateway.cloudflare.id

  depends_on = [aws_ec2_transit_gateway_vpc_attachment.workload]
}

# All destinations not selected above use direct Internet Gateway egress.
resource "aws_route" "internet" {
  route_table_id         = aws_route_table.workload.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.workload.id
}

resource "aws_customer_gateway" "cloudflare" {
  for_each = local.customer_gateways

  bgp_asn    = var.cloudflare_asn
  ip_address = each.value.cloudflare_anycast_ip
  type       = "ipsec.1"
  # AWS returns an existing customer gateway for an identical type, IP address,
  # and ASN unless the device name differs, so this name keeps the gateway unique.
  device_name = "${var.deployment_name}-cgw-${each.key}"

  tags = {
    Name = "${var.deployment_name}-cgw-${each.key}"
  }

  lifecycle {
    # AWS cannot delete an in-use customer gateway, so establish its replacement first.
    create_before_destroy = true
  }
}

resource "aws_vpn_connection" "cloudflare" {
  for_each = local.customer_gateways

  customer_gateway_id   = aws_customer_gateway.cloudflare[each.key].id
  transit_gateway_id    = aws_ec2_transit_gateway.cloudflare.id
  type                  = "ipsec.1"
  static_routes_only    = false
  preshared_key_storage = "Standard"

  local_ipv4_network_cidr  = "0.0.0.0/0"
  remote_ipv4_network_cidr = "0.0.0.0/0"
  outside_ip_address_type  = "PublicIpv4"
  tunnel_bandwidth         = "standard"
  tunnel_inside_ip_version = "ipv4"

  # Each VPN consumes the two tunnel inputs assigned to its anycast IP.
  tunnel1_inside_cidr                  = var.tunnel_inside_cidrs[each.value.tunnel1_index]
  tunnel1_preshared_key                = var.vpn_preshared_keys[each.value.tunnel1_index]
  tunnel1_replay_window_size           = 2048
  tunnel1_dpd_timeout_action           = "restart"
  tunnel1_phase1_encryption_algorithms = ["AES256"]
  tunnel1_phase2_encryption_algorithms = ["AES256"]
  tunnel1_phase1_integrity_algorithms  = ["SHA2-256"]
  tunnel1_phase2_integrity_algorithms  = ["SHA2-256"]
  tunnel1_phase1_dh_group_numbers      = [20]
  tunnel1_phase2_dh_group_numbers      = [20]
  tunnel1_phase1_lifetime_seconds      = 28800
  tunnel1_phase2_lifetime_seconds      = 3600
  tunnel1_ike_versions                 = ["ikev2"]
  tunnel1_startup_action               = "start"

  tunnel2_inside_cidr                  = var.tunnel_inside_cidrs[each.value.tunnel2_index]
  tunnel2_preshared_key                = var.vpn_preshared_keys[each.value.tunnel2_index]
  tunnel2_replay_window_size           = 2048
  tunnel2_dpd_timeout_action           = "restart"
  tunnel2_phase1_encryption_algorithms = ["AES256"]
  tunnel2_phase2_encryption_algorithms = ["AES256"]
  tunnel2_phase1_integrity_algorithms  = ["SHA2-256"]
  tunnel2_phase2_integrity_algorithms  = ["SHA2-256"]
  tunnel2_phase1_dh_group_numbers      = [20]
  tunnel2_phase2_dh_group_numbers      = [20]
  tunnel2_phase1_lifetime_seconds      = 28800
  tunnel2_phase2_lifetime_seconds      = 3600
  tunnel2_ike_versions                 = ["ikev2"]
  tunnel2_startup_action               = "start"

  tags = {
    Name = "${var.deployment_name}-vpn-${each.key}"
  }
}

resource "aws_ec2_transit_gateway_route_table_association" "vpn" {
  for_each = aws_vpn_connection.cloudflare

  transit_gateway_attachment_id  = each.value.transit_gateway_attachment_id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.cloudflare.id
}

resource "aws_ec2_transit_gateway_route_table_propagation" "vpn" {
  for_each = aws_vpn_connection.cloudflare

  transit_gateway_attachment_id  = each.value.transit_gateway_attachment_id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.cloudflare.id
}
```

#### `cloudflare.tf`

Create `cloudflare.tf` to map the AWS tunnels to Cloudflare WAN, select Cloudflare host addresses, and enable health checks and BGP for every configured anycast IP.

```hcl
locals {
  # Cloudflare tunnel names must be unique within the account. Use the supplied names, or
  # prefix generated names with deployment_name. Periods are replaced because tunnel names
  # avoid special characters.
  cloudflare_tunnel_name_prefix = replace(var.deployment_name, ".", "-")

  # Expand each anycast IP and AWS VPN connection into two Cloudflare WAN tunnel objects. Tunnel keys
  # remain globally ordered as 01-02 for anycast IP 1 and 03-04 for anycast IP 2.
  cloudflare_tunnels = merge([
    for vpn_key, vpn in local.customer_gateways : {
      (format("%02d", vpn.tunnel1_index + 1)) = {
        name                  = length(var.cloudflare_tunnel_names) > 0 ? var.cloudflare_tunnel_names[vpn.tunnel1_index] : "${local.cloudflare_tunnel_name_prefix}-tun-${format("%02d", vpn.tunnel1_index + 1)}"
        vpn_connection_key    = vpn_key
        cloudflare_anycast_ip = vpn.cloudflare_anycast_ip
        customer_endpoint     = aws_vpn_connection.cloudflare[vpn_key].tunnel1_address
        aws_bgp_holdtime      = aws_vpn_connection.cloudflare[vpn_key].tunnel1_bgp_holdtime
        inside_cidr           = var.tunnel_inside_cidrs[vpn.tunnel1_index]
        psk                   = var.vpn_preshared_keys[vpn.tunnel1_index]
      }
      (format("%02d", vpn.tunnel2_index + 1)) = {
        name                  = length(var.cloudflare_tunnel_names) > 0 ? var.cloudflare_tunnel_names[vpn.tunnel2_index] : "${local.cloudflare_tunnel_name_prefix}-tun-${format("%02d", vpn.tunnel2_index + 1)}"
        vpn_connection_key    = vpn_key
        cloudflare_anycast_ip = vpn.cloudflare_anycast_ip
        customer_endpoint     = aws_vpn_connection.cloudflare[vpn_key].tunnel2_address
        aws_bgp_holdtime      = aws_vpn_connection.cloudflare[vpn_key].tunnel2_bgp_holdtime
        inside_cidr           = var.tunnel_inside_cidrs[vpn.tunnel2_index]
        psk                   = var.vpn_preshared_keys[vpn.tunnel2_index]
      }
    }
  ]...)
}

resource "cloudflare_magic_wan_ipsec_tunnel" "aws" {
  for_each = local.cloudflare_tunnels

  account_id          = var.cloudflare_account_id
  name                = each.value.name
  description         = "AWS Transit Gateway tunnel ${each.key} managed by Terraform"
  customer_endpoint   = each.value.customer_endpoint
  cloudflare_endpoint = each.value.cloudflare_anycast_ip
  # AWS uses host 1 and Cloudflare uses host 2 in each /30 inside network.
  interface_address = "${cidrhost(each.value.inside_cidr, 2)}/30"
  # The provider writes this secret to Terraform state; protect the backend.
  psk               = each.value.psk
  replay_protection = true
  # BGP and the Cloudflare routing table, not automatic return routing, select paths.
  automatic_return_routing = false

  # Leave target unset so bidirectional checks use the default target: the AWS
  # inside address in the same /30.
  health_check = {
    enabled   = true
    rate      = "mid"
    type      = "request"
    direction = "bidirectional"
  }

  bgp = {
    customer_asn   = var.aws_transit_gateway_asn
    extra_prefixes = var.cloudflare_additional_advertised_prefixes
  }
}
```

#### `outputs.tf`

Create `outputs.tf` to expose resource IDs, workload egress routes, ordered tunnel mappings, and provider-refreshed status for every configured anycast IP.

```hcl
output "resource_ids" {
  description = "AWS resource IDs used to verify the deployment."
  value = {
    vpc_id                      = aws_vpc.workload.id
    workload_subnet_id          = aws_subnet.workload.id
    internet_gateway_id         = aws_internet_gateway.workload.id
    transit_gateway_id          = aws_ec2_transit_gateway.cloudflare.id
    transit_gateway_route_table = aws_ec2_transit_gateway_route_table.cloudflare.id
    vpc_attachment_id           = aws_ec2_transit_gateway_vpc_attachment.workload.id
    vpn_connection_ids          = { for key, vpn in aws_vpn_connection.cloudflare : key => vpn.id }
    vpn_attachment_ids          = { for key, vpn in aws_vpn_connection.cloudflare : key => vpn.transit_gateway_attachment_id }
    cloudflare_tunnel_ids       = { for key, tunnel in cloudflare_magic_wan_ipsec_tunnel.aws : key => tunnel.id }
  }
}

output "workload_egress_routes" {
  description = "Workload route-table destinations split between the VPN and Internet Gateway paths."
  value = {
    vpn              = sort(var.vpn_routed_prefixes)
    internet_default = aws_route.internet.destination_cidr_block
  }
}

output "bgp_asns" {
  description = "BGP ASNs used to configure Cloudflare WAN."
  value = {
    aws_transit_gateway = var.aws_transit_gateway_asn
    cloudflare          = var.cloudflare_asn
  }
}

output "cloudflare_tunnel_mapping" {
  description = "AWS and Cloudflare values for the two or four Terraform-managed tunnels."
  # Keep this list in tunnel-number order for direct operational correlation.
  value = [
    for key in sort(keys(local.cloudflare_tunnels)) : {
      tunnel                       = cloudflare_magic_wan_ipsec_tunnel.aws[key].name
      vpn_connection_id            = aws_vpn_connection.cloudflare[local.cloudflare_tunnels[key].vpn_connection_key].id
      aws_outside_ip               = local.cloudflare_tunnels[key].customer_endpoint
      cloudflare_anycast_ip        = local.cloudflare_tunnels[key].cloudflare_anycast_ip
      inside_cidr                  = local.cloudflare_tunnels[key].inside_cidr
      aws_inside_ip                = cidrhost(local.cloudflare_tunnels[key].inside_cidr, 1)
      cloudflare_interface_address = "${cidrhost(local.cloudflare_tunnels[key].inside_cidr, 2)}/30"
      # AWS sets this value; Cloudflare negotiates the lower of it and 240 seconds.
      aws_bgp_holdtime     = local.cloudflare_tunnels[key].aws_bgp_holdtime
      cloudflare_tunnel_id = cloudflare_magic_wan_ipsec_tunnel.aws[key].id
    }
  ]
}

output "cloudflare_bgp_status" {
  description = "Cloudflare BGP status refreshed during the most recent Terraform operation."
  # Provider status is a state snapshot, not continuous monitoring telemetry.
  value = {
    for key, tunnel in cloudflare_magic_wan_ipsec_tunnel.aws : key => tunnel.bgp_status
  }
}

output "vpn_telemetry" {
  description = "AWS tunnel telemetry refreshed during the most recent Terraform operation."
  # Refresh state before relying on these provider-reported values.
  value = {
    for key, vpn in aws_vpn_connection.cloudflare : key => vpn.vgw_telemetry
  }
}
```

#### `terraform.tfvars.example`

Create `terraform.tfvars.example` as the reviewed input template. Copy it to `terraform.tfvars` and replace every placeholder before planning. It configures the baseline of one anycast IP, two tunnel CIDRs, and two PSKs. Customers with two anycast IPs use the following template instead.

```hcl
expected_account_id   = "REPLACE_WITH_AWS_ACCOUNT_ID"
cloudflare_account_id = "REPLACE_WITH_CLOUDFLARE_ACCOUNT_ID"
deployment_name       = "cloudflare-wan-reference"
aws_region            = "us-east-2"

# These three subnets must be canonical, non-overlapping networks inside the VPC.
vpc_cidr             = "192.168.0.0/16"
workload_subnet_cidr = "192.168.125.0/24"
tgw_subnet_a_cidr    = "192.168.124.0/28"
tgw_subnet_b_cidr    = "192.168.124.16/28"

# Only these destinations use the Site-to-Site VPNs. All other IPv4 traffic
# uses the Internet Gateway default route.
vpn_routed_prefixes = [
  "172.16.0.0/16",
]

# Accounts using one anycast IP have two tunnels.
tunnel_inside_cidrs = [
  "169.254.240.44/30",
  "169.254.240.48/30",
]

# PSKs are sensitive and persist in Terraform state; never commit terraform.tfvars.
vpn_preshared_keys = [
  "REPLACE_WITH_UNIQUE_TUNNEL_01_PSK",
  "REPLACE_WITH_UNIQUE_TUNNEL_02_PSK",
]

# One anycast IP is the baseline and backs tunnels 01-02.
cloudflare_anycast_ips  = ["162.159.135.1"]
aws_transit_gateway_asn = 65020
# Set to the CF Account ASN shown under Networking > Routes > WAN configuration.
cloudflare_asn                            = 13335
cloudflare_additional_advertised_prefixes = []

# Optional. Cloudflare tunnel names must be unique in the Cloudflare account.
# Leave empty to use <deployment_name>-tun-01, -02, and so on, or list two names
# per anycast IP in tunnel order, for example:
# cloudflare_tunnel_names = ["aws-tun-01", "aws-tun-02"]
cloudflare_tunnel_names = []
```

#### `terraform.tfvars.two-anycast-ips.example`

Create `terraform.tfvars.two-anycast-ips.example` as the input template for customers with two anycast IPs. Copy it to `terraform.tfvars` instead of `terraform.tfvars.example`. It configures two anycast IPs, four tunnel CIDRs, and four PSKs, which creates two customer gateways, two Site-to-Site VPN connections, and four Cloudflare WAN tunnels.

```hcl
expected_account_id   = "REPLACE_WITH_AWS_ACCOUNT_ID"
cloudflare_account_id = "REPLACE_WITH_CLOUDFLARE_ACCOUNT_ID"
deployment_name       = "cloudflare-wan-reference"
aws_region            = "us-east-2"

# These three subnets must be canonical, non-overlapping networks inside the VPC.
vpc_cidr             = "192.168.0.0/16"
workload_subnet_cidr = "192.168.125.0/24"
tgw_subnet_a_cidr    = "192.168.124.0/28"
tgw_subnet_b_cidr    = "192.168.124.16/28"

# Only these destinations use the Site-to-Site VPNs. All other IPv4 traffic
# uses the Internet Gateway default route.
vpn_routed_prefixes = [
  "172.16.0.0/16",
]

# Accounts using two anycast IPs have four tunnels. Entries 1-2 belong to
# anycast IP 1 and entries 3-4 belong to anycast IP 2.
tunnel_inside_cidrs = [
  "169.254.240.44/30",
  "169.254.240.48/30",
  "169.254.240.52/30",
  "169.254.240.56/30",
]

# PSKs are sensitive and persist in Terraform state; never commit terraform.tfvars.
vpn_preshared_keys = [
  "REPLACE_WITH_UNIQUE_TUNNEL_01_PSK",
  "REPLACE_WITH_UNIQUE_TUNNEL_02_PSK",
  "REPLACE_WITH_UNIQUE_TUNNEL_03_PSK",
  "REPLACE_WITH_UNIQUE_TUNNEL_04_PSK",
]

# List order is resource identity. Anycast IP 1 backs tunnels 01-02 and VPN
# connection 01; anycast IP 2 backs tunnels 03-04 and VPN connection 02.
cloudflare_anycast_ips  = ["162.159.135.1", "172.64.135.1"]
aws_transit_gateway_asn = 65020
# Set to the CF Account ASN shown under Networking > Routes > WAN configuration.
cloudflare_asn                            = 13335
cloudflare_additional_advertised_prefixes = []

# Optional. Cloudflare tunnel names must be unique in the Cloudflare account.
# Leave empty to use <deployment_name>-tun-01, -02, and so on, or list two names
# per anycast IP in tunnel order, for example:
# cloudflare_tunnel_names = ["aws-tun-01", "aws-tun-02", "aws-tun-03", "aws-tun-04"]
cloudflare_tunnel_names = []
```

#### `tests/reference.tftest.hcl`

Create the `tests/` directory first, then create `tests/reference.tftest.hcl` to run 24 tests: five positive cases and nineteen invalid-input cases, without contacting AWS or Cloudflare.

```hcl
# Mock providers let plan assertions run without credentials or live API calls.
mock_provider "aws" {}
mock_provider "cloudflare" {}

# terraform test also loads terraform.tfvars from the module directory. Set every
# input here so a deployment's terraform.tfvars cannot change the test baseline.
variables {
  expected_account_id   = "123456789012"
  cloudflare_account_id = "0123456789abcdef0123456789abcdef"
  aws_region            = "us-east-2"
  deployment_name       = "cloudflare-wan-reference"
  vpc_cidr              = "192.168.0.0/16"
  workload_subnet_cidr  = "192.168.125.0/24"
  tgw_subnet_a_cidr     = "192.168.124.0/28"
  tgw_subnet_b_cidr     = "192.168.124.16/28"
  # Exercise multiple selective routes instead of relying on the empty default.
  vpn_routed_prefixes    = ["172.16.0.0/16", "172.17.0.0/16"]
  cloudflare_anycast_ips = ["162.159.135.1"]
  tunnel_inside_cidrs = [
    "169.254.240.44/30",
    "169.254.240.48/30",
  ]
  vpn_preshared_keys = [
    "A0123456789abcdef0123456789abcdef0123456789abcdef",
    "B0123456789abcdef0123456789abcdef0123456789abcdef",
  ]
  aws_transit_gateway_asn                   = 65020
  cloudflare_asn                            = 13335
  cloudflare_additional_advertised_prefixes = []
  cloudflare_tunnel_names                   = []
}

# Supply deterministic AZs so the two-AZ safety precondition can be tested offline.
override_data {
  target = data.aws_availability_zones.available
  values = {
    names = ["us-east-2a", "us-east-2b"]
  }
}

# AWS normally computes public IPv4 addresses during apply. Fixed plan-time values make the
# default AWS VPN connection's two Cloudflare WAN-to-AWS tunnel mappings testable offline.
override_resource {
  target          = aws_vpn_connection.cloudflare["01"]
  override_during = plan
  values = {
    tunnel1_address = "198.51.100.1"
    tunnel2_address = "198.51.100.2"
  }
}

# Make route targets known during plan so assertions can verify exact wiring.
override_resource {
  target          = aws_route_table.workload
  override_during = plan
  values = {
    id = "rtb-0123456789abcdef0"
  }
}

override_resource {
  target          = aws_internet_gateway.workload
  override_during = plan
  values = {
    id = "igw-0123456789abcdef0"
  }
}

override_resource {
  target          = aws_ec2_transit_gateway.cloudflare
  override_during = plan
  values = {
    id = "tgw-0123456789abcdef0"
  }
}

# This default positive case protects one anycast IP, one VPN, and two tunnels.
run "valid_reference_configuration" {
  command = plan

  assert {
    condition = (
      length(var.cloudflare_anycast_ips) == 1 &&
      length(var.tunnel_inside_cidrs) == 2 &&
      length(nonsensitive(var.vpn_preshared_keys)) == 2 &&
      length(aws_customer_gateway.cloudflare) == 1 &&
      length(aws_vpn_connection.cloudflare) == 1 &&
      length(cloudflare_magic_wan_ipsec_tunnel.aws) == 2
    )
    error_message = "The default configuration must create one customer gateway, one VPN, and two tunnels from two CIDRs and two PSKs."
  }

  assert {
    condition     = aws_ec2_transit_gateway.cloudflare.amazon_side_asn == var.aws_transit_gateway_asn
    error_message = "The Transit Gateway ASN does not match aws_transit_gateway_asn."
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["01"].name == "cloudflare-wan-reference-tun-01" && cloudflare_magic_wan_ipsec_tunnel.aws["02"].name == "cloudflare-wan-reference-tun-02"
    error_message = "Cloudflare tunnel names must be prefixed with deployment_name."
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["01"].interface_address == "${cidrhost(var.tunnel_inside_cidrs[0], 2)}/30" && cloudflare_magic_wan_ipsec_tunnel.aws["02"].interface_address == "${cidrhost(var.tunnel_inside_cidrs[1], 2)}/30"
    error_message = "A Cloudflare interface address does not match its tunnel CIDR."
  }

  assert {
    condition = (
      cloudflare_magic_wan_ipsec_tunnel.aws["01"].customer_endpoint == "198.51.100.1" &&
      cloudflare_magic_wan_ipsec_tunnel.aws["02"].customer_endpoint == "198.51.100.2"
    )
    error_message = "A customer_endpoint value is mapped to the wrong AWS VPN tunnel."
  }

  assert {
    condition = (
      cloudflare_magic_wan_ipsec_tunnel.aws["01"].cloudflare_endpoint == var.cloudflare_anycast_ips[0] &&
      cloudflare_magic_wan_ipsec_tunnel.aws["02"].cloudflare_endpoint == var.cloudflare_anycast_ips[0]
    )
    error_message = "A Cloudflare anycast IP is mapped to the wrong tunnel."
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["01"].replay_protection == true && cloudflare_magic_wan_ipsec_tunnel.aws["01"].automatic_return_routing == false && cloudflare_magic_wan_ipsec_tunnel.aws["01"].health_check.type == "request" && cloudflare_magic_wan_ipsec_tunnel.aws["01"].health_check.direction == "bidirectional"
    error_message = "The Cloudflare tunnel liveness or routing settings changed unexpectedly."
  }

  assert {
    condition = alltrue([
      for tunnel in cloudflare_magic_wan_ipsec_tunnel.aws :
      tunnel.health_check.enabled == true && tunnel.health_check.rate == "mid" && tunnel.health_check.type == "request" && tunnel.health_check.direction == "bidirectional"
    ])
    error_message = "Every Cloudflare tunnel must use enabled, medium-rate, request-type, bidirectional health checks."
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["01"].bgp.customer_asn == var.aws_transit_gateway_asn && toset(cloudflare_magic_wan_ipsec_tunnel.aws["01"].bgp.extra_prefixes) == toset(var.cloudflare_additional_advertised_prefixes)
    error_message = "The Cloudflare BGP settings do not match the configured inputs."
  }

  assert {
    condition     = aws_ec2_transit_gateway.cloudflare.default_route_table_association == "disable" && aws_ec2_transit_gateway.cloudflare.default_route_table_propagation == "disable" && aws_ec2_transit_gateway.cloudflare.vpn_ecmp_support == "enable"
    error_message = "The Transit Gateway routing controls changed unexpectedly."
  }

  assert {
    condition     = aws_subnet.workload.map_public_ip_on_launch == true && aws_route.internet.destination_cidr_block == "0.0.0.0/0" && aws_route.internet.gateway_id == "igw-0123456789abcdef0" && aws_route.internet.route_table_id == "rtb-0123456789abcdef0"
    error_message = "The public workload subnet must use the Internet Gateway default route."
  }

  assert {
    condition     = toset([for route in aws_route.cloudflare : route.destination_cidr_block]) == toset(var.vpn_routed_prefixes) && alltrue([for route in aws_route.cloudflare : route.transit_gateway_id == "tgw-0123456789abcdef0" && route.route_table_id == "rtb-0123456789abcdef0"])
    error_message = "The Transit Gateway routes do not match vpn_routed_prefixes."
  }

  assert {
    condition     = aws_customer_gateway.cloudflare["01"].ip_address == var.cloudflare_anycast_ips[0] && tostring(aws_customer_gateway.cloudflare["01"].bgp_asn) == tostring(var.cloudflare_asn)
    error_message = "The AWS customer gateway settings do not match the Cloudflare inputs."
  }

  assert {
    condition     = aws_vpn_connection.cloudflare["01"].tunnel1_inside_cidr == var.tunnel_inside_cidrs[0] && aws_vpn_connection.cloudflare["01"].tunnel2_inside_cidr == var.tunnel_inside_cidrs[1]
    error_message = "The default VPN has the wrong inside CIDRs."
  }

  assert {
    condition     = aws_vpn_connection.cloudflare["01"].static_routes_only == false && aws_vpn_connection.cloudflare["01"].tunnel1_startup_action == "start" && aws_vpn_connection.cloudflare["01"].tunnel1_dpd_timeout_action == "restart" && aws_vpn_connection.cloudflare["01"].tunnel1_replay_window_size == 2048
    error_message = "The dynamic-routing or tunnel liveness settings changed unexpectedly."
  }

  assert {
    condition     = aws_vpn_connection.cloudflare["01"].tunnel2_phase1_encryption_algorithms == toset(["AES256"]) && aws_vpn_connection.cloudflare["01"].tunnel2_phase2_integrity_algorithms == toset(["SHA2-256"]) && aws_vpn_connection.cloudflare["01"].tunnel2_phase1_dh_group_numbers == toset([20]) && aws_vpn_connection.cloudflare["01"].tunnel2_ike_versions == toset(["ikev2"])
    error_message = "The tunnel cryptographic settings changed unexpectedly."
  }

  assert {
    condition     = aws_vpn_connection.cloudflare["01"].tunnel1_phase1_lifetime_seconds == 28800 && aws_vpn_connection.cloudflare["01"].tunnel1_phase2_lifetime_seconds == 3600
    error_message = "The tunnel lifetimes changed unexpectedly."
  }
}

# Customers with two distinct anycast IPs use two VPNs and four tunnels.
run "valid_two_anycast_ip_configuration" {
  command = plan

  override_resource {
    target          = aws_vpn_connection.cloudflare["02"]
    override_during = plan
    values = {
      tunnel1_address = "198.51.100.3"
      tunnel2_address = "198.51.100.4"
    }
  }

  variables {
    cloudflare_anycast_ips = ["162.159.135.1", "162.159.135.2"]
    tunnel_inside_cidrs = [
      "169.254.240.44/30",
      "169.254.240.48/30",
      "169.254.240.52/30",
      "169.254.240.56/30",
    ]
    vpn_preshared_keys = [
      "A0123456789abcdef0123456789abcdef0123456789abcdef",
      "B0123456789abcdef0123456789abcdef0123456789abcdef",
      "C0123456789abcdef0123456789abcdef0123456789abcdef",
      "D0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  assert {
    condition     = length(aws_customer_gateway.cloudflare) == 2 && length(aws_vpn_connection.cloudflare) == 2 && length(cloudflare_magic_wan_ipsec_tunnel.aws) == 4
    error_message = "The two-anycast-IP configuration must create two customer gateways, two AWS Site-to-Site VPN connections, and four Cloudflare WAN tunnels."
  }

  assert {
    condition     = aws_customer_gateway.cloudflare["01"].ip_address == var.cloudflare_anycast_ips[0] && aws_customer_gateway.cloudflare["02"].ip_address == var.cloudflare_anycast_ips[1]
    error_message = "A customer gateway is mapped to the wrong Cloudflare anycast IP."
  }

  assert {
    condition     = aws_customer_gateway.cloudflare["01"].device_name == "${var.deployment_name}-cgw-01" && aws_customer_gateway.cloudflare["02"].device_name == "${var.deployment_name}-cgw-02"
    error_message = "Each customer gateway must use a unique device name derived from deployment_name."
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["03"].cloudflare_endpoint == var.cloudflare_anycast_ips[1] && cloudflare_magic_wan_ipsec_tunnel.aws["04"].cloudflare_endpoint == var.cloudflare_anycast_ips[1]
    error_message = "Anycast IP 2 must map to Cloudflare tunnels 03 and 04."
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["03"].customer_endpoint == "198.51.100.3" && cloudflare_magic_wan_ipsec_tunnel.aws["04"].customer_endpoint == "198.51.100.4"
    error_message = "Cloudflare WAN tunnels 03 and 04 must map to AWS VPN 02 tunnels 1 and 2."
  }

  assert {
    condition     = [for key in sort(keys(cloudflare_magic_wan_ipsec_tunnel.aws)) : cloudflare_magic_wan_ipsec_tunnel.aws[key].name] == ["cloudflare-wan-reference-tun-01", "cloudflare-wan-reference-tun-02", "cloudflare-wan-reference-tun-03", "cloudflare-wan-reference-tun-04"]
    error_message = "All four Cloudflare tunnel names must be unique and prefixed with deployment_name."
  }

  assert {
    condition     = nonsensitive(cloudflare_magic_wan_ipsec_tunnel.aws["03"].psk) == nonsensitive(var.vpn_preshared_keys[2]) && nonsensitive(cloudflare_magic_wan_ipsec_tunnel.aws["04"].psk) == nonsensitive(var.vpn_preshared_keys[3])
    error_message = "Cloudflare tunnels 03 and 04 must use PSKs 3 and 4."
  }

  assert {
    condition     = aws_vpn_connection.cloudflare["02"].tunnel1_inside_cidr == var.tunnel_inside_cidrs[2] && aws_vpn_connection.cloudflare["02"].tunnel2_inside_cidr == var.tunnel_inside_cidrs[3]
    error_message = "VPN 02 must consume tunnel CIDRs at indexes 2 and 3."
  }
}

# The remaining runs verify a private CF Account ASN and confirm that unsafe or ambiguous inputs fail at plan time.
run "reject_non_network_tunnel_cidr" {
  command = plan

  variables {
    tunnel_inside_cidrs = [
      "169.254.240.45/30",
      "169.254.240.48/30",
    ]
  }

  expect_failures = [var.tunnel_inside_cidrs]
}

run "reject_unsupported_vpc_prefix" {
  command = plan

  variables {
    vpc_cidr = "192.168.0.0/15"
  }

  expect_failures = [var.vpc_cidr]
}

run "reject_unsupported_subnet_prefix" {
  command = plan

  variables {
    workload_subnet_cidr = "192.168.125.0/29"
  }

  expect_failures = [var.workload_subnet_cidr]
}

run "reject_overlapping_subnets" {
  command = plan

  variables {
    tgw_subnet_a_cidr = "192.168.125.0/24"
  }

  expect_failures = [aws_vpc.workload]
}

run "reject_duplicate_cloudflare_anycast_ips" {
  command = plan

  variables {
    cloudflare_anycast_ips = ["162.159.135.1", "162.159.135.1"]
    tunnel_inside_cidrs = [
      "169.254.240.44/30",
      "169.254.240.48/30",
      "169.254.240.52/30",
      "169.254.240.56/30",
    ]
    vpn_preshared_keys = [
      "A0123456789abcdef0123456789abcdef0123456789abcdef",
      "B0123456789abcdef0123456789abcdef0123456789abcdef",
      "C0123456789abcdef0123456789abcdef0123456789abcdef",
      "D0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.cloudflare_anycast_ips]
}

run "accept_private_cloudflare_asn" {
  command = plan

  variables {
    cloudflare_asn = 65000
  }

  assert {
    condition     = tostring(aws_customer_gateway.cloudflare["01"].bgp_asn) == "65000"
    error_message = "The AWS customer gateway must use a configured private CF Account ASN."
  }
}

run "reject_unsupported_cloudflare_asn" {
  command = plan

  variables {
    cloudflare_asn = 64511
  }

  expect_failures = [var.cloudflare_asn]
}

run "reject_cloudflare_asn_matching_tgw_asn" {
  command = plan

  variables {
    cloudflare_asn = 65020
  }

  expect_failures = [var.cloudflare_asn]
}

run "accept_aws_psk_characters" {
  command = plan

  variables {
    vpn_preshared_keys = [
      "Key.01_a",
      "_Key.02_b",
    ]
  }

  assert {
    condition     = nonsensitive(aws_vpn_connection.cloudflare["01"].tunnel1_preshared_key) == "Key.01_a"
    error_message = "An 8-character PSK with a period and underscore must be accepted."
  }
}

run "reject_psk_with_unsupported_character" {
  command = plan

  variables {
    vpn_preshared_keys = [
      "Invalid-psk-1",
      "B0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.vpn_preshared_keys]
}

run "reject_short_psk" {
  command = plan

  variables {
    vpn_preshared_keys = [
      "Short_7",
      "B0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.vpn_preshared_keys]
}

run "reject_psk_with_leading_zero" {
  command = plan

  variables {
    vpn_preshared_keys = [
      "0LeadingZeroKey",
      "B0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.vpn_preshared_keys]
}

run "reject_default_route_as_vpn_prefix" {
  command = plan

  variables {
    vpn_routed_prefixes = ["0.0.0.0/0"]
  }

  expect_failures = [var.vpn_routed_prefixes]
}

run "reject_mismatched_tunnel_input_cardinality" {
  command = plan

  variables {
    tunnel_inside_cidrs = [
      "169.254.240.44/30",
      "169.254.240.48/30",
      "169.254.240.52/30",
    ]
  }

  expect_failures = [var.tunnel_inside_cidrs]
}

run "reject_mismatched_psk_cardinality" {
  command = plan

  variables {
    vpn_preshared_keys = [
      "A0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.vpn_preshared_keys]
}

run "reject_duplicate_psks" {
  command = plan

  variables {
    vpn_preshared_keys = [
      "A0123456789abcdef0123456789abcdef0123456789abcdef",
      "A0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.vpn_preshared_keys]
}

run "reject_zero_cloudflare_anycast_ips" {
  command = plan

  variables {
    cloudflare_anycast_ips = []
    tunnel_inside_cidrs    = []
    vpn_preshared_keys     = []
  }

  expect_failures = [var.cloudflare_anycast_ips]
}

run "reject_more_than_two_cloudflare_anycast_ips" {
  command = plan

  variables {
    cloudflare_anycast_ips = ["162.159.135.1", "162.159.135.2", "162.159.135.3"]
    tunnel_inside_cidrs = [
      "169.254.240.44/30",
      "169.254.240.48/30",
      "169.254.240.52/30",
      "169.254.240.56/30",
      "169.254.240.60/30",
      "169.254.240.64/30",
    ]
    vpn_preshared_keys = [
      "A0123456789abcdef0123456789abcdef0123456789abcdef",
      "B0123456789abcdef0123456789abcdef0123456789abcdef",
      "C0123456789abcdef0123456789abcdef0123456789abcdef",
      "D0123456789abcdef0123456789abcdef0123456789abcdef",
      "E0123456789abcdef0123456789abcdef0123456789abcdef",
      "F0123456789abcdef0123456789abcdef0123456789abcdef",
    ]
  }

  expect_failures = [var.cloudflare_anycast_ips]
}

run "reject_vpn_prefix_overlapping_vpc" {
  command = plan

  variables {
    vpn_routed_prefixes = ["192.168.64.0/18"]
  }

  expect_failures = [var.vpn_routed_prefixes]
}

run "accept_custom_tunnel_names" {
  command = plan

  variables {
    cloudflare_tunnel_names = ["aws-usw1-tun-a", "aws-usw1-tun-b"]
  }

  assert {
    condition     = cloudflare_magic_wan_ipsec_tunnel.aws["01"].name == "aws-usw1-tun-a" && cloudflare_magic_wan_ipsec_tunnel.aws["02"].name == "aws-usw1-tun-b"
    error_message = "Supplied tunnel names must be applied in tunnel order."
  }
}

run "reject_duplicate_tunnel_names" {
  command = plan

  variables {
    cloudflare_tunnel_names = ["aws-tun", "aws-tun"]
  }

  expect_failures = [var.cloudflare_tunnel_names]
}

run "reject_mismatched_tunnel_name_count" {
  command = plan

  variables {
    cloudflare_tunnel_names = ["aws-tun-01"]
  }

  expect_failures = [var.cloudflare_tunnel_names]
}
```

The configuration validates the following conditions before creating resources:

- The active AWS account matches `expected_account_id`
- The Region provides at least two Availability Zones
- All three subnet CIDRs are inside the VPC and do not overlap
- `vpn_routed_prefixes` contains unique canonical IPv4 networks more specific than `0.0.0.0/0` that do not overlap the VPC CIDR
- `tunnel_inside_cidrs` contains exactly two unique `/30` networks within `169.254.240.0/20` per configured anycast IP
- `vpn_preshared_keys` contains exactly two unique PSKs per configured anycast IP, and each PSK meets the AWS requirements of 8-64 letters, numbers, periods, or underscores without a leading `0`
- `cloudflare_anycast_ips` contains one or two distinct IPv4 addresses
- `cloudflare_asn` is `13335` or a private ASN from `64512` through `65534`, and differs from `aws_transit_gateway_asn`
- `cloudflare_tunnel_names` is empty or contains two unique names per configured anycast IP, using only letters, numbers, hyphens, and underscores
- The Cloudflare account ID is a 32-character hexadecimal identifier
- Additional advertised prefixes are unique IPv4 network CIDRs

:::caution[Protect Terraform state]
The AWS and Cloudflare providers store IPsec tunnel pre-shared keys in Terraform state as plaintext. Use an encrypted remote backend with restricted access for shared or production use. For local testing, protect `terraform.tfvars`, saved plan files, and all Terraform state files. Do not commit them to source control.
:::

:::caution[Public workload subnet]
The workload subnet assigns public IPv4 addresses on launch and sends unmatched IPv4 destinations to the Internet Gateway. Direct Internet Gateway egress requires a public IPv4 address and appropriate network and host controls. The Internet Gateway does not perform NAT for private-only hosts.

Terraform does not create:

- Virtual machines
- Standalone Elastic Network Interfaces (ENIs); AWS creates ENIs for the Transit Gateway VPC attachment in the attachment subnets
- Security groups
- Elastic IP addresses
- Test hosts

Add and review those controls separately before deploying a workload.
:::

### Initialize and validate Terraform

Run these commands from the Terraform directory you created:

```bash
terraform init
terraform fmt -check -recursive
terraform validate
terraform test
```

Initialization resolves the provider constraints, downloads the AWS and Cloudflare providers, and creates or updates `.terraform.lock.hcl`. Validation checks the Terraform syntax and provider schemas without creating resources. The tests use mock providers and do not create resources.

### Review the Terraform plan

Create a saved plan:

```bash
terraform plan -out cloudflare-wan.tfplan
```

Review every proposed resource and value. For the baseline, the plan should create:

- One VPC
- One public workload subnet
- Two Transit Gateway attachment subnets
- One Internet Gateway
- One workload route table
- One Internet default route
- One Transit Gateway route for each `vpn_routed_prefixes` entry
- One Transit Gateway
- One dedicated Transit Gateway route table
- One VPC attachment
- One customer gateway
- One Site-to-Site VPN connection containing two AWS IPsec/BGP tunnels
- Two Cloudflare WAN IPsec tunnels with BGP
- The required Transit Gateway route-table associations and propagations

:::note[Secondary anycast IP customers]
The plan also creates:

- One additional customer gateway
- One additional Site-to-Site VPN connection containing two AWS IPsec/BGP tunnels
- Two additional Cloudflare WAN IPsec tunnels with BGP

:::

The plan should not create:

- Virtual machines or test hosts
- Standalone Elastic Network Interfaces (ENIs); AWS creates ENIs for the Transit Gateway VPC attachment in the attachment subnets
- Security groups
- Elastic IP addresses
- NAT services

Saved plan files can contain sensitive values. Protect and delete the plan file according to your organization's secrets-handling requirements.

### Apply the Terraform plan

Apply the reviewed plan:

```bash
terraform apply cloudflare-wan.tfplan
```

Terraform waits for AWS to assign each VPN public IPv4 address before creating the corresponding Cloudflare tunnel. If an operation fails, correct the reported issue and run `terraform plan` again. Terraform retains successfully created resources in state and proposes only the remaining work.

If the apply creates the AWS resources but the Cloudflare requests fail with error `9106` stating that authorization headers are missing, `CLOUDFLARE_API_TOKEN` was not available to the Terraform process. Set the environment variable, create and review a fresh saved plan, and apply the new plan. Do not reuse the partially applied saved plan because Terraform state changed during the failed apply.

Cloudflare error `1016` with `local IP not in Cloudflare assigned IPs` means a configured Cloudflare anycast IP is not leased to the account identified by `cloudflare_account_id`. In this message, `local IP` refers to `cloudflare_endpoint`, not the link-local `interface_address`. Verify the account ID and every configured anycast IP under **Address Space** > **Leased IPs**, update `terraform.tfvars`, and create a fresh plan. Changing an anycast IP replaces its AWS customer gateway and updates the VPN connection to use the replacement gateway. This can interrupt its tunnels, so review the actions and use a maintenance window.

The Terraform configuration creates a replacement customer gateway before destroying the existing gateway. This ordering lets AWS retarget the VPN connection before the old customer gateway is removed. If an apply fails with `IncorrectState: The customer gateway is in use`, confirm that the `aws_customer_gateway` lifecycle includes `create_before_destroy = true`, discard the failed saved plan, and create a fresh plan.

Delete `cloudflare-wan.tfplan` after the apply completes. The saved plan can contain the VPN pre-shared keys.

### Display Terraform outputs

Display the generated tunnel values after the apply completes:

```bash
terraform output cloudflare_tunnel_mapping
terraform output workload_egress_routes
```

The tunnel output provides each Cloudflare tunnel ID, AWS public IPv4 address, Cloudflare anycast IP, inside CIDR, AWS inside IP, and Cloudflare interface address. The egress output lists destinations routed through the VPN path and the Internet Gateway default. Terraform has already applied these values to the managed resources. The outputs are authoritative when you change any reference input.

| **Tunnel**                        | **Inside CIDR**     | **AWS inside IP** | **Cloudflare interface address** |
| --------------------------------- | ------------------- | ----------------- | -------------------------------- |
| `cloudflare-wan-reference-tun-01` | `169.254.240.44/30` | `169.254.240.45`  | `169.254.240.46/30`              |
| `cloudflare-wan-reference-tun-02` | `169.254.240.48/30` | `169.254.240.49`  | `169.254.240.50/30`              |

The table uses the default `deployment_name`; the `tunnel` field in `cloudflare_tunnel_mapping` shows the actual names.

:::note[Secondary anycast IP customers]
The second anycast IP adds tunnel 03 with `169.254.240.52/30`, AWS address `169.254.240.53`, and Cloudflare address `169.254.240.54/30`; and tunnel 04 with `169.254.240.56/30`, AWS address `169.254.240.57`, and Cloudflare address `169.254.240.58/30`.
:::

Match tunnels by VPN connection and inside CIDR. Do not infer tunnel order by sorting the generated AWS public IPv4 addresses.

The table shows the reference values. If you changed the tunnel CIDRs, use only the values returned by Terraform.

Skip the manual Cloudflare tunnel and BGP creation procedures when using Terraform. Continue with [Verification](#verification). Use the manual sections only to understand or audit the values Terraform configured.

### Verify the Terraform deployment

Refresh Terraform state after the apply:

```bash
terraform apply -refresh-only
```

Review the refresh-only plan before approving it. Then display AWS tunnel telemetry and Cloudflare BGP status:

```bash
terraform output vpn_telemetry
terraform output cloudflare_bgp_status
terraform output workload_egress_routes
```

Every configured tunnel must report `UP`. The accepted route count should match the number of prefixes that Cloudflare advertises. Confirm in the Cloudflare dashboard that every configured BGP session is **Established**. Confirm that Cloudflare learned the VPC CIDR from AWS. Compare `workload_egress_routes` with the approved routing inventory: AWS longest-prefix matching sends a destination matching a configured VPN prefix to the Transit Gateway even though the Internet Gateway default also matches, while unmatched IPv4 destinations use `0.0.0.0/0`. Complete every route, traffic, and failover check in [Verification](#verification).

Also verify these resources in the AWS console:

- The VPC attachment and every configured VPN attachment use the dedicated Transit Gateway route table
- Propagation is active for the VPC and every configured VPN attachment
- The complete VPC CIDR appears as an active propagated route to the VPC attachment
- The expected RFC 1918 prefixes and any explicitly enabled public prefixes appear as active propagated routes through the VPN attachments
- The workload subnet is associated with the intended route table and enables public IPv4 assignment on launch
- Every `vpn_routed_prefixes` destination targets the Transit Gateway
- The workload route table sends `0.0.0.0/0` to the Internet Gateway, not the Transit Gateway
- No VMs, standalone ENIs, security groups, Elastic IP addresses, NAT services, or test hosts were created by this configuration

### Remove the Terraform deployment

Create and review a destroy plan:

```bash
terraform plan -destroy -out cloudflare-wan-destroy.tfplan
```

Apply the reviewed destroy plan:

```bash
terraform apply cloudflare-wan-destroy.tfplan
```

Terraform removes the managed Cloudflare tunnels before removing their AWS dependencies, including the selective Transit Gateway routes, Internet default route, Internet Gateway, and VPC resources. It removes only resources tracked in the current Terraform state. It does not remove manually created Cloudflare or AWS resources unless you imported them into this state.

Delete the saved destroy plan after use. Follow the retention policy for local or remote state versions that contain PSKs.

## Resources

### Cloudflare documentation

- [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/)
- [Configure BGP routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#configure-bgp-routes)
- [GRE and IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/)
- [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/)
- [Maximum transmission unit and maximum segment size](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/)
- [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/)
- [Tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/)

### AWS documentation

- [AWS Site-to-Site VPN attachments in AWS Transit Gateway](https://docs.aws.amazon.com/vpc/latest/tgw/tgw-vpn-attachments.html)
- [Transit Gateway route tables](https://docs.aws.amazon.com/vpc/latest/tgw/tgw-route-tables.html)
- [Transit Gateway route evaluation order](https://docs.aws.amazon.com/vpc/latest/tgw/how-transit-gateways-work.html#tgw-route-evaluation-overview)
- [Tunnel options for AWS Site-to-Site VPN](https://docs.aws.amazon.com/vpn/latest/s2svpn/VPNTunnels.html)
- [Modify AWS Site-to-Site VPN tunnel options](https://docs.aws.amazon.com/vpn/latest/s2svpn/modify-vpn-tunnel-options.html)
- [Static and dynamic routing in AWS Site-to-Site VPN](https://docs.aws.amazon.com/vpn/latest/s2svpn/vpn-static-dynamic.html)
- [AWS Site-to-Site VPN tunnel initiation options](https://docs.aws.amazon.com/vpn/latest/s2svpn/initiate-vpn-tunnels.html)

# Check Point

import { Render } from "~/components";

<Render
	file="cloudflare-wan/third-party/check-point"
	product="networking-services"
	params={{
		antiReplayLink:
			"/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/",
		tunnelInstabilityLink:
			"/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/#ipsec-tunnel-instability-or-packet-drops",
		antiReplayMismatchLink:
			"/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/ipsec-troubleshoot/#anti-replay-mismatch",
		configureTunnelsLink:
			"/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/",
	}}
/>

# Cisco IOS XE

import { Render } from "~/components";

<Render file="cloudflare-wan/third-party/cisco-ios-xe"
  product="networking-services"
/>

# Cisco Meraki MX (static routing)

This guide provides step-by-step instructions for configuring Cisco Meraki MX appliances to establish IPsec VPN tunnels to Cloudflare WAN. It is intended for network engineers who are familiar with Cisco Meraki administration and have an active Cloudflare WAN subscription.

## Test environment

| **Field** | **Value**    |
| --------- | ------------ |
| Vendor    | Cisco Meraki |
| Model     | MX68         |
| Release   | MX 19.2.7    |

:::note
Meraki 19.2.7 is the minimum required version to support Route-Based IPsec VPN with third-party endpoints, including Cloudflare WAN. Refer to the [Meraki implementation and compatibility notes](#meraki-implementation-and-compatibility-notes) section for more details.
:::

## IKE and IPsec crypto settings

| **Field**                          | **Value**       |
| ---------------------------------- | --------------- |
| Traffic Selection Criteria         | Route-Based VPN |
| Routing                            | Static          |
| Redundant Tunnels                  | Yes             |
| Tunnel Load Balancing              | Active/Standby  |
| IKE Version                        | IKEv2           |
| Authentication                     | Pre-Shared Key  |
| Anti-Replay Protection             | Enabled         |
| NAT Traversal (NAT-T)              | Not Tested      |
| NAT-T Port                         | Not Applicable  |
| Phase 1 — DH-Group                 | Group 14        |
| Phase 1 — Encryption               | AES-256-CBC     |
| Phase 1 — Authentication/Integrity | SHA-256         |
| Phase 2 — DH-Group                 | Group 14        |
| Phase 2 — Transport                | ESP             |
| Phase 2 — Encryption               | AES-256-CBC     |

:::note
Cloudflare recommends DH Group 20, which is not supported by the MX platform. DH Group 14 is compatible with the MX platform.
:::

## Cloudflare WAN and Cisco Meraki MX configuration

Replace all object names and IP addresses in the examples below to match your environment.

:::note
The Cloudflare IPsec tunnel health checks require non-standard configuration settings to support the MX platform. The note in each section below identifies the affected fields.
:::

### Cloudflare WAN tunnel 1 of 2

| **Attribute**                     | **Value/Address**                   |
| --------------------------------- | ----------------------------------- |
| Name (required)                   | CF_WAN_TUN_01                       |
| Description                       | —                                   |
| IPv4 Interface Address (required) | 169.254.250.0/31                    |
| IPv6 Interface Address            | —                                   |
| Customer Endpoint                 | 203.0.113.100                       |
| Cloudflare Endpoint               | 162.159.135.1                       |
| Tunnel health checks              | True                                |
| Rate                              | Low                                 |
| Type                              | Request                             |
| Direction                         | Bidirectional                       |
| Target                            | Custom                              |
| Target address                    | 192.168.125.1 (MX LAN Interface IP) |
| Turn on replay protection         | True                                |
| Automatic return routing          | True                                |

:::note
The values for `Rate`, `Type`, `Direction`, `Target`, `Target address`, `Turn on replay protection`, and `Automatic return routing` are the non-standard settings required to support the MX platform.
:::

Obtain the IKE identity and pre-shared key after tunnel creation:

| **Attribute**  | **Value/Address**                           |
| -------------- | ------------------------------------------- |
| FQDN ID        | `bf6c493d03<REDACTED>.ipsec.cloudflare.com` |
| Pre-shared key | `Cloudflare-WAN-T1-PSK-1234!`               |

### Cloudflare WAN tunnel 2 of 2

| **Attribute**                     | **Value/Address**                   |
| --------------------------------- | ----------------------------------- |
| Name (required)                   | CF_WAN_TUN_02                       |
| Description                       | —                                   |
| IPv4 Interface Address (required) | 169.254.250.2/31                    |
| IPv6 Interface Address            | —                                   |
| Customer Endpoint                 | 203.0.113.100                       |
| Cloudflare Endpoint               | 172.64.135.1                        |
| Tunnel health checks              | True                                |
| Rate                              | Low                                 |
| Type                              | Request                             |
| Direction                         | Bidirectional                       |
| Target                            | Custom                              |
| Target address                    | 192.168.125.1 (MX LAN Interface IP) |
| Turn on replay protection         | True                                |
| Automatic return routing          | True                                |

:::note
The same non-standard settings called out for tunnel 1 of 2 also apply to tunnel 2 of 2.
:::

Obtain the IKE identity and pre-shared key after tunnel creation:

| **Attribute**  | **Value/Address**                           |
| -------------- | ------------------------------------------- |
| FQDN ID        | `0287844e9d<REDACTED>.ipsec.cloudflare.com` |
| Pre-shared key | `Cloudflare-WAN-T2-PSK-1234!`               |

## Customer premise equipment: Cisco Meraki

Mode: Routed

| **WAN Interface (Port 1)** | **Tunnel 1 of 2** | **Tunnel 2 of 2** |
| -------------------------- | ----------------- | ----------------- |
| WAN Interface              | WAN 1             | WAN 1             |
| IP Address                 | 203.0.113.100/24  | 203.0.113.100/24  |

| **LAN Interface (Port 3)** | **Tunnel 1 of 2** | **Tunnel 2 of 2** |
| -------------------------- | ----------------- | ----------------- |
| LAN Interface              | LAN               | LAN               |
| IP Address                 | 192.168.125.1/24  | 192.168.125.1/24  |

:::note
Virtual Tunnel Interfaces (VTIs) cannot be configured on MX appliances.
:::

## Assumptions and constraints

### Meraki implementation and compatibility notes

- **Firmware prerequisite**: The minimum required firmware for this configuration is MX 19.2.7.
- **Hardware compatibility**: Older Meraki hardware may be physically incapable of running 19.2.7. Route-Based VPN support is required for this architecture. Refer to [Product firmware restrictions](https://documentation.meraki.com/Platform_Management/Product_Information/Compatibility_and_Firmware/Firmware_Upgrades/Product_Firmware_Version_Restrictions) to determine whether your MX platform supports firmware release 19.2.7 or later.
- **Active/Standby configuration**: Redundant tunnels associated with Non-Meraki VPN connections are Active/Standby. Both tunnels are established, but Meraki only routes traffic via the primary IPsec VPN peer and dynamically fails over to the secondary IPsec VPN peer based on tunnel monitoring probes.
  - **Anycast and tunnel redundancy**: Despite the Active/Standby nature of IPsec VPN tunnels on the MX platform, high availability is maintained at the network layer because the Cloudflare remote endpoint IPs are advertised via BGP anycast across the Cloudflare global network and provide inherent geographic and logical redundancy.
- **Route-Based VPN support**: While often associated with specific cloud integrations, version 19.2.7 supports Route-Based IPsec VPN for third-party devices generally, including Cloudflare WAN.
- **Redundancy and Multi-Uplink**: This documentation specifically covers Active/Standby tunnel configurations.
  - **Multi-Uplink IPsec VPN**: The Meraki [Multi-Uplink IPsec VPN](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Site-to-site_VPN/Multi-Uplink_IPsec_VPN) feature is outside the scope of this guide.
- **Anti-Replay Protection**: Cloudflare recommends [disabling Anti-Replay Protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/) for optimal performance with Cloudflare WAN. The Cisco Meraki MX platform does not permit administrators to disable this feature.
  - This is a known Meraki platform limitation.
  - In environments with high jitter or out-of-order packet delivery on the underlay (ISP network), this may cause intermittent packet drops on the MX side of the IPsec VPN tunnels.
- **MSS Clamping**: Cloudflare recommends specific Maximum Segment Size (MSS) [clamping](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/#mss-clamping) values to account for IPsec overhead and prevent fragmentation.
  - The Meraki Dashboard does not provide a user-accessible field to modify the MSS clamping value for third-party VPN tunnels.
  - Customers must contact Meraki Technical Support to request a manual backend modification of the MSS value (approximately 1360; the value may vary) for the specific network or tunnel.
- **ISP scope**: The provided configuration is validated for a single Internet Service Provider (ISP). The logic can be extended to accommodate redundant ISPs, but multi-homed configuration is outside the scope of this guide.

### Cloudflare

- This configuration requires the [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing) dataplane to support [Automatic Return Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#automatic-return-routing).
- You have already configured IPsec tunnels and static routes in the Cloudflare dashboard.
- You have used the Cloudflare dashboard to obtain the local identifier (FQDN/hostname) and generate a pre-shared key for each IPsec tunnel.
- You understand the importance of [MSS clamping](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/#mss-clamping) and adjusting it based on the traffic flows traversing the Cloudflare WAN IPsec tunnels.

## Prerequisites: MX platform site-to-site VPN configuration

The following details from the Cloudflare configuration are required before proceeding with the Meraki configuration:

- IPv4 interface address values (in Classless Inter-Domain Routing (CIDR) notation)
- Cloudflare anycast IPs
- Local ID (FQDN/hostname)
- Pre-shared keys
- Remote subnets

### CF_WAN_TUN_01

| **Attribute**          | **Value/Address**                           | **Meraki — Applies To** | **Required to**                         |
| ---------------------- | ------------------------------------------- | ----------------------- | --------------------------------------- |
| IPv4 Interface Address | 169.254.250.0/31                            | Private subnets         | Support Cloudflare tunnel health checks |
| Cloudflare Endpoint    | 162.159.135.1                               | Public IP or hostname   | Tunnel peer IP — primary IPsec peer     |
|                        | 162.159.135.1                               | Remote ID               | IKE remote ID — primary IPsec peer      |
| FQDN ID                | `bf6c493d03<REDACTED>.ipsec.cloudflare.com` | Local ID                | IKE local ID — primary IPsec peer       |
| Pre-Shared Key         | `Cloudflare-WAN-T1-PSK-1234!`               | Shared secret           | Shared secret — primary IPsec peer      |
| Remote subnets         | 172.16.10.0/24, 172.16.11.0/24              | Private subnets         | Add routes for east/west traffic flows  |

### CF_WAN_TUN_02

| **Attribute**          | **Value/Address**                           | **Meraki Setting**    | **Required to**                         |
| ---------------------- | ------------------------------------------- | --------------------- | --------------------------------------- |
| IPv4 Interface Address | 169.254.250.2/31                            | Private subnets       | Support Cloudflare tunnel health checks |
| Cloudflare Endpoint    | 172.64.135.1                                | Public IP or hostname | Tunnel peer IP — secondary IPsec peer   |
|                        | 172.64.135.1                                | Remote ID             | IKE remote ID — secondary IPsec peer    |
| FQDN ID                | `0287844e9d<REDACTED>.ipsec.cloudflare.com` | Local ID              | IKE local ID — secondary IPsec peer     |
| Pre-Shared Key         | `Cloudflare-WAN-T2-PSK-1234!`               | Shared secret         | Shared secret — secondary IPsec peer    |
| Remote subnets         | 172.16.10.0/24, 172.16.11.0/24              | Private subnets       | Add routes for east/west traffic flows  |

### Remote subnets

In the MX platform, "Private subnets" refers to the remote networks the MX appliance routes through the IPsec tunnels.

This document assumes the following subnets are remote subnets:

- 172.16.10.0/24
- 172.16.11.0/24

## Cloudflare

### Authorize the Meraki tunnel health probe source IP

The MX platform uses tunnel monitoring to enable failover between primary and secondary IPsec VPN tunnels. Tunnel monitoring detects connectivity through the tunnels (not supported on BGP-enabled tunnels). Tunnel monitoring operates independently of Dead Peer Detection, which determines the status of the IPsec tunnels.

The tunnel health probes are used in addition to Dead Peer Detection to determine overall reachability of resources on the remote side of the IPsec tunnels.

Meraki reserves the IP address `192.0.2.3/32` (part of TEST-NET-1, defined in [RFC 5737](https://datatracker.ietf.org/doc/html/rfc5737)) as the source IP for tunnel monitor probes. Refer to [Primary and secondary IPsec tunnels](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Site-to-site_VPN/Primary_and_Secondary_IPsec_VPN_Tunnels) for details.

As `192.0.2.3/32` falls outside the traditional [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) address space, you must add it to the [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing) dataplane associated with your Cloudflare account.

Contact Cloudflare to request assistance with adding the `internal_authorized_prefixes` option to your account, with `192.0.2.3/32` included.

### Cloudflare Gateway HTTP policy

Define an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to permit the tunnel monitoring probe source IP address to reach the IP/URL (HTTP — port 80/tcp).

:::note
The IP/URL is available from the **Configure health checks** section of the Meraki Dashboard (**Security & SD-WAN** > **Site-to-site VPN** > **Configure health checks**).
:::

In the Cloudflare dashboard, go to **Zero Trust** > **Traffic policies** > **Firewall policies** > **HTTP**.

1. Add a new rule.
2. For **Policy Name**, enter `Meraki Tunnel Health Checks - HTTP Policy`.
3. Build an expression of type **Traffic**.
4. For **Selector**, enter `Source Internal IP is 192.0.2.3`.
5. For **Action**, select **Do Not Inspect**.

Position this policy at or near the top of the HTTP policy rulebase.

### Diagram: Meraki tunnel monitoring with Cloudflare WAN

The following diagram shows the traffic flow from the tunnel monitoring reserved IP (`192.0.2.3/32`) as it traverses the IPsec tunnels to Cloudflare WAN, then through Cloudflare Gateway as the requests egress to the Internet. The response path is fully symmetric.

```mermaid
flowchart LR
accTitle: Meraki tunnel monitoring with Cloudflare WAN
accDescr: Traffic flow from the tunnel monitoring source IP through the Meraki MX, IPsec tunnels, Cloudflare WAN, and Cloudflare Gateway to an HTTP target on the public internet.
 subgraph CPE["Cisco Meraki (CPE) Active/Standby Model"]
    direction TB
        FW["Cisco Meraki MX
        WAN: 203.0.113.100/24
        ---
        LAN: 192.168.125.1/24
        ---
        LAN Subnet: 192.168.125.0/24"]
        L7_Health_Check["L7 Health Check
        ---
        Internal Src IP: 192.0.2.3/32"]
  end
 subgraph T1["Active - IPsec Tunnel 1"]
    direction LR
        T1_CPE["CPE VTI 1
        Internal to MX"]
        T1_CF["Cloudflare VTI 1
        169.254.250.0/31"]
  end
 subgraph T2["Standby - IPsec Tunnel 2"]
    direction LR
        T2_CPE["CPE VTI 2
        Internal to MX"]
        T2_CF["Cloudflare VTI 2
        169.254.250.2/31"]
  end
 subgraph CF["Cloudflare WAN"]
    direction TB
        EP1["Anycast Endpoint 1
        162.159.135.1"]
        EP2["Anycast Endpoint 2
        172.64.135.1"]
  end
 subgraph CF_GW["Cloudflare Gateway"]
    direction TB
        GW["Policy
        Src IP 192.0.2.3
        Allow"]
  end
    L7HCT["HTTP Target"]

    T1_CPE === T1_CF
    T2_CPE === T2_CF
    FW <==> T1_CPE & T2_CPE
    T1_CF <==> EP1
    T2_CF <==> EP2

    L7_Health_Check -.-> FW
    FW -.-> T1_CPE
    FW -.-> T2_CPE
    T1_CPE -.-> T1_CF
    T2_CPE -.-> T2_CF
    T1_CF -.-> EP1
    T2_CF -.-> EP2
    EP1 -.-> GW
    EP2 -.-> GW
    GW -.-> L7HCT
    FW@{ shape: stadium}
    T1_CPE@{ shape: stadium}
    T1_CF@{ shape: stadium}
    T2_CPE@{ shape: stadium}
    T2_CF@{ shape: stadium}
    EP1@{ shape: stadium}
    EP2@{ shape: stadium}
    GW@{ shape: stadium}
```

## Meraki configuration

### Meraki management model and Cloudflare WAN integration

The Meraki configuration management is built on a two-tier hierarchy. Objects and their associated settings are defined as either:

- **Organization-wide**: Global objects defined once for the entire tenant.
- **Network-specific**: Settings applied to an individual site or device.

The Non-Meraki VPN configuration is an Organization-tier object. It is pushed to specific MX appliances when they are associated with a corresponding Network Tag. This inheritance model is a critical factor: the tag controls which physical hardware attempts to establish tunnels to Cloudflare.

### Meraki Organization

`Orbital Path Ventures` is a fictitious company referenced throughout the configuration to represent an Organization defined in the Meraki Dashboard.

The company manages a single Meraki MX appliance at their Austin, TX branch office, which is associated with a Network named `Orbital Path Ventures - Austin TX`.

A Network Tag labeled `Orbital_Path_AUS_Office` is associated with the `Orbital Path Ventures - Austin TX` Network.

| Organization        | Network                         | Tag                       |
| ------------------- | ------------------------------- | ------------------------- |
| Orbit Path Ventures | Orbit Path Ventures - Austin TX | `Orbital_Path_AUS_Office` |

### Network Tag

Go to **Network** > **Networks**, then select the Organization.

- **Orbit Path Ventures** (substitute your Organization name).
- **Network**: `Orbit Path Ventures - Austin TX` (substitute your Network name).
- **Tag**: `Orbital_Path_AUS_Office` (substitute the Tag associated with the Network name).

### Traffic steering

When integrating with Cloudflare WAN, the Meraki Network Tag determines which appliances inherit the Cloudflare tunnel configuration.

The Non-Meraki VPN configuration is a global object: any MX appliance with the associated Network Tag attempts to establish tunnels to Cloudflare using the same IPsec VPN peers.

To ensure predictable traffic flows and prevent routing conflicts, Cloudflare recommends the following best practices:

- **Strict tunnel correlation**: Maintain a 1-to-1 mapping between the redundant IPsec tunnel pairs defined in Cloudflare and the specific MX appliance initiating those tunnels.
- **Site-specific Network Tags**: Use granular, site-specific tags (for example, `Orbital_Path_AUS_Office`) rather than broad, generic tags to ensure only the intended MX inherits the tunnel configuration.
- **Unique IPsec VPN peer objects**: Create distinct Non-Meraki VPN peer objects at the Organization level for different physical geographic locations. Use the **Availability** option to establish the 1-to-1 mapping.

Return traffic from Cloudflare WAN is steered based on the Cloudflare virtual network routing table (refer to [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/) for details). Routes are specified based on the MX LAN prefix and corresponding IPsec tunnels.

:::note
Before applying a Network Tag to a Non-Meraki VPN object, verify that the subnets defined in the **Availability** section of the Meraki IPsec configuration match the routing logic defined in the Cloudflare Unified Routing dataplane.
:::

:::note
If multiple MX appliances with different private subnets inherit the same Non-Meraki VPN object, they all attempt to establish tunnels using identical identifiers. This causes flapping or unpredictable routing behavior, because the Cloudflare dataplane forwards traffic destined for the prefixes associated with the private subnets indiscriminately through tunnels with the same identifier. The dataplane cannot determine if there is a mismatch of the private networks behind each MX.
:::

#### Source-based default routing

[Source-Based Default Routing](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Networks_and_Routing/Source_Based_Default_Routing) enables an administrator to create a source-based default route and specify a next hop as a security appliance over Auto VPN or on a device on the LAN.

Source-Based Default Routing cannot be used in conjunction with Non-Meraki VPN endpoints, including Cloudflare WAN.

Define private subnets in the IPsec VPN peer configuration to control how MX appliances steer traffic through the respective tunnels.

#### Routing with private subnets

Any IP prefixes defined as private subnets in the IPsec VPN peer configuration control what traffic is routed across the primary and secondary VPN tunnels. They are visible in the routing table corresponding to a given MX appliance.

This document considers three route topologies:

1. East/west only:
   - Private traffic via Cloudflare WAN.
   - Internet via local Internet.
2. Internet only via Cloudflare Gateway:
   - Only route Internet traffic through Cloudflare WAN.
3. All traffic via Cloudflare WAN and Gateway:
   - East/west and Internet traffic routed via Cloudflare WAN.

All three topologies are covered in the [IPsec VPN peers](#ipsec-vpn-peers) section.

## MX site-to-site VPN configuration

Go to **Security & SD-WAN** > **Site-to-site VPN**.

### Type

Select **Hub (Mesh)**.

### VPN settings

#### Local networks

Turn on VPN mode for the local network behind the MX devices.

From:

| **Name** | **VPN Mode** | **Subnet**       | **Uplink** |
| -------- | ------------ | ---------------- | ---------- |
| Default  | **Disabled** | 192.168.125.0/24 | —          |

To:

| **Name** | **VPN Mode** | **Subnet**       | **Uplink** |
| -------- | ------------ | ---------------- | ---------- |
| Default  | **Enabled**  | 192.168.125.0/24 | —          |

#### IPsec VPN peers

Go to **Security & SD-WAN** > **Site-to-site VPN** > **Organization Wide Settings**.

##### Configure health checks

Configure a Layer 7 health check HTTP probe that the MX platform uses to determine reachability of resources through the IPsec VPN tunnels:

1. Select **Configure Health Checks**.
2. Provide the following values:

   | **Name** | **Endpoint**            |
   | -------- | ----------------------- |
   | Google   | `http://www.google.com` |

3. Select **OK**.

:::note
The Layer 7 health check probes only support connections via HTTP (port 80/tcp). The Cloudflare Zero Trust configuration must permit the Meraki tunnel health check probe IP (`192.0.2.3/32`) to access the designated URL without requiring SSL/TLS encryption or any authentication or authorization policies.
:::

##### Add primary and secondary IPsec VPN peers

IPsec VPN peer configurations are provided for the following topologies:

- East/west traffic only
- Internet only via Cloudflare Gateway
- All traffic via Cloudflare WAN and Gateway

###### Topology: east/west traffic only

Routing east/west traffic via Cloudflare WAN requires:

- Cloudflare routes specified for the LAN subnet behind the MX appliance (`192.168.125.0/24`) via `CF_WAN_TUN_01` and `CF_WAN_TUN_02`.
- Remote subnets (`172.16.10.0/24` and `172.16.11.0/24`) specified as private subnets on the Meraki primary and secondary IPsec VPN peers.
- The IPv4 interface address prefixes specified on both `CF_WAN_TUN_01` and `CF_WAN_TUN_02` (`169.254.250.0/31` and `169.254.250.2/31`) specified as private subnets on the Meraki primary and secondary IPsec VPN peers.

:::note
Automatic Return Routing is not required for east/west traffic only.
:::

This ensures that:

- Cloudflare routes traffic destined for the LAN subnet behind the MX appliance via `CF_WAN_TUN_01` and `CF_WAN_TUN_02`.
- The MX appliance explicitly routes traffic destined for the remote subnets (`172.16.10.0/24` and `172.16.11.0/24`) via the primary and secondary IPsec VPN peers respectively.
- The MX appliance explicitly routes ICMP Reply packets associated with Cloudflare tunnel health checks to the IPv4 interface addresses (`169.254.250.0/31` and `169.254.250.2/31`) specified on `CF_WAN_TUN_01` and `CF_WAN_TUN_02` via the primary and secondary IPsec VPN peers respectively.
- Internet traffic from the LAN subnet behind the MX appliance is routed via the WAN uplink.
- The MX appliance establishes IPsec tunnels to Cloudflare endpoints (`162.159.135.1` and `172.64.135.1`) via the WAN uplink.

Configure the following:

Cloudflare IPsec tunnels — automatic return routing:

| **Tunnel**    | **Automatic Return Routing** |
| ------------- | ---------------------------- |
| CF_WAN_TUN_01 | Disabled                     |
| CF_WAN_TUN_02 | Disabled                     |

Cloudflare routes:

| **Prefix**       | **Description** | **Next hop**  | **Priority** | **Region code** | **Type** |
| ---------------- | --------------- | ------------- | ------------ | --------------- | -------- |
| 192.168.125.0/24 | CF_WAN_TUN_01   | CF_WAN_TUN_01 | 100          | All regions     | Static   |
| 192.168.125.0/24 | CF_WAN_TUN_02   | CF_WAN_TUN_02 | 100          | All regions     | Static   |

Meraki private subnets:

| **Private Subnet** | **Scope**                                              |
| ------------------ | ------------------------------------------------------ |
| 172.16.10.0/24     | Remote site                                            |
| 172.16.11.0/24     | Remote site                                            |
| 169.254.250.0/31   | CF_WAN_TUN_01 — tunnel health check ICMP Reply packets |
| 169.254.250.2/31   | CF_WAN_TUN_02 — tunnel health check ICMP Reply packets |

###### Primary IPsec VPN peer: east/west traffic only

1. Select **+ Add a peer**.
2. Provide the following values:

   | **Attribute**                    | **Value**                                                          |
   | -------------------------------- | ------------------------------------------------------------------ |
   | Name                             | `cf-wan-tun-01`                                                    |
   | IKE Version                      | IKEv2                                                              |
   | Public IP or Hostname            | 162.159.135.1                                                      |
   | Local ID                         | `bf6c493d03<REDACTED>.ipsec.cloudflare.com`                        |
   | Remote ID                        | —                                                                  |
   | Shared Secret                    | `Cloudflare-WAN-T1-PSK-1234!`                                      |
   | Routing                          | Static                                                             |
   | Private Subnets                  | 169.254.250.0/31, 169.254.250.2/31, 172.16.10.0/24, 172.16.11.0/24 |
   | Availability                     | `Orbital_Path_AUS_Office`                                          |
   | Tunnel Monitoring                | Google Health Check                                                |
   | Failover directly to internet    | —                                                                  |
   | IPsec Policy                     | —                                                                  |
   | Preset                           | Custom                                                             |
   | Phase 1 — Encryption             | AES 256                                                            |
   | Phase 1 — Authentication         | SHA256                                                             |
   | Phase 1 — Pseudo-Random Function | SHA256                                                             |
   | Phase 1 — Diffie-Hellman group   | 14                                                                 |
   | Phase 1 — Lifetime (sec)         | 28800                                                              |
   | Phase 2 — Encryption             | AES256                                                             |
   | Phase 2 — Authentication         | SHA256                                                             |
   | Phase 2 — PFS Group              | 14                                                                 |
   | Phase 2 — Lifetime (sec)         | 28800                                                              |

3. Select **Save**.

:::note
Any private subnets specified in `cf-wan-tun-01` are automatically inherited by `cf-wan-tun-02`.
:::

:::note
**Availability**: specifying the `Orbital_Path_AUS_Office` Network Tag pushes the IPsec VPN peer configuration to any MX appliances associated with the `Orbit Path Ventures - Austin TX` Network.
:::

###### Secondary IPsec VPN peer: east/west traffic only

1. Select the `---` icon in the settings column.
2. Select **+ Add secondary peer**.
3. Do not select **Inherit primary peer configurations**. This ensures the **Public IP or Hostname**, **Local ID**, **Remote ID**, and **Shared secret** are configured with the settings required to successfully negotiate an IPsec tunnel `CF_WAN_TUN_02`.
4. Provide the following values:

   | **Attribute**                    | **Value**                                                                      |
   | -------------------------------- | ------------------------------------------------------------------------------ |
   | Name                             | `cf-wan-tun-02`                                                                |
   | IKE Version                      | IKEv2 (Inherited)                                                              |
   | Public IP or Hostname            | 172.64.135.1                                                                   |
   | Local ID                         | `0287844e9d<REDACTED>.ipsec.cloudflare.com`                                    |
   | Remote ID                        | 172.64.135.1                                                                   |
   | Shared Secret                    | `Cloudflare-WAN-T2-PSK-1234!`                                                  |
   | Routing                          | Static (Inherited)                                                             |
   | Private Subnets                  | 169.254.250.0/31, 169.254.250.2/31, 172.16.10.0/24, 172.16.11.0/24 (Inherited) |
   | Availability                     | `Orbital_Path_AUS_Office` (Inherited)                                          |
   | Tunnel Monitoring                | Google Health Check                                                            |
   | Failover directly to internet    | —                                                                              |
   | IPsec Policy                     | —                                                                              |
   | Preset                           | Custom                                                                         |
   | Phase 1 — Encryption             | AES 256                                                                        |
   | Phase 1 — Authentication         | SHA256                                                                         |
   | Phase 1 — Pseudo-Random Function | SHA256                                                                         |
   | Phase 1 — Diffie-Hellman group   | 14                                                                             |
   | Phase 1 — Lifetime (sec)         | 28800                                                                          |
   | Phase 2 — Encryption             | AES256                                                                         |
   | Phase 2 — Authentication         | SHA256                                                                         |
   | Phase 2 — PFS Group              | 14                                                                             |
   | Phase 2 — Lifetime (sec)         | 28800                                                                          |

5. Select **Save**.

:::note
**Inherited** indicates the value was automatically applied based on the configuration as specified on `cf-wan-tun-01`.
:::

##### Route table: east/west traffic only

Confirm the MX appliance route table includes routes for the private subnets defined in the primary and secondary IPsec VPN peer configuration.

Go to **Security & SD-WAN** > **Monitor** > **Route table**.

The Meraki route table reflects routes via `cf-wan-tun-01` and `cf-wan-tun-02` as follows:

| Status | Version | Subnet           | Name          | VLAN | Next-Hop      | Destination   | Type              |
| ------ | ------- | ---------------- | ------------- | ---- | ------------- | ------------- | ----------------- |
| —      | 4       | 0.0.0.0/0        | Default       | —    | —             | WAN uplink    | Default WAN Route |
| —      | 4       | 169.254.250.0/31 | cf-wan-tun-01 | —    | cf-wan-tun-01 | —             | IPsec Peer        |
| —      | 4       | 169.254.250.0/31 | cf-wan-tun-02 | —    | cf-wan-tun-02 | —             | IPsec Peer        |
| —      | 4       | 169.254.250.2/31 | cf-wan-tun-01 | —    | cf-wan-tun-01 | —             | IPsec Peer        |
| —      | 4       | 169.254.250.2/31 | cf-wan-tun-02 | —    | cf-wan-tun-02 | —             | IPsec Peer        |
| —      | 4       | 172.16.10.0/24   | cf-wan-tun-01 | —    | cf-wan-tun-01 | —             | IPsec Peer        |
| —      | 4       | 172.16.10.0/24   | cf-wan-tun-02 | —    | cf-wan-tun-02 | —             | IPsec Peer        |
| —      | 4       | 172.16.11.0/24   | cf-wan-tun-01 | —    | cf-wan-tun-01 | —             | IPsec Peer        |
| —      | 4       | 172.16.11.0/24   | cf-wan-tun-02 | —    | cf-wan-tun-02 | —             | IPsec Peer        |
| 🟢     | 4       | 192.168.125.0/24 | LAN           | 1    | 192.168.125.1 | 192.168.125.1 | Local VLAN        |

#### Tunnel health and failover

Meraki uses tunnel monitoring to determine when to fail over automatically to the secondary IPsec VPN peer. Meraki uses Dead Peer Detection to determine the overall health of the IPsec tunnels.

Non-Meraki VPN peers support an Active/Standby model. Traffic is sent via `cf-wan-tun-01` until a failover event occurs, at which point `cf-wan-tun-02` becomes active. Traffic is dynamically reverted to `cf-wan-tun-01` once its tunnel is reconnected.

Failover testing indicates traffic may be disrupted for a few seconds. Cloudflare has observed some failover events taking 15 to 20 seconds, but these incidents are rare.

##### Cloudflare tunnel health

Cloudflare tunnel health checks indicate 100% failure on the tunnel marked as standby. This ensures traffic is only steered through the active tunnel.

| **Active Peer**          | **Tunnel health**          |                            |
| ------------------------ | -------------------------- | -------------------------- |
| Primary IPsec VPN peer   | **CF_WAN_TUN_01**: 🟢 0%   | **CF_WAN_TUN_02**: 🔴 100% |
| Secondary IPsec VPN peer | **CF_WAN_TUN_01**: 🔴 100% | **CF_WAN_TUN_02**: 🟢 0%   |

##### Meraki tunnel health

Use the Meraki Dashboard to determine the status of the IPsec tunnels:

1. Go to **Security & SD-WAN** > **Monitor** > **VPN Status**.
2. Scroll to the **Overview** section.
3. Select the filter labeled **2 IPsec peers**.

###### East/west traffic only

Active tunnel: `cf-wan-tun-01`:

| Status          | Name          | Public IP     | Subnets          | Tunnel monitor |
| --------------- | ------------- | ------------- | ---------------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 169.254.250.0/31 | Details (link) |
| 🟢 Health check |               |               | 169.254.250.2/31 |                |
|                 |               |               | 172.16.10.0/24   |                |
|                 |               |               | 172.16.11.0/24   |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 169.254.250.0/31 | Details (link) |
| 🟢 Health check |               |               | 169.254.250.2/31 |                |
|                 |               |               | 172.16.10.0/24   |                |
|                 |               |               | 172.16.11.0/24   |                |

Active tunnel: `cf-wan-tun-02`:

| Status          | Name          | Public IP     | Subnets          | Tunnel monitor |
| --------------- | ------------- | ------------- | ---------------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 169.254.250.0/31 | Details (link) |
| 🔴 Health check |               |               | 169.254.250.2/31 |                |
|                 |               |               | 172.16.10.0/24   |                |
|                 |               |               | 172.16.11.0/24   |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 169.254.250.0/31 | Details (link) |
| 🟢 Health check |               |               | 169.254.250.2/31 |                |
|                 |               |               | 172.16.10.0/24   |                |
|                 |               |               | 172.16.11.0/24   |                |

###### Internet only via Cloudflare Gateway

Active tunnel: `cf-wan-tun-01`:

| Status          | Name          | Public IP     | Subnets   | Tunnel monitor |
| --------------- | ------------- | ------------- | --------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 0.0.0.0/0 | Details (link) |
| 🟢 Health check |               |               |           |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 0.0.0.0/0 | Details (link) |
| 🟢 Health check |               |               |           |                |

Active tunnel: `cf-wan-tun-02`:

| Status          | Name          | Public IP     | Subnets   | Tunnel monitor |
| --------------- | ------------- | ------------- | --------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 0.0.0.0/0 | Details (link) |
| 🔴 Health check |               |               |           |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 0.0.0.0/0 | Details (link) |
| 🟢 Health check |               |               |           |                |

###### All traffic via Cloudflare WAN and Gateway

Active tunnel: `cf-wan-tun-01`:

| Status          | Name          | Public IP     | Subnets   | Tunnel monitor |
| --------------- | ------------- | ------------- | --------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 0.0.0.0/0 | Details (link) |
| 🟢 Health check |               |               |           |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 0.0.0.0/0 | Details (link) |
| 🟢 Health check |               |               |           |                |

Active tunnel: `cf-wan-tun-02`:

| Status          | Name          | Public IP     | Subnets   | Tunnel monitor |
| --------------- | ------------- | ------------- | --------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 0.0.0.0/0 | Details (link) |
| 🔴 Health check |               |               |           |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 0.0.0.0/0 | Details (link) |
| 🟢 Health check |               |               |           |                |

## Troubleshooting

### MX platform routing table

Review the MX route table to determine what traffic is routed over the IPsec tunnels compared to direct internet routing.

Go to **Security & SD-WAN** > **Monitor** > **Route Table**.

### MX tunnel monitoring

VPN Status reports that the health checks are failing on both tunnels:

| Status          | Name          | Public IP     | Subnets          | Tunnel monitor |
| --------------- | ------------- | ------------- | ---------------- | -------------- |
| 🟢 IPsec        | cf-wan-tun-01 | 162.159.135.1 | 169.254.250.0/31 | Details (link) |
| 🔴 Health check |               |               | 169.254.250.2/31 |                |
|                 |               |               | 172.16.10.0/24   |                |
|                 |               |               | 172.16.11.0/24   |                |
| 🟢 IPsec        | cf-wan-tun-02 | 172.64.135.1  | 169.254.250.0/31 | Details (link) |
| 🔴 Health check |               |               | 169.254.250.2/31 |                |
|                 |               |               | 172.16.10.0/24   |                |
|                 |               |               | 172.16.11.0/24   |                |

Check the Cloudflare Gateway logs and policy to determine if HTTP requests originating from `192.0.2.3/32` are being blocked.

If blocked, create a rule to restore tunnel monitoring HTTP requests. Refer to [Cloudflare Gateway HTTP policy](#cloudflare-gateway-http-policy) for details.

### Cloudflare and Meraki IPsec logs

Available in:

- [Log Explorer](/observability/logs/log-explorer/)
- [Logpush](/logs/logpush/) > [IPsec logs](/logs/logpush/logpush-job/datasets/account/ipsec_logs/)

IPsec logs can help diagnose a variety of issues related to IPsec tunnels, including:

- Using unsupported Phase 1 or Phase 2 encryption or integrity settings — look for messages indicating `No proposal chosen`.
  - Confirm that the Phase 1 and Phase 2 encryption or integrity values defined are supported by Cloudflare WAN.
  - Refer to [Supported configuration parameters](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters).
- IKE/IPsec identity: local or remote identity not defined or with incorrect values.
  - Refer to the [Palo Alto third-party integration guide](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/palo-alto/) for an example of FQDN-based local identification.
- Authentication failures: wrong pre-shared key.

Refer to [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) for more details.

### Cloudflare tunnel health checks

Ensure tunnel health checks for both `CF_WAN_TUN_01` and `CF_WAN_TUN_02` are configured with the following settings:

| **Attribute**  | **Value**     | **Notes**                                                                     |
| -------------- | ------------- | ----------------------------------------------------------------------------- |
| Enabled        | True          | Ensure the indicator displays 🟢 Enabled.                                     |
| Type           | Request       | A stateful firewall drops ICMP Reply probes.                                  |
| Direction      | Bidirectional | Ensures probes are sent and received via the tunnel.                          |
| Target         | Custom        | The MX platform does not support VTIs, so probes must target an alternate IP. |
| Target address | 192.168.125.1 | Send probes to the LAN interface on the MX appliance.                         |

## Meraki references

- [Connection Monitoring for WAN Failover](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Firewall_and_Traffic_Shaping/Connection_Monitoring_for_WAN_Failover#Enhanced_WAN_Failover_and_Failback)
- [MX Routing Behavior](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Networks_and_Routing/MX_Routing_Behavior)
- [Organization Overview](https://documentation.meraki.com/Platform_Management/Dashboard_Administration/Operate_and_Maintain/Inventory_and_Devices/Organization_Overview)
- [Primary and Secondary IPsec VPN Tunnels](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Site-to-site_VPN/Primary_and_Secondary_IPsec_VPN_Tunnels)
- [Site-to-Site VPN](https://documentation.meraki.com/SASE_and_SD-WAN/MX/Design_and_Configure/Configuration_Guides/Site-to-site_VPN/Site-to-Site_VPN_Settings#Peer_availability)

# Furukawa Electric FITELnet

This tutorial describes how to configure the Furukawa Electric's FITELnet F220 and F70 devices to connect to Cloudflare WAN (formerly Magic WAN) via IPsec (Internet Protocol Security) tunnels. The use cases described in this tutorial are for both east-west (branch to branch) and north-south (Internet-bound).

## Testing environment

These configurations were tested on FITELnet F220 and F70 series with the following firmware versions:

- **F220 series**: Version 01.11(00)
- **F70 series**: Version 01.09(00)

## IPsec configuration

### Cloudflare WAN configuration

1. Follow the [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) instructions to create the required IPsec tunnels.
2. For the first IPsec tunnel, ensure the following settings are defined:
   - **Tunnel name**: `FITEL-tunnel-1`
   - **Interface address**: Enter `10.0.0.1/31` for your first tunnel.
   - **Customer endpoint**: This setting is not required unless your router is using an IKE ID of [type `ID_IPV4_ADDR`](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).
   - **Cloudflare endpoint**: One of the Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
   - **Pre-shared key**: Create a pre-shared key for your first tunnel.
3. For the second IPsec tunnel, make the same changes as you did for the first tunnel, and ensure these additional settings are defined:
   - **Tunnel name**: `FITEL-tunnel-2`
   - **Interface address**: Enter `10.0.0.3/31` for your second tunnel.
   - **Customer endpoint**: This setting is not required unless your router is using an IKE ID of [type `ID_IPV4_ADDR`](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).
   - **Cloudflare endpoint**: One of the Cloudflare anycast IP addresses assigned to your account.
   - **Pre-shared key**: Create a pre-shared key for your second tunnel.

### FITELnet router configuration

#### Router 1 settings

Use the CLI (Command Line Interface) to configure these settings:

```txt
interface Tunnel 1
 ip address 10.0.0.0 255.255.255.254
 tunnel mode ipsec map MAP1
 link-state sync-sa
exit
!

crypto ipsec policy IPsec_POLICY
 set security-association always-up
 set security-association lifetime seconds 28800
 set security-association transform-keysize aes 256 256 256
 set security-association transform esp-aes esp-sha256-hmac
 set mtu 1460
 set mss 1350
 set ip df-bit 0
 set ip fragment post
 ! if there is a NAT router between Cloudflare and FITELnet,
 ! add the two udp-encapsulation options below
 set udp-encapsulation nat-t keepalive interval 30 always-send
 set udp-encapsulation-force
exit
!
crypto ipsec selector SELECTOR
 src 1 ipv4 any
 dst 1 ipv4 any
exit
!
crypto isakmp keepalive
crypto isakmp log sa
crypto isakmp log session
crypto isakmp log negotiation-fail
crypto isakmp negotiation always-up-params interval 100 max-initiate 10 max-pending 10 delay 1
crypto ipsec replay-check disable
!
crypto isakmp policy ISAKMP_POLICY
 authentication pre-share
 encryption aes
 encryption-keysize aes 256 256 256
 group 20
 lifetime 86400
 hash sha sha-256
 initiate-mode aggressive
exit
!
crypto isakmp profile PROF1
 ! set the value of FQDN ID for self-identify
 self-identity fqdn <FQDN-ID-TUNNEL01>
 set isakmp-policy ISAKMP_POLICY
 set ipsec-policy IPsec_POLICY
 set peer <CLOUDFLARE-ANYCAST-ADDRESS>
 ike-version 2
 local-key <PRE-SHARED-KEY-TUNNEL01>
exit
!
crypto map MAP1 ipsec-isakmp
 match address SELECTOR
 set isakmp-profile PROF1
exit
!
```

#### Router 2 settings

Use the CLI to configure these settings:

```txt
interface Tunnel 2
 ip address 10.0.0.2 255.255.255.254
 tunnel mode ipsec map MAP1
 link-state sync-sa
exit
!

crypto ipsec policy IPsec_POLICY
 set security-association always-up
 set security-association lifetime seconds 28800
 set security-association transform-keysize aes 256 256 256
 set security-association transform esp-aes esp-sha256-hmac
 set mtu 1460
 set mss 1350
 set ip df-bit 0
 set ip fragment post
 ! if there is a NAT router between Cloudflare and FITELnet,
 ! add the two udp-encapsulation options below
 set udp-encapsulation nat-t keepalive interval 30 always-send
 set udp-encapsulation-force
exit
!
crypto ipsec selector SELECTOR
 src 1 ipv4 any
 dst 1 ipv4 any
exit
!
crypto isakmp keepalive
crypto isakmp log sa
crypto isakmp log session
crypto isakmp log negotiation-fail
crypto isakmp negotiation always-up-params interval 100 max-initiate 10 max-pending 10 delay 1
crypto ipsec replay-check disable
!
crypto isakmp policy ISAKMP_POLICY
 authentication pre-share
 encryption aes
 encryption-keysize aes 256 256 256
 group 20
 lifetime 86400
 hash sha sha-256
 initiate-mode aggressive
exit
!
crypto isakmp profile PROF1
 ! set the value of FQDN ID for self-identify
 self-identity fqdn <FQDN-ID-TUNNEL02>
 set isakmp-policy ISAKMP_POLICY
 set ipsec-policy IPsec_POLICY
 set peer <CLOUDFLARE-ANYCAST-ADDRESS>
 ike-version 2
 local-key <PRE-SHARED-KEY-TUNNEL02>
exit
!
crypto map MAP1 ipsec-isakmp
 match address SELECTOR
 set isakmp-profile PROF1
exit
!
```

## Static route configuration

To configure routes for east-west (branch to branch) connections, refer to the following settings.

### Cloudflare WAN

1. Follow the [Configure static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) instructions to create a static route.
2. For the first route, ensure the following settings are defined:

- **Prefix**: `192.168.0.0/24`
- **Tunnel/Next hop**: _FITEL-tunnel-1 / 10.0.0.0_

3. For the second route, ensure the following settings are defined:

- **Prefix**: `192.168.1.0/24`
- **Tunnel/Next hop**: _FITEL-tunnel-2 / 10.0.0.2_

### FITELnet router configuration

#### Router 1

Use the CLI to configure these settings:

```txt
ip route 192.168.0.0 255.255.255.0 tunnel 1
```

#### Router 2

Use the CLI to configure these settings:

```txt
ip route 192.168.1.0 255.255.255.0 tunnel 2
```

## Connection test

### IPsec status

In the FITELnet router CLI, you can run `show crypto sa` to check the status of the IPsec security associations (SAs). `Total number of ISAKMP/IPSEC SA` shows the number of established SAs.

```txt
show crypto sa

  IKE_SA
    Mode: <I>
    Local IP : <LOCAL_IP>/500
    Local ID : <LOCAL_ID> (ipv4)
    Remote IP : anycast-address/500
    Remote ID : anycast-address (ipv4)
    Local Authentication method : Pre-shared key
    Remote Authentication method : Pre-shared key
    Encryption algorithm : aes256-cbc
    Hash algorithm : hmac-sha256-128
    Diffie-Hellman group : 20
    Initiator Cookie : aaaaaaaa bbbbbbbb
    Responder Cookie : cccccccc dddddddd
    Life time : 6852/14400 sec
    DPD : on

  CHILD_SA <I>
    Selector :
      0.0.0.0/0 ALL ALL <---> 0.0.0.0/0 ALL ALL
    Interface : tunnel 1
    Peer IP : anycast-address/500
    Local IP : xxx.xxx.xxx.xxx/500
    Encryption algorithm : AES-CBC/256
    Authentication algorithm : HMAC-SHA2-256
    Life time : 22868/28800 sec
    PFS : off ESN : off
    IN
      SPI : eeeeeeee
      Packets       : 0
      Octets        : 0
      Replay error  : 0
      Auth error    : 0
      Padding error : 0
      Rule error    : 0
    OUT
      SPI : ffffffff
      Packets       : 0
      Octets        : 0
      Seq lapped    : 0

  Total number of ISAKMP SA 1
  Total number of IPSEC SA 1
```

### Route Status

In the FITELnet router CLI, you can run `show ip route` to check the route information. A `*` in the route information indicates that the route information is valid.

```txt
show ip route

Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
       B - BGP, T - Tunnel, i - IS-IS, V - VRRP track,
       Iu - ISAKMP SA up, It - ISAKMP tunnel route, Ip - ISAKMP l2tpv2-ppp
       Dc - DHCP-client, L - Local Breakout
       > - selected route, * - FIB route, p - stale info

<snip>
S > * 192.168.1.0/24 [100/0] is directly connected, Tunnel1
<snip>
#
```

# Fortinet

import { DashButton } from "~/components";

This guide provides information and examples of how to configure Cloudflare WAN (formerly Magic WAN) with Internet Protocol Security (IPsec) tunnels in conjunction with Fortinet FortiGate firewalls.

The FortiGate configuration settings presented here support [bidirectional health checks](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) as required by Cloudflare WAN. However, they do not factor in any other traffic flows outside of the tunnel health checks. The configuration may need to be adjusted based on your current FortiGate configuration.

## Testing Environment

The FortiGate configuration was tested on two different FortiGate firewalls:

- FortiGate Virtual Appliance version 7.0.8, running on VMware ESXi 6.5
- FortiGate FG80F, version 7.0.12

## Cloudflare WAN configuration

To set up Cloudflare WAN, add IPsec tunnels and static routes to your Cloudflare account using the dashboard or API.

Before proceeding, ensure that you have the IPv4 anycast address assigned to your account. You can find it in the Cloudflare dashboard under **Address Space** > [**Leased IPs**](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).

### IPsec tunnels

Cloudflare handles failures on its network automatically by advertising your endpoint IP from multiple nodes across many globally distributed data centers. To handle failures on your network, configure two IPsec tunnels from separate routers.

1. Follow the [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) instructions to create the required IPsec tunnels with the following options:
   - **Health check type**: Change to _Request_.
   - **Replay Protection**: Do not change from the default setting.

### Static routes

Add two static routes to define the IP address space that exists behind the IPsec tunnels - one to each of the two IPsec tunnels defined in the previous section.

By default, the static routes are defined with the priority set to `100`. Cloudflare leverages [Equal Cost Multipath Routing (ECMP)](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#equal-cost-multi-path-routing) and will load balance the traffic equally across the two tunnels. If you prefer to use an Active/Passive model, you can leave the default value for the first route set to `100`, and set the value for the second tunnel to `150` (higher value is a lower priority).

1. Follow the [Configure static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) instructions to create a static route.
2. For the first route, ensure the following settings are defined:

   - **Prefix**: Specify the [RFC1918](https://datatracker.ietf.org/doc/html/rfc1918) subnet that exists behind the first IPsec tunnel you have defined in the previous section.
   - **Tunnel/Next hop**: Select your first tunnel (Tunnel 01 of 02).

3. For the second route, ensure the following settings are defined:

   - **Prefix**: Specify the [RFC1918](https://datatracker.ietf.org/doc/html/rfc1918) subnet that exists behind the second IPsec tunnel defined in the previous section.
   - **Tunnel/Next hop**: Select your second tunnel (Tunnel 02 of 02).

## Fortinet FortiGate configuration

### Enable asymmetric routing

Enable asymmetric routing for ICMP to ensure health checks work as expected. This option is required. Otherwise, the tunnel health checks, which are critical for proper Cloudflare WAN functionality, will not work as designed.

Enabling asymmetric routing will affect FortiGate behavior. To learn more, refer to [How FortiGate behaves when asymmetric routing is enabled](https://community.fortinet.com/t5/FortiGate/Technical-Note-How-the-FortiGate-behaves-when-asymmetric-routing/ta-p/198575).

```txt
config system settings
    set asymroute-icmp enable
end
```

### Configure NAT-T (optional)

:::note
This configuration is no longer required. Cloudflare now supports NAT-T starting on UDP port `500`, so devices can negotiate the standard NAT-T flow without forcing port `4500`. Refer to the [changelog entry](/changelog/post/2026-05-11-nat-t-port-500/) for details. This guide has not been re-tested since the change.
:::

If you have Network Address Translation Traversal (NAT-T) on your network, you can enable this feature and initiate Internet Key Exchange (IKE) communications on port `4500`.

To set the IKE port, add the following to your system settings:

```txt
config system settings
    set ike-port 4500
end
```

To enable NAT-T, add `set nattraversal enable` to the IPsec tunnels you are configuring.

```txt
fortigate # config vpn ipsec phase1-interface
    edit "<NAME_OF_YOUR_TUNNEL>"
        set nattraversal enable
```

Refer to [Fortinet's documentation](https://community.fortinet.com/t5/FortiGate/Technical-Tip-IPSec-VPN-NAT-traversal/ta-p/197873) for more details.

### Disable anti-replay protection

For route-based IPsec configurations, you will need to disable anti-replay protection. The following command disables anti-replay protection globally, but you can also do this per firewall policy. Refer to Fortinet's documentation on [anti-replay support per policy](https://community.fortinet.com/t5/FortiGate/Technical-Tip-Anti-Replay-option-support-per-policy/ta-p/191435) to learn more.

```txt
config system global
    set anti-replay disable
end
```

### IPsec tunnels

IPsec tunnels leverage a route-based site-to-site Virtual Private Network (VPN) model. This model relies on the use of virtual tunnel interfaces and routing to define the traffic that flows across the IPsec tunnels.

Configure two IPsec tunnels using the `phase1-interface` and `phase2-interface` objects.

:::note
Refer to the Cloudflare WAN dashboard to obtain the FQDN ID value when specifying the `localid` attribute/value pair in the `phase1-interface` configuration. To find this value go to the **Connectors** page. Then, in the **IPsec/GRE tunnels** tab, select your IPsec tunnel to reveal all the information associated to it.

<DashButton url="/?to=/:account/magic-networks/connections" />
:::

The following examples assume `wan1` is the external/egress interface of the FortiGate firewall.

#### Add Phase 1 interfaces

`MWAN_IPsec_Tun1` corresponds to Tunnel 01 of 02 added earlier in the Cloudflare section of the configuration. `MWAN_IPsec_Tun2` corresponds to Tunnel 02 of 02 added earlier in the Cloudflare section of the configuration.

```txt
fortigate # config vpn ipsec phase1-interface
    edit "MWAN_IPsec_Tun1"
        set interface "wan1"
        set ike-version 2
        set keylife 86400
        set peertype any
        set net-device enable
        set proposal aes256gcm-prfsha512 aes256gcm-prfsha384 aes256gcm-prfsha256
        set localid "f1473dXXXXXXX72e33.49561179.ipsec.cloudflare.com"
        set dhgrp 20
        set nattraversal disable
        set remote-gw 162.159.67.210
        set add-gw-route enable
        set psksecret <YOUR_PRE-SHARED_KEY>
    next
    edit "MWAN_IPsec_Tun2"
        set interface "wan1"
        set ike-version 2
        set keylife 86400
        set peertype any
        set net-device enable
        set proposal aes256gcm-prfsha512 aes256gcm-prfsha384 aes256gcm-prfsha256
        set localid "de91565XXXXXXXfbbd6632.49561179.ipsec.cloudflare.com"
        set dhgrp 20
        set nattraversal disable
        set remote-gw 172.XX.XX.210
        set add-gw-route enable
        set psksecret ENC <YOUR_PRE-SHARED_KEY>
    next
end
```

#### Add Phase 2 interfaces

Add two `phase2-interfaces` - one for each of the two `phase1-interfaces` as follows:

```txt
fortigate # config vpn ipsec phase2-interface
    edit "MWAN_IPsec_Tun1"
        set phase1name "MWAN_IPsec_Tun1"
        set proposal aes256gcm aes128gcm
        set dhgrp 20
        set replay disable
        set keylifeseconds 28800
        set auto-negotiate enable
        set keepalive enable
    next
    edit "MWAN_IPsec_Tun2"
        set phase1name "MWAN_IPsec_Tun2"
        set proposal aes256gcm aes128gcm
        set dhgrp 20
        set replay disable
        set keylifeseconds 28800
        set auto-negotiate enable
        set keepalive enable
    next
end
```

### Network interfaces

#### Virtual tunnel interfaces

Configure the virtual tunnel interfaces that were automatically added when specifying the `set net-device enable` within the `phase1-interface` settings.

These are the only settings that should need to be added to the virtual tunnel interfaces:

- `ip`: The local IP address (specify with a `/32` netmask - `255.255.255.255`).
- `remote-ip`: The value associated with the interface address specified earlier in the IPsec tunnels section (specify with a `/31` netmask - `255.255.255.254`).
- `alias`: This value is optional.

The following examples assume `wan1` is the external/egress interface of the FortiGate firewall.

```txt
fortigate # config system interface
    edit "MWAN_IPsec_Tun1"
        set vdom "root"
        set ip 10.252.2.91 255.255.255.255
        set allowaccess ping
        set type tunnel
        set remote-ip 10.252.2.90 255.255.255.254
        set alias "MWAN_IPsec_Tun1"
        set snmp-index 17
        set interface "wan1"
    next
    edit "MWAN_IPsec_Tun2"
        set vdom "root"
        set ip 10.252.2.93 255.255.255.255
        set allowaccess ping
        set type tunnel
        set remote-ip 10.252.2.92 255.255.255.254
        set alias "MWAN_IPsec_Tun2"
        set snmp-index 18
        set interface "wan1"
    next
end
```

### Validate communication across virtual tunnel interfaces

Once the virtual tunnel interfaces have been configured, you should be able to ping the IP address associated with the `remote-ip` attribute.

The following examples show successful results from pinging across both virtual tunnel interfaces:

#### MWAN_IPsec_Tun1

```txt
fortigate # execute ping 10.252.2.90
PING 10.252.2.90 (10.252.2.90): 56 data bytes
64 bytes from 10.252.2.90: icmp_seq=0 ttl=64 time=5.8 ms
64 bytes from 10.252.2.90: icmp_seq=1 ttl=64 time=5.8 ms
64 bytes from 10.252.2.90: icmp_seq=2 ttl=64 time=5.8 ms
64 bytes from 10.252.2.90: icmp_seq=3 ttl=64 time=5.8 ms
64 bytes from 10.252.2.90: icmp_seq=4 ttl=64 time=5.7 ms

--- 10.252.2.90 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 5.7/5.7/5.8 ms
```

#### MWAN_IPsec_Tun2

```txt
fortigate # execute ping 10.252.2.92
PING 10.252.2.92 (10.252.2.92): 56 data bytes
64 bytes from 10.252.2.92: icmp_seq=0 ttl=64 time=6.1 ms
64 bytes from 10.252.2.92: icmp_seq=1 ttl=64 time=6.1 ms
64 bytes from 10.252.2.92: icmp_seq=2 ttl=64 time=6.1 ms
64 bytes from 10.252.2.92: icmp_seq=3 ttl=64 time=6.1 ms
64 bytes from 10.252.2.92: icmp_seq=4 ttl=64 time=6.0 ms

--- 10.252.2.92 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 6.0/6.0/6.1 ms
```

### Zone objects (optional)

This sample configuration assumes there are three zones configured on the FortiGate firewall. These zone objects are used in the policies referenced later in this document:

- `Trust_Zone`: Contains the LAN interface(s).
- `Untrust_Zone`: Contains the WAN interface.
- `Cloudflare_Zone`: Contains both IPsec Tunnel interfaces.

```txt
fortigate # config system zone
    edit "Cloudflare_Zone"
        set intrazone allow
        set interface "MWAN_IPsec_Tun1" "MWAN_IPsec_Tun2"
    next
    edit "Trust_Zone"
        set intrazone allow
        set interface "internal"
    next
    edit "Untrust_Zone"
        set intrazone allow
        set interface "wan1"
    next
end
```

### Create Address Objects

Create Address Objects to represent the [Cloudflare IPv4 address space](https://www.cloudflare.com/ips) as well as objects for the bidirectional health check anycast IPs:

```txt
config firewall address
    edit "Cloudflare_IPv4_01"
        set color 9
        set subnet 173.245.48.0 255.255.240.0
    next
    edit "Cloudflare_IPv4_02"
        set color 9
        set subnet 103.21.244.0 255.255.252.0
    next
    edit "Cloudflare_IPv4_03"
        set color 9
        set subnet 103.22.200.0 255.255.252.0
    next
    edit "Cloudflare_IPv4_04"
        set color 9
        set subnet 103.31.4.0 255.255.252.0
    next
    edit "Cloudflare_IPv4_05"
        set color 9
        set subnet 141.101.64.0 255.255.192.0
    next
    edit "Cloudflare_IPv4_06"
        set color 9
        set subnet 108.162.192.0 255.255.192.0
    next
    edit "Cloudflare_IPv4_07"
        set color 9
        set subnet 190.93.240.0 255.255.240.0
    next
    edit "Cloudflare_IPv4_08"
        set color 9
        set subnet 188.114.96.0 255.255.240.0
    next
    edit "Cloudflare_IPv4_09"
        set color 9
        set subnet 197.234.240.0 255.255.252.0
    next
    edit "Cloudflare_IPv4_10"
        set color 9
        set subnet 198.41.128.0 255.255.128.0
    next
    edit "Cloudflare_IPv4_11"
        set color 9
        set subnet 162.158.0.0 255.254.0.0
    next
    edit "Cloudflare_IPv4_12"
        set color 9
        set subnet 104.16.0.0 255.248.0.0
    next
    edit "Cloudflare_IPv4_13"
        set color 9
        set subnet 104.24.0.0 255.252.0.0
    next
    edit "Cloudflare_IPv4_14"
        set color 9
        set subnet 172.64.0.0 255.248.0.0
    next
    edit "Cloudflare_IPv4_15"
        set color 9
        set subnet 131.0.72.0 255.255.252.0
    next
    edit "Bidirect_HC_Endpoint_01"
        set comment "Bidirectional health check endpoint address"
        set color 9
        set subnet 172.64.240.253 255.255.255.255
    next
    edit "Bidirect_HC_Endpoint_02"
        set comment "Bidirectional health check endpoint address"
        set color 9
        set subnet 172.64.240.254 255.255.255.255
    next
end
```

### Configure Address Group Object

Create an Address Object that contains all Cloudflare IPv4 subnets. Copy and paste the following CLI commands into an SSH terminal to create the objects automatically:

```txt
config firewall addrgrp
    edit "Cloudflare_IPv4_Nets"
        set member "Cloudflare_IPv4_01" "Cloudflare_IPv4_02" "Cloudflare_IPv4_03" "Cloudflare_IPv4_04" "Cloudflare_IPv4_05" "Cloudflare_IPv4_06" "Cloudflare_IPv4_07" "Cloudflare_IPv4_08" "Cloudflare_IPv4_09" "Cloudflare_IPv4_10" "Cloudflare_IPv4_11" "Cloudflare_IPv4_12" "Cloudflare_IPv4_13" "Cloudflare_IPv4_14" "Cloudflare_IPv4_15"
        set color 9
    next
end
```

### Add security policy

Add a firewall rule to permit the ICMP traffic associated with the reply style bidirectional health checks.

:::note
This example assumes this is the second firewall policy rule (`edit 2`). If you copy and paste the example into an SSH session, edit the numeric value associated with the rule position accordingly.
:::

```txt
fortigate (policy) # show
config firewall policy
    edit 2
        set name "CF_Magic_Health_Checks"
        set uuid 80eb76ce-3033-51ee-c5e5-d5a670dff3b3
        set srcintf "Cloudflare_Zone"
        set action accept
        set srcaddr "Cloudflare_IPv4_Nets"
        set dstaddr "Bidirect_HC_Endpoint_01" "Bidirect_HC_Endpoint_02"
        set schedule "always"
        set service "ALL_ICMP"
        set logtraffic all
    next
end
```

### Policy-based routing

Add policy-based routing rules to ensure traffic associated with bidirectional health checks received over an IPsec tunnel returns across the same tunnel.

Add two policy-based routing rules, one for each of the two IPsec tunnels.

:::note
This example assumes these are the first and second rules respectively (`edit 1` and `edit 2`). If you copy and paste the example into an SSH session, edit the numeric value associated with the rule position accordingly.
:::

```txt
fortigate # config router policy
    edit 1
        set input-device "MWAN_IPsec_Tun1"
        set srcaddr "all"
        set dstaddr "all"
        set gateway 10.252.2.90
        set output-device "MWAN_IPsec_Tun1"
    next
    edit 2
        set input-device "MWAN_IPsec_Tun2"
        set srcaddr "all"
        set dstaddr "all"
        set gateway 10.252.2.92
        set output-device "MWAN_IPsec_Tun2"
    next
end
```

## Monitor Cloudflare IPsec tunnel health checks

The Cloudflare dashboard monitors the health of all anycast tunnels on your account that route traffic from Cloudflare to your origin network. Refer to [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) for more information.

## Troubleshooting

### Packet Capture

Packet captures determine whether the policy-based routing rules are working as expected.

:::note
Reply-style tunnel health checks produce ICMP Reply packets in both the ingress and egress direction. This is expected behavior.
:::

Traffic ingressing Tunnel 01 of 02 should egress the same tunnel, as shown in the following example:

```txt
fortigate # diagnose sniffer packet any 'host 172.64.240.253' 4
interfaces=[any]
filters=[host 172.64.240.253]
0.601569 MWAN_IPsec_Tun1 in 172.64.240.253 -> 162.158.176.118: icmp: echo reply
0.601585 MWAN_IPsec_Tun1 out 172.64.240.253 -> 162.158.176.118: icmp: echo reply
0.611164 MWAN_IPsec_Tun1 in 172.64.240.253 -> 172.71.87.94: icmp: echo reply
0.611178 MWAN_IPsec_Tun1 out 172.64.240.253 -> 172.71.87.94: icmp: echo reply
0.617562 MWAN_IPsec_Tun1 in 172.64.240.253 -> 172.71.129.214: icmp: echo reply
0.617574 MWAN_IPsec_Tun1 out 172.64.240.253 -> 172.71.129.214: icmp: echo reply
0.622042 MWAN_IPsec_Tun1 in 172.64.240.253 -> 172.69.61.43: icmp: echo reply
0.622056 MWAN_IPsec_Tun1 out 172.64.240.253 -> 172.69.61.43: icmp: echo reply
0.624092 MWAN_IPsec_Tun1 in 172.64.240.253 -> 172.68.9.214: icmp: echo reply
```

Conversely, traffic ingressing Tunnel 02 of 02 should egress the same tunnel:

```txt
fortigate # diagnose sniffer packet any 'host 172.64.240.254' 4
interfaces=[any]
filters=[host 172.64.240.254]
0.912041 MWAN_IPsec_Tun2 in 172.64.240.254 -> 172.70.177.56: icmp: echo reply
0.912057 MWAN_IPsec_Tun2 out 172.64.240.254 -> 172.70.177.56: icmp: echo reply
0.913579 MWAN_IPsec_Tun2 in 172.64.240.254 -> 172.70.221.154: icmp: echo reply
0.913592 MWAN_IPsec_Tun2 out 172.64.240.254 -> 172.70.221.154: icmp: echo reply
0.914247 MWAN_IPsec_Tun2 in 172.64.240.254 -> 162.158.1.85: icmp: echo reply
0.914260 MWAN_IPsec_Tun2 out 172.64.240.254 -> 162.158.1.85: icmp: echo reply
0.918533 MWAN_IPsec_Tun2 in 172.64.240.254 -> 172.71.125.75: icmp: echo reply
0.918550 MWAN_IPsec_Tun2 out 172.64.240.254 -> 172.71.125.75: icmp: echo reply
0.924465 MWAN_IPsec_Tun2 in 172.64.240.254 -> 172.69.21.134: icmp: echo reply
```

### Flow Debugging

Flow debugging helps determine whether traffic is ingressing/egressing the firewall via the expected path. It provides more detail than the sniffer packet captures in the previous section, but creates substantial logging and should only be enabled when absolutely necessary.

Additionally, customers will likely need to contact Fortinet technical support for assistance with interpreting the flow debug logs, as well as to obtain recommendations in terms of how to configure FortiGate to ensure flows are routed correctly based on the application's requirements.

```txt
fortigate # diagnose debug disable
fortigate # diagnose debug flow filter clear
fortigate # diagnose debug reset
fortigate # diagnose debug flow filter addr 172.64.240.253
fortigate # diagnose debug show flow show function-name enable
fortigate # diagnose debug config-error-log timestamps enable
fortigate # diagnose debug flow trace start 999
fortigate # diagnose debug enable
fortigate # 2023-08-01 09:27:26 id=20085 trace_id=2871 func=print_pkt_detail line=5844 msg="vd-root:0 received a packet(proto=1, 172.64.240.253:56968->172.70.121.28:0) tun_id=162.159.67.210 from MWAN_IPsec_Tun1. type=0, code=0, id=56968, seq=0."
2023-08-01 09:27:26 id=20085 trace_id=2871 func=rpdb_srv_match_input line=1036 msg="Match policy routing id=1: to 10.252.2.90 via ifindex-34"
2023-08-01 09:27:26 id=20085 trace_id=2871 func=vf_ip_route_input_common line=2605 msg="find a route: flag=00000000 gw-162.159.67.210 via MWAN_IPsec_Tun1"
2023-08-01 09:27:26 id=20085 trace_id=2871 func=ipsecdev_hard_start_xmit line=669 msg="enter IPSec interface MWAN_IPsec_Tun1, tun_id=0.0.0.0"
2023-08-01 09:27:26 id=20085 trace_id=2871 func=_do_ipsecdev_hard_start_xmit line=229 msg="output to IPSec tunnel MWAN_IPsec_Tun1"
2023-08-01 09:27:26 id=20085 trace_id=2871 func=esp_output4 line=844 msg="IPsec encrypt/auth"
2023-08-01 09:27:26 id=20085 trace_id=2871 func=ipsec_output_finish line=544 msg="send to 172.71.91.34 via intf-wan1"
2023-08-01 09:27:26 id=20085 trace_id=2872 func=print_pkt_detail line=5844 msg="vd-root:0 received a packet(proto=1, 172.64.240.253:18685->162.158.209.64:0) tun_id=162.159.67.210 from MWAN_IPsec_Tun1. type=0, code=0, id=18685, seq=0."
2023-08-01 09:27:26 id=20085 trace_id=2872 func=rpdb_srv_match_input line=1036 msg="Match policy routing id=1: to 10.252.2.90 via ifindex-34"
2023-08-01 09:27:26 id=20085 trace_id=2872 func=vf_ip_route_input_common line=2605 msg="find a route: flag=00000000 gw-162.159.67.210 via MWAN_IPsec_Tun1"
2023-08-01 09:27:26 id=20085 trace_id=2872 func=ipsecdev_hard_start_xmit line=669 msg="enter IPSec interface MWAN_IPsec_Tun1, tun_id=0.0.0.0"
2023-08-01 09:27:26 id=20085 trace_id=2872 func=_do_ipsecdev_hard_start_xmit line=229 msg="output to IPSec tunnel MWAN_IPsec_Tun1"
2023-08-01 09:27:26 id=20085 trace_id=2872 func=esp_output4 line=844 msg="IPsec encrypt/auth"
2023-08-01 09:27:26 id=20085 trace_id=2872 func=ipsec_output_finish line=544 msg="send to 172.71.91.34 via intf-wan1"
```

### Disable Flow Debugging

The typical use of `CTRL + C` will not stop Flow Debugging.

You can disable Flow Debugging simply by typing the following at any point while the debug logs are scrolling by:

```txt
fortigate # diagnose debug disable
```

# Google Cloud VPN

This tutorial explains how to configure IPsec VPN between Cloudflare WAN (formerly Magic WAN) and a Google Cloud Platform (GCP) Cloud VPN.

## Prerequisites

You need to have a GCP VPN gateway created in your GCP account. This is needed to route traffic between your GCP virtual private cloud (VPC) and Cloudflare WAN. Refer to the [GCP documentation](https://cloud.google.com/network-connectivity/docs/vpn/how-to/creating-static-vpns) for more information about creating a Cloud VPN gateway.

A Classic VPN Gateway is required to support static routing. Route tables will also need to be manually configured to allow the routing between the VPN and Cloudflare WAN to work. Refer to [GCP routing options](https://cloud.google.com/network-connectivity/docs/vpn/concepts/choosing-networks-routing#ts-tun-routing) to learn more about GCP VPC routing.

## Google Cloud Platform

### Create a GCP Cloud VPN Gateway

1. Go to **Network Connectivity** > **VPN**.
2. Select the **Cloud VPN Gateways** tab > **Create VPN Gateway**.
3. Give your gateway a descriptive name.
4. Choose the network you want to connect to with this Cloud VPN Gateway (VPC).
5. Select a region where this Cloud VPN Gateway should be located.
6. Choose **IPv4** as the IP traffic type that will flow through this Gateway.

:::note
Cloudflare WAN does not yet support private routing via IPv6.
:::

### Configure the VPN connection

1. Go to **Network Connectivity** > **VPN**.
2. Select the **Cloud VPN Tunnels** tab > **Create VPN Tunnel**.
3. Select the VPN Gateway you have created > **Continue**.
4. Give your tunnel a descriptive name.
5. For **Remote Peer IP Address**, use one of the Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
6. In **IKE version**, select **IKEv2**.
7. You can generate an IKE pre-shared key, or add one you already own. If you generate one during this set up, keep it somewhere safe since you will need it in other steps to finish setting up Cloudflare WAN and GCP.
8. Choose **Route-based** as routing option.
9. In **Remote network IP range** define the network you are going to expose to GCP via Cloudflare WAN.

:::note
You can add new IP ranges once the VPN object is created. They will need to be created as VPC routes using this VPN connection (refer to the **Static Routes** section).
:::

10. Repeat steps 2-9 using your second Cloudflare anycast IP to create a second VPN tunnel.

### Static Routes

Static routing is necessary to route traffic between your VPN and Cloudflare WAN. Follow these steps to create them for your VPC. Refer to [VPN route documentation](https://cloud.google.com/vpc/docs/routes) to learn more about VPN routing.

1. Go to **VPC network** > **Routes**.
2. Select **Route Management**.
3. Create a route.
4. Choose the VPC network you want to use for that route.
5. In **Route type** select **Static Routing**.
6. In **IP Version** select **IPv4**.
7. Configure the network you want to expose to your VPN in the **Destination IPv4 Range**.
8. Choose a priority for your static route.
9. (Optional) You can link that route to a specific instance tag, so only impacted instances will use that route.
10. In **Next hop** select the VPN tunnel you created previously.
11. Select **Create**.

## Cloudflare WAN

After configuring the Cloud VPN gateway VPN and the tunnels as mentioned above, go to the Cloudflare dashboard and create the corresponding IPsec tunnels and static routes on the Cloudflare WAN side.

### IPsec tunnels

1. Refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) to learn how to add an IPsec tunnel. When creating your IPsec tunnel, make sure you define the following settings:
   - **Tunnel name**: `tunnel01`
   - **Interface address**: The IPsec tunnel inner `/30` Classless Inter-Domain Routing (CIDR) block. For example, `169.254.244.2`.
   - **Customer endpoint**: The IP address from GCP VPN tunnel outside IP address. For example, `35.xx.xx.xx`.
   - **Cloudflare endpoint**: Enter the first of your two anycast IPs.
   - **Pre-shared key**: Choose **Use my own pre-shared key**, and enter the PSK you created for the GCP VPN tunnel.
   - **Health check type**: Choose **Reply**
   - **Health check destination**: Choose **custom** and set the IP corresponding to the interface address for the tunnel
   - **Health check direction**: Choose **Bidirectional**
   - **Replay protection**: Select **Enabled**.
2. Select **Save**.
3. Repeat the above steps for `tunnel02`. Chose the same prefix, but select the second IPsec tunnel for **Tunnel/Next hop**.

:::note
Do not forget to create a route in the corresponding GCP VPC covering for the healthcheck configuration of the tunnel. The route subnet should match the interface address CIDR block of the IPsec tunnel (`169.254.244.2` in the example above).

Refer to the **Static Routes** section for more detail on how to create a VPC route leading to your newly created tunnel.
:::

### Static routes

Create a static route in Cloudflare WAN that points to the appropriate virtual machine (VM) subnet you created inside your GCP virtual private cloud. For example, if your VM has a subnet of `192.168.192.0/26`, you should use it as the prefix for your static route.

To create a static route:

1. Refer to [Create a static route](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) to learn how to create one.
2. In **Prefix**, enter the subnet for your VM. For example, `192.168.192.0/26`.
3. For the **Tunnel/Next hop**, choose the IPsec tunnel you created in the previous step.
4. Repeat the steps above for the second IPsec tunnel you created.

# Third-party integration

import { DirectoryListing } from "~/components"

<DirectoryListing />

# HPE Juniper Networking SRX Series Firewalls

This guide provides step-by-step instructions for configuring HPE Juniper Networking SRX Series Firewalls to establish IPsec VPN tunnels to Cloudflare WAN. It is intended for network engineers who are familiar with HPE Juniper Networking SRX Series Firewalls administration and have an active Cloudflare WAN subscription.

## Test Environment

| **Field**                          | **Value**                                      |
| ---------------------------------- | ---------------------------------------------- |
| Vendor                             | HPE Juniper Networking                         |
| Model                              | SRX 320                                        |
| Release                            | JUNOS 23.4R2-S3.9                              |
| Date Tested                        | March 2026                                     |

## IKE/IPsec Crypto & Relevant Settings

| **Field**                          | **Value**                                      |
| ---------------------------------- | ---------------------------------------------- |
| Traffic Selection Criteria         | Route-Based VPN                                |
| Routing                            | Static                                         |
| Redundant Tunnels                  | Yes                                            |
| Tunnel Load Balancing              | Active/Active                                  |
| IKE Version                        | IKEv2                                          |
| Authentication                     | Pre-Shared Key                                 |
| Anti-Replay Protection             | Disabled                                       |
| NAT Traversal (NAT-T)              | Not Tested                                     |
| NAT-T Port                         | Not Applicable                                 |
| Phase 1 - DH-Group                 | Group 20                                       |
| Phase 1 - Encryption               | AES-256-CBC                                    |
| Phase 1 - Authentication/Integrity | SHA-256                                        |
| Phase 2 - DH-Group                 | Group 20                                       |
| Phase 2 - Transport                | ESP                                            |
| Phase 2 - Encryption               | AES-256-CBC                                    |

## Cloudflare WAN and HPE Juniper Networking SRX Series Firewalls - Configuration Settings

- While following these steps, ensure you update all object names and IP addresses to match your environment.
- Aligning these elements with your actual naming conventions and network scheme ensures the configuration works correctly in your production setup.
- Use Find and Replace to parse the examples below, update the names and addresses accordingly, and maintain consistency.

### Cloudflare WAN - Tunnel 01 of 02

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| Name (required)                      | CF_WAN_TUN_01                                  |
| Description                          | ---                                            |
| IPv4 Interface Address (required)    | 169.254.250.0/31                               |
| IPv6 Interface Address               | ---                                            |
| Customer Endpoint                    | 203.0.113.100                                  |
| Cloudflare Endpoint                  | 162.159.135.1                                  |
| Tunnel health checks                 | True                                           |
| Rate                                 | Medium                                         |
| **Type**                             | **Request**                                    |
| **Direction**                        | **Bidirectional**                              |
| Target                               | Default                                        |
| ---                                  | ---                                            |
| Turn on replay protection            | False                                          |
| **Automatic return routing**         | **True**                                       |

- IKE Identity and Pre-shared Key (obtained after tunnel creation):

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| FQDN ID                              | `bf6c493d03<REDACTED>.ipsec.cloudflare.com`      |
| Pre-shared key                       | Cloudflare-WAN-T1-PSK-1234!                    |

### Cloudflare WAN - Tunnel 02 of 02

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| Name (required)                      | CF_WAN_TUN_02                                  |
| Description                          | ---                                            |
| IPv4 Interface Address (required)    | 169.254.250.2/31                               |
| IPv6 Interface Address               | ---                                            |
| Customer Endpoint                    | 203.0.113.100                                  |
| Cloudflare Endpoint                  | 172.64.135.1                                   |
| Tunnel health checks                 | True                                           |
| Rate                                 | Medium                                         |
| **Type**                             | **Request**                                    |
| **Direction**                        | **Bidirectional**                              |
| Target                               | Default                                        |
| ---                                  | ---                                            |
| Turn on replay protection            | False                                          |
| **Automatic return routing**         | **True**                                       |

- IKE Identity and Pre-shared Key (obtained after tunnel creation):

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| FQDN ID                              | `0287844e9d<REDACTED>.ipsec.cloudflare.com`      |
| Pre-shared key                       | Cloudflare-WAN-T2-PSK-1234!                    |

## Customer Premise Equipment - HPE Juniper Networking

| **WAN Interface**                   | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ----------------------------------- | --------------------- | --------------------- |
| WAN Interface                       | ge-0/0/0.0            | ge-0/0/0.0            |
| IP Address                          | 203.0.113.100/24      | 203.0.113.100/24      |
| Security Zone                       | untrust               | untrust               |

| **Virtual Tunnel Interface (VTI)**  | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ----------------------------------- | --------------------- | --------------------- |
| Tunnel interface                    | st0.1                 | st0.2                 |
| IP Address                          | 169.254.250.1/31      | 169.254.250.3/31      |
| Security Zone                       | cloudflare            | cloudflare            |

| **LAN Interface**                   | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ----------------------------------- | --------------------- | --------------------- |
| LAN Interface                       | ge-0/0/1.0            | ge-0/0/1.0            |
| IP Address                          | 192.168.125.1/24      | 192.168.125.1/24      |
| Security Zone                       | trust                 | trust                 |

### HPE Juniper Networking SRX Object Names

| **Element**                          | **Object Hierarchy**             | **Name**                        |
| ------------------------------------ | -------------------------------- | ------------------------------- |
| Security Zone - Trust                | [ security zones security-zone ] | trust                           |
| Security Zone - Untrust              | [ security zones security-zone ] | untrust                         |
| Security Zone - Cloudflare WAN       | [ security zones security-zone ] | cloudflare                      |
| IKE Proposal (only one required)     | [ security ike proposal ]        | ike-aes256cbc-sha256-dh20       |
| IKE Policy - Tunnel 1                | [ security ike policy ]          | cf-wan-ike-pol-01               |
| IKE Policy - Tunnel 2                | [ security ike policy ]          | cf-wan-ike-pol-02               |
| IKE Gateway - Tunnel 1               | [ security ike gateway ]         | cf-wan-ike-gw-01                |
| IKE Gateway - Tunnel 2               | [ security ike gateway ]         | cf-wan-ike-gw-02                |
| IPsec Proposal (only one required)   | [ security ipsec proposal ]      | esp-aes256cbc-sha256-128        |
| IPsec Policy (only one required)     | [ security ipsec policy ]        | ipsec-aes256cbc-sha256-128-dh20 |
| IPsec Tunnel - Tunnel 1              | [ security ipsec vpn ]           | cf-wan-ipsec-vpn-01             |
| IPsec Tunnel - Tunnel 2              | [ security ipsec vpn ]           | cf-wan-ipsec-vpn-02             |

## Assumptions

This guide assumes the following apply:

- Already configured [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) and [static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/) in the Cloudflare dashboard
- Used the Cloudflare Dashboard to obtain the Local Identifier (FQDN/hostname) and generate a Pre-Shared Key for each of the IPsec tunnels
- Understand the importance of [MSS clamping](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/#mss-clamping) and adjusting it based on the traffic flows traversing the Cloudflare WAN IPsec Tunnels

## High-Level Steps

- Add Virtual Tunnel Interfaces
- Create a Security Zone (Recommended)
- Add VTIs to Security Zone
- Define IKE Policy and Proposals (Phase 1)
- Add IKE Gateways
- IPsec Policy and Proposal (Phase 2)
- IPsec Tunnel Configuration
- Define Security policy to permit traffic to/from Cloudflare WAN
- Define Policy-Based Forwarding rules to selectively route traffic across the IPsec tunnels

## HPE Juniper Networking SRX - Configuration

All examples are provided via the Junos Command-Line Interface (CLI). J-Web examples are not provided.

### Junos Modes

Junos OS operates with two main command-line interface (CLI) modes, Operational Mode and Configuration Mode, which serve distinct purposes in managing Juniper network devices.

#### Operational Mode (>)

[Operational mode](https://www.juniper.net/documentation/us/en/software/junos/cli/topics/topic-map/junos-cli-operational-overview.html) is the default state upon logging into a Junos device, used for monitoring, troubleshooting, and displaying device status.

- Prompt: `user@host>`
- Purpose: View real-time information, check interface status, view routing tables, test connectivity (ping/traceroute), and restart processes.
- Key Commands: show, monitor, ping, traceroute, request.
- Action: Changes made here do not affect the persistent device configuration.

#### Configuration Mode (#)

[Configuration mode](https://www.juniper.net/documentation/us/en/software/junos/cli/topics/topic-map/cli-configuration.html) is used to make changes to the device's configuration, such as defining interfaces, routing protocols, and system properties.

- Prompt: `user@host#`
- Purpose: Edit, add, or remove configuration statements.
- Key Commands: edit, set, delete, commit, rollback.
- Action: Changes are made to a "candidate configuration" and are not active until explicitly committed at which point they become part of the "running configuration".

Each section will indicate whether the commands are applicable to configuration mode or operational mode.

### Virtual Tunnel Interfaces

_Perform in Configuration Mode_

```txt
set interfaces st0 unit 1 family inet address 169.254.250.1/31
set interfaces st0 unit 2 family inet address 169.254.250.3/31
```

### Security Zone

_Perform in Configuration Mode_

Add `st0.1` and `st0.2` to the Security Zone `cloudflare` and permit `system-services ping`. This is required to ensure the Cloudflare WAN IPsec Tunnel Health Checks are able to verify reachability across the Virtual Tunnel Interfaces.

```txt
set security zones security-zone cloudflare interfaces st0.1 host-inbound-traffic system-services ping
set security zones security-zone cloudflare interfaces st0.2 host-inbound-traffic system-services ping
```

### IKE - Phase 1

_Perform in Configuration Mode_

Configure the following:

- IKE Proposal
- IKE Policies (one required per Cloudflare WAN IPsec Tunnel)
- IKE Gateways (one required per Cloudflare WAN IPsec Tunnel)

#### IKE Proposal

Define an IKE Proposal with the following settings:

| **Attribute**            | **Value**       |
| ------------------------ | ----------------|
| authentication-method    | pre-shared-keys |
| dh-group                 | group20         |
| authentication-algorithm | sha256          |
| encryption-algorithm     | aes-256-cbc     |
| lifetime-seconds         | 28800           |

```txt
set security ike proposal ike-aes256cbc-sha256-dh20 authentication-method pre-shared-keys
set security ike proposal ike-aes256cbc-sha256-dh20 dh-group group20
set security ike proposal ike-aes256cbc-sha256-dh20 authentication-algorithm sha-256
set security ike proposal ike-aes256cbc-sha256-dh20 encryption-algorithm aes-256-cbc
set security ike proposal ike-aes256cbc-sha256-dh20 lifetime-seconds 28800
```

#### IKE Policies

Configure one IKE policy per IPsec tunnel:

| **Attribute**             | **Value**                 |
| ------------------------- | ------------------------- |
| mode                      | main                      |
| proposals                 | ike-aes256cbc-sha256-dh20 |
| pre-shared-key ascii-text | _specify pre-shared-key_  |

```txt
set security ike policy cf-wan-ike-pol-01 mode main
set security ike policy cf-wan-ike-pol-01 proposals ike-aes256cbc-sha256-dh20
set security ike policy cf-wan-ike-pol-01 pre-shared-key ascii-text "Cloudflare-WAN-T1-PSK-1234!"

set security ike policy cf-wan-ike-pol-02 mode main
set security ike policy cf-wan-ike-pol-02 proposals ike-aes256cbc-sha256-dh20
set security ike policy cf-wan-ike-pol-02 pre-shared-key ascii-text "Cloudflare-WAN-T2-PSK-1234!"
```

#### IKE Gateways

Configure one IKE Gateway per IPsec tunnel:

```txt
set security ike gateway cf-wan-ike-gw-01 ike-policy cf-wan-ike-pol-01
set security ike gateway cf-wan-ike-gw-01 address 162.159.135.1
set security ike gateway cf-wan-ike-gw-01 local-identity hostname bf6c493d03<REDACTED>.ipsec.cloudflare.com
set security ike gateway cf-wan-ike-gw-01 external-interface ge-0/0/0.0
set security ike gateway cf-wan-ike-gw-01 local-address 203.0.113.100
set security ike gateway cf-wan-ike-gw-01 version v2-only

set security ike gateway cf-wan-ike-gw-02 ike-policy cf-wan-ike-pol-02
set security ike gateway cf-wan-ike-gw-02 address 172.64.135.1
set security ike gateway cf-wan-ike-gw-02 local-identity hostname 0287844e9d<REDACTED>.ipsec.cloudflare.com
set security ike gateway cf-wan-ike-gw-02 external-interface ge-0/0/0.0
set security ike gateway cf-wan-ike-gw-02 local-address 203.0.113.100
set security ike gateway cf-wan-ike-gw-02 version v2-only
```

### IPsec - Phase 2

_Perform in Configuration Mode_

Configure the following:

- IPsec Proposal
- IPsec Policy
- IPsec Tunnels (one required per Cloudflare WAN IPsec Tunnel)

#### IPsec Proposal

Define an IPsec Proposal with the following settings:

| **Attribute**            | **Value**        |
| ------------------------ | ---------------- |
| protocol                 | esp              |
| authentication-algorithm | hmac-sha-256-128 |
| encryption-algorithm     | aes-256-cbc      |
| lifetime-seconds         | 28800            |

```txt
set security ipsec proposal esp-aes256cbc-sha256-128 protocol esp
set security ipsec proposal esp-aes256cbc-sha256-128 authentication-algorithm hmac-sha-256-128
set security ipsec proposal esp-aes256cbc-sha256-128 encryption-algorithm aes-256-cbc
set security ipsec proposal esp-aes256cbc-sha256-128 lifetime-seconds 28800
```

#### IPsec Policy

```txt
set security ipsec policy ipsec-aes256cbc-sha256-128-dh20 perfect-forward-secrecy keys group20
set security ipsec policy ipsec-aes256cbc-sha256-128-dh20 proposals esp-aes256cbc-sha256-128
```

#### IPsec VPN Tunnels

Create two IPsec VPN tunnels - each corresponding to its respective IKE Gateway.

```txt
set security ipsec vpn cf-wan-ipsec-vpn-01 bind-interface st0.1
set security ipsec vpn cf-wan-ipsec-vpn-01 ike gateway cf-wan-ike-gw-01
set security ipsec vpn cf-wan-ipsec-vpn-01 ike no-anti-replay
set security ipsec vpn cf-wan-ipsec-vpn-01 ike ipsec-policy ipsec-aes256cbc-sha256-128-dh20
set security ipsec vpn cf-wan-ipsec-vpn-01 establish-tunnels immediately

set security ipsec vpn cf-wan-ipsec-vpn-02 bind-interface st0.2
set security ipsec vpn cf-wan-ipsec-vpn-02 ike gateway cf-wan-ike-gw-02
set security ipsec vpn cf-wan-ipsec-vpn-02 ike no-anti-replay
set security ipsec vpn cf-wan-ipsec-vpn-02 ike ipsec-policy ipsec-aes256cbc-sha256-128-dh20
set security ipsec vpn cf-wan-ipsec-vpn-02 establish-tunnels immediately
```

:::note
Enabling `establish-tunnels-immediately` ensures the SRX is always the tunnel initiator. This is crucial as Cloudflare will never initiate the IPsec tunnel. If you do not enable this option, you will need to generate some traffic that will traverse the IPsec tunnels at which point the SRX will initiate the tunnel in an on-demand fashion.
:::

### MSS Clamping

_Perform in Configuration Mode_

The SRX platform is unique in that it allows you to configure MSS Clamping that only applies to IPsec tunnels as opposed to per interface or globally.

This ensures the overhead associated with IKE/IPsec packet headers is factored in and will minimize opportunities for fragmentation as traffic ingresses and egresses the IPsec tunnels.

:::note
The recommended MSS value 1360 may need to be adjusted based on the nature of the traffic traversing the Cloudflare WAN IPsec Tunnels.
:::

```txt
set security flow tcp-mss ipsec-vpn mss 1360
```

See [How to change the MSS of TCP traffic passing through an IPsec VPN](https://supportportal.juniper.net/s/article/SRX-How-to-change-the-MSS-of-TCP-traffic-passing-through-an-IPsec-VPN) for more details.

### Security Policies

_Perform in Configuration Mode_

- Security policies are required to permit traffic between zones
- The Ethernet interface `ge-0/0/1.0` is in the `trust` security zone
- The tunnel interfaces `st0.1` and `st0.2` are in the `cloudflare` security zone

The following example allows all source & destination IPs, ports, and protocols/services between `cloudflare` and `trust` as well as between `trust` and `cloudflare`.

| **Attribute**            | **Value**     |
| ------------------------ | ------------- |
| source-address           | any           |
| destination-address      | any           |
| application              | any           |
| action                   | permit        |
| log                      | session-close |

```txt
set security policies from-zone cloudflare to-zone trust policy cloudflare-to-trust-permit match source-address any
set security policies from-zone cloudflare to-zone trust policy cloudflare-to-trust-permit match destination-address any
set security policies from-zone cloudflare to-zone trust policy cloudflare-to-trust-permit match application any
set security policies from-zone cloudflare to-zone trust policy cloudflare-to-trust-permit then permit
set security policies from-zone cloudflare to-zone trust policy cloudflare-to-trust-permit then log session-close

set security policies from-zone trust to-zone cloudflare policy trust-to-cloudflare-permit match source-address any
set security policies from-zone trust to-zone cloudflare policy trust-to-cloudflare-permit match destination-address any
set security policies from-zone trust to-zone cloudflare policy trust-to-cloudflare-permit match application any
set security policies from-zone trust to-zone cloudflare policy trust-to-cloudflare-permit then permit
set security policies from-zone trust to-zone cloudflare policy trust-to-cloudflare-permit then log session-close
```

### Filter-Based Forwarding - Policy-Based Routing

_Perform in Configuration Mode_

HPE Juniper Networking provides multiple methods for performing Policy-Based Routing. [Filter-Based Forwarding](https://www.juniper.net/documentation/us/en/software/junos/routing-policy/topics/concept/firewall-filter-option-filter-based-forwarding-overview.html) is the preferred method as it permits selectively routing traffic based on source, destination, protocol (and a wide variety of other matching criteria) through Cloudflare WAN or local Internet breakout with ease.

This example assumes traffic originating from 192.168.125.0/24 (ingress interface `ge-0/0/1.0` zone `trust`) to any destination will be routed via the Cloudflare WAN IPsec Tunnels.

Implementing Filter-Based Forwarding (FBF) requires four steps:

1. Create a Forwarding Routing Instance
    - Think of the Routing Instance as a "bucket" containing an alternate routing table
    - The Routing Instance contains the destination prefix(es) and next hop addresses (VTI on Cloudflare side of the tunnels)
2. Create a Firewall Filter
    - Think of a Firewall Filter as the "brain" that determines which packets to send to the "bucket"
    - The Firewall Filter acts as a "classifier" that identifies the traffic you want to divert to the Routing Instance
3. Configure the RIB Group and Bind Routes
    - Think of this as the "bridge" that copies interface routes to the "bucket"
    - The Routing Instance is not part of the default routing table.
    - This step instructs the SRX how to reach directly connected networks and resolve next-hops found in the main table (`inet.0`) and the Cloudflare WAN Routing Instance's routing table `CF_WAN_RI.inet.0`
4. Apply the Firewall Filter to the ingress traffic interface(s)
    - Think of this as the "trigger" that starts processing packets as they enter the ingress interface
    - As traffic ingresses the interface(s) to which it is applied, traffic is processed in a top-down fashion

#### Define a Routing Instance

The Routing Instance defines the destination for your steered traffic. Unlike a standard VRF, FBF typically uses an instance type of `forwarding`.

This example effectively sets the default gateway (0.0.0.0/0) for any traffic landing on this Routing Instance to get routed to the IP address of the VTIs on the Cloudflare side of the IPsec tunnels:

```txt
set routing-instances CF_WAN_RI instance-type forwarding
set routing-instances CF_WAN_RI routing-options static route 0.0.0.0/0 next-hop 169.254.250.0
set routing-instances CF_WAN_RI routing-options static route 0.0.0.0/0 next-hop 169.254.250.2
```

#### Create a Firewall Filter

Add a firewall filter called `CF_WAN_FBF_ALL` with two `terms` (rules):

The first term `CF_WAN_FWD_RI` ensures any traffic originating from the LAN subnet (192.168.125.0/24) to any destination address (0.0.0.0/0) is processed against the `CF_WAN_RI` routing instance.

The second term `EVERYTHING_ELSE` effectively instructs the SRX to continue processing any traffic not matching the term `CF_WAN_FWD_RI` via the default routing table (`inet.0`).

Note the addition of the action `count` in both statements. This option defines a counter you can view to determine how many packets are processed on each `term`.

```txt
set firewall family inet filter CF_WAN_FBF_ALL term CF_WAN_FWD_RI from source-address 192.168.125.0/24
set firewall family inet filter CF_WAN_FBF_ALL term CF_WAN_FWD_RI from destination-address 0.0.0.0/0
set firewall family inet filter CF_WAN_FBF_ALL term CF_WAN_FWD_RI then count CF_WAN_FWD_RI_count
set firewall family inet filter CF_WAN_FBF_ALL term CF_WAN_FWD_RI then routing-instance CF_WAN_RI
set firewall family inet filter CF_WAN_FBF_ALL term EVERYTHING_ELSE then count EVERYTHING_ELSE_count
set firewall family inet filter CF_WAN_FBF_ALL term EVERYTHING_ELSE then accept
```

#### Configure the RIB Group and Bind Interface Routes

Create a RIB Group and import both the default route table (`inet.0`) and the route table associated with the newly created Forwarding Routing Instance:

```txt
set routing-options rib-groups CF_WAN_RG import-rib inet.0
set routing-options rib-groups CF_WAN_RG import-rib CF_WAN_RI.inet.0
```

Bind the RIB Group to the Interface Routes:

```txt
set routing-options interface-routes rib-group inet CF_WAN_RG
```

#### Apply the Firewall Filter to the Ingress Interface

- Traffic originating on the LAN subnet will ingress interface `ge-0/0/1.0`
- Apply the Firewall Filter `CF_WAN_FBF_ALL` as an `input` filter

```txt
set interfaces ge-0/0/1 unit 0 family inet filter input CF_WAN_FBF_ALL
```

Commit changes, then test traffic from a host on the 192.168.125.0/24 subnet to ensure it is forwarded through the Cloudflare WAN IPsec Tunnels.

:::note
If you have Cloudflare One configured to perform [HTTPS traffic inspection](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), ensure that you [install the Root CA certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) prior to testing connectivity to any HTTPS-based sites, otherwise you will receive untrusted certificate warning messages.
:::

## Troubleshooting

### Common issues

- Always check IKE Phase 1 & IPsec Phase 2 negotiated successfully - look for "no proposal chosen" in logs
- Verify Pre-Shared-Key and/or Local-Identity values are accurate and assigned to the correct tunnel
- Use ping to determine reachability between the CPE and Cloudflare sides of the VTI
  - Tunnel 1: CPE VTI to Cloudflare VTI: `ping source 169.254.250.1 169.254.250.0`
  - Tunnel 2: CPE VTI to Cloudflare VTI: `ping source 169.254.250.3 169.254.250.2`

### View IKE Security Associations

_Perform in Operational Mode_

Use the CLI to verify IKE (Phase 1) and IPsec (Phase 2) security associations established.

```txt
admin@srx> show security ike security-associations
Index   State  Initiator cookie  Responder cookie  Mode           Remote Address
403838  UP     a2d16e54c9d83ad5  873b1da714f0ca8f  IKEv2          162.159.135.1
403839  UP     476288ac95d878e2  e72ef64e00b623e6  IKEv2          172.64.135.1
```

### View IPsec Security Associations

_Perform in Operational Mode_

```txt
admin@srx> show ipsec security associations
  Total active tunnels: 2
  ID    Algorithm       SPI      Life:sec/kb  Mon lsys Port  Gateway
  <131073 ESP:aes-cbc-256/sha256-96 9b429dd3 27739/unlim - root 500 162.159.135.1
  >131073 ESP:aes-cbc-256/sha256-96 28931d57 27739/unlim - root 500 162.159.135.1
  <131074 ESP:aes-cbc-256/sha256-96 eb2a275e 27739/unlim - root 500 172.64.135.1
  >131074 ESP:aes-cbc-256/sha256-96 4134d7a8 27739/unlim - root 500 172.64.135.1
```

### Enable Debug Logging (traceoptions) for IKE (Phase 1) and IPsec (Phase 2)

In the event you encounter issues with IPsec tunnel negotiation, you can enable `traceoptions` for IKE and/or IPsec.

:::note
Debug logging can create significant overhead on firewalls with high utilization. Ensure you monitor CPU and memory utilization closely and only enable debug logging when it is needed.
:::

#### Enable IKE Traceoptions

_Perform in Configuration Mode_

```txt
set security ike traceoptions file ike-debug.log
set security ike traceoptions file size 1m
set security ike traceoptions file files 3
set security ike traceoptions file world-readable
set security ike traceoptions flag all
```

#### View IKE Debug Log

_Perform in Operational Mode_

View the log with the following command:

```txt
admin@srx> show log ike-debug.log
```

Press `CTRL + C` to stop viewing the log.

#### Disable IKE Traceoptions

_Perform in Configuration Mode_

```txt
delete security ike traceoptions
commit
```

#### Enable IPsec Traceoptions

_Perform in Configuration Mode_

```txt
set security ipsec traceoptions file ipsec-debug.log
set security ipsec traceoptions file size 1m
set security ipsec traceoptions file files 3
set security ipsec traceoptions file world-readable
set security ipsec traceoptions flag all
```

#### View IPsec Debug Logging

_Perform in Operational Mode_

View the log with the following command:

```txt
admin@srx> show log ipsec-debug.log
```

Press `CTRL + C` to stop viewing the log.

#### Disable IPsec Debug Logging

_Perform in Configuration Mode_

```txt
delete security ipsec traceoptions
commit
```

### Disable/Enable IKE Gateways and/or IPsec VPN Tunnels

_Perform in Configuration Mode_

Junos provides the ability to administratively enable/disable IKE gateways and IPsec tunnels independently. This allows you to forcefully set up and tear down VPN tunnels which can be very useful during troubleshooting.

#### Deactivate IKE Gateway

```txt
deactivate security ike gateway cf-wan-ike-gw-01
```

#### Deactivate IPsec VPN

```txt
deactivate security ipsec vpn cf-wan-ipsec-vpn-01
```

Perform a `commit` to ensure the IKE Gateway and IPSec VPN objects are disabled.

#### Verify Inactive State

Note the presence of `inactive: security ike gateway cf-wan-ike-gw-01` at the top of the IKE gateway stanza:

```txt
admin@srx# show security ike gateway cf-wan-ike-gw-01
##
## inactive: security ike gateway cf-wan-ike-gw-01
##
ike-policy cf-wan-ike-pol-01;
address 162.159.135.1;
local-identity hostname bf6c493d03<REDACTED>.ipsec.cloudflare.com;
external-interface ge-0/0/0.0;
local-address 203.0.113.100;
version v2-only;
```

Note the presence of `inactive: security ipsec vpn cf-wan-ike-gw-01` at the top of the IPsec VPN stanza:

```txt
[edit]
admin@srx# show security ipsec vpn cf-wan-ipsec-vpn-01
##
## inactive: security ipsec vpn cf-wan-ipsec-vpn-01
##
bind-interface st0.1;
ike {
    gateway cf-wan-ike-gw-01;
    no-anti-replay;
    ipsec-policy ipsec-aes256cbc-sha256-128-dh20;
}
establish-tunnels immediately;
```

#### Activate IKE Gateway and IPsec VPN Objects

Reverse the process with the `activate` command:

```txt
activate security ike gateway cf-wan-ike-gw-01

activate security ipsec vpn cf-wan-ipsec-vpn-01

commit
```

### Restart IPsec Daemon

_Perform in Operational Mode_

The IKE and IPsec lifetimes are set to 28800 seconds (8 hours). You can force tunnel establishment by restarting the IPsec daemon (kmd). This will invalidate the IKE and IPsec security associations and forcefully reconnect the IPsec VPN tunnels.

This can be accomplished with the following command:

```txt
admin@srx> restart ipsec-key-management
```

### Ensure Reachability Across IPsec Tunnels

_Perform in Operational Mode_

Use ping to verify connectivity to the Cloudflare side of the Virtual Tunnel Interface

```txt
admin@srx> ping 169.254.250.0 source 169.254.250.1
admin@srx> ping 169.254.250.2 source 169.254.250.3
```

### Show Tunnel Event Statistics

_Perform in Operational Mode_

```txt
admin@srx> show security ipsec tunnel-events-statistics
```

Resulting output:

```txt
External interface's zone received. Information updated                     : 2
Bind-interface's zone received. Information updated                         : 2
Bind-interface's address received. Information updated                      : 2
IKE SA negotiation successfully completed                                   : 2
IPSec SA negotiation successfully completed                                 : 2
Tunnel is ready. Waiting for trigger event or peer to trigger negotiation   : 2
```

### Display Route Tables

_Perform in Operational Mode_

#### Default Route Table - inet.0

```txt
show route table inet.0

inet.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

169.254.247.0/31   *[Direct/0] 00:02:10
                    > via st0.1
169.254.247.1/32   *[Local/0] 1d 05:35:54
                      Local via st0.1
169.254.247.2/31   *[Direct/0] 00:02:09
                    > via st0.2
169.254.247.3/32   *[Local/0] 1d 05:35:54
                      Local via st0.2
169.254.250.0/31   *[Direct/0] 00:02:09
                    > via st0.1
169.254.250.1/32   *[Local/0] 00:02:09
                      Local via st0.1
169.254.250.2/31   *[Direct/0] 00:02:09
                    > via st0.2
169.254.250.3/32   *[Local/0] 00:02:09
                      Local via st0.2
192.168.125.0/24   *[Direct/0] 00:02:10
                    > via ge-0/0/1.0
192.168.125.1/32   *[Local/0] 00:02:10
                      Local via ge-0/0/1.0
203.0.113.100/32   *[Local/0] 00:02:10
                      Reject
```

#### Routing Instance Route Table (CF_WAN_RI.inet.0)

```txt
show route table CF_WAN_RI.inet.0

CF_WAN_RI.inet.0: 12 destinations, 12 routes (12 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *[Static/5] 00:01:04
                    > to 169.254.250.0 via st0.1
                      to 169.254.250.2 via st0.2
169.254.247.0/31   *[Direct/0] 00:02:58
                    > via st0.1
169.254.247.1/32   *[Local/0] 00:02:58
                      Local via st0.1
169.254.247.2/31   *[Direct/0] 00:02:57
                    > via st0.2
169.254.247.3/32   *[Local/0] 00:02:57
                      Local via st0.2
169.254.250.0/31   *[Direct/0] 00:02:57
                    > via st0.1
169.254.250.1/32   *[Local/0] 00:02:57
                      Local via st0.1
169.254.250.2/31   *[Direct/0] 00:02:57
                    > via st0.2
169.254.250.3/32   *[Local/0] 00:02:57
                      Local via st0.2
192.168.125.0/24   *[Direct/0] 00:02:58
                    > via ge-0/0/1.0
192.168.125.1/32   *[Local/0] 00:02:58
                      Local via ge-0/0/1.0
203.0.113.100/32   *[Local/0] 00:02:58
                      Reject
```

### Display Firewall Filter Counters

```txt
admin@srx> show firewall counter filter CF_WAN_FBF_ALL CF_WAN_FWD_RI_count

Filter: CF_WAN_FBF_ALL
Counters:
Name                                Bytes              Packets
CF_WAN_FWD_RI_count                 14855935          189746
```

```txt
admin@srx> show firewall counter filter CF_WAN_FBF_ALL EVERYTHING_ELSE_count

Filter: CF_WAN_FBF_ALL
Counters:
Name                                Bytes              Packets
EVERYTHING_ELSE_count               4371377            18732
```

## Resources - Juniper Product Documentation

Refer to the CLI Reference Guide for further details on each command referenced within this document:

- [CLI Reference Guide](https://www.juniper.net/documentation/us/en/software/junos/cli-reference/)

- [Route-Based IPsec VPNs](https://www.juniper.net/documentation/us/en/software/junos/vpn-ipsec/topics/topic-map/security-route-based-ipsec-vpns.html)

- [Route-Based VPN with IKEv2](https://www.juniper.net/documentation/us/en/software/junos/vpn-ipsec/topics/topic-map/security-vpns-for-ikev2.html)

- [Route-Based and Policy-Based VPNs with NAT-T](https://www.juniper.net/documentation/us/en/software/junos/vpn-ipsec/topics/topic-map/security-route-based-and-policy-based-vpns-with-nat-t.html)

- [Configuring Filter-Based Forwarding](https://www.juniper.net/documentation/us/en/software/junos/routing-policy/topics/example/filter-based-forwarding-example.html)

## Resources - Juniper Knowledge Base

Valid support credentials may be required to view some/all of the following documents:

- [[SRX] How do I tell if a VPN Tunnel SA (Security Association) is active - KB10090](https://supportportal.juniper.net/s/article/SRX-How-do-I-tell-if-a-VPN-Tunnel-SA-Security-Association-is-active)

- [[SRX] How to configure syslog to display VPN status messages - KB10097](https://supportportal.juniper.net/s/article/SRX-How-to-configure-syslog-to-display-VPN-status-messages)

- [[SRX] How to troubleshoot IKE Phase 2 VPN connection issues - KB10099](https://supportportal.juniper.net/s/article/SRX-How-to-troubleshoot-IKE-Phase-2-VPN-connection-issues)

- [[SRX] How to enable VPN (IKE/IPsec) traceoptions for specific SAs (Security Associations) - KB19943](https://supportportal.juniper.net/s/article/SRX-How-to-enable-VPN-IKE-IPsec-traceoptions-for-specific-SAs-Security-Associations)

# Oracle Cloud

This tutorial shows how to configure IPsec (Internet Protocol Security) between Cloudflare WAN (formerly Magic WAN) and an Oracle Cloud Site-to-site VPN.

## Prerequisites

You need a pre-shared key to establish the IPsec tunnel. You can use the following code to create a random key:

```js
		const a = new Uint8Array(48);
		crypto.getRandomValues(a);
		let base64String = btoa(String.fromCharCode.apply(null, a));

		base64String = base64String.replace(/\+/g, '')
								   .replace(/\//g, '')
								   .replace(/=/g, '');

		console.log(base64String.substring(0, 32));
```

:::caution
The code above is an example of how you might generate a random key. However, make sure you generate a key that is strong enough to comply with your security needs.
:::

You can try this code in the [Workers playground](https://workers.cloudflare.com/playground#LYVwNgLglgDghgJwgegGYHsHALQBM4RwDcABAEbogB2+CAngLzbPYDqApmQNJQQBimYACFKNRHSoBzAB4ArAEoBBANYR5AEVYAJAOJCAagA0AXCxYduvAVhHVaEmQpVrNug4YCwAKADC6KhDsAdjqUADOMOhhvFD+xiQYWHgExCRUcMDsDABEUDTs0gB0smHZpKhQYEEZWbn5RSXZ3n4BQRDYACp0MOzxcDAwYFAAxgSxVMiycABucGHDCLAQANTA6Ljg7N7eBZFIJLjsqHDgECQA3l4AkHMSwwnsEMMAFgAUAJQXXtdXw-5hZzgJAYaXYAHcSABVPIQAAcigQCDgdFeABZYe8iD8Ft0IOhCpJHvI4DR0MB9HAwCB2GFXnBMT8qmcyHN2AA2VEAZQgiykwPIeLgr25vMkhVQCDJPmeiD8h0K-UGKKo4DAABoSPSGT8WWF2VyeXlJPzdfqRUbCgh2IM4MN2K9kAAdZbISQagDk7vePyuvr9JADlutYFt9qdyFdHq9Pr9voDJCDNrtDoYkZInu1vr+VABCTylPNfJBpo5hbFYRAZABoteAAYNQBmABMmauVogIAQVFBEPkNMiOftFXSYDLGsufue7DghwQYXiE792WzgWCEG67Gy8WygWkKGeEGAYGyap9AF9T76zwyrhevGesd4zMwLDx+IJbGJ6FI5EpVBptD0Ixmn8Vd2lCCIohiOIEkEZJCFIdJMhyTJCHwQgyjzKokNqMgwHQMgml8UC2k6Dc+gGIZRmgfxJjCfxti8c5lzJeBoDISpeDoAB9dDN2MbIm1rJtUWwWsGzEgB2E8WOANioA4oZ1241AQ0kUpjAAbWyKh1nYEpuL+OSCGyABdNVsmAOA8m4tYNiqLc6kOBpSjPJ9n1fKwP1Eewfycf9XCAwxmG8IA).

## Oracle Cloud

### 1. Create Oracle Cloud customer-premises equipment

1. Go to **Networking** > **Customer connectivity**, and select **Customer-premises equipment**.
2. Select **Create CPE**.
3. Select the following settings (you can leave settings not mentioned here with their default values):
   - **Name**: Enter a name.
   - **IP Address**: Enter your Cloudflare anycast IP address.
   - **CPE vendor information**: Select **Other**.
4. Select **Create CPE**.

### 2. Create Oracle Cloud dynamic routing gateways

1. Go to **Networking** > **Customer connectivity**, and select **Dynamic routing gateways**.
2. Select **Create Dynamic routing gateways**.
3. Select the following settings (you can leave settings not mentioned here with their default values):
   - **Name**: Enter a name.
4. Select **Create Dynamic routing gateways**.

### 3. Create an IPsec connection

1. Go to **Networking** > **Customer connectivity**, and select **Site-to-Site VPN**.
2. Select **Create IPsec connection**.
3. Select the following settings (you can leave settings not mentioned here with their default values):
   - **Name**: Enter a name.
   - **Customer-premises equipment (CPE)**: Select the CPE you created in step 1.
   - **Dynamic routing gateways (DRG)**: Select the DRG you created in step 2.
   - **Routes to your on-premises network**: Enter a CIDR (Classless Inter-Domain Routing) range you want to route to Cloudflare WAN.
   - **Tunnel 1**
      - **Name**: Enter a name.
      - Select **Provide custom shared secret**.
      - Enter the **pre-shared key** you created in the Prerequisites section.
      - **IKE (Internet Key Exchange) version**: **IKEv2**
      - **Routing type**: **Static routing**
      - **IPv4 inside tunnel interface - CPE**:  Enter the internal tunnel IP on the Cloudflare side of the IPsec tunnel. In this example, it is `10.200.1.0/31`.
      - **IPv4 inside tunnel interface - Oracle**: Enter the internal tunnel IP on the Oracle side of the IPsec tunnel. In this example, it is `10.200.1.1/31`. This matches with the Cloudflare side for this tunnel.
         1. Select **Show advanced options**
         2. Select **Phase one (ISAKMP) configuration**
            - Select **Set custom configurations**
            - **Custom encryption algorithm**: **AES_256_CBC**
            - **Custom authentication algorithm**: **SHA2_256**
            - **Custom Diffie-Hellman group**: **GROUP20**
            - **IKE session key lifetime in seconds**: **86400**
         3. Select **Phase two (IPsec) configuration**
            - Select **Set custom configurations**
            - **Custom encryption algorithm**: **AES_256_CBC**
            - **HMAC (Hash-based Message Authentication Code)_SHA2_256_128**: **HMAC_SHA2_256_128**
            - **IPsec session key lifetime in seconds**: **28800**
            - **Perfect forward secrecy Diffie-Hellman group**: **GROUP20**
    - **Tunnel 2**
      - Repeat these steps for Tunnel 2. Select the right IP for **IPv4 inside tunnel interface - CPE (Customer-Premises Equipment)**: `10.200.2.0/31` and **IPv4 inside tunnel interface - Oracle**: `10.200.2.1/31`
4. Select **Create IPsec connection**

## Cloudflare WAN

After configuring the Oracle Site-to-site VPN connection and the tunnels, go to the Cloudflare dashboard and create the corresponding IPsec tunnel and static routes on the Cloudflare WAN side.

### IPsec tunnels

1. Refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) to learn how to add an IPsec tunnel. When creating your IPsec tunnel, make sure you define the following settings:
   - **Tunnel name**: Enter a name.
   - **Interface address**: Enter the internal tunnel IP on the Cloudflare side of the IPsec tunnel. In this example, it is `10.200.1.0/31`.
   - **Customer endpoint**: The Oracle VPN public IP address.
   - **Cloudflare endpoint**: Enter one of the Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
   - **Health check type**: **Request**
   - **Health check direction**: **Unidirectional**
   - **Health check target**: **Default**
   - **Pre-shared key**: Choose **Use my own pre-shared key**, and enter the pre-shared key you created in the Prerequisites section.
   - **Replay protection**: **Enabled**.
2. Select **Add tunnels**.
3. Repeat these steps for Tunnel 2. Choose the same Cloudflare anycast IP address and select the right IP for **Interface address**: `10.200.2.0/31`

### Static routes

The static route in Cloudflare WAN should point to the appropriate virtual machine (VM) subnet you created inside your Oracle Virtual Cloud Network (VCN). For example, if your VM has a subnet of `192.168.192.0/26`, you should use it as the prefix for your static route.

To create a static route:

1. Refer to [Create a static route](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) to learn how to create one.
2. In **Prefix**, enter the subnet for your VM. For example, `192.xx.xx.xx/24`.
3. For the **Tunnel/Next hop**, choose the IPsec tunnel you created in the previous step.
4. Repeat these steps for the second IPsec tunnel you created.

# Palo Alto Networks NGFW

This guide provides step-by-step instructions for configuring Palo Alto Networks Next-Generation Firewall (NGFW) to establish IPsec VPN tunnels to Cloudflare WAN. The configuration has been validated by Cloudflare with the documented firmware release and is intended for network engineers who are familiar with Palo Alto Networks NGFW Firewalls administration and have an active Cloudflare WAN subscription.

## Test Environment

| **Field**                          | **Value**                                  |
| ---------------------------------- | ------------------------------------------ |
| Vendor                             | Palo Alto Networks                         |
| Model                              | PA-440                                     |
| Release                            | PAN-OS 11.2.8                              |
| Date Tested                        | March 2026                                 |

## IKE/IPsec Crypto & Relevant Settings

| **Field**                          | **Value**                                  |
| ---------------------------------- | ------------------------------------------ |
| Traffic Selection Criteria         | Route-Based VPN                            |
| Routing                            | Static                                     |
| Redundant Tunnels                  | Yes                                        |
| Tunnel Load Balancing              | Active/Active                              |
| IKE Version                        | IKEv2                                      |
| Authentication                     | Pre-Shared Key                             |
| Anti-Replay Protection             | Disabled                                   |
| NAT Traversal (NAT-T)              | Not Tested                                 |
| NAT-T Port                         | Not Applicable                             |
| Phase 1 - DH-Group                 | Group 20                                   |
| Phase 1 - Encryption               | AES-256-CBC                                |
| Phase 1 - Authentication/Integrity | SHA-256                                    |
| Phase 2 - DH-Group                 | Group 20                                   |
| Phase 2 - Transport                | ESP                                        |
| Phase 2 - Encryption               | AES-256-CBC                                |

## Cloudflare WAN and Palo Alto Networks NGFW - Configuration Settings

- While following these steps, ensure you update all object names and IP addresses to match your environment.
- Aligning these elements with your actual naming conventions and network scheme ensures the configuration works correctly in your production setup.
- Use Find and Replace to parse the examples below, update the names and addresses accordingly, and maintain consistency.

### Cloudflare WAN - Tunnel 01 of 02

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| Name (required)                      | CF_WAN_TUN_01                                  |
| Description                          | ---                                            |
| IPv4 Interface Address (required)    | 169.254.250.0/31                               |
| IPv6 Interface Address               | ---                                            |
| Customer Endpoint                    | 203.0.113.100                                  |
| Cloudflare Endpoint                  | 162.159.135.1                                  |
| Tunnel health checks                 | True                                           |
| Rate                                 | Medium                                         |
| **Type**                             | **Request**                                    |
| **Direction**                        | **Bidirectional**                              |
| Target                               | Default                                        |
| ---                                  | ---                                            |
| Turn on replay protection            | False                                          |
| **Automatic return routing**         | **True**                                       |

- IKE Identity and Pre-shared Key (obtained after tunnel creation):

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| FQDN ID                              | `bf6c493d03<REDACTED>.ipsec.cloudflare.com`    |
| Pre-shared key                       | Cloudflare-WAN-T1-PSK-1234!                    |

### Cloudflare WAN - Tunnel 02 of 02

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| Name (required)                      | CF_WAN_TUN_02                                  |
| Description                          | ---                                            |
| IPv4 Interface Address (required)    | 169.254.250.2/31                               |
| IPv6 Interface Address               | ---                                            |
| Customer Endpoint                    | 203.0.113.100                                  |
| Cloudflare Endpoint                  | 172.64.135.1                                   |
| Tunnel health checks                 | True                                           |
| Rate                                 | Medium                                         |
| **Type**                             | **Request**                                    |
| **Direction**                        | **Bidirectional**                              |
| Target                               | Default                                        |
| ---                                  | ---                                            |
| Turn on replay protection            | False                                          |
| **Automatic return routing**         | **True**                                       |

- IKE Identity and Pre-shared Key (obtained after tunnel creation):

| **Attribute**                        | **Value/Address**                              |
| ------------------------------------ | ---------------------------------------------- |
| FQDN ID                              | `0287844e9d<REDACTED>.ipsec.cloudflare.com`    |
| Pre-shared key                       | Cloudflare-WAN-T2-PSK-1234!                    |

## Customer Premise Equipment - Palo Alto Networks

| **WAN Interface**                  | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ---------------------------------- | --------------------- | --------------------- |
| WAN Interface                      | ethernet1/1           | ethernet1/1           |
| IP Address                         | 203.0.113.100/24      | 203.0.113.100/24      |
| Security Zone                      | untrust               | untrust               |

| **Virtual Tunnel Interface (VTI)** | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ---------------------------------- | --------------------- | --------------------- |
| Tunnel interface                   | tunnel.1              | tunnel.2              |
| IP Address                         | 169.254.250.1/31      | 169.254.250.3/31      |
| Security Zone                      | cloudflare            | cloudflare            |

| **LAN Interface**                  | **Tunnel 01 of 02**   | **Tunnel 02 of 02**   |
| ---------------------------------- | --------------------- | --------------------- |
| LAN Interface                      | ethernet1/2           | ethernet1/2           |
| IP Address                         | 192.168.125.1/24      | 192.168.125.1/24      |
| Security Zone                      | trust                 | trust                 |

### Palo Alto Networks NGFW Object Names

| **Role**                           | **Label/Name**            | **Address**                 |
| ---------------------------------- | ------------------------- | --------------------------- |
| CPE Security Zone - Trust          | Zone                      | trust                       |
| CPE Security Zone - Untrust        | Zone                      | untrust                     |
| CPE Security Zone - Cloudflare WAN | Zone                      | cloudflare                  |
| CPE IKE Crypto Profile Name        | IKE Crypto Profile        | ike-aes256cbc-sha256-dh20   |
| CPE IPsec Crypto Profile Name      | IPsec Crypto Profile      | ipsec-aes256cbc-sha256-dh20 |

## Assumptions

This guide assumes the following apply:

- Already configured [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) and [static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/) in the Cloudflare dashboard
- Used the Cloudflare Dashboard to obtain the Local Identifier (FQDN/hostname) and generate a Pre-Shared Key for each of the IPsec tunnels
- Understand the importance of [MSS clamping](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/#mss-clamping) and adjusting it based on the traffic flows traversing the Cloudflare WAN IPsec Tunnels
- Highly Available/Fault Tolerant Palo Alto Networks NGFW configurations, while possible, are out of scope.

## High-Level Steps

- Create Address Objects for:
  - Virtual Tunnel Interfaces (2x) - Local (/31 netmask) and Remote (/32 netmask)
  - Cloudflare Anycast IPs (2x)
  - Local Subnet(s)
  - Remote Cloudflare WAN Subnet(s)
- Create Interface Management Profile
- Create a Security Zone (Recommended)
- Define Tunnel interfaces
- Define IKE and IPsec Crypto Profiles
- Add two IKE Gateways - one for each of the two Cloudflare IPsec Tunnels
- Add two IPsec Tunnels - one for each of the two Cloudflare IPsec Tunnels
- Define Security policy to permit traffic to/from Cloudflare WAN
- Define Policy-Based Forwarding rules to selectively route traffic across the IPsec tunnels

## Palo Alto Networks NGFW - Configuration

There are examples for both the Command-Line Interface (CLI) and Web UI wherever possible.

### Objects & Addressing

Define Address Objects to represent the attribute/value pairs throughout the remainder of the configuration.

#### CLI

```txt
set address cf_wan_anycast_01 ip-netmask 162.159.135.1
set address cf_wan_anycast_02 ip-netmask 172.64.135.1
set address cf-wan-ipsec-vti-01-local ip-netmask 169.254.250.1/31
set address cf-wan-ipsec-vti-02-local ip-netmask 169.254.250.3/31
set address cf-wan-ipsec-vti-01-remote ip-netmask 169.254.250.0/32
set address cf-wan-ipsec-vti-02-remote ip-netmask 169.254.250.2/32
set address lan-net-192-168-125-0--24 ip-netmask 192.168.125.0/24
set address internet_203-0-113-100--24 ip-netmask 203.0.113.100/24
```

#### Web UI

1. Go to **Objects** > **Addresses**.
2. Select **Add**.
3. Create objects of type `IP Netmask` for the following networks:
   - `cf_wan_anycast_01` - specify 162.159.135.1 (or 162.159.135.1/32)
   - `cf_wan_anycast_02` - specify 172.64.135.1 (or 172.64.135.1/32)
   - `cf-wan-ipsec-vti-01-local` - specify 169.254.250.1/31
   - `cf-wan-ipsec-vti-02-local` - specify 169.254.250.3/31
   - `cf-wan-ipsec-vti-01-remote` - specify 169.254.250.0 (or 169.254.250.0/32)
   - `cf-wan-ipsec-vti-02-remote` - specify 169.254.250.2 (or 169.254.250.2/32)

### Interface Management Profile

Allow the applicable network interfaces to respond to pings (ICMP Echo Request). This is required to ensure the Cloudflare WAN Tunnel Health Checks are able to verify reachability across the Virtual Tunnel Interfaces.

#### CLI

```txt
set network profiles interface-management-profile allow_ping ping yes
```

#### Web UI

1. Go to **Network** > **Network Profiles** > **Interface Mgmt**.
2. Select **Add**.
3. Name: `allow_ping`
4. Select `Ping` under `Network Services`.
5. Select **OK**.

### Virtual Tunnel Interfaces (VTIs)

Add two tunnel interfaces - one for each of the two Cloudflare IPsec tunnels.

Note: The workflows for the CLI and Web UI can vary.

#### CLI - Add Tunnel Interfaces

Add two tunnel interfaces taking advantage of the Address objects and Interface Management Profile configured earlier.

:::note
If you want to assign the tunnel interfaces to a Security Zone in this step, the Security Zone must already exist. This example assumes the Security Zone is created in the next step.
:::

```txt
set network interface tunnel units tunnel.1 ip cf-wan-ipsec-vti-01-local
set network interface tunnel units tunnel.1 interface-management-profile allow_ping

set network interface tunnel units tunnel.2 ip cf-wan-ipsec-vti-02-local
set network interface tunnel units tunnel.2 interface-management-profile allow_ping
```

#### CLI - Assign Tunnel Interfaces to the Virtual Router

Assign both `tunnel` interfaces to the default Virtual Router:

```txt
set network virtual-router default interface tunnel.1
set network virtual-router default interface tunnel.2
```

#### CLI - Assign Tunnel Interfaces to Security Zone

Create the `cloudflare` security zone if it does not already exist and bind `tunnel.1` and `tunnel.2` interfaces.

```txt
set zone cloudflare network layer3  [ tunnel.1 tunnel.2 ]
```

#### Web UI - Add Tunnel Interfaces

1. Go to **Network** > **Interfaces** > **Tunnel**.
2. Select **Add**.
3. Enter 1 in the field to the right of "Interface Name".
4. Config Tab > Virtual Router: `default`.
5. Config Tab > Security Zone: `cloudflare` (or assign to `trust` based on your security policy).
6. IPv4 Tab > Select `cf-wan-ipsec-vti-01-local` from the drop-down.
7. Advanced tab > Management Profile: `allow_ping`.
8. Select **OK**.

Repeat steps for tunnel 2

1. Go to **Network** > **Interfaces** > **Tunnel**.
2. Select **Add**.
3. Enter 2 in the field to the right of "Interface Name".
4. Config Tab > Virtual Router: `default`.
5. Config Tab > Security Zone: `cloudflare` (or assign to `trust` based on your security policy).
6. IPv4 Tab > Select `cf-wan-ipsec-vti-02-local` from the drop-down.
7. Advanced tab > Management Profile: `allow_ping`.
8. Select **OK**.

### IPsec Tunnel Configuration

#### Phase 1 - IKE

##### Define Cryptographic Settings

Define an IKE Crypto Profile with the following settings:

| **Attribute**   | **Value**   |
| --------------- | ----------- |
| hash            | sha256      |
| dh-group        | group20     |
| encryption      | aes-256-cbc |
| lifetime hours  | 8           |

###### CLI

```txt
set network ike crypto-profiles ike-crypto-profiles ike-aes256cbc-sha256-dh20 hash sha256
set network ike crypto-profiles ike-crypto-profiles ike-aes256cbc-sha256-dh20 dh-group group20
set network ike crypto-profiles ike-crypto-profiles ike-aes256cbc-sha256-dh20 encryption aes-256-cbc
set network ike crypto-profiles ike-crypto-profiles ike-aes256cbc-sha256-dh20 lifetime hours 8
```

###### Web UI

1. Go to **Network** > **Network Profiles** > **IKE Crypto**.
2. Select **Add**.
3. Name: `ike-aes256cbc-sha256-dh20`
4. DH Group: `group20`
5. Authentication: `sha256`
6. Encryption: `aes-256-cbc`
7. Timers - Key Lifetime: 8 hours

##### Define IKE Gateway Objects

Each tunnel will have its own Pre-Shared Key and Local ID (FQDN/hostname) - ensure you obtain/update the values from the Cloudflare Dashboard.

###### CLI

```txt
set network ike gateway cf-wan-ike-gw-01 authentication pre-shared-key key "Cloudflare-WAN-T1-PSK-1234!"
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 pq-ppk enabled no
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 pq-ppk negotiation-mode preferred
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 pq-kem enable no
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 pq-kem block-vulnerable-cipher yes
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 ikev2-fragment enable no
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 dpd enable yes
set network ike gateway cf-wan-ike-gw-01 protocol ikev2 ike-crypto-profile ike-aes256cbc-sha256-dh20
set network ike gateway cf-wan-ike-gw-01 protocol ikev1 dpd enable yes
set network ike gateway cf-wan-ike-gw-01 protocol version ikev2
set network ike gateway cf-wan-ike-gw-01 local-address interface ethernet1/1 ip internet_203-0-113-100--24
set network ike gateway cf-wan-ike-gw-01 protocol-common nat-traversal enable no
set network ike gateway cf-wan-ike-gw-01 protocol-common fragmentation enable no
set network ike gateway cf-wan-ike-gw-01 peer-address ip cf_wan_anycast_01
set network ike gateway cf-wan-ike-gw-01 local-id type fqdn id "bf6c493d03<REDACTED>.ipsec.cloudflare.com"

set network ike gateway cf-wan-ike-gw-02 authentication pre-shared-key key "Cloudflare-WAN-T2-PSK-1234!"
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 pq-ppk enabled no
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 pq-ppk negotiation-mode preferred
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 pq-kem enable no
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 pq-kem block-vulnerable-cipher yes
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 ikev2-fragment enable no
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 dpd enable yes
set network ike gateway cf-wan-ike-gw-02 protocol ikev2 ike-crypto-profile ike-aes256cbc-sha256-dh20
set network ike gateway cf-wan-ike-gw-02 protocol ikev1 dpd enable yes
set network ike gateway cf-wan-ike-gw-02 protocol version ikev2
set network ike gateway cf-wan-ike-gw-02 local-address interface ethernet1/1 ip internet_203-0-113-100--24
set network ike gateway cf-wan-ike-gw-02 protocol-common nat-traversal enable no
set network ike gateway cf-wan-ike-gw-02 protocol-common fragmentation enable no
set network ike gateway cf-wan-ike-gw-02 peer-address ip cf_wan_anycast_02
set network ike gateway cf-wan-ike-gw-02 local-id type fqdn id "0287844e9d<REDACTED>.ipsec.cloudflare.com"
```

##### Web UI

1. Go to **Network** > **Network Profiles** > **IKE Gateways**.
2. Select **Add**.
3. Name: `cf-wan-ike-gw-01`
4. Version: `IKEv2 only mode`
5. Address Type: `IPv4`
6. Interface: `ethernet1/1`
7. Local IP Address: `internet_203-0-113-100--24`
8. Peer IP Address Type: `IP`
9. Authentication: `Pre-Shared Key`
10. Enter Pre-shared key and confirm value (obtain from the Cloudflare Dashboard).
11. Local Identification: `FQDN (hostname)` (obtain FQDN value for Tunnel 1 from the Cloudflare Dashboard).
12. Advanced Options tab > General > IKE Crypto Profile: `ike-aes256cbc-sha256-dh20`.
13. Select **OK**.

Repeat steps for tunnel 2

1. Go to **Network** > **Network Profiles** > **IKE Gateways**.
2. Select **Add**.
3. Name: `cf-wan-ike-gw-02`
4. Version: `IKEv2 only mode`
5. Address Type: `IPv4`
6. Interface: `ethernet1/1`
7. Local IP Address: `internet_203-0-113-100--24`
8. Peer IP Address Type: `IP`
9. Authentication: `Pre-Shared Key`
10. Enter Pre-shared key and confirm value (obtain from the Cloudflare Dashboard).
11. Local Identification: `FQDN (hostname)` (obtain FQDN value for Tunnel 2 from the Cloudflare Dashboard).
12. Advanced Options tab > General > IKE Crypto Profile: `ike-aes256cbc-sha256-dh20`.
13. Select **OK**.

#### IPsec (Phase 2)

##### Define Cryptographic Settings

Define an IPsec Crypto Profile with the following settings:

| **Attribute**      | **Value**   |
| ------------------ | ----------- |
| dh-group           | group20     |
| esp encryption     | aes-256-cbc |
| esp authentication | sha256      |
| lifetime hours     | 8           |

###### CLI

```txt
set network ike crypto-profiles ipsec-crypto-profiles ipsec-aes256cbc-sha256-dh20 esp authentication sha256
set network ike crypto-profiles ipsec-crypto-profiles ipsec-aes256cbc-sha256-dh20 esp encryption aes-256-cbc
set network ike crypto-profiles ipsec-crypto-profiles ipsec-aes256cbc-sha256-dh20 lifetime hours 8
set network ike crypto-profiles ipsec-crypto-profiles ipsec-aes256cbc-sha256-dh20 dh-group group20
```

###### Web UI

1. Go to **Network** > **Network Profiles** > **IPsec Crypto**.
2. Select **Add**.
3. Name: `ipsec-aes256cbc-sha256-dh20`
4. IPsec Protocol: `ESP`
5. Encryption: `aes-256-cbc`
6. Authentication: `sha256`
7. DH Group: `group20`
8. Lifetime (Hours): `8`

##### Define IPsec tunnel objects

###### CLI - Define IPsec tunnels

- Tunnel 1

```txt
set network tunnel ipsec cf-wan-ipsec-tun-01 auto-key ike-gateway cf-wan-ike-gw-01
set network tunnel ipsec cf-wan-ipsec-tun-01 auto-key ipsec-crypto-profile ipsec-aes256cbc-sha256-dh20
set network tunnel ipsec cf-wan-ipsec-tun-01 tunnel-monitor enable no
set network tunnel ipsec cf-wan-ipsec-tun-01 tunnel-interface tunnel.1
set network tunnel ipsec cf-wan-ipsec-tun-01 anti-replay no

set network tunnel ipsec cf-wan-ipsec-tun-02 auto-key ike-gateway cf-wan-ike-gw-02
set network tunnel ipsec cf-wan-ipsec-tun-02 auto-key ipsec-crypto-profile ipsec-aes256cbc-sha256-dh20
set network tunnel ipsec cf-wan-ipsec-tun-02 tunnel-monitor enable no
set network tunnel ipsec cf-wan-ipsec-tun-02 tunnel-interface tunnel.2
set network tunnel ipsec cf-wan-ipsec-tun-02 anti-replay no
```

###### Web UI - Define IPsec Tunnels

1. Go to **Network** > **IPsec Tunnels**.
2. Select **Add**.
3. Name: `cf-wan-ipsec-tun-01`
4. Tunnel interface: `tunnel.1`
5. Type: `Auto Key`
6. Address Type: `IPv4`
7. IKE Gateway: `cf-wan-ike-gw-01`
8. IPsec Crypto Profile: `ipsec-aes256cbc-sha256-dh20`
9. Show Advanced Options - check the box.
10. Uncheck `Enable Replay Protection`.
11. IPsec Mode: `Tunnel`

Repeat steps for tunnel 2

1. Go to **Network** > **IPsec Tunnels**.
2. Select **Add**.
3. Name: `cf-wan-ipsec-tun-02`
4. Tunnel interface: `tunnel.2`
5. Type: `Auto Key`
6. Address Type: `IPv4`
7. IKE Gateway: `cf-wan-ike-gw-02`
8. IPsec Crypto Profile: `ipsec-aes256cbc-sha256-dh20`
9. Show Advanced Options - check the box.
10. Uncheck `Enable Replay Protection`.
11. IPsec Mode: `Tunnel`

### Commit Changes

This is a good place to stop and perform a `Commit` to apply the configuration settings. You should be able to validate that tunnel connectivity is established.

### IPSec Tunnel Verification

#### Web UI - View IPsec Tunnel Status

1. Go to **Network** > **IPsec Tunnels**.

View the status of the red/green indicators - select **Tunnel Info** and **IKE Info** to obtain real-time status indicators.

#### Web UI - View IPsec Log Details

1. Go to **Monitor** > **Logs** > **System**.
2. Add the following to the filter/search dialog across the top: `( subtype eq vpn )`

This will provide valuable information as to IKE/IPsec Phase 1 and Phase 2 status and error messages.

### Security Policy

Palo Alto Networks NGFW automatically permits traffic originating from and destined to the same zone (intra-zone traffic). If you opted to add `tunnel.1` and `tunnel.2` into a separate Security Zone, you will require explicit firewall rules to allow traffic to flow from `trust` to `cloudflare` as well as from `cloudflare` to `trust`.

### CLI - Add Security Policy from `trust` to `cloudflare`

:::note
Firewall rules added from the CLI will always be at the very bottom of the rulebase.
:::

Use the `move rulebase security rules` `<RULE_NAME>` `[after|before|top|bottom]` `<RULE_NAME - Desired position>`

```txt
set rulebase security rules trust-to-cloudflare to cloudflare
set rulebase security rules trust-to-cloudflare from trust
set rulebase security rules trust-to-cloudflare source any
set rulebase security rules trust-to-cloudflare destination any
set rulebase security rules trust-to-cloudflare application any
set rulebase security rules trust-to-cloudflare service application-default
set rulebase security rules trust-to-cloudflare action allow
set rulebase security rules trust-to-cloudflare log-start no
set rulebase security rules trust-to-cloudflare log-end yes
set rulebase security rules trust-to-cloudflare rule-type universal
```

### Web UI - Add Security Policy from `trust` to `cloudflare`

1. Go to **Policies** > **Security**.
2. Select **Add**.
3. General > Name: `trust-to-cloudflare`
4. Rule Type: `universal (default)` or `interzone`
5. Source > Source Zone: `trust`
6. Destination > Destination Zone: `cloudflare`
7. Application > `Any`
8. Service/URL Category > `application-default`
9. Actions > Action setting: `Allow`
10. Log Setting: `Log at Session End`

### CLI - Add Security Policy from `cloudflare` to `trust`

:::note
Firewall rules added from the CLI will always be at the very bottom of the rulebase.
:::

Use the `move rulebase security rules` `<RULE_NAME>` `[after|before|top|bottom]` `<RULE_NAME - Desired position>`

```txt
set rulebase security rules cloudflare-to-trust to trust
set rulebase security rules cloudflare-to-trust from cloudflare
set rulebase security rules cloudflare-to-trust source any
set rulebase security rules cloudflare-to-trust destination any
set rulebase security rules cloudflare-to-trust application any
set rulebase security rules cloudflare-to-trust service application-default
set rulebase security rules cloudflare-to-trust action allow
set rulebase security rules cloudflare-to-trust log-start no
set rulebase security rules cloudflare-to-trust log-end yes
set rulebase security rules cloudflare-to-trust rule-type universal
```

### Web UI - Add Security Policy from `cloudflare` to `trust`

1. Go to **Policies** > **Security**.
2. Select **Add**.
3. General > Name: `cloudflare-to-trust`
4. Rule Type: `universal (default)` or `interzone`
5. Source > Source Zone: `cloudflare`
6. Destination > Destination Zone: `trust`
7. Application > `Any`
8. Service/URL Category > `application-default`
9. Actions > Action setting: `Allow`
10. Log Setting: `Log at Session End`

## Policy Based Forwarding

[Policy Based Forwarding](https://docs.paloaltonetworks.com/pan-os/11-1/pan-os-admin/policy/policy-based-forwarding) (aka Policy-Based Routing) allows you to apply additional matching criteria to specific traffic flows that will override routes defined within the Virtual Router.

You may only want to direct traffic through Cloudflare WAN if destined for another Cloudflare WAN site, while Internet-bound traffic continues to get forwarded directly through local Internet breakout.

The following example routes _ALL_ traffic from the LAN subnet behind NGFW (192.168.125.0/24) through the Cloudflare WAN IPsec tunnels. This lets you use the Cloudflare Secure Web Gateway functionality.

You can route traffic to specific destinations simply by adding subnets to the Destination match criteria.

Ensure any traffic flows processed by Policy Based Forwarding is exempted from NAT policies. Cloudflare Gateway will ensure NAT is applied to Internet bound traffic without the need for policy on local devices.

### CLI - Add Policy Based Forwarding Rules

- Tunnel 1

```txt
set rulebase pbf rules cf-wan-to-internet-01 action forward nexthop ip-address cf-wan-ipsec-vti-01-remote
set rulebase pbf rules cf-wan-to-internet-01 action forward egress-interface tunnel.1
set rulebase pbf rules cf-wan-to-internet-01 from zone trust
set rulebase pbf rules cf-wan-to-internet-01 enforce-symmetric-return enabled no
set rulebase pbf rules cf-wan-to-internet-01 source lan-net-192-168-125-0--24
set rulebase pbf rules cf-wan-to-internet-01 destination any
set rulebase pbf rules cf-wan-to-internet-01 source-user any
set rulebase pbf rules cf-wan-to-internet-01 application any
set rulebase pbf rules cf-wan-to-internet-01 service any
```

- Tunnel 2

```txt
set rulebase pbf rules cf-wan-to-internet-02 action forward nexthop ip-address cf-wan-ipsec-vti-02-remote
set rulebase pbf rules cf-wan-to-internet-02 action forward egress-interface tunnel.2
set rulebase pbf rules cf-wan-to-internet-02 from zone trust
set rulebase pbf rules cf-wan-to-internet-02 enforce-symmetric-return enabled no
set rulebase pbf rules cf-wan-to-internet-02 source lan-net-192-168-125-0--24
set rulebase pbf rules cf-wan-to-internet-02 destination any
set rulebase pbf rules cf-wan-to-internet-02 source-user any
set rulebase pbf rules cf-wan-to-internet-02 application any
set rulebase pbf rules cf-wan-to-internet-02 service any
```

### Web UI - Add Policy Based Forwarding Rules

- Tunnel 1:

1. Go to **Policies** > **Policy Based Forwarding**.
2. Select **Add**.
3. Name: `cf-wan-to-internet-01`
4. Source Zone: `trust`
5. Source Address: `lan-net-192-168-125-0--24`
6. Destination/Application/Service - Any/Any/Any
7. Forwarding > Action: Forward, Egress Interface: tunnel.1, Next Hop - IP Address: `cf-wan-ipsec-vti-01-remote`

- Tunnel 2:

1. Go to **Policies** > **Policy Based Forwarding**.
2. Select **Add**.
3. Name: `cf-wan-to-internet-02`
4. Source Zone: `trust`
5. Source Address: `lan-net-192-168-125-0--24`
6. Destination/Application/Service - Any/Any/Any
7. Forwarding > Action: Forward, Egress Interface: tunnel.2, Next Hop - IP Address: `cf-wan-ipsec-vti-02-remote`

Commit changes, then test traffic from a host on the 192.168.125.0/24 subnet to ensure it is forwarded through the Cloudflare WAN IPsec Tunnels.

:::note
If you have Cloudflare One configured to perform [HTTPS traffic inspection](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), ensure that you [install the Root CA certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) prior to testing connectivity to any HTTPS-based sites, otherwise you will receive untrusted certificate warning messages.
:::

## Troubleshooting

### Common issues

- Always check IKE Phase 1 & IPsec Phase 2 negotiated successfully - look for "no proposal chosen" in logs
- Verify Pre-Shared-Key and/or Local-Identity values are accurate and assigned to the correct tunnel
- Use ping to determine reachability between the CPE and Cloudflare sides of the VTI
  - Tunnel 1: CPE VTI to Cloudflare VTI: `ping source 169.254.250.1 169.254.250.0`
  - Tunnel 2: CPE VTI to Cloudflare VTI: `ping source 169.254.250.3 169.254.250.2`

### Quick Reference Guide

#### Display IKE & IPsec Security Associations

Use [show](https://docs.paloaltonetworks.com/network-security/ipsec-vpn/administration/troubleshooting/troubleshooting-site-to-site-vpn-issues-using-cli) commands to display Phase 1 and Phase 2 security associations:

```txt
admin@panfw01> show vpn ike-sa

IKEv2 SAs
Gateway ID      Peer-Address       Gateway Name       Role SN    Algorithm             Established     Expiration      Xt Child  ST
----------      ------------       ------------       ---- --    ---------             -----------     ----------      -- -----  --
1               162.159.135.1      cf-wan-ike-gw-01   Init 46    PSK/DH14/A256/SHA256  Mar.22 23:14:24 Mar.23 07:14:24 0  1      Established
2               172.64.135.1       cf-wan-ike-gw-02   Init 45    PSK/DH14/A256/SHA256  Mar.22 23:05:02 Mar.23 07:05:02 0  1      Established
```

```txt
IKEv2 IPSec Child SAs
Gateway Name                   TnID     Tunnel                     ID       Parent   Role SPI(in)  SPI(out) MsgID    ST              
------------                   ----     ------                     --       ------   ---- -------  -------- -----    --              
cf-wan-ike-gw-01               1        cf-wan-ipsec-tun-01        452741   97       Init B7D055D3 4CB26B43 00000001 Mature           
cf-wan-ike-gw-02               2        cf-wan-ipsec-tun-02        452742   98       Init B4629A07 165D416C 00000001 Mature           

Show IKEv2 SA: Total 2 gateways found. 2 ike sa found.
```

#### Manually Initiate IKE & IPsec Security Associations

Use [test](https://docs.paloaltonetworks.com/network-security/ipsec-vpn/administration/troubleshooting/troubleshooting-site-to-site-vpn-issues-using-cli) commands to force Phase 1 and Phase 2 security associations:

```txt
admin@panfw01> test vpn ike-sa gateway cf-wan-ike-gw-01 

Start time: Mar.30 21:23:23
Initiate 1 IKE SA.

admin@panfw01> test vpn ike-sa gateway cf-wan-ike-gw-02

Start time: Mar.30 21:23:24
Initiate 1 IKE SA.
```

```txt
admin@panfw01> test vpn ipsec-sa tunnel cf-wan-ipsec-tun-01 

Start time: Mar.30 21:26:50
Initiate 1 IPSec SA for tunnel cf-wan-ipsec-tun-01.

admin@panfw01> test vpn ipsec-sa tunnel cf-wan-ipsec-tun-02

Start time: Mar.30 21:26:52
Initiate 1 IPSec SA for tunnel cf-wan-ipsec-tun-02.
```

### Palo Alto Networks Documentation

- [Troubleshoot your IPSec VPN tunnel connection](https://docs.paloaltonetworks.com/network-security/ipsec-vpn/administration/troubleshooting/test-vpn-connectivity)

- [Troubleshoot site-to-site VPN issues using CLI](https://docs.paloaltonetworks.com/network-security/ipsec-vpn/administration/troubleshooting/troubleshooting-site-to-site-vpn-issues-using-cli)

### Palo Alto Networks Knowledge Base

- [How to troubleshoot IPSec VPN connectivity issues](https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClivCAC)

# pfSense

import { DashButton } from "~/components";

This tutorial includes the steps required to configure IPsec tunnels to connect a pfSense firewall to Cloudflare WAN (formerly Magic WAN).

## Software tested

| Manufacturer | Firmware revision |
| ------------ | ----------------- |
| pfSense      | 24.03             |

## Prerequisites

This tutorial requires the following information:

- Anycast IP addresses (Cloudflare provides these)
- External IP addresses
- Internal IP address ranges
- Inside tunnel `/31` ranges

## Example scenario

This tutorial uses the following IP addresses. These examples replace legally routable IP addresses with IPv4 Address Blocks Reserved for Documentation ([RFC 5737](https://datatracker.ietf.org/doc/html/rfc5737)) addresses within the `203.0.113.0/24` subnet.

| Tunnel name                        | `PF_TUNNEL_01`                | `PF_TUNNEL_02`                |
| ---------------------------------- | ----------------------------- | ----------------------------- |
| Interface address                  | `10.252.2.26/31`              | `10.252.2.28/31`              |
| Customer endpoint                  | `203.0.113.254`               | `203.0.113.254`               |
| Cloudflare endpoint                | `<YOUR_ANYCAST_IP_ADDRESS_1>` | `<YOUR_ANYCAST_IP_ADDRESS_2>` |
| pfSense IPsec Phase 2 Local IP     | `10.252.2.27`                 | `10.252.2.29`                 |
| pfSense IPsec Phase 2 Remote IP    | `10.252.2.26`                 | `10.252.2.28`                 |
| Cloudflare WAN static routes - Prefix   | `10.1.100.0/24`               | `10.1.100.0/24`               |
| Cloudflare WAN static routes - Next hop | `PF_TUNNEL_01`                | `PF_TUNNEL_02`                |

## 1. Configure Cloudflare WAN IPsec tunnels

Use the Cloudflare dashboard or API to [configure two IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels). This guide uses the settings mentioned below for the IPsec tunnels throughout the remainder.

### Add IPsec tunnels

1. Follow the [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) instructions to create the required IPsec tunnels with the following options:
   - **Tunnel name**: `PF_TUNNEL_01`
   - **Interface address**: `10.252.2.26/31`
   - **Customer endpoint**: `203.0.113.254`
   - **Cloudflare endpoint**: Enter one of the anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
   - **Health check rate**: _Medium_
   - **Health check type**: _Request_
   - **Health check direction**: _Bidirectional_
	- **Turn on replay protection**: Enable
2. Select **Add pre-shared key later** > **Add tunnels**.
3. Repeat the process to create a second IPsec tunnel with the following options:
   - **Tunnel name**: `PF_TUNNEL_02`
   - **Interface address**: `10.252.2.28/31`
   - **Customer endpoint**: `203.0.113.254`
   - **Cloudflare endpoint**: Enter the second anycast IP address assigned to your account.
   - **Health check rate**: _Medium_
   - **Health check type**: _Request_
   - **Health check direction**: _Bidirectional_
   - **Turn on replay protection**: Enable
4. Select **Add pre-shared key later** > **Add tunnels**.

:::note
If site-to-site traffic is a requirement, enable replay protection. Refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) > IPsec tunnel to learn how to enable this feature.
:::

### Generate pre-shared keys

When creating IPsec tunnels with the option **Add pre-shared key later**, the Cloudflare dashboard will show a warning indicator.

1. Select **Edit** to edit the properties of each IPsec tunnel.
2. Select **Generate a new pre-shared key** > **Update and generate pre-shared key**.
3. Copy the pre-shared key value for each IPsec tunnel, and save these values. Then, select **Done**.

:::note
Take note of the pre-shared keys to use later in pfSense.
:::

### IPsec identifier - User ID

After creating IPsec tunnels, the Cloudflare dashboard will list them under **Tunnels**. To retrieve the IPsec tunnel's user ID:

1. Go to the **Connectors** page.

<DashButton url="/?to=/:account/magic-networks/connections" />

2. In the **IPsec/GRE tunnels** tab, select the IPsec tunnel.
3. Scroll to **User ID** and copy the string. For example, `ipsec@long_string_of_letters_and_numbers`.

Configuring IKE Phase 1 on the pfSense firewall requires the User ID.

## 2. Create Cloudflare WAN static routes

Create a [static route](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) for each of the two IPsec tunnels configured in the previous section, with the following settings (settings not mentioned here can be left with their default values):

### Tunnel 01

- **Description**: `PF_TUNNEL_01`
- **Prefix**: `10.1.100.0/24`
- **Tunnel/Next hop**: `PF_TUNNEL_01`

### Tunnel 02

- **Description**: `PF_TUNNEL_02`
- **Prefix**: `10.1.100.0/24`
- **Tunnel/Next hop**: `PF_TUNNEL_02`

## 3. Configure the pfSense firewall

Install pfSense and boot up. Then, assign and set LAN and WAN interfaces, as well as IP addresses. For example:

- **LAN**: `203.0.113.254`
- **WAN**: `<YOUR_WAN_ADDRESS>`

### Configure IPsec Phase 1

Add a new IPsec tunnel [Phase 1 entry](https://docs.netgate.com/pfsense/en/latest/vpn/ipsec/configure-p1.html), with the following settings:

- **General Information**
  - **Description**: `CF1_IPsec_P1`
- **IKE Endpoint Configuration**
  - **Key exchange version**: _IKE_v2_
  - **Internet Protocol**: _IPv4_
  - **Interface**: _WAN_
  - **Remote gateway**: Enter the Cloudflare Anycast IP address.
- **Phase 1 Proposal (Authentication)**
  - **Authentication method**: _Mutual PSK_
  - **My identifier**: _User Fully qualified domain name_ > `ipsec@long_string_of_letters_and_numbers` <br/> (Find this identifier in the Cloudflare IPsec tunnel configuration > **User ID**)
  - **Peer identifier**: _Peer IP Address_ (Cloudflare Anycast IP)
  - **Pre-Shared Key (PSK)**: Enter the pre-shared key from the Cloudflare IPsec tunnel.
- **Phase 1 proposal (Encryption algorithm)**
  - **Encryption algorithm**: _AES 256 bits_
  - **Key length**: _256 bits_
  - **Hash algorithm**: _SHA256_
  - **DH key group**: _20_
  - **Lifetime**: `86400`

### Configure IPsec Phase 2

Add a new IPsec tunnel [Phase 2 entry](https://docs.netgate.com/pfsense/en/latest/vpn/ipsec/configure-p2.html), with the following settings. Create two separate Phase 2 entries (one for tunnel 1 and one for tunnel 2), adjusting the IP addresses for local and remote networks accordingly:

- **General Information**
  - **Description**: `CF1_IPsec_P2`
  - **Mode**: _Routed (VTI)_ (Virtual Tunnel Interface)
- **Networks**
  - **Local Network**: _Address_ > Higher IP address in the `/31` assigned in Cloudflare tunnel. For example, `10.252.2.27` for tunnel 1 and `10.252.2.29` for tunnel 2.
  - **Remote Network**: _Address_ > Lower IP address in the `/31` for Cloudflare side. For example, `10.252.2.26` for tunnel 1, and `10.252.2.28` for tunnel 2.
- **Phase 2 Proposal (SA/Key Exchange)**
  - **Protocol**: _ESP_ (Encapsulating Security Payload)
  - **Encryption algorithm**: _AES 256 bits_
  - **Hash algorithm**: _SHA256_
  - **DH key group**: _20_
  - **Lifetime**: `28800`

Apply the changes. Navigate to **Status** > **IPsec** to verify that both Phase 1 and Phase 2 are connected.

<div class="full-img">

![pfSense IPsec overview](~/assets/images/cloudflare-wan/third-party/pfsense/ipsec-overview.png)

</div>

### Interface assignments

In **Interfaces** > **Assignments** > **Add**, create a new interface to assign to the first IPsec tunnel, with the following settings:

- **General configuration**
  - **Description**: `CF1_IPsec_1`
  - **MSS**: `1446`
- **Interface Assignments**
  - **WAN**: Add the WAN interface. For example, `vnet1`.
  - **LAN**: Add the LAN interface. For example, `vnet0`.
  - Add the **CF_IPsec_1** interface from Phase 1 above.

Select **Save** to apply the changes.

<div class="full-img">

![Assign a new interface to the first IPsec tunnel](~/assets/images/cloudflare-wan/third-party/pfsense/interfaces.png)

</div>

<div class="full-img">

![Configuring interface assignments](~/assets/images/cloudflare-wan/third-party/pfsense/interface-assignments.png)

</div>

### Gateway

In **System** > **Routing** > **Gateways** there should already be a gateway. For this example, it is named `CF1_IPSEC_1_VTIV4`.

<div class="full-img">

![There should already be a gateway configured in the interface](~/assets/images/cloudflare-wan/third-party/pfsense/gateways.png)

</div>

### Firewall Rules IPsec

1. In **Firewall Rules** > **IPsec interface**, allow any type of traffic.

<div class="full-img">

![Allow all traffic for IPsec](~/assets/images/cloudflare-wan/third-party/pfsense/firewall-ipsec.png)

</div>

2. Navigate to **Status** > **Gateways**. `CF1_IPSEC_1_VTIV4` should now be online.

<div class="full-img">

![The gateway should now be online](~/assets/images/cloudflare-wan/third-party/pfsense/status-gateways.png)

</div>

### Firewall Rules LAN

1. In **Firewall** > **Rules** > **LAN**, allow any type of traffic.
2. Expand the **Advanced** section.
3. Change the Gateway to `CF1_IPSEC_1_VTIV4`.

<div class="full-img">

![Change the gateway in the firewall rules for LAN traffic](~/assets/images/cloudflare-wan/third-party/pfsense/firewall-lan.png)

</div>

# SonicWall

This tutorial shows you how to use Cloudflare WAN (formerly Magic WAN) with the following versions of the SonicWall appliances:

- **Hardware tested**:
  - SonicWall NSv 470
  - SonicWall 3700
- **Software versions tested**:
  - SonicOS 7.0.1

You can connect your SonicWall appliance through [IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) to Cloudflare WAN. Generic Routing Encapsulation (GRE) is not supported on SonicWall.

## Topology

![Topology diagram showing how to connect SonicWall appliances to Cloudflare WAN](~/assets/images/cloudflare-wan/third-party/sonicwall/topology.png)

_Note: Labels in this image may reflect previous product names._

The following instructions show how to set up an IPsec connection on your SonicWall device. We will use the IP ranges from the above topology example to create the connections needed. Settings not explicitly mentioned can be left with their default values.

## 1. Create an IPsec tunnel on your Cloudflare account

1. Start by [creating your IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) on Cloudflare. Name and describe the tunnels as needed, and add the following settings:

   - **Interface address**: Enter the internal tunnel IP on the Cloudflare side of the IPsec tunnel. In this example, it is `10.200.1.0/31`.
   - **Customer endpoint**: Enter the WAN IP address of your SonicWall device. In our example, this is `198.51.100.2`.
   - **Cloudflare endpoint**: Enter one of the Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space). In our example, this is `1.2.3.4`.
   - **Pre-shared key**: Select **Use my own pre-shared key** and paste a secure key of your own.

2. Select **Add tunnels** when you are finished.

3. After you create your tunnel, Cloudflare dashboard will load a list of tunnels set up for your account. Select the arrow to expand the tunnels you have just created, and check the following settings:

   - **Customer endpoint**: Refers to the SonicWall WAN IP that the VPN policy is bound to (in red).
   - **Cloudflare endpoint**: Refers to the Cloudflare anycast IP address (in blue).
   - **FQDN ID**: The ID used in the VPN policy for the SonicWall's Local IKE ID. Copy this ID and save it. You will need it when configuring the tunnel on your SonicWall (in green).

   ![An example of what your IPsec tunnel should look like](~/assets/images/cloudflare-wan/third-party/sonicwall/step3.png)

:::note
The interface address on the Cloudflare side of the tunnel is `10.200.1.0/31`. You will need to use `10.200.1.1/31` on the SonicWall side of the tunnel.
:::

## 2. Create static routes on Cloudflare dashboard

Static routes are required for any networks that will be reached via the IPsec tunnel. In our example, there are two networks: `172.31.3.0/24` and the tunnel network `10.200.1.0/31`.

1. [Create your static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route). Name and describe them as needed, and add the following settings:

   - **First tunnel**: Following our example, add `10.200.1.0/31` as the **Prefix** and `10.200.1.1` for the **Tunnel/Next hop**.
   - **Second tunnel**: Following our example, add `172.31.3.0/24` as the **Prefix** and `10.200.1.1` for the **Tunnel/Next hop**.

2. Select **Add routes** when you are finished.

## 3. Add a VPN configuration in SonicWall

1. Go to **Network** > **IPsec VPN** > **Rules and Settings**.
2. Select **Add**.
3. In **General** > **Security Policy** group, add the following settings:
   - **Authentication Method**: _IKE Using Preshared Secret_.
   - **IPsec Primary Gateway Name or Address**: Enter Cloudflare's anycast IP address for the primary gateway (in blue).
4. In the **IKE Authentication** group, add the following settings:
   - **Shared secret**: Paste the pre-shared key you use to create the IPsec tunnel in step 1 (in purple).
   - **Local IKE ID**: Select _Domain name_ from the drop-down menu, and paste here the **FQDN ID** you saved from step 1, after creating the IPsec tunnel (in green).
   - **Peer IKE IDE**: Select _IPv4_ Address from the drop-down menu, and enter the Cloudflare anycast IP address (in blue).

<div class="large-img">

![Configure a VPN policy on your SonicWall device](~/assets/images/cloudflare-wan/third-party/sonicwall/3-vpn-config.png)

</div>

5. Select **Proposals**. VPN Policy is somewhat flexible. Adjust these settings to match your organization's preferred security policy. As an example, you can use the settings in the examples below.
6. In the **IKE (Phase 1) Proposal** group, select the following settings:
   - **Exchange**: _IKEv2 Mode_
   - **DH Group**: _Group 20_
   - **Encryption**: _AES-256_
   - **Authentication**: _SHA256_
   - **Life Time (seconds)**: `86400`
7. In the **IPsec (Phase 2) Proposal** group, add the following settings:
   - **Protocol**: _ESP_
   - **Encryption**: _AESGCM16-256_
   - **Authentication**: _None_
   - **Enable Perfect Forward Secrecy**: Enabled
   - **DH Group**: _Group 20_
   - **Life Time (seconds)**: `28800`
8. Select **Advanced**.
9. Enable **Disable IPsec Anti-Replay**.
10. In **VPN Policy bound to** select your WAN interface from the drop-down menu, to bind it to your VPN.
11. Select **Save**.

<div class="large-img">

![Enable anti-replay on your SonicWall device](~/assets/images/cloudflare-wan/third-party/sonicwall/5-anti-replay.png)

</div>

## 4. Add a VPN tunnel interface

SonicOS requires a VPN tunnel interface to route traffic via Cloudflare WAN. When creating the interface, use the prefix `10.200.1.1/31`. This matches with the Cloudflare side for this tunnel, which is `10.200.1.0`.

:::note
You will need to use a different IP pair for each tunnel/site.
:::

1. Go to **Network** > **System** > **Interfaces**.
2. Select **Add interface** > **VPN Tunnel Interface**.
3. For IP Address, use `10.200.1.1`.
4. Enable **Ping**. This is required so the interface can be pinged for debugging and Cloudflare WAN health checks.

<div class="large-img">

![Enable ping so that your interface can be pinged for debugging and Cloudflare WAN health checks](~/assets/images/cloudflare-wan/third-party/sonicwall/6-vpn-ping.png)

</div>

5. Select **Advanced**.
6. Enable the **Enable Asymmetric Route Support** option. This is required for the IPsec tunnel health check.

<div class="large-img">

![Enable Asymmetric Route Support. It is required for Cloudflare WAN health checks](~/assets/images/cloudflare-wan/third-party/sonicwall/6-vpn-assymetric.png)

</div>

7. Select **OK**.

## 5. Add address object(s)

Address objects are necessary for route policies. In our example, we have one other site that will be reached via Cloudflare WAN. First, you need to create address objects for each network. Then, you need to create an address group that contains all the remote networks. This address group will be used in the next step to create the correct route policies.

To add an address object:

1. Select **Object** > **Match Objects** > **Addresses**.
2. Select **Address Objects** > **Add**.
3. Enter the information for your address object - refer to the topology image for the examples this tutorial is using. Since the addresses are in the VPN zone, set the **Zone Assignment** for the object to _VPN_.
4. Select **Save**. The window will stay on to facilitate multiple entries. Select **X** to close it.

<div class="large-img">

![Enter the appropriate settings for your object](~/assets/images/cloudflare-wan/third-party/sonicwall/7-address-objects-settings.png)

</div>

5. Select **Address Groups** > **Add** to add a new address group.
6. Enter a **Name** for your address group.
7. Select the individual network objects you have created on the left menu, and add them to the group by selecting the right-facing arrow in the middle column.
8. Select **Save**.

<div class="large-img">

![Copy the individual network objects and add them to your group](~/assets/images/cloudflare-wan/third-party/sonicwall/7-add-objects-group.png)

</div>

## 6. Set up routing

Add a route using the address object or group just created as the destination.

1. Select **Policy** > **Rules and Policies** > **Routing Rules**.
2. Select **Add** to add your route policy.
3. The **Next Hop** should be the VPN tunnel interface that was previously created in the interface panel.

## 7. Add access rule for health checks

An additional access rule is required for Cloudflare WAN health checks to work properly. This will enable the WAN IP to receive ICMP pings via the tunnel, and return them over the WAN.

1. Select **Policy** > **Rules and Policies**.
2. Select **Access Rules** > **Add**.
3. Enter a descriptive name for your policy.
4. In **Source / Destination** > **Destination > Port/Services**, select _ICMP_ from the drop-down menu.
5. Select **Optional Settings**.
6. In **Others**, enable **Allow Management traffic**.

## 8. Setup health checks

You have to [configure Cloudflare WAN health checks](/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) correctly. Here is an example of how to set up health checks:

```bash
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{tunnel_id} \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "health_check": {
    "direction": "bidirectional",
    "enabled": true,
    "type": "request",
    "rate": "low"
  }
}'
```

Health checks might take some time to stabilize after the configuration is changed.

## 9. Verify tunnel status on Cloudflare dashboard

The Cloudflare dashboard monitors the health of all anycast tunnels on your account that route traffic from Cloudflare to your origin network. Refer to [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) for more information.

# Sophos Firewall

This tutorial shows you how to use Cloudflare WAN (formerly Magic WAN) with the following versions of the Sophos Firewall:

- **Sophos form factor tested:**

  - Sophos Firewall XGS and XG series hardware
  - Sophos Firewall virtual appliance on VMware

- **Sophos software versions tested:**
  - SFOS Version 19.0 MR2-Build 472
  - SFOS Version 19.5.1 MR1-Build 278

You can connect through [Generic Routing Encapsulation (GRE) or IPsec tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) to Cloudflare WAN.

## IPsec connection

The following instructions show how to setup an IPsec connection on your Sophos Firewall device. Settings not explicitly mentioned can be left with their default values.

### 1. Add an IPsec profile

1. Go to **System** > **Profiles**.
2. In **IPsec profiles**, select **Add**.
3. In the **General settings** group, make sure you have the following settings:
   - **Name**: Give your profile a descriptive name.
   - **Key exchange**: **IKEv2**
   - **Authentication mode**: **Main mode**
4. In the **Phase 1** group, make sure you have the following settings:
   - **DH group (key group)**: _20_
   - **Encryption**: _AES256_
   - **Authentication**: _SHA2 256_
5. In the **Phase 2** group, select the following:
   - **PFS group (DH group)**: _Same as phase-1_
   - **Key life**: _28800_
   - **Encryption**: _AES256_
   - **Authentication**: _SHA2 256_
6. Enable **Dead Peer Detection**.
7. In **When peer unreachable**, select _Re-initiate_.
8. Select **Save**.

### 2. Create IPsec connection tunnel

The next step involves configuring a site-to-site IPsec VPN connection on your Sophos Firewall device.

1. Go to **Configure** > **Site-to-site VPN**.
2. In **IPsec**, select **Add**.
3. In the **General settings** group, make sure you have the following settings:
   - **Name**: Give your site-to-site VPN a descriptive name.
   - **Connection type**: _Tunnel interface_
   - **Gateway type**: _Initiate the connection_
4. In the **Encryption** group, make sure you have the following settings:
   - **Authentication type**: **Preshared key**
5. In **Gateway settings**, make sure you have the following settings:
   - **Gateway address**: Enter one of the Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
   - **Local ID type**: Add the [IKE ID](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-ike-id-formats) provided by Cloudflare.

![Configure an IPsec tunnel.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/2-ipsec-tunnel.png)

_Note: Labels in this image may reflect a previous product name._

After setting up your IPsec tunnel, it will show up on the IPsec connections list with an **Active** status.

![The IPsec tunnel should show up on the IPsec connections list.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/2b-ipsec-tunnel.png)

_Note: Labels in this image may reflect a previous product name._

### 3. Assign the XFRM interface address

You must use an interface address from the `/31` subnet required to [configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) on Cloudflare WAN.

1. Go to **Configure** > **Network**.
2. In **Interfaces**, select the corresponding interface to the IPsec tunnel you created in [step 2](#2-create-ipsec-connection-tunnel).
3. Edit the interface to assign an address from the `/31` subnet required to [configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/). When you are finished, it should look similar to the following:

![Configure a XFRM interface.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/3-xfrm-interface.png)

_Note: Labels in this image may reflect a previous product name._

### 4. Add a firewall rule

1. Go to **Protect** > **Rules and policies**.
2. In **Firewall rules**, create a firewall rule with the criteria and security policies from your company that allows traffic to flow between Sophos and Cloudflare WAN.

![Create a firewall rule with the criteria and security policies from your company](~/assets/images/cloudflare-wan/third-party/sophos-firewall/4-firewall-rule.png)

### 5. Disable IPsec anti-replay

Disable IPsec Anti-Replay on your Sophos Firewall. Changing the anti-replay settings restarts the IPsec service, which causes tunnel-flap for all IPsec tunnels. This will also disable IPsec anti-replay protection for all VPN connections globally. Plan these changes accordingly.

Below are instructions on how to achieve this on SFOS version 19 and SFOS version 19.5:

#### SFOS 19.0 MR2-Build 472 or 19.5 MR1-Build278 or later versions:

1. Sign in to the CLI.
2. Enter **4** to choose **Device console**, and enter the following command:

   ```bash
   set vpn ipsec-performance anti-replay window-size 0
   ```

   ![Access the CLI to disable anti-replay](~/assets/images/cloudflare-wan/third-party/sophos-firewall/5-sfos-19.png)

#### Older SFOS versions

Contact Sophos support.

## GRE connection

### 1. Configure a GRE tunnel between SFOS and Cloudflare

Start by configuring a GRE tunnel between SFOS and the Cloudflare anycast IP address.

1. Sign in to the CLI.
2. Enter **4** to choose **Device console**, and enter the following command:

   ```bash
   system gre tunnel add name <NAME_OF_YOUR_GRE_TUNNEL> local-gw <WAN_PORT> remote-gw <REMOTE_GATEWAY_IP_ADDRESS> local-ip <LOCAL_IP_ADDRESS> remote-ip <REMOTE_IP_ADDRESS>
   ```

   ![Access the CLI to configure a GRE tunnel](~/assets/images/cloudflare-wan/third-party/sophos-firewall/1-gre-connection.png)

   For more details, refer to the [Sophos Firewall knowledge base](https://support.sophos.com/support/s/article/KB-000035813?language=en_US).

### 2. Add a GRE or SD-WAN route to redirect traffic through the GRE tunnel

Refer to [Traffic redirection mechanism on Sophos Firewall](#traffic-redirection-mechanism-on-sophos-firewall) for information on how to add a GRE or SD-WAN route to redirect traffic through the GRE tunnel.

### 3. Add a firewall rule for LAN/DMZ to VPN

Create a firewall rule with the criteria and security policies from your company that allows traffic to flow between Sophos and Cloudflare WAN. This firewall rule should include the required networks and services.

1. Go to **Protect** > **Rules and policies**.
2. In **Firewall rules**, select **IPv4** > **Add firewall rule**.

![Create a firewall rule with the criteria and security policies from your company](~/assets/images/cloudflare-wan/third-party/sophos-firewall/4-firewall-rule.png)

## Traffic redirection mechanism on Sophos Firewall

To redirect traffic, you can add a static or an SD-WAN route.

### IPsec

#### Static route

Go to **Configure** > **Routing** > **Static routes** to add an XFRM interface-based route. The interface will be automatically created when you set up a tunnel interface based on IPsec (such as the Cloudflare_MWAN example from above).

![Go to static routes to add an XFRM interface-based route](~/assets/images/cloudflare-wan/third-party/sophos-firewall/static-route.png)

_Note: Labels in this image may reflect a previous product name._

#### SD-WAN route

1. Go to **Configure** > **Routing** > **Gateways** to create a custom gateway on the XFRM interface. The interface will be automatically created when you set up a tunnel interface based on IPsec (such as the Cloudflare_MWAN example from above).

![Go to Gateways to add an XFRM interface-based route](~/assets/images/cloudflare-wan/third-party/sophos-firewall/1-sd-wan-gateway.png)

_Note: Labels in this image may reflect a previous product name._

2. In **Configure** > **Routing** > **SD-WAN routes**, select **Add** to add the desired networks and services in the route to redirect traffic to Cloudflare. Enter a descriptive name for your connection, and the IP addresses you set up for your IPsec tunnels in **Incoming interface** and **Source networks**. Do not forget to choose the correct **Primary gateway** option.

![Go to SD-WAN to add the desired networks and services in the route.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/2-sd-wan-routes.png)

### GRE

Add a GRE route, an SD-WAN route, or both depending on your routing requirements.

#### GRE route

Add the route on the CLI.

1. Sign in to the CLI.
2. Enter **4** to choose **Device console**, and enter the following command to create the tunnel:

```bash
system gre route add net <IP_ADDRESS> tunnelname <TUNNEL_NAME>
```

![Add the route on the CLI.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/gre-route-cli.png)

#### SD-WAN route

1. Add a custom gateway on GRE with the peer IP address (from the `/31` subnet you chose earlier) as the Gateway IP address, and disable **Health check**.

![Add a custom gateway on GRE.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/sd-wan-1-gre.png)

2. Add an SD-WAN route with the desired networks and services in the route to redirect traffic to Cloudflare.

![Add an SD-WAN route.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/2-sd-wan-routes.png)

## Verify tunnel status on Cloudflare dashboard

The Cloudflare dashboard monitors the health of all anycast tunnels on your account that route traffic from Cloudflare to your origin network. Refer to [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) for more information.

### Configure Cloudflare health checks

1. The ICMP probe packet from Cloudflare must be the type ICMP request, with anycast source IP. In the following example, we have used `172.64.240.252` as a target example:

```bash
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{tunnel_id} \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "health_check": {
    "enabled": true,
    "target": "172.64.240.252",
    "type": "request",
    "rate": "mid"
  }
}'
```

2. Go to **Configure** > **Network** > **Interfaces** > **Add alias**. Add the IP address provided by Cloudflare for the ICMP probe traffic. This is needed to prevent Sophos firewall from dropping them as spoof packets. This is not the same IP used to create VPN. This is the special IP address for probe traffic only.

![Add the IP address provided by Cloudflare to prevent the probe from being dropped by the firewall.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/2-icmp-probe-firewall.png)

3. ICMP reply from SFOS should go back via the same tunnel on which the probe packets are received. You will need to create an additional SD-WAN policy route.

![Configure an SD-WAN route so the ICMP reply goes back to Cloudflare via the same tunnel.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/3-icmp-probe-reply.png)

Packet flow will look like the following:

```sh
tcpdump -nn proto 1
```

```sh output
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes

13:09:55.500453 xfrm1, IN: IP 172.70.51.31 > 172.64.240.252: ICMP echo request, id 33504, seq 0, length 64
13:09:55.500480 xfrm1, OUT: IP 172.64.240.252 > 172.70.51.31: ICMP echo reply, id 33504, seq 0, length 64

13:09:55.504669 xfrm1, IN: IP 172.71.29.66 > 172.64.240.252: ICMP echo request, id 60828, seq 0, length 64
13:09:55.504695 xfrm1, OUT: IP 172.64.240.252 > 172.71.29.66: ICMP echo reply, id 60828, seq 0, length 64
```

## Verify tunnel status on Sophos Firewall dashboard

### IPsec

When the tunnel is working, its **Status** will be green.

![If the tunnel is working, it will show up with a green status.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/2b-ipsec-tunnel.png)

_Note: Labels in this image may reflect a previous product name._

The corresponding XFRM interface will also show a **Connected** status.

![The XFRM interface will also show a connected status.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/1-sd-wan-gateway.png)

_Note: Labels in this image may reflect a previous product name._

### GRE

Access the CLI and type `system gre tunnel show` to check the status of a GRE tunnel. When the tunnel is working, its status will show up as **Enabled**.

![The GRE tunnel will show a status of Enabled when working.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/gre-status-enabled.png)

![The GRE tunnel will show a status of Enabled when working.](~/assets/images/cloudflare-wan/third-party/sophos-firewall/gre-status-enabled-b.png)

## Troubleshooting

If a tunnel shows a connected status at both ends, but is not established:

- Check if the IPsec profile configuration is correct.
- Make sure the corresponding tunnel interfaces are up.
- Make sure routing configuration and route precedence are correctly set on SFOS.
- Make sure a static back route is added on Cloudflare.
- Firewall rules for specific zones and host or service must be added in SFOS. GRE and IPsec belong to the VPN zone.
- Perform `tcpdump` to check if packets are going through the VPN or GRE tunnel as expected.
- Perform a packet capture on Cloudflare to see if traffic is reaching the Cloudflare platform.

# strongSwan

This tutorial explains how to set up strongSwan along with Cloudflare WAN (formerly Magic WAN). You will learn how to configure strongSwan, configure an IPsec tunnel, and create Policy-Based Routing (PBR).

## 1. Configure health checks

Configure the [bidirectional health checks](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) target for Cloudflare WAN. For this tutorial, use `172.64.240.252` as the target IP address, and `type` as the request.

This can be set up [with the API](/api/resources/magic_transit/subresources/ipsec_tunnels/methods/update/). For example:

```bash
curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/magic/ipsec_tunnels/{tunnel_id} \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "health_check": {
    "enabled": true,
    "target": "172.64.240.252",
    "type": "request",
    "rate": "mid"
  }
}'
```

## 2. Configure strongSwan

1. [Install strongSwan](https://docs.strongswan.org/docs/5.9/install/install.html). For example, open the console and run:

```sh
sudo apt-get install strongswan -y
```

2. Open `/etc/strongswan.conf` and add the following settings:

```txt
charon {
    load_modular = yes
    install_routes = no
    install_virtual_ip = no

    plugins {
        include strongswan.d/charon/*.conf
    }
}

include strongswan.d/*.conf
```

## 3. Configure the IPsec file

1. Open `/etc/ipsec.conf` and add the following settings:

```txt
# ipsec.conf - strongSwan IPsec configuration file
config setup
    charondebug="all"
    uniqueids = yes

conn %default
    ikelifetime=24h
    rekey=yes
    reauth=no
    keyexchange=ikev2
    authby=secret
    dpdaction=restart
    closeaction=restart

# Sample VPN connections
conn cloudflare-ipsec
    auto=start
    type=tunnel
    fragmentation=no
    leftauth=psk
    # Private IP of the VM
    left=%any
    # Tunnel ID from dashboard, in this example FQDN is used
    leftid=<YOUR_TUNNEL_ID>.<YOUR_ACCOUNT_ID>.ipsec.cloudflare.com
    leftsubnet=0.0.0.0/0
    # Cloudflare Anycast IP
    right=<YOUR_CLOUDFLARE_ANYCAST_IP>
    rightid=<YOUR_CLOUDFLARE_ANYCAST_IP>
    rightsubnet=0.0.0.0/0
    rightauth=psk
    ike=aes256-sha256-ecp384!
    esp=aes256-sha256-ecp384!
    replay_window=0
    mark_in=42
    mark_out=42
    leftupdown=/etc/strongswan.d/ipsec-vti.sh
```

2. Create a virtual tunnel interface (VTI) with the IP configured as the target for Cloudflare's health checks (`172.64.240.252`) to route IPsec packets. Open `/etc/strongswan.d/`.

3. Create a script called `ipsec-vti.sh` and add the following:

```txt
#!/bin/bash

set -o nounset
set -o errexit

VTI_IF="vti0"

case "${PLUTO_VERB}" in
    up-client)
        ip tunnel add "${VTI_IF}" local "${PLUTO_ME}" remote "${PLUTO_PEER}" mode vti \
        key "${PLUTO_MARK_OUT%%/*}"
        ip link set "${VTI_IF}" up
        ip addr add 172.64.240.252/32 dev vti0
        sysctl -w "net.ipv4.conf.${VTI_IF}.disable_policy=1"
        sysctl -w "net.ipv4.conf.${VTI_IF}.rp_filter=0"
        sysctl -w "net.ipv4.conf.all.rp_filter=0"
        ip rule add from 172.64.240.252 lookup viatunicmp
        ip route add default dev vti0 table viatunicmp
        ;;
    down-client)
        ip tunnel del "${VTI_IF}"
        ip rule del from 172.64.240.252 lookup viatunicmp
        ip route del default dev vti0 table viatunicmp
        ;;
esac
echo "executed"
```

## 4. Add policy-based routing

Create Policy-Based Routing (PBR) to redirect returning traffic through the IPsec tunnel. Without it, the ICMP replies to the health probes sent by Cloudflare will be returned through the Internet, instead of the same IPsec tunnel.

This tutorial uses [iproute2](https://en.wikipedia.org/wiki/Iproute2) to route IP packets from `172.64.240.252` to the tunnel interface.

1. Open `/etc/iproute2/`.

2. Edit the `rt_tables` file to add a routing table number and name. In this example, use `viatunicmp` as the name and `200` as the number for the routing table.

```txt
#
# reserved values
#
255 local
254 main
253 default
0   unspec
200 viatunicmp
#
# local
#
#1  inr.ruhep
```

3. Add a rule to match the routing table. This rule instructs the system to use routing table `viatunicmp` if the packet's source address is `172.64.240.252`:

```sh
ip rule add from 172.64.240.252 lookup viatunicmp
```

4. Add a route to the `viatunicmp` routing table. This is the default route through the interface `vti0` in the `viatunicmp` table.

```sh
ip route add default dev vti0 table viatunicmp
```

5. Start IPsec. You can also `stop`, `restart`, and show the `status` for the IPsec connection:

```bash
ipsec start
```

```bash output
Security Associations (1 up, 0 connecting):
cloudflare-ipsec[1]: ESTABLISHED 96 minutes ago, <IPSEC_TUNNEL_IDENTIFIER>.ipsec.cloudflare.com]...162.159.67.88[162.159.67.88]
cloudflare-ipsec{4}:  INSTALLED, TUNNEL, reqid 1, ESP SPIs: c4e20a95_i c5373d00_o
cloudflare-ipsec{4}:   0.0.0.0/0 === 0.0.0.0/0
```

## 5. Check connection status

Use tcpdump to investigate the status of health checks originated from Cloudflare.

```sh
sudo tcpdump -i <OUTGOING_INTERFACE> esp and host <TUNNEL_CLOUDFLARE_ENDPOINT_IP>
```

In this example, the outgoing Internet interface shows that the IPsec encrypted packets (ESP) from Cloudflare's health check probes (both the request and response) are going through the IPsec tunnel.

![tcpdump shows the IPsec encrypted packets from Cloudflare's health probes](~/assets/images/cloudflare-wan/third-party/strongswan/ipsec.png)

Run tcpdump on `vti0` to check the decrypted packets.

```sh
sudo tcpdump -i vti0 host 172.64.240.252
```

![If you run tcpdump on vti0 you can check for decrypted packets](~/assets/images/cloudflare-wan/third-party/strongswan/tcpdump.png)

# Ubiquiti

import { DashButton, Details } from "~/components";

Connect a Ubiquiti UniFi Gateway to Cloudflare's network using Cloudflare WAN (formerly Magic WAN). These steps use the Cloud Gateway Max (UCG-Max) but work with other UniFi gateways supporting route-based IPsec (Internet Protocol Security) VPNs (Virtual Private Networks), like the Dream Machine series.

## Prerequisites

- Cloudflare account with Cloudflare WAN enabled (contact your account team)
- UniFi Cloud Gateway or Dream Machine with IPsec support
- UniFi Network Application (self-hosted or cloud)
- Static public IP from your ISP
- Admin access to both Cloudflare and UniFi
- Gather a **Magic Anycast IPv4** address from the **Leased IPs** section in the dashboard
	- <DashButton url="/?to=/:account/ip-addresses/address-space" />
	- Contact your account team if you do not see any IP addresses listed.

## 1. Configure Cloudflare WAN

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Connectors** > **Cloudflare WAN**, and select **Create**.

2. Select **IPsec tunnel** > **Next**, and fill in the following settings:
	   - **Name**: `unifi-gw-primary`
	   - **IPv4 Interface Address**: `10.252.2.28/31` or refer to the [Tunnel endpoints documentation](/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/)
	   - **Customer Endpoint**: This should be your UniFi Gateway's WAN IP (for example, `203.0.113.10`)
   - **Cloudflare Endpoint**: This should be one of the IPv4 addresses gathered from Leased IPs.
   - Under **Tunnel Health checks**, select:
      - **Health check rate**: Set to desired level
      - **Health check type**: _Request_
      - **Health check direction**: _Bidirectional_
      - **Health check target**: _Default_
   - Under **Pre-shared key**:
      - Select **Add pre-shared key later**. This key will be given during the UniFi site-to-site VPN configuration.

## 2. Configure site-to-site VPN on UniFi

1. In UniFi Network, go to **Settings** > **VPN** > **Site-to-Site VPN**.
2. Select **Create New**.
3. Configure the following settings:
   - **VPN Type:** `IPsec`.
   - **Name:** `Cloudflare-Magic-WAN`.
   - **Pre-shared key:** Copy this key. You need it for the IPsec tunnel.
   - **Local IP:** Select the WAN interface (for example, `WAN1`).
   - **Remote IP:** Enter the Cloudflare endpoint IP from [Step 1](#1-configure-cloudflare-wan).
   - **VPN Method:** Route Based.
   - **Tunnel IP:** `10.252.2.29/31` or refer to the [Tunnel endpoints documentation](/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/).
   - **Remote Networks:** Inside Cloudflare tunnel address (for example, `10.252.2.28/31`) and other remote subnets to access through Cloudflare WAN.
4. Set Advanced settings:
   - **Key Exchange Version**: IKEv2.
   - **IKE Encryption**: AES-256.
   - **IKE Hash**: SHA256.
   - **IKE DH Group**: 14.
   - **IKE Lifetime**: 28800.
   - **ESP Encryption**: AES-256.
   - **ESP Hash**: SHA256.
   - **ESP DH Group**: 14.
   - **ESP Lifetime**: 28800.
   - **PFS**: Enabled.
   - **Local Authentication ID**: Auto.
   - **Remote Authentication ID**: Uncheck **Auto**, and enter the Cloudflare Endpoint IP from [Step 1](#1-configure-cloudflare-wan).
   - **MTU**: 1436.
5. Select **Apply**

## 3. Add pre-shared key to Cloudflare

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. In **Cloudflare WAN**, find the IPsec tunnel you have just created.

3. Select your tunnel and then **Edit**.
4. Paste the preshared key from [Step 2](#2-configure-site-to-site-vpn-on-unifi).
5. Select **Save**.

## 4. Configure Routes

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Networks**.
2. Go to **Routes** > **WAN routes** > **Create**.

3. Enter the following settings:
   - **Prefix**: Your local network (for example, `192.168.1.0/24`).
   - **Tunnel/Next hop**: Select your tunnel.
   - **Priority**: `100`.
4. Select **Add routes** to add your static route.

## Verify connections

Wait a few minutes, then access both Cloudflare and UniFi to verify the tunnel's status:

<Details header="Cloudflare">

1. Log in to [Cloudflare One](https://one.dash.cloudflare.com/), and go to **Insights**.
2. Go to **Network visibility** > **WAN connector health**.
3. Find the tunnel you have just created and make sure its status shows **Up**. Refer to [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) for more information.

</Details>

<Details header="UniFi">
Go to **Settings** > **VPN**, and make sure the status is **Connected**.
</Details>

## Troubleshooting

**Tunnel down:**
- Verify Peer IP, pre-shared key, and IPsec settings match on both sides
- Check that the ISP is not blocking UDP ports `500`/`4500`

**Traffic not routing:**
- Verify Remote Subnets setting in UniFi VPN configuration
- Check firewall rules are not blocking VPN traffic

**Health check fails:**
- Allow ICMP from Cloudflare to the customer-side tunnel IP
- Target should be the `/31` interface IP, not your LAN gateway

## Policy-based routing

To route only specific devices through Cloudflare (UniFi Network Application):

1. Remove unnecessary routes from Remote Subnets in your VPN configuration.
2. Go to **Settings** > **Policy Table**.
3. Under **Policy Engine** select **Create New Policy** with the following settings:
   - Select `Route`.
   - **Name**: Provide a name for the policy.
   - **Type**: _Policy-Based_.
   - **Interface/VPN Tunnel**: Select the VPN Tunnel (for example, `Cloudflare-Magic-WAN`).
   - **Kill Switch**: _Enabled_ (recommended).
   - **Source**: Select `Device/Network` and then choose the Device(s) or Network(s).
   - **Destination**: _Any_.
   - **Interface**: Your VPN tunnel.

## Next Steps

- Use [Cloudflare Network Firewall](/cloudflare-network-firewall/) for network policies.
- Configure a second tunnel for redundancy.
- Monitor traffic in the Cloudflare WAN dashboard.

---

You are now routing traffic through Cloudflare's network using Cloudflare WAN.

# Velocloud

This document is intended to provide Arista VeloCloud customers with the steps to provision non SD-WAN destinations through Edge for connectivity with Cloudflare WAN (formerly Magic WAN).

## VeloCloud Edge Nodes profile configuration

1. Log into VeloCloud Orchestrator and go to **Configure** > **Profiles**.
2. Select **New profile** to create a new profile (for example, `vc-edge-03-profile`).
3. Select the **Device** tab and expand the **Interfaces** section.
4. Select the **Edge Model** corresponding to the device (Virtual Edge). The default interface scheme for the Virtual Edge will be displayed. For example: eight interfaces, from GE1 to GE8.
5. You are only using interfaces **GE3** and **GE4.** Disable all unused interfaces to ensure anyone with physical access to the edge node cannot connect any unused interfaces. Do this by selecting each interface one at a time and unchecking **Interface Enabled** followed by **Save**.

### Configure interfaces

This documentation assumes:

- **GE3**: WAN Interface (Static IP)
- **GE4**: LAN Interface (Static IP)

### Interface GE3 - WAN interface

Configure interface GE3 with the following settings:

- **Interface enabled**: Enabled
- **Capability**: Routed
- **Segments**: All Segments
- **Radius Authentication**: Not applicable
- **ICMP Echo Response**: Enabled
- **Underlay Accounting**: Enabled
- **Enable WAN Link**: Enabled
- **Edge to Edge Encryption**: Enabled
- **DNS Proxy**: Disabled
- **VLAN**: Unspecified (this example assumes the device is connected to an access-layer switch port)
- **EVDSL Modem Attached**: Disabled

#### IPv4 settings

- **Addressing Type**: Static
- **WAN Link**: User Defined
- **OSPF**: Not applicable
- **Multicast**: Not applicable
- **Advertise**: Disabled
- **NAT Direct Traffic**: Enabled
- **Trusted Source**: Disabled
- **Reverse Path Forwarding**: (unspecified)

#### IPv6 settings

IPv6 is currently not supported with Cloudflare WAN. Uncheck the **Enabled** checkbox.

**Router Advertisement Host Settings**

- Disabled

**L2 Settings**

- **Autonegotiate**: Enabled
- **MTU**: 1500

Select **Save** to apply changes for Interface **GE3**.

### Interface GE4 - LAN Interface

- **Interface Enabled**: Enabled
- **Capability**: Routed
- **Segments**: Global Segment
- **Radius Authentication**: Disabled (Not applicable)
- **ICMP Echo Response**: Enabled
- **Underlay Accounting**: Enabled
- **WAN Link**: Disabled
- **Edge To Edge Encryption**: Enabled
- **DNS Proxy**: Disabled
- **VLAN**: Unspecified (this example assumes the device is connected to an access-layer switch port)
- **EVDSL Modem Attached**: Disabled

#### IPv4 Settings

- **Addressing Type**: DHCP or Static (example assumes Static IP)
- **WAN Link**: User Defined
- **OSPF**: Not applicable
- **Multicast**: Not applicable
- **Advertise**: Disabled
- **NAT Direct Traffic**: Enabled
- **Trusted Source**: Disabled
- **Reverse Path Forwarding**: Unspecified

#### IPv6 Settings

IPv6 is currently not supported with Cloudflare WAN. Uncheck the **Enabled** checkbox.

**Router Advertisement Host Settings**

- Disabled

**L2 Settings**

- **Autonegotiate**: Enabled
- **MTU**: 1500

Select **Save** to apply changes for Interface **GE4**.

The Interfaces section should indicate the **GE3** (WAN) and **GE4** (LAN) interfaces are configured and all other interfaces are administratively disabled.

### VPN Services

- Enable **Cloud VPN**.

Select **Save** to apply changes for the Profile.

## Network Services

1. Go to **Configure** > **Network Services**.
2. Expand **Non SD-WAN Destinations through Edge** and select **New**.

### General

- **Service** **Name**: Name of destination here. For example, `Magic_WAN_vc-edge-03`.
- **Tunneling Protocol**: **IPsec**
- **Service Type**: _Generic IKEv2 Router (Route Based VPN)_
- **Tunnel Mode**: _Active/Hot-Standby_ or _Active/Standby_

### IKE/IPsec settings

1. In the **IKE/IPsec Settings** tab, select:
   1. **IP Version**: _IPv4_
   2. **Primary VPN Gateway**
      1. **Public IP**: Specify one of the two Cloudflare anycast IP addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).
2. In **IKE Proposal**, expand **View advanced settings for IKE Proposal**:
   1. **Encryption**: _AES 256 CBC_
   2. **DH Group**: _14_
   3. **Hash**: _SHA-256_
   4. **IKE SA Lifetime (min)**: _1440_
   5. **DPD Timeout(sec)**: **20**
3. Expand **View advanced settings for IPsec Proposal**:
   1. **Encryption**: _AES 256 CBC_
   2. **PFS**: _14_
   3. **Hash**: _SHA 256_
   4. **IPsec SA Lifetime (min)**: **480**
4. Scroll up **Secondary VPN Gateway**, and select **Add**.
   1. **Public IP**: Specify the second of the two Cloudflare anycast IP addresses
   2. **Keep Tunnel Active**: Enabled (this is read-only and cannot be modified)
   3. Tunnel settings are the same as the primary — therefore they are greyed out in this section.

## Provision Edge Devices

1. Go to **Configure** > **Edges**, and select **Add Edge**.
2. Select the following settings:
   1. **Mode**: SD-WAN Edge
   2. **Name**: The name for your edge. For example, ` vc-edge-03`
   3. **Model**: _Virtual Edge_ (select the model of your Arista VeloCloud Edge appliance)
   4. **Profile**: Select the Profile created in the Provision configuration section. For example, `vc-edge-03-profile`
   5. **Edge License**: Select the appropriate license
   6. **Authentication**: _Certificate Acquire_
   7. **Encrypt Device Secrets**: Unchecked (do not select)
   8. **High Availability**: Unchecked (configure accordingly based on your environment)
   9. **Contact Info**: Provide a local contact name and local contact email
3. Select **Next** to advance to the next section.

### Additional settings

1. Configure the following settings based on your environment — left blank in the following example.

![This example was left blank. You should configure this based on your environment.](~/assets/images/cloudflare-wan/third-party/velocloud/image1.png)

2. Select **Add Edge** to save changes.

### Edge — Device Settings

Once the Edge device is added, you should land on the **Device** tab.

#### Connectivity — Interfaces

1. Expand the **Interfaces** section.
2. Note the interface configuration is inherited from the Profile configured in the previous section. Interfaces **GE3** and **GE4** will display a `WARNING` indicator as these interfaces require additional configuration.
3. Select **GE3** to open the properties for the WAN interface.
4. Many of the properties in this section were inherited from the Profile — as such, they are greyed out. You can select **Override** to modify the configuration specifically for this interface.
5. Scroll down to **IPv4 Settings**, and configure the following options:
   1. **Addressing Type**: _Static_ (this is inherited from the Profile)
   2. **IP Address**: Specify the WAN interface IP address
   3. **CIDR Prefix**: Add your subnet mask in Classless Inter-Domain Routing (CIDR) notation
   4. **Gateway**: Default Gateway (`0.0.0.0/0`)
   5. All other settings are inherited from the Profile.
6. Scroll down and select **Save**.
7. Select **GE4** to open the properties for the LAN interface.
8. Scroll down to **IPv4 Settings**, and configure the following options:
   1. **Addressing Type**: Static (inherited from the Profile)
   2. **IP Address**: Specify the WAN interface IP address
   3. **CIDR Prefix**: (subnet mask in CIDR notation)
   4. **Gateway**: Default Gateway (0.0.0.0/0)
   5. All other settings are inherited from the Profile.
9. Scroll down and select **Save**.

#### User Defined WAN Link

Note the indicator next to **GE3**. The steps in the Profile section disabled **Auto WAN Link Detection**. As a result,  the WAN Link must be specified.

![Note the indicator next to GE3.](~/assets/images/cloudflare-wan/third-party/velocloud/image2.png)

1. Scroll down to **WAN Link Configuration** > **Add User Defined WAN Link**, and configure the following options:
   1. **Link Type**: _Public_ (the WAN interface is connected directly to the Internet in this example — you may need to select _Private_ depending on your environment)
   2. **Interfaces**: Check the box for **GE3** (WAN Interface)
2. This example assumes default settings under **View optional** **configuration** and **View advanced settings**.
3. Select **Add Link** to save the changes.
4. Confirm the **User Defined WAN Link** is displayed and an indicator no longer appears next to interface **GE3**.

#### VPN Services

1. Scroll down to **VPN Services**.
2. Expand **Non SD-WAN Destination through Edge** and select the **Override** checkbox.
3. Select **Add**.
4. Select the drop-down under the **Name** column.
5. Select the **Network Service** defined earlier. For example, `Magic_WAN_vc-edge-03`.
6. In the **Action** column, select the **+** button, and configure the following options:
   1. **Public WAN Link**: Choose the Public WAN Link (refer to User-Defined WAN Link)
   2. **Local Identification Type**: *FQDN*
   3. **Local Identification**: Enter the FQDN specified when configuring Cloudflare WAN IPsec tunnels through the Custom FQDN IKE ID API endpoint.
   4. **PSK**: Enter the Pre-Shared Key. Ensure you use the same PSK for both Cloudflare WAN IPsec tunnels.
   5. **Destination Primary Public IP**: Pre-populated from the Network Service defined earlier.
   6. **Destination Secondary Public IP**: Pre-populated from the Network Service defined earlier.
7. Select **Save** to finish defining the IPsec tunnel settings.
8. Scroll down to the bottom of the Edge configuration page, and select **Save Changes** to finalize the Edge device configuration.

## VeloCloud to Cloudflare WAN routing

Configure the **Site Subnets** to facilitate:

* Routing traffic from one Cloudflare WAN site to other Cloudflare WAN sites.
* Ensure Cloudflare WAN IPsec tunnel health checks perform optimally.

1. Go to **Configure** > **Network Services**.
2. Expand the **Non SD-WAN Destinations through Edge** section.
3. Select the desired non SD-WAN destination, like `Magic_WAN_vc-edge-03`.

### Site Subnets

Configure a minimum of three IPsec tunnels. This example demonstrates two routes for tunnel health checks and two routes for traffic destined for remote sites:

- Cloudflare WAN IPsec tunnel health checks
- Primary VPN Gateway:
  - To the respective Cloudflare WAN IPv4 interface address associated with the primary Cloudflare anycast tunnel endpoint IP address
  - Routed through the Primary VPN Gateway.
- Secondary VPN Gateway:
  - To the respective Cloudflare WAN IPv4 interface address associated with the secondary Cloudflare anycast tunnel endpoint IP address
  - Routed through the Secondary VPN Gateway.
- Remote Cloudflare WAN site(s): CIDR blocks to route through Cloudflare WAN
  - The LAN interface for vc-edge-03 is:
    - 172.16.34.254/24 (subnet address: 172.16.34.0/24).
    - This does not need to be specified under Site Subnets as it is local.
  - Assume two remote sites, each of which need to be defined as Site Subnets and routed through both the Primary VPN Gateway and Secondary VPN Gateway.
    - 172.16.32.0/24
    - 172.16.33.0/24

1. Select the **Site Subnets** > **Add**. Then, select the following configurations for routes:
   1. Tunnel Health Check - Primary:
      1. 10.252.11.4/32 - Primary VPN Gateway
   2. Tunnel Health Check - Secondary:
      1. 10.252.11.6/32 - Secondary VPN Gateway
   3. Site vc-edge-01:
      1. 172.16.32.0/24 - Primary and Secondary VPN Gateways
   4. Site vc-edge-02:
      1. 172.16.33.0/24 - Primary and Secondary VPN Gateways
2. The **Site Subnets** tab should look like the following when configured as indicated:

![An example of how the Site Subnets tab should look like when configured as indicated.](~/assets/images/cloudflare-wan/third-party/velocloud/image3.png)

3. Select **Save** to commit changes to the Site Subnets.

## Cloudflare WAN and Cloudflare Gateway

Cloudflare WAN and Secure Web Gateway (Cloudflare Gateway) are tightly integrated. Arista VeloCloud customers can easily route traffic through Cloudflare WAN to Cloudflare Gateway. All Internet egress traffic is subject to Cloudflare Gateway policies.

Arista VeloCloud's Business Policies allow for intelligent routing of traffic destined for the Internet with only a few selections.

### Configure Business Policy

1. Go to **Configure** > **Edges**, and select the appropriate Edge appliance.
2. Select the **Business Policy** tab.
3. Select **Add** to create a Business Policy Rule:
   1. **Rule Name**: Provide a meaningful name to describe Internet traffic routed through the Cloudflare global anycast network.
   2. **IP Version**: _IPv4_
   3. **Match**
      1. **Source**: Select _Any_, _Object Groups_, or _Define_ to classify the relevant traffic flows.
      2. **Destination**: Select _Define_ > _Internet_
      3. **Application**: _Any_
   4. **Action**
      1. **Priority**: Normal
      2. **Enable Rate Limit**: Unchecked
      3. **Network Service**: _Internet Backhaul_ > _Non SD-WAN Destination through Edge / Cloud Security Service_.
      4. **Non SD-WAN Destination through Edge / Cloud Security Service**: Select the Network Service associated with the respective Edge device. For example, `Magic_WAN_vc-edge-03`.
4. Select **Create** to save the rule.

# Cisco SD-WAN

Cloudflare partners with Cisco's SD-WAN solution to provide users with an integrated SASE solution. The Cisco SD-WAN appliances (physical and virtual) manage subnets associated with branch offices and cloud instances. Anycast tunnels are set up between these SD-WAN edge devices and Cloudflare to securely route Internet-bound traffic. This tutorial describes how to configure the Cisco Catalyst 8000 Edge Platforms (physical or virtual) in the SD-WAN mode for north-south (Internet-bound) use cases.

## Prerequisites

Before setting up a connection between Cisco SD-WAN and Cloudflare, you must have:

- Purchased Cloudflare WAN (formerly Magic WAN) and Secure Web Gateway.
- Cloudflare provisions Cloudflare WAN and Secure Web Gateway.
- Received two Cloudflare tunnel endpoints (anycast IP address) assigned to Cloudflare WAN.
- Cisco SD-WAN appliances (physical or virtual). This ensures specific Internet-bound traffic from the sites' private networks is routed over the anycast GRE tunnels to Secure Web Gateway to enforce a user's specific web access policies.
- A static IP pair to use with the tunnel endpoints. The static IPs should be `/31` addresses separate from the IPs used in the subnet deployment.
- Release 20.6 Controllers and vEdge Device Builds. You should also pair them with devices that are on at least version Cisco IOS XE SD-WAN 17.6. Refer to [Cisco documentation](https://www.cisco.com/c/en/us/support/docs/routers/sd-wan/215676-cisco-tac-and-bu-recommended-sd-wan-soft.html) to learn more about Cisco software versions.

:::note[Note]
The SASE integration between Cisco SD-WAN and Cloudflare SSE was validated with Cisco SD-WAN 20.6.2 version with Catalyst 8kv router. For connectivity, we used GRE tunnels.
:::

## 1. Create a SIG template on Cisco vManage

Cisco vManage is Cisco's SD-WAN management tool that is used to manage all the SD-WAN appliances in branch offices.

For this example scenario, a generic template for `SIG-Branch` was created.

![Traffic flow diagram for GRE](~/assets/images/cloudflare-wan/third-party/viptela/viptela-flow-diagram-gre.png)

_Note: Labels in this image may reflect a previous product name._

To create a Secure Internet Gateway (SIG) using vManage:

1. From **Cisco vManage** under **Configuration**, select **Generic** and **Add Tunnel**.
2. The table below shows the setting fields and their options.

| Setting             | Type/Detail                               |
| ------------------- | ----------------------------------------- |
| **Global Template** | Factory_Default_Global_CISCO_Template |
| **Cisco Banner**    | Factory_Default_Retail_Banner          |
| **Policy**          | Branch-Local-Policy                       |

**Transport & Management VPN settings**

| Setting                           | Type/Detail                         |
| --------------------------------- | ----------------------------------- |
| **Cisco VPN 0**                   | GCP-Branch-VPN0                     |
| **Cisco Secure Internet Gateway** | Branch-SIG-GRE-Template             |
| **Cisco VPN Interface Ethernet**  | GCP-Branch-Public-Internet-TLOC     |
| **Cisco VPN Interface Ethernet**  | GCP-VPN0-Interface                  |
| **Cisco VPN 512**                 | Default_AWS_TGW_CSR_VPN512_V01 |

**Basic Information settings**

| Setting            | Type/Detail                                 |
| ------------------ | ------------------------------------------- |
| **Cisco System**   | Default_BootStrap_Cisco_System_Template |
| **Cisco Logging**  | Default_Logging_Cisco_V01                |
| **Cisco AAA**      | AWS-Branch-AAA-Template                     |
| **Cisco BFD**      | Default_BFD_Cisco-V01                     |
| **Cisco OMP**      | Default_AWS_TGW_CSR_OMP_IPv46_...     |
| **Cisco Security** | Default_Security_Cisco_V01               |

When creating the Feature Template, you can choose values that apply globally or that are device specific. For example, the **Tunnel Source IP Address**, **Interface Name** and fields from **Update Tunnel** are device specific and should be chosen accordingly.

## 2. Create tunnels in vManage

From vManage, select **Configuration** > **Templates**. You should see the newly created template where you will update the device values.

Because the template was created to add GRE tunnels, you only need to update the device values. Note that **VPN0** is the default, and the WAN interface used to build the tunnel must be part of **VPN0**.

![Update template fields for GRE tunnel](~/assets/images/cloudflare-wan/third-party/viptela/viptela-update-device-template-gre.png)

## 3. Create tunnels in Cloudflare

Refer to [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) for more information on creating a GRE tunnel.

![Established GRE tunnel in Cloudflare dashboard](~/assets/images/cloudflare-wan/third-party/viptela/viptela-gre-tunnel.png)

## 4. Define static routes

Refer to [Configure static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) for more information on configuring your static routes.

![Established GRE static routes in Cloudflare dashboard](~/assets/images/cloudflare-wan/third-party/viptela/viptela-gre-static-routes.png)

## 5. Validate traffic flow

In the example below, a request for `neverssl.com` was issued, which has a Cloudflare policy blocking traffic to `neverssl.com`.

On the client VM (192.168.30.3), a blocked response is visible.

![cURL example for a request to neverssl.com](~/assets/images/cloudflare-wan/third-party/viptela/viptela-curl-traffic-flow.png)

A matching blocked log line is visible from the Cloudflare logs.

![A blocked log from Gateway Activity Log in the Cloudflare dashboard](~/assets/images/cloudflare-wan/third-party/viptela/viptela-gre-swg-traffic.png)

## Add new tunnels using IPsec

IPsec tunnels to Cloudflare can only be created on Cisco 8000v in the router mode today. Refer to the [Cisco IOS XE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/cisco-ios-xe/) for more information.

# VyOS

import { Render } from "~/components";

<Render file="cloudflare-wan/third-party/vyos"
  product="networking-services"
/>

# Yamaha RTX Router

This tutorial describes how to configure the Yamaha RTX840 and RTX1300 series router to connect to Cloudflare WAN (formerly Magic WAN) via IPsec tunnels.

## Testing environment

These configurations were tested on the Yamaha RTX840 and RTX1300 series with the following firmware versions:

- **RTX840 series**: 23.02.02
- **RTX1300 series**: 23.00.17

## Cloudflare WAN configuration

You need to add IPsec tunnels and static routes to your Cloudflare account via the Cloudflare dashboard.

Before proceeding, ensure that you have the anycast IPs assigned to your account. You can find them in the Cloudflare dashboard under **Address Space** > [**Leased IPs**](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space).

### IPsec tunnels

1. Follow the [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) instructions to create the required IPsec tunnel. When creating your IPsec tunnel, make sure you define the following settings:
	- **Tunnel name**: Enter your tunnel name. In this example, it is `RTX840-vpn01`.
	- **Interface address**: Enter the internal tunnel IP on the Cloudflare side of the IPsec tunnel. In this example, it is `172.30.223.2/31`.
	- **Customer endpoint**: Enter the WAN IP address of your RTX router. In our example, this is `194.xx.xx.xx`. This is the fixed public IPv4 address you get from your ISP for your internet service.
	- **Cloudflare endpoint**: One of the Cloudflare anycast IP addresses assigned to your account.
	- **Health check rate**: _Medium_.
	- **Health check type**: _Request_.
	- **Health check direction**: _Bidirectional_.
	- **Health check target**: _Default_.
	- **Pre-shared key**: Select **Use my own pre-shared key** and paste a secure key of your own.
	- **Replay protection**: Do not check the box, to keep this disabled.

2. After you create your tunnel, the Cloudflare dashboard will load a list of tunnels set up for your account. Select the IPsec tunnel you have just created, and check the following setting:
	- **FQDN ID**: Copy this ID and save it. You will need it when configuring the IPsec tunnel on your RTX router.

### Static routes

Static routes are required for any networks that will be reached via the IPsec tunnel. In our example, there is one network: `172.16.2.0/24`.

Follow the [Configure static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/) instructions to create a static route (settings not mentioned here can be left with their default values):

- **Description**: `RTX840-lan01`
- **Prefix**: `172.16.2.0/24`
- **Tunnel/Next hop**: _RTX840-vpn01_

## RTX router configuration

Use the CLI to configure these settings.

### Route settings

```txt
ip route default gateway tunnel 1

ip route <Cloudflare Anycast IP> gateway <ISP provided Gateway IP>

ip route < ISP's DNS server IP > gateway <ISP provided Gateway IP>
```

### LAN settings

```txt
ip lan1 address 172.16.2.254/24
```

### Wired WAN settings

```txt
ip lan2 address 194.xx.xx.xx/29

ip lan2 nat descriptor 1000
```

### IPsec VPN main side settings

```txt
tunnel select 1

ipsec tunnel 1

ipsec sa policy 1 1 esp aes256-cbc sha256-hmac anti-replay-check=off

ipsec ike version 1 2

ipsec ike duration ipsec-sa 1 3600

ipsec ike duration isakmp-sa 1 28800

ipsec ike encryption 1 aes256-cbc

ipsec ike group 1 modp2048

ipsec ike hash 1 sha256

ipsec ike keepalive log 1 off

ipsec ike keepalive use 1 on rfc4306 10 6

ipsec ike local address 1 194.xx.xx.xx

ipsec ike log 1 key-info message-info payload-info

ipsec ike local name 1 <Cloudflare Magic IPsec Tunnel FQDN IP> fqdn

ipsec ike pfs 1 on

ipsec ike proposal-limitation 1 on

ipsec ike pre-shared-key 1 text <Pre-shared key>

ipsec ike remote address 1 <Cloudflare Anycast IP>

ipsec ike remote name 1 <Cloudflare Anycast IP> ipv4-addr

ip tunnel address 172.30.223.3/31

ip tunnel tcp mss limit auto

tunnel enable 1

ipsec auto refresh on

! Note: 172.30.223.3/31 is internal tunnel IP on the RTX side.
```

### NAT settings

```txt
nat descriptor type 1000 masquerade

nat descriptor address outer 1000 primary

nat descriptor masquerade static 1000 1 194.xx.xx.xx udp 500

nat descriptor masquerade static 1000 2 194.xx.xx.xx esp
```

### DHCP settings

```txt
dhcp service server

dhcp server rfc2131 compliant except remain-silent

dhcp scope 1 172.16.2.2-172.16.2.191/24
```

### DNS settings

```txt
dns host lan1

dns server select 1 <ISP's DNS server IP> any .

dns private address spoof on
```

## Connection test

In the Yamaha RTX router CLI, you can run `show ipsec sa` and `show status tunnel` to check the status of the IPsec VPN.

### `show ipsec sa`

```txt
Total: isakmp:1 send:1 recv:1

sa    sgw   isakmp        connection    	dir 	 life[s] 	           remote-id

------------------------------------------------------------------------------------------

1     1    	     -     		ike         	  -   	 27384         （Cloudflare Anycast IP）

2     1         1    		 tun[0001]esp  send 	 2185           （Cloudflare Anycast IP）

3     1         1    		 tun[0001]esp  recv 	 2185           （Cloudflare Anycast IP）
```

### `show status tunnel 1`

```txt
TUNNEL[1]:

Description:

Interface type: IPsec

Current status is Online.

from 2025/12/08 13:14:20.

20 minutes 56 seconds  connection.

Maximum Transmission Unit(MTU):

IPv4: 1280 octets

IPv6: 1280 octets

Received:    (IPv4) 171847 packets [58823472 octets]

(IPv6) 0 packet [0 octet]

Transmitted: (IPv4) 154224 packets [19191955 octets]

(IPv6) 0 packet [0 octet]

IKE keepalive:

[Type]: rfc4306

[Status]: OK

[Next send]: 1 sec after
```

# Microsoft Azure Virtual WAN

This tutorial provides information on how to connect Cloudflare WAN (formerly Magic WAN) to a Microsoft Azure Virtual WAN hub.

## Prerequisites

You will need to have an existing Resource group, Virtual Network, and Virtual Machine created in your Azure account. Refer to [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/virtual-network/) to learn more on how to create these.

## Start Azure configuration

### 1. Create a Virtual WAN

To connect one or more VNets to Cloudflare WAN via a Virtual WAN hub, you first need to create a Virtual WAN (vWAN) resource representing your Azure network. If you already have a vWAN that you wish to connect to Cloudflare WAN, continue to the next step. Refer to [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-site-to-site-portal#openvwan) to learn more.

1. In the Azure portal, go to your **Virtual WANs** page.
2. Select the option to create a **Virtual WAN**.
3. Create a Virtual WAN with the **Type** set to **Standard**.

### 2. Create a Virtual WAN Hub

Using traditional hub and spoke terminology, a Virtual WAN Hub deployed within a vWAN is the hub to which your VNet(s) and Cloudflare WAN attach as spokes. The vWAN hub deployed in this step will contain a VPN Gateway for connecting to Cloudflare WAN.

1. Create a **Virtual WAN Hub**.
2. In **Basics**:
   1. Select your resource group as well as your desired region, capacity, and hub routing preference. Microsoft recommends using the default hub routing preference of **ExpressRoute** unless you have a specific need to change this setting. Refer to [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/virtual-wan/about-virtual-hub-routing-preference) to learn more about Azure hub routing preferences.
   2. Configure the **Hub Private Address Space**. Choose an [address space with a subnet mask of `/24` or greater](https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-site-to-site-portal#hub) that does not overlap with the address spaces of any VNets you wish to attach to the vWAN Hub, nor with any of your Cloudflare WAN sites.
3. In **Site to Site**:
   1. In **Do you want to create a Site to site (VPN gateway)?** select **Yes**.
   2. Select your desired **Gateway scale units** and **Routing Preference**. Refer to [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/routing-preference-overview#routing-via-microsoft-global-network) to learn more about Azure routing preferences.
4. Select **Create**. Note that the deployment time for the vWAN Hub and VPN Gateway may take 30 minutes or more.
5. After the VPN Gateway has finished provisioning, go to **Virtual WAN** > **Hubs** > **Your vHub** > **Connectivity** > **VPN (Site to site)**.
6. In the **Essentials** dropdown select the VPN Gateway listed.
7. Select the JSON View for the VPN Gateway and take note of the JSON attributes at the paths `properties.ipConfigurations[0].publicIpAddress` and  `properties.ipConfigurations[1].publicIpAddress`. These will be the customer endpoints needed when configuring IPsec tunnels for Cloudflare WAN.

### 3. Create a VPN site

A VPN site represents the remote site your Azure vWAN can reach through a VPN connection. This is typically an on-premises location. In this case, the VPN site represents Cloudflare WAN.

1. Go to **Virtual WAN** > **VPN sites** > **Create site**.
2. In **Basics**:
   1. Configure your desired region and name.
   2. Configure the **Device vendor** as Cloudflare.
   3. In **Private address space**, specify the address range(s) you wish to access from your vWAN through Cloudflare WAN. This could include other private networks connected to your Cloudflare WAN, or a default route (`0.0.0.0/0`) if you want Internet egress traffic to traverse Cloudflare WAN (that is, to be scanned by Cloudflare Gateway). The address space can be modified after VPN site creation.
3. In **Links**:
   1. Configure a single link. Provide a name, speed (in Mbps), and provider name (here, enter `Cloudflare`) for your link. For the **Link IP address**, enter your Cloudflare anycast address. The **BGP address** and **ASN** fields should be left empty. BGP is not supported at the time of writing this tutorial.
4. Select **Create**.

### 4. Configure VPN site for IPsec tunnel health checks

Cloudflare WAN uses [Tunnel Health Checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) to monitor whether a tunnel is available.

Tunnel health checks make use of ICMP probes sent from the Cloudflare side of the IPsec tunnel to the remote endpoint (Azure). Probes are sent from the tunnel's interface address, which you specify in two places:

- **Cloudflare Dashboard:** In your IPsec tunnel configuration as the address of the virtual tunnel interface (VTI) (so that Cloudflare knows what address to send probes from). Cloudflare requires this address in CIDR notation with a `/31` netmask.
- **Azure Portal:** In your VPN site's address space (so that Azure routes probe responses back over the tunnel). Azure requires this address in CIDR notation with a `/32` netmask.

Cloudflare recommends that you select a unique `/31` subnet ([RFC 1918 — Address Allocation for Private Internets](https://datatracker.ietf.org/doc/html/rfc1918)) for each IPsec tunnel which is treated as a Point-to-Point Link and provides the ideal addressing scheme to satisfy both requirements.

Example:

- Select `169.254.251.137/31` as your unique Point-to-Point Link subnet.
- In the Cloudflare dashboard, set `169.254.251.137/31` as your tunnel's **IPv4 Interface address**. (Refer to [Configure Cloudflare WAN](#configure-cloudflare-wan) below.)
- In the Azure portal, add `169.254.251.137/32` to your VPN site's **Private address space**.

:::note
It is important to ensure the subnet selected for the Interface Address does not overlap with any other subnet.

You should also refer to RFC 3021 for more information on using 31-bit prefixes on [IPv4 Point-to-Point Links](https://datatracker.ietf.org/doc/html/rfc3021).
:::

To configure the Address Space for the Local Network Gateway to support Tunnel Health Checks:

1. Go to **Virtual WAN** > **VPN sites** > **Your VPN Site** > **Edit site** to edit the VPN site configured in the previous section.
2. Update the **Private address space** to include two `/32` subnets in CIDR notation as described above. When using Azure VPN Gateways with vWAN Hubs, a single VPN Gateway Connection maps to two Cloudflare WAN IPsec Tunnels. For this reason, we need to select two unique `/31` subnets, one for each Cloudflare IPsec Tunnel. The upper address of each `/31` is then added to the VPN Site's Private address space as a `/32`subnet.
3. Select **Confirm**.

### 5. Create a Virtual Network Connection

To connect your existing VNet to your newly created vHub:

1. Go to **Virtual WAN** > **Virtual network connections** and select **Add connection**.
2. Configure the connection to connect the desired VNet to the vHub created above.
3. Ensure that within the connection's **Routing configuration**:
   1. **Propagate to none** is set to **No.**
   2. **Bypass Next Hop IP for workloads within this VNet** is set to **No**
   3. And **Propagate static route** is set to **Yes**.
4. Select **Create**.

## Configure Cloudflare WAN

When connecting your Azure vHub VPN Gateway to Cloudflare WAN, you need to create two Cloudflare WAN IPsec tunnels to map to the single Azure VPN Gateway Connection created above. This is because Azure VPN Gateways are deployed with two public IP addresses.

1. Create an [IPsec tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) in the Cloudflare dashboard.
2. Make sure you have the following settings:
   1. **Interface address**: Add the upper IP address within the first `/31` subnet selected in step 4 of the Start Azure Configuration section. Refer to [Tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) for more details.
   2. **Customer endpoint**: The first public IP associated with your Azure VPN Gateway. For example, `40.xxx.xxx.xxx`.
   3. **Cloudflare endpoint**: Use one of the Cloudflare anycast addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space). This will also be the IP address corresponding to the VPN Site in Azure. For example, `162.xxx.xxx.xxx`.
   4. **Health check rate**: Medium (default).
   5. **Health check type**: Reply (default).
   6. **Health check direction**: Bidirectional (default).
   7. **Health check target**: Custom; enter the customer endpoint.
   8. **Add pre-shared key later**: Select this option to create a PSK that will be used later in Azure.
   9. **Replay protection**: **Enable**.
3. Edit the tunnel. Generate a new pre-shared key and copy the key to a safe location.
4. Create static routes for your Azure Virtual Network subnets, specifying the newly created tunnel as the next hop.
5. Create the second IPsec tunnel in the Cloudflare dashboard. Copy the configuration of the first tunnel with the following exceptions:
   1. **Interface address**: Add the upper IP address within the **second** `/31` subnet selected in step 4 of the Start Azure Configuration section.
   2. **Customer endpoint**: The **second** Public IP associated with your Azure VPN Gateway.
   3. **Health check target**: Enter the new customer endpoint as a custom target.
   4. **Use my own pre-shared key**: Select this option and enter the key generated for the first tunnel.
6. Create static routes for your Azure Virtual Network subnets, specifying the newly created tunnel as the next hop. To use one tunnel as primary and the other as backup, give the primary tunnel's route a lower priority. To ECMP load balance across both tunnels, assign both routes the same priority.

## Finish Azure Configuration

### 1. Create an IPsec VPN Gateway Connection

To create a **VPN Gateway Connection**:

1. Go to **Virtual WAN** > **Hubs** > **Your vHub** > **Connectivity** > **VPN (Site to site)** and remove the default filter **Hub association: Connected** to display the **VPN Site** created above.
2. Check the box next to your VPN Site and select **Connect VPN sites**.

Choose the following settings. These settings have been tested by Cloudflare. However, when setting up your VPN connection note that there are other configuration parameters are also technically feasible, as documented in the [Azure documentation](https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-ipsec) and in the [Cloudflare documentation](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters).

1. **PSK**: Provide the PSK generated by Cloudflare for your IPsec tunnels.
2. **Protocol**: *IKEv2*
3. **IPsec**: *Custom*
   1. **IPsec SA lifetime in seconds**: 28800
   2. **IKE Phase 1**
      1. **Encryption**: *AES256*
      2. **Integrity/PRF**: *SHA256*
      3. **DH Group**: *ECP384*
   3. **IKE Phase 2(IPsec)**
      1. **IPsec Encryption**: *AES256*
      2. **IPsec Integrity**: *SHA256*
      3. **PFS Group**: *ECP384*
   4. **Propagate Default Route:** **Disable**
   5. **Use policy based traffic selector**: **Disable**
   6. **Connection mode**: **Initiator Only**
   7. **Configure traffic selector?**: **Disabled**

4. Select **Connect**.

# Microsoft Azure VPN Gateway

This tutorial provides information on how to connect Cloudflare WAN (formerly Magic WAN) to your Azure Virtual Network, using the Azure Virtual Network Gateway.

## Prerequisites

You will need to have an existing Resource group, Virtual Network, and Virtual Machine created in your Azure account. Refer to [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/virtual-network/) to learn more on how to create these.

## Configure Azure Virtual Network Gateway

### 1. Create a Gateway subnet

You should already have a Virtual Network (VNET) created with a subnet assigned to it. The next step is to create a gateway subnet that Azure will use for addressing services related to Azure's Virtual Network Gateway. If you already have a gateway subnet, Azure will prevent you from creating a second one. If that is your case, update your gateway subnet settings.

1. Go to your **Virtual Network** > **Subnets**.
2. Select the option to add a **Gateway subnet**.
3. Configure the subnet address range. The gateway subnet must be contained by the address space of the virtual network, and have a subnet mask of `/27` or greater.
4. Make sure all other settings are set to **None**.

### 2. Create a Virtual Network Gateway

The Virtual Network Gateway is used to form the tunnel to the devices on your premises.

:::note
This configuration guide applies to Azure Virtual Network Gateway which includes the functionality found in the Azure VPN Gateway.

Active/Active and Active/Standby configurations are both supported. Two Azure public IP addresses and two Cloudflare WAN IPsec tunnels are required for the Active/Active configuration.
:::

#### Active/Active configuration

1. Create a Virtual Network Gateway.
2. Create two new public IP addresses or use existing IPs. Take note of the public IP addresses assigned to the Virtual Network Gateway as these will be the **Customer endpoint** for Cloudflare WAN's IPsec tunnels configuration.
3. Navigate to the Virtual Network Gateway created earlier.
4. In **Configuration**, enable **Active-active mode** and disable **Gateway Private IPs**.
5. Select **Create**.

#### Active/Standby configuration

1. Create a Virtual Network Gateway.
2. Create a new public IP address or use an existing IP. Take note of the public IP address assigned to the Virtual Network Gateway as this will be the **Customer endpoint** for Cloudflare WAN's IPsec tunnels configuration.
3. Select the resource group and VNET you have already created.
4. In **Configuration**, disable **Active-active mode** and **Gateway Private IPs**.
5. Select **Create**.

:::note
The time it takes for Azure to fully provision the Virtual Network Gateway depends on the deployment region.
:::

## Configure Cloudflare WAN

1. Create an [IPsec tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) in the Cloudflare dashboard.
2. Make sure you have the following settings:
   1. **Interface address**: As the Azure Local Network Gateway will only permit specifying the lower IP address in a `/31` subnet, add the upper IP address within the `/31` subnet. You will configure the corresponding `/32` address in Azure in a later step (refer to [Configure Local Network Gateway for IPsec tunnel health checks](#2-configure-local-network-gateway-for-ipsec-tunnel-health-checks)). Refer to [Tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) for more details.
   2. **Customer endpoint**: The Public IP associated with your Azure Virtual Network Gateway. For example, `40.xxx.xxx.xxx`.
   3. **Cloudflare endpoint**: Use one of the Cloudflare anycast addresses assigned to your account, available in [Leased IPs](https://dash.cloudflare.com/?to=/:account/ip-addresses/address-space). This will also be the IP address corresponding to the Local Network Gateway in Azure. For example, `162.xxx.xxx.xxx`.
   4. **Health check rate**: Leave the default option (Medium) selected.
   5. **Health check type**: Leave the default option (Reply) selected.
   6. **Health check direction**: Leave default option (Bidirectional) selected.
   7. **Health check target**: Select **Custom**.
   8. **Target address**: Enter the same address that is used in the **Customer endpoint** field.
   9. **Add pre-shared key later**: Select this option to create a PSK that will be used later in Azure.
   10. **Replay protection**: **Enable**.
3. If you are using the Active/Active configuration, select **Add IPsec tunnel** and repeat step 2 to create the second Cloudflare WAN IPsec tunnel. Use the same **Cloudflare endpoint** as for the first tunnel.
4. Select **Add Tunnels** when you are finished.
5. The Cloudflare dashboard will show you a list of your tunnels. Edit the tunnel(s) you have created > select **Generate a new pre-shared key** > copy the generated key. If using the Active/Active configuration, select **Change to a new custom pre-shared key** on the second tunnel and use the PSK generated for the first tunnel.
6. Create [static routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) for your Azure Virtual Network subnets, specifying the newly created tunnel as the next hop.

:::note
Both tunnels in an Active/Active configuration must use the same **Cloudflare endpoint**, because an Active/Active Azure VPN connection creates two tunnels to the same remote address.
:::

## Complete the Azure Configuration

### 1. Create a Local Network Gateway

The Local Network Gateway typically refers to your on-premises location. In this case, the Local Network Gateway represents the Cloudflare side of the connection.

We recommend creating a Local Network Gateway for your Cloudflare IPsec tunnel.

1. Create a new local network gateway.
2. In **Instance details** > **Endpoint**, select **IP address** and enter the Cloudflare anycast address in the IP address field.
3. In **Address space(s)**, specify the address range of any subnets you wish to access remotely through the Cloudflare WAN connection. For example, if you want to reach a network with an IP range of `192.168.1.0/24`, and this network is connected to your Cloudflare WAN tenant, you would add `192.168.1.0/24` to the local network gateway address space.
4. Go to the **Advanced** tab > **BGP settings**, and make sure you select **No**.

:::note
A single Cloudflare anycast address must be used in both Active/Active and Active/Standby configurations.
:::

### 2. Configure Local Network Gateway for IPsec tunnel health checks

Cloudflare WAN uses [Tunnel Health Checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) to monitor whether a tunnel is available.

Tunnel health checks make use of ICMP probes sent from the Cloudflare side of the IPsec tunnel to the remote endpoint (Azure). Probes are sent from the tunnel's interface address, which you specify in two places:

1. **Cloudflare Dashboard:** In your IPsec tunnel configuration as the address of the virtual tunnel interface (VTI) (so that Cloudflare knows what address to send probes from). Cloudflare requires this address in Classless Inter-Domain Routing (CIDR) notation with a `/31` netmask.
2. **Azure Portal:** In your VPN site's address space (so that Azure routes probe responses back over the tunnel). Azure requires this address in CIDR notation with a `/32` netmask.

Cloudflare recommends customers select a unique `/31` subnet ([RFC 1918 - Address Allocation for Private Internets](https://datatracker.ietf.org/doc/html/rfc1918)) for each IPsec tunnel which is treated as a Point-to-Point Link and provides the ideal addressing scheme to satisfy both requirements.

Example:
- Select 10.252.3.55/31 as your unique point-to-point link subnet.
- In the Cloudflare dashboard, set `10.252.3.55/31` as your tunnel's **IPv4 Interface address** (refer to [Configure Cloudflare WAN](#configure-cloudflare-wan)).
- In the Azure portal, add `10.252.3.55/32` to your Local Network Gateway's **Address space**.

:::note
It is important to ensure the subnet selected for the Interface Address does not overlap with any other subnet.
:::

:::note
Refer to RFC 3021 for more information on using 31-bit prefixes on [IPv4 Point-to-Point Links](https://datatracker.ietf.org/doc/html/rfc3021).
:::

To configure the Address Space for the Local Network Gateway to support Tunnel Health Checks:

1. Edit the Local Network Gateway configured in the previous section.
2. Select **Connections**.
3. Under **Address Space(s)** add the Interface Address of the IPsec Tunnel from the Cloudflare dashboard in CIDR notation (for example, `10.252.3.55/32`).
4. If using an Active/Active configuration, also add the Interface Address of the second IPsec Tunnel from the Cloudflare Dashboard in CIDR notation (for example, `10.252.3.56/32`) under **Address Space(s)**. Both tunnel interface addresses must be configured in the Local Network Gateway Address Space to ensure both tunnels remain healthy.
5. Select **Save**.

:::note
The IPsec Tunnel Interface Address should be entered as a `/31` in the Cloudflare Dashboard, but as a `/32` when configuring the Local Network Gateway Address Space(s) in the Azure portal.
:::

### 3. Create an IPsec VPN Connection

Choose the following settings when creating your VPN Connection:

1. **Virtual network gateway**: Select the Virtual Network Gateway you created in [Create a Virtual Network Gateway](#2-create-a-virtual-network-gateway).
2. **Local network gateway**: Select the Local Network Gateway created in [Create a Local Network Gateway](#1-create-a-local-network-gateway).
3. **Use Azure Private IP Address**: **Disabled**
4. **BGP**: **Disabled**
5. **IPsec / IKE policy**: **Custom**
   1. **IKE Phase 1**
      1. **Encryption**: _GCMAES256_
      2. **Integrity/PRF**: _SHA384_
      3. **DH Group**: _ECP384_
   2. **IKE Phase 2(IPsec)**
      1. **IPsec Encryption**: _GCMAES256_
      2. **IPsec Integrity**: _GCMAES256_
      3. **PFS Group**: _ECP384_
   3. **IPsec SA lifetime in KiloBytes**: `0`
   4. **IPsec SA lifetime in seconds**: `28800`
   5. **Use policy based traffic selector**: **Disable**
   6. **DPD timeout in seconds**: `45`
   7. **Connection mode**: **InitiatorOnly**
   8. **Use custom traffic selectors**: **Disabled**
6. After the connection is created, select **Settings** > **Authentication**, and input your PSK (this will need to match the PSK used by the Cloudflare WAN configuration).

Repeat this process to define the settings for the Connection to the Local Network Gateway that corresponds to the redundant Cloudflare anycast IP address.

### 4. Route all Internet traffic through Cloudflare WAN and Cloudflare Gateway

Cloudflare Zero Trust customers can route Internet-bound traffic through Cloudflare WAN to the Internet through Cloudflare Gateway.

Microsoft does not permit specifying a default route (`0.0.0.0/0`) under Address Space in the Local Network Gateway. However, it is possible to work around this limitation through the use of route summarization.

1. Go to **Local network gateways** and select the desired object.
2. Go to **Configuration** > **Address Space(s)** and specify the following two subnets: `0.0.0.0/1` & `128.0.0.0/1`.
3. Do not remove the subnet configured to support the Tunnel Health Checks.
4. Select **Save**.

## Install Cloudflare Zero Trust CA Certificate

If you opt to route all Internet bound traffic through Cloudflare WAN and want to take advantage of [HTTPS TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), it will be necessary to install and trust the Cloudflare Zero Trust root certificate authority (CA) certificate on your user's devices. You can either install the certificate provided by Cloudflare (default option), or generate your own custom certificate and upload it to Cloudflare.

More details on how to install the root CA certificate can be found in [User-side certificates](/cloudflare-one/team-and-resources/devices/user-side-certificates/) in the Cloudflare Zero Trust documentation.

Once the root CA certificate is installed, open a web browser or use curl to validate Internet connectivity:

```sh
curl https://ipinfo.io
```

```json output
{
	"ip": "104.xxx.xxx.225",
	"city": "Reston",
	"region": "Virginia",
	"country": "US",
	"loc": "xx.xxxx,-xx.xxxx",
	"org": "AS13335 Cloudflare, Inc.",
	"postal": "20190",
	"timezone": "America/New_York",
	"readme": "https://ipinfo.io/missingauth"
}
```

:::note
Internet Control Message Protocol (ICMP) (ping/traceroute) will work to remote Cloudflare WAN sites, but is not forwarded to the Internet. Ensure you validate connectivity via HTTP.
:::

## Validate connectivity and disable Azure Virtual Network Gateway anti-replay protection

Once you have determined that connectivity has been established, Cloudflare recommends you disable anti-replay protection for the Azure Virtual Network Gateway site-to-site VPN connection. This can be accomplished through Microsoft Azure API.

1. Determine the API token via PowerShell:

```powershell
Get-AzAccessToken
```

```txt output
Token: eyJ0e<REDACTED>AH-PdSPg
ExpiresOn : 04/08/2024 23:32:47 +00:00
Type      : Bearer
TenantId  : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
UserId    : user@domain.com
```

2. Issue the API call to display the details of the site-to-site VPN Connection associated with the Azure Virtual Network Gateway (`GET` request):

```bash
curl --location 'https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}?api-version=2022-05-01' \
--header 'Authorization: Bearer eyJ0e<REDACTED>AH-PdSPg'
```

3. Copy/paste the entire response into a text editor:

```json
{
    "name": "{{virtualNetworkGatewayName}}",
    "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}",
    "etag": "W/\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
    "type": "Microsoft.Network/virtualNetworkGateways",
    "location": "eastus"
    },
    "properties": {
        "provisioningState": "Succeeded",
        "resourceGuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "packetCaptureDiagnosticState": "None",
        "enablePrivateIpAddress": false,
        "isMigrateToCSES": false,
        "ipConfigurations": [
            {
                "name": "default",
                "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}/ipConfigurations/default",
                "etag": "W/\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
                "type": "Microsoft.Network/virtualNetworkGateways/ipConfigurations",
                "properties": {
                    "provisioningState": "Succeeded",
                    "privateIPAllocationMethod": "Dynamic",
                    "publicIPAddress": {
                        "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/publicIPAddresses/{{virtualNetworkGatewayPublicIpAddress}}"
                    },
                    "subnet": {
                        "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworks/{{virtualNetworkGatewayName}}/subnets/GatewaySubnet"
                    }
                }
            }
        ],
        "natRules": [],
        "virtualNetworkGatewayPolicyGroups": [],
        "enableBgpRouteTranslationForNat": false,
        "disableIPSecReplayProtection": false,
        "sku": {
            "name": "VpnGw2AZ",
            "tier": "VpnGw2AZ",
            "capacity": 2
        },
        "gatewayType": "Vpn",
        "vpnType": "RouteBased",
        "enableBgp": false,
        "activeActive": false,
        "bgpSettings": {
            "asn": 65515,
            "bgpPeeringAddress": "172.25.40.30",
            "peerWeight": 0,
            "bgpPeeringAddresses": [
                {
                    "ipconfigurationId": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}/ipConfigurations/default",
                    "defaultBgpIpAddresses": [
                        "172.25.40.30"
                    ],
                    "customBgpIpAddresses": [],
                    "tunnelIpAddresses": [
                        "{{CF ANYCAST IP}}"
                    ]
                }
            ]
        },
        "gatewayDefaultSite": {
            "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/localNetworkGateways/{{localNetworkGatewayName}}"
        },
        "vpnGatewayGeneration": "Generation2",
        "allowRemoteVnetTraffic": false,
        "allowVirtualWanTraffic": false
    }
}
```

4. Locate the line that controls disabling IPsec anti-replay protection, and change it from `false` to `true`:

```txt
"disableIPSecReplayProtection": true
```

5. Upload the entire response in a subsequent API call (`PUT` request):

```bash
curl --location --request PUT \
'https://management.azure.com/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}?api-version=2022-05-01' \
--header "Authorization: Bearer eyJ0e<REDACTED>AH-PdSPg" \
--header "Content-Type: application/json" \
--data '{
    "name": "{{virtualNetworkGatewayName}}",
    "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}",
    "etag": "W/\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
    "type": "Microsoft.Network/virtualNetworkGateways",
    "location": "eastus"
    },
    "properties": {
        "provisioningState": "Succeeded",
        "resourceGuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "packetCaptureDiagnosticState": "None",
        "enablePrivateIpAddress": false,
        "isMigrateToCSES": false,
        "ipConfigurations": [
            {
                "name": "default",
                "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}/ipConfigurations/default",
                "etag": "W/\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"",
                "type": "Microsoft.Network/virtualNetworkGateways/ipConfigurations",
                "properties": {
                    "provisioningState": "Succeeded",
                    "privateIPAllocationMethod": "Dynamic",
                    "publicIPAddress": {
                        "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/publicIPAddresses/{{virtualNetworkGatewayPublicIpAddress}}"
                    },
                    "subnet": {
                        "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworks/{{virtualNetworkGatewayName}}/subnets/GatewaySubnet"
                    }
                }
            }
        ],
        "natRules": [],
        "virtualNetworkGatewayPolicyGroups": [],
        "enableBgpRouteTranslationForNat": false,
        "disableIPSecReplayProtection": true,
        "sku": {
            "name": "VpnGw2AZ",
            "tier": "VpnGw2AZ",
            "capacity": 2
        },
        "gatewayType": "Vpn",
        "vpnType": "RouteBased",
        "enableBgp": false,
        "activeActive": false,
        "bgpSettings": {
            "asn": 65515,
            "bgpPeeringAddress": "172.25.40.30",
            "peerWeight": 0,
            "bgpPeeringAddresses": [
                {
                    "ipconfigurationId": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/virtualNetworkGateways/{{virtualNetworkGatewayName}}/ipConfigurations/default",
                    "defaultBgpIpAddresses": [
                        "172.25.40.30"
                    ],
                    "customBgpIpAddresses": [],
                    "tunnelIpAddresses": [
                        "{{CF ANYCAST IP}}"
                    ]
                }
            ]
        },
        "gatewayDefaultSite": {
            "id": "/subscriptions/{{subscriptionId}}/resourceGroups/{{resourceGroupName}}/providers/Microsoft.Network/localNetworkGateways/{{localNetworkGatewayName}}"
        },
        "vpnGatewayGeneration": "Generation2",
        "allowRemoteVnetTraffic": false,
        "allowVirtualWanTraffic": false
    }
}'
```

6. Leave the replay protection setting checked in the Cloudflare dashboard, and wait several minutes before validating connectivity again.

# Microsoft Azure

import { DirectoryListing } from "~/components"

Microsoft Azure integration guides currently available:

<DirectoryListing />

# Third party licenses

import text from "./3rdparty.txt?raw";
import { Markdown } from "~/components";

<Markdown text={text} inline={false} />

# Legal

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Anti-replay protection

import { GlossaryTooltip } from "~/components";

If you use Cloudflare WAN and <GlossaryTooltip term="anycast">anycast</GlossaryTooltip> <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnels, we recommend disabling anti-replay protection. Cloudflare disables this setting by default. However, you can enable it through the API or the Cloudflare dashboard for devices that do not support disabling it, including Cisco Meraki, Velocloud, and AWS VPN Gateway.

Refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) to learn how to set up replay protection. This page explains replay attacks, why Cloudflare recommends disabling IPsec anti-replay, and related considerations.

## Replay attacks

Replay attacks occur when a malicious actor intercepts and records a [packet](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/), and later sends the recorded packet to the target network again with an intent that benefits the attacker.

### Example

Consider a poorly designed Internet of Things (IoT) garage door opener. The device has a simple protocol for operation: A User Datagram Protocol (UDP) packet contains the garage door password and either `open` or `shut` in its data segment. The garage door's key encrypts the data segment, and the owner's phone sends it to either open or close the garage door.

An attacker likely cannot open or close the garage door by guessing the encryption key and password. While the attacker cannot see the recorded packet's encrypted content, if the garage is in their line-of-sight, they could potentially correlate and guess which packets are responsible for opening the garage door. When the attacker wants to open the door, they send the recorded `open` packet, and because the recorded packet would contain the password and already be encrypted with the right key, this door would open.

To prevent this replay attack, a user could add a packet number to each command sent to the garage door. The first could be `packet 1`, the second `packet 2` and so on, and the garage door would only accept packets containing the next number in the sequence each time. For example, after the garage door receives `packet 1`, it would only accept packet 2, and if an attacker tries to replay `packet 1`, the garage door ignores the request.

## IPsec anti-replay protection

IPsec anti-replay protection works similarly to the prevention example in the scenario above. The sender assigns each IPsec packet a sequence number. The receiver tracks which sequence numbers it has already seen and only accepts packets in a small window around the highest value the receiver has seen, and the window is typically 64-1024 packets. IPsec uses a window instead of strict sequencing because sometimes packets are reordered or lost on the Internet - having a range of acceptable packet sequence numbers helps absorb these issues.

## Cloudflare WAN and anti-replay protection

Standard IPsec anti-replay protection assumes a single sender and a single receiver. The sender stores a sequence number in memory and increments it for every packet. The receiver tracks which sequence numbers it has already processed.

Cloudflare's anycast architecture does not fit this model. Because Cloudflare WAN uses <GlossaryTooltip term="GRE tunnel">anycast</GlossaryTooltip>, any packet can be processed by any of thousands of servers across hundreds of data centers. This distributed processing is what gives Cloudflare WAN its performance and resiliency benefits — but it means no single server has a complete view of the sequence number state.

If you enable replay protection for Cloudflare WAN IPsec tunnels, Cloudflare routes packets for a single tunnel to one server that keeps track of the sequence number. The replay protection mechanism works correctly in this mode, but you lose the benefit of distributing traffic across Cloudflare's global servers. It also only applies in one direction (Cloudflare to customer network) — Cloudflare does not route packets from the customer network to a single server and does not apply replay protection in that direction.

## Additional considerations

IPsec anti-replay protection is extremely important for transport mode - host-to-host or even app-to-app IPsec. In transport mode, an attacker has a relatively easy time identifying the encrypted protocol and identifying which packets to replay if the protocol is even subject to replay attacks. Cloudflare WAN, however, uses tunnel mode, not transport mode, which makes a successful replay attack much harder.

There are several reasons that make replay attacks difficult with tunnel mode:

- IPsec encrypts the entire inner packet, which means the attacker would know almost nothing about the user packet they capture and perform correlation for replay attack. The only information an attacker would know is the outer site network that encrypted the packet, the outer site network that receives it, and the approximate size of the packet. However, this information is not enough to identify specific inner user packet flows to correlate and replay.
- Replay attacks only work when attackers use the same encryption keys. After rekeying, the router drops old replayed packets.
- Most protocols are not susceptible to replay at the packet level. The Internet can duplicate packets, which means TCP and many protocols built on UDP already include sequence numbers or similar to handle duplicate packets coming off the wire. For those, the replay traffic just looks like a duplicate packet and is handled by the end host correctly.
- Anti-replay protection is available in a higher Open Systems Interconnection (OSI) layer. Many modern day applications use secure communication protocols such as Secure Sockets Layer/Transport Layer Security (SSL/TLS), Secure Shell (SSH), or SSH File Transfer Protocol (SFTP) to transport application data. These secure communication protocols (at a higher OSI layer than network layer) natively support anti-replay protection.
- The reduced attack surface lowers the probability for packet interception. IPsec tunnels are site-to-site VPN tunnels between a user's site router and Cloudflare's global network, through dedicated Internet Service Provider (ISP) network connections, which are typically very secure. Additionally, the anycast nature of Cloudflare's IPsec implementation terminates the IPsec tunnel to one of the more than 300 Cloudflare data centers closest to the customer's edge router, which minimizes the physical distance and footprint the encrypted packets have to traverse.

## Troubleshooting

If you're experiencing tunnel instability or packet drops related to anti-replay protection, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/#ipsec-tunnel-instability-or-packet-drops).

If your IPsec tunnel has replay protection turned off in Cloudflare and a small, steady percentage of health checks fail, your device may still be enforcing anti-replay protection. Refer to [Health checks fail at a low, steady rate](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/ipsec-troubleshoot/#anti-replay-mismatch).

# Bandwidth measurement

Cloudflare measures Cloudflare WAN (formerly Magic WAN) usage based on the 95th percentile of bandwidth utilized by your configured network. This measurement reflects your overall network capacity consumption.

## How bandwidth is measured

Cloudflare WAN bandwidth includes the sum of traffic routed to and from the Cloudflare WAN network namespace across all your connections. This measurement includes traffic from the following tunnel types:

- [GRE (Generic Routing Encapsulation)](https://www.cloudflare.com/learning/network-layer/what-is-gre-tunneling/)
- [IPsec (Internet Protocol Security)](https://www.cloudflare.com/learning/network-layer/what-is-ipsec/)
- [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-tunnel/)
- [Cloudflare Network Interconnect](/network-interconnect/)

For each tunnel, Cloudflare uses the highest 95th percentile value (ingress or egress traffic). The usage measurement excludes [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) traffic.

## 95th percentile calculation

The 95th percentile method is an industry-standard approach to bandwidth measurement that accounts for short traffic spikes. By discarding the highest 5% of samples, the measurement reflects your sustained bandwidth usage rather than momentary peaks.

To calculate the 95th percentile, Cloudflare records bandwidth to and from the global network at five-minute intervals, sorts these measurements in descending order, and discards the top 5% of recorded measurements. The highest remaining value is the 95th percentile bandwidth measurement for that time period.

# Device compatibility

import { GlossaryTooltip } from "~/components";

Cloudflare WAN (formerly Magic WAN) is compatible with any device that supports <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> with the [supported configuration parameters](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters) or supports <GlossaryTooltip term="GRE tunnel">GRE</GlossaryTooltip>.

The matrix below includes example devices and links to the integration guides.

| Appliance | GRE tunnel | IPsec tunnel |
| --- | --- | --- |
| [Aruba EdgeConnect](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/aruba-edgeconnect/) |✅ | ✅ |
| Cisco ASA | Compatibility on roadmap | Specifications compatible[^1] |
| [Cisco IOS XE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/cisco-ios-xe/) | ✅ | ✅ |
| [Cisco Meraki MX (static routing)](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/cisco-meraki-static/) | - | ✅ |
| [Cisco SD-WAN](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/viptela/)  | ✅ | ✅ |
| [Fortinet](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/fortinet/)| Specifications compatible[^1] | ✅ |
| [Furukawa Electric FITELnet](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/fitelnet/) | - | ✅ |
| [HPE Juniper Networking SRX Series Firewalls](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/juniper/) | Specifications compatible[^1] | ✅ |
| [Palo Alto Networks Next-Generation Firewall](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/palo-alto/) | Specifications compatible[^1] | ✅ |
| [pfSense](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/pfsense/)| ✅ | ✅ |
| Prisma SD-WAN (Palo Alto) | Specifications compatible[^1] | Specifications compatible[^1] |
| Riverbed  | Specifications compatible[^1] | Specifications compatible[^1] |
| [SonicWall](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/sonicwall/)| - | ✅ |
| [Sophos Firewall](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/sophos-firewall/) | ✅ | ✅ |
| [strongSwan](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/strongswan/) | - | ✅ |
| [Ubiquiti](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/ubiquiti/) | - | ✅ |
| [Velocloud](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/velocloud/) | - | ✅ |
| Versa  | Specifications compatible[^1] | Compatibility on roadmap |
| [VyOS](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/vyos/)| ✅ | ✅ |
| [Yamaha RTX Router](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/yamaha/) | - | ✅ |

| VPN | GRE tunnel | IPsec tunnel |
| --- | --- | --- |
| [Alibaba Cloud VPN Gateway](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/alibaba-cloud/) | - | ✅ |
| [Amazon AWS Transit Gateway](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/aws/)          | - | ✅ |
| [Azure VPN Gateway](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/azure/)                 | - | ✅ |
| [GCP Cloud VPN](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/google/)                    | - | ✅ |
| [Oracle Cloud](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/oracle/) 										| - | ✅ |

[^1]: Specifications compatible per vendor documentation

# GRE and IPsec tunnels

import { Details, GlossaryTooltip } from "~/components";

## Tunnels and encapsulation

To route traffic between Cloudflare's global network and your origin network, Cloudflare WAN wraps your original packets inside an outer packet — a process called encapsulation. The outer packet carries your traffic across the Internet to its destination, where it is unwrapped (decapsulated) and delivered.

Cloudflare WAN uses two encapsulation protocols: <GlossaryTooltip term="GRE tunnel" link="https://www.cloudflare.com/learning/network-layer/what-is-gre-tunneling/">Generic Routing Encapsulation (GRE)</GlossaryTooltip> and <GlossaryTooltip term="IPsec tunnel" link="https://www.cloudflare.com/learning/network-layer/what-is-ipsec/">IPsec</GlossaryTooltip>. GRE is stateless and simpler to configure but does not encrypt traffic. IPsec encrypts traffic and authenticates the source, providing stronger security. Both create tunnels — logical point-to-point connections between Cloudflare and your network. Cloudflare sets up tunnel endpoints on global network servers inside your network namespace, and you set up tunnel endpoints on routers at your data center.

To accommodate additional header data introduced by encapsulation, you must adjust the <GlossaryTooltip term="maximum segment size (MSS)">maximum segment size (MSS)</GlossaryTooltip> to comply with the standard Internet routable maximum transmission unit (MTU), which is 1500 bytes.

For instructions, refer to [Set maximum segment size](/cloudflare-one/networks/connectors/cloudflare-wan/get-started/#set-maximum-segment-size).

This diagram illustrates the flow of traffic with Cloudflare WAN.

<pre class="mermaid">
{`sequenceDiagram
accTitle: Tunnels and encapsulation
accDescr: This diagram shows the flow of traffic with Cloudflare WAN.
participant A as Client machine
participant B as Cloudflare Cloudflare WAN
participant C as Origin router
A->>B: Payload <br> Protocol <br> IP header
Note left of A: Ingress <br> traffic
B->>C: Payload <br> Protocol <br> IP header <br> GRE <br> IP header
C->>A: IP header <br> Protocol <br> Payload
Note right of C: Egress <br> traffic`}
</pre>

<br />

:::note
By default, your Internet Service Provider (ISP) interface routes egress packets, not Cloudflare.
:::

## Anycast

Traditional tunnels connect two fixed endpoints — one device on each side. Cloudflare WAN uses a different model: <GlossaryTooltip term="anycast" link="https://www.cloudflare.com/learning/cdn/glossary/anycast-network/">anycast</GlossaryTooltip> IP addresses for Cloudflare's tunnel endpoints. In the anycast model, any server in any Cloudflare data center can receive traffic and must be capable of encapsulating and decapsulating packets for any tunnel. This means your tunnel is not tied to a single Cloudflare server — traffic is handled by whichever data center is closest to the source.

This works with <GlossaryTooltip term="GRE tunnel">GRE</GlossaryTooltip> tunnels because the GRE protocol is stateless. Cloudflare processes each packet independently without requiring any negotiation or coordination between tunnel endpoints. Tunnel endpoints bind to IP addresses but not to specific devices. Any device that can strip off the outer headers and then route the inner packet can handle any GRE packet sent over the tunnel.

For <GlossaryTooltip term="IPsec tunnel">IPsec</GlossaryTooltip> tunnels, the customer's router negotiates the creation of an IPsec tunnel with Cloudflare using the <GlossaryTooltip term="Internet key exchange (IKE)">Internet Key Exchange (IKE)</GlossaryTooltip> protocol. Because IPsec is stateful (it requires shared keys and session parameters), one Cloudflare server handles the initial negotiation, then propagates the tunnel details (traffic selectors, keys, etc.) across all Cloudflare data centers. The result is that any Cloudflare server can handle traffic for that IPsec tunnel, even though only one server negotiated the setup.

Cloudflare's anycast architecture provides a conduit to your tunnel for every server in every data center on Cloudflare's global network. The following image shows this architecture.

```mermaid
flowchart LR
accTitle: Anycast tunnel
accDescr: Multiple servers in data center preparing packets to send through anycast tunnel.

a(User)

subgraph 1
direction LR
b(Cloudflare global <br> network server)
c(Cloudflare global <br> network server)
d(Cloudflare global <br> network server)
e(Cloudflare global <br> network server)
f(Cloudflare global <br> network server)
g(Cloudflare global <br> network server)
h(Cloudflare global <br> network server)
end

subgraph 2
i("Acme router <br> 198.51.100.1")
j("FTP server <br> (203.0.113.100)")
end

subgraph 3
x("Acme router <br> 198.51.100.1")
z("FTP server <br> (203.0.113.100)")
end

a --> 1== Cloudflare anycast GRE <br> single endpoint ==>i --> j

1== Cloudflare anycast IPsec <br> single endpoint ==>x --> z
```

## IPsec tunnels

:::note[Post-quantum IPsec]
Post-quantum key agreement for IPsec tunnels with third-party devices is generally available, with confirmed interoperability for [validated third-party platforms](#tested-third-party-vendor-interoperability). Post-quantum IPsec is also generally available when using the [Cloudflare One Appliance](/cloudflare-wan/configuration/appliance/).
:::

[IPsec](https://www.cloudflare.com/learning/network-layer/what-is-ipsec/) is a group of protocols that work together to set up encrypted connections between devices. It helps keep data you send over public networks secure. Organizations often use IPsec to set up Virtual Private Networks (VPNs), and it works by encrypting IP packets and authenticating the source where the packets come from.

For information on how to set up an IPsec tunnel, refer to [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/). To learn more about the configuration parameters Cloudflare WAN uses to create an IPsec tunnel, keep reading.

### How IKEv2 establishes an IPsec tunnel

Cloudflare WAN uses the following stages to establish an IPsec tunnel:

- **Initial Exchange** (`IKE_SA_INIT`): IKE peers negotiate parameters for the IKE Security Association (SA) and establish a shared secret for key derivation, and when relevant, signal support for post-quantum key exchange with [RFC 9370](https://datatracker.ietf.org/doc/rfc9370/). When [downgrade protection](#improved-downgrade-protection-beta) is enabled, Cloudflare also sends an `IKE_SA_INIT_FULL_TRANSCRIPT_AUTH` notification during this exchange to signal support for full transcript authentication. After this exchange, the peers have a secure communication channel but they have not yet authenticated each other.
- **Intermediate Exchange** (`IKE_INTERMEDIATE`): If both peers support RFC 9370, they perform an additional key exchange using ML-KEM (Module-Lattice-based Key-Encapsulation Mechanism), a post-quantum key exchange specified in [draft-ietf-ipsecme-ikev2-mlkem](https://datatracker.ietf.org/doc/draft-ietf-ipsecme-ikev2-mlkem/). This creates a hybrid shared secret by combining a secret derived from classical Diffie-Hellman (established during the `IKE_SA_INIT`) with post-quantum ML-KEM to protect against [harvest-now, decrypt-later](https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later) attacks.
- **Auth Exchange** (`IKE_AUTH`): Using the keys established from both the `IKE_SA_INIT` and the `IKE_INTERMEDIATE` exchange, IKE peers mutually authenticate each other. After authentication, they establish the IKE security association (SA). Next, the peers negotiate and establish an IPsec tunnel, known as a Child SA.
- **Rekeying**: Periodically, or through manual intervention, IKE SAs can be rekeyed to generate new SAs with fresh keys for the session. This rekey operation is performed for both the IKE SA (to refresh the control plane) and the Child SAs (to refresh the data plane). When a hybrid exchange is in use (RFC 9370), the rekey process for the IKE SA will once again perform the parallel classical (DH) and post-quantum (ML-KEM) exchanges to ensure continued quantum resistance.

:::note
The IKE SA and the Child SA are separate entities, each with their own parameters. The Child SA is the dataplane IPsec tunnel where user traffic flows (that is, the ESP layer of IPsec). The IKE SA sets up and manages the Child SA.
:::

In summary, IKEv2 creates an IKE SA that uses certain cryptographic transforms. It then uses that IKE SA to create a Child SA which itself uses certain cryptographic transforms. The following configuration section details which of these transforms Cloudflare WAN currently supports for IKE SAs and Child SAs.

:::note
IKE is one of the protocols that makes up IPsec. Cloudflare only operates as an IKE responder.
:::

### Supported configuration parameters

Choose from the following configuration parameters that Cloudflare WAN supports, based on what your appliance supports.

<Details header="IKE SA (also known as Phase 1)">

Documentation sometimes refers to IKE SA as Phase 1 as per IKEv1 language.

- **Encryption**
  - AES-GCM-16 with 128-bit or 256-bit key length
  - AES-CBC with 256-bit key length

- **Integrity** (sometimes referred to as Authentication)
  - SHA2-256

- **Key Exchange Method** (formerly Diffie-Hellman group):
  Cloudflare supports the following key exchange methods for the IKE SA. Note that [RFC 9370](https://datatracker.ietf.org/doc/rfc9370/) renames "DH Group" to "Key Exchange Method" to accommodate non-DH algorithms.

  - **Post-quantum hybrid (recommended)**: ML-KEM-768 as an additional Key Exchange to DH Group 20 (per RFC 9370 and [draft-ietf-ipsecme-ikev2-mlkem](https://datatracker.ietf.org/doc/draft-ietf-ipsecme-ikev2-mlkem/))
  - Post-quantum hybrid: ML-KEM-1024 as an additional Key Exchange to DH Group 20 (per RFC 9370 and [draft-ietf-ipsecme-ikev2-mlkem](https://datatracker.ietf.org/doc/draft-ietf-ipsecme-ikev2-mlkem/))
  - Classical DH group 20 (384-bit random ECP group)
  - Classical DH group 14 (2048-bit MODP group)
  - Classical DH group 5 (1536-bit MODP group)

	:::caution
	Cloudflare recommends the **ML-KEM-768 + DH Group 20** hybrid exchange for post-quantum key agreement. If your appliance does not yet support RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem, use DH group 20.
	:::

- **Pseudorandom function (PRF)**

	Do not confuse this with Perfect Forward Secrecy (PFS). You often cannot configure PRF.
  - SHA2-256
  - SHA2-384
  - SHA2-512

</Details>

<Details header="Child SA (also known as Phase 2 or IPsec SA)">

The Child SA. Documentation sometimes refers to this as Phase 2 as per IKEv1 language.

- **Encryption**:

  - AES-GCM-16 with 128-bit or 256-bit key length
  - AES-CBC with 128-bit or 256-bit key length

- **Integrity** (sometimes referred to as Authentication.)

  - SHA2-256
  - SHA-1

  :::note
  When using AES-GCM-16, you do not need an integrity algorithm because AES GCM includes integrity checking (since it is an Authenticated Encryption with Associated Data (AEAD) algorithm). Even when using an AEAD algorithm, however, some routers still require you to select an integrity algorithm.
  :::

- **Perfect Forward Secrecy (PFS) group**

	Documentation sometimes refers to this as Phase 2 Diffie-Hellman Group. Do not confuse this with PRF. Cloudflare supports the following Diffie-Hellman (DH) groups.

  - DH group 20 (384-bit random ECP group)
  - DH group 14 (2048-bit MODP group)
  - DH group 5 (1536-bit MODP group)

	:::note[Post-quantum security]
	If the Child SA uses DH groups for Perfect Forward Secrecy, it is still protected against quantum threats if the parent IKE SA was established using a hybrid ML-KEM exchange.
	:::

	:::caution
	Cloudflare recommends that you use only one DH group when configuring your device, specifically **DH group 20**.
	:::

	:::note
	Cloudflare recommends configuring the Child SA rekey interval (SA lifetime) between 30 minutes and 8 hours.
	:::

</Details>

<Details header="Required configuration parameters">

- The IKE version must be IKEv2.
- The IKE authentication method must be Pre-Shared Key (PSK).
- Cloudflare supports NAT traversal (NAT-T). Cloudflare also supports NAT-T starting on port `4500`.
- (Uncommon) You must disable Extended Sequence Numbers (ESN).
- If your tunnels need replay protection, enable Dead Peer Detection (DPD) in your router and select the option that restarts your IKE session when a DPD timeout occurs. This "restart" option ensures that the connection can recover in the event that a Cloudflare server goes offline. If your router does not offer this setting, check the router documentation for its dead peer detection behavior.
- **Multiple Key Exchange ([RFC 9370](https://datatracker.ietf.org/doc/rfc9370/))**: To use post-quantum security, your router must support the `IKE_INTERMEDIATE` and `IKE_FOLLOWUP_KE` exchange as defined in RFC 9370 and [draft-ietf-ipsecme-ikev2-mlkem](https://datatracker.ietf.org/doc/draft-ietf-ipsecme-ikev2-mlkem/). Because post-quantum public keys and ciphertexts (like ML-KEM-768) are larger than classical keys, you must enable IKEv2 fragmentation on your router to prevent packets from exceeding the 1,500-byte MTU. When configuring the first Additional Key Exchange, use the IANA-assigned Transform ID `36` for ML-KEM-768, or Transform ID `37` for ML-KEM-1024.

</Details>

<Details header="Optional configuration parameters">

- Disable [anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/).
- **`NULL` encryption for IPsec (not recommended):** Do not use this option unless necessary because it reduces security by leaving IPsec traffic unencrypted. You must explicitly opt in to use this option. Using this option also eliminates post-quantum protections.

</Details>

### Tested third-party vendor interoperability

The following third-party vendors have been tested and validated to interoperate with Cloudflare IPsec for post-quantum key agreement:

| Vendor   | Product / Version                                            | ML-KEM variant | DH group | Notes                                                         |
| -------- | ------------------------------------------------------------ | -------------- | -------- | ------------------------------------------------------------- |
| Cisco    | Cisco 8000 Series Secure Routers with IOS XR Release 26.1.1 | ML-KEM-1024    | Group 20 | Requires RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem support. |
| Fortinet | FortiOS 7.6.6+                                               | ML-KEM-768     | Group 20 | Requires RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem support. |
| Fortinet | FortiOS 7.6.6+                                               | ML-KEM-1024    | Group 20 | Requires RFC 9370 and draft-ietf-ipsecme-ikev2-mlkem support. |

Cloudflare continues to test and validate additional third-party devices. If you have successfully configured post-quantum IPsec with a vendor not listed here, contact your account team.

### Supported IKE ID formats

Cloudflare WAN supports the following IKE ID types for IPsec:

<Details header="Request for Comments (RFC) name `ID_RFC822_ADDR`">
- **Format**: `ipsec@<TUNNEL_ID>.<ACCOUNT_ID>.ipsec.cloudflare.com`
- **Example**: `ipsec@f5407d8db1a542b196c59f6d04ba8bd1.123456789.ipsec.cloudflare.com`
</Details>

<Details header="RFC name `ID_FQDN`">
- **Format**: `<TUNNEL_ID>.<ACCOUNT_ID>.ipsec.cloudflare.com`
- **Example**: `f5407d8db1a542b196c59f6d04ba8bd1.123456789.ipsec.cloudflare.com`
</Details>

<Details header="RFC name `ID_KEY_ID`">
- **Format**: `<ACCOUNT_ID>_<TUNNEL_ID>`
- **Example**: `123456789_f5407d8db1a542b196c59f6d04ba8bd1`
</Details>

Additionally, Cloudflare supports the IKE ID type of `ID_IPV4_ADDR` if the following two conditions are met:

1. You set the IPsec tunnel's `customer_endpoint` value.
2. The combination of `cloudflare_endpoint` and `customer_endpoint` is unique among the customer's IPsec tunnels.

:::caution
Make sure each IPsec tunnel has a unique combination of a [Cloudflare endpoint and customer endpoint](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/). If this combination is not unique among your IPsec tunnels, you should use one of the custom IKE formats (`ID_RFC822_ADDR`, `ID_FQDN`, or `ID_KEY_ID`) to specify the tunnel ID and account ID. This helps Cloudflare link the IKE packet to the right IPsec tunnel for tasks like authentication.
:::

### Route-based vs. policy-based VPNs

Although Cloudflare supports both route-based and policy-based VPNs, we recommend route-based VPNs.

If route-based VPNs are not an option and you must use policy-based VPNs, be aware of the following limitations:

- Cloudflare only supports a single set of traffic selectors per Child SA.
- A policy must cover reply-style health checks — that is, they must match traffic selectors — otherwise, Cloudflare drops them, just like any other traffic from an IPsec tunnel that does not match a policy.
- A single IPsec tunnel can only contain around 100 Child SAs. Therefore, there is effectively a limit on the number of different policies per tunnel.

### Improved downgrade protection (beta)

:::note[Beta]
This feature is in beta. Contact your account team to turn on the `ipsec_downgrade_protection` flag for your account.
:::

IKEv2's original authentication design has each endpoint sign only its own outbound messages, not the full handshake transcript. A quantum-capable [on-path attacker](https://www.cloudflare.com/learning/security/threats/on-path-attack/) can exploit this to create a "split view" of the handshake, tricking the endpoints into downgrading a post-quantum connection back to classical cryptography even when both sides support post-quantum key exchange. 

To address this, Cloudflare supports the [`IKE_SA_INIT_FULL_TRANSCRIPT_AUTH`](https://datatracker.ietf.org/doc/draft-ietf-ipsecme-ikev2-downgrade-prevention/) IKEv2 extension. When enabled, both IKEv2 peers sign the entire handshake transcript during the authentication exchange, rather than only their own messages. This prevents an attacker from downgrading the connection without being detected. 

**How it works:**

- When the feature flag is enabled, Cloudflare (acting as IKE responder) unconditionally includes an `IKE_SA_INIT_FULL_TRANSCRIPT_AUTH` notification in its `IKE_SA_INIT` response.
- If the initiator also supports the extension, both sides use full transcript authentication, which improves protection again downgrade attacks.
- If the initiator does not support the extension, the handshake proceeds with standard IKEv2 authentication. Both parties must support the extension for downgrade protection to be effective.

**Requirements:**

- Your IKEv2 initiator must support the `IKE_SA_INIT_FULL_TRANSCRIPT_AUTH` notification as defined in [draft-ietf-ipsecme-ikev2-downgrade-prevention](https://datatracker.ietf.org/doc/draft-ietf-ipsecme-ikev2-downgrade-prevention/).

### Troubleshooting

For help resolving tunnel issues:

- [Troubleshoot tunnel health](/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/) - Diagnose and fix health check failures
- [Troubleshoot with IPsec logs](/logs/logpush/logpush-job/datasets/account/ipsec_logs/) - Use Logpush to analyze IPsec handshake issues

## Troubleshooting

For help resolving tunnel issues:
- [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/) - Diagnose and fix health check failures
- [Troubleshoot with IPsec logs](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/ipsec-troubleshoot/) - Use Logpush to analyze IPsec handshake issues

# How Cloudflare calculates tunnel health alerts

Tunnel health alerts notify you when the reliability of your tunnel connections drops below an acceptable threshold. Understanding how Cloudflare calculates these alerts helps you interpret notifications and distinguish between brief, recoverable issues and sustained problems that require attention.

Cloudflare uses a multi-window approach that combines short-term and long-term metrics to avoid alerting on transient issues while still detecting real degradation. The following sections explain the key concepts behind this process.

### Service-level indicator (SLI)

SLI is the ratio of positive events to total events. An SLI of 0% means the feature is not working at all, and an SLI of 100% means the feature is fully working as expected.

:::note
Cloudflare counts degraded health checks as failed health checks when calculating SLIs.
:::

### Service-level objectives (SLOs)

SLOs are the threshold for the SLI and set a target level of reliability for IPsec/GRE tunnels. For example, an SLO could be 99.9% of tunnel states being healthy over the past 30 days. Cloudflare calculates the SLI values for the SLO based on the [`down` tunnel state value](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/#tunnel-state-determination), not on the timeout results from tunnel health checks.

### Error budget

The error budget is the amount of unsuccessful events that can happen over the course of the SLO time window while maintaining the service at the level of availability the SLO defines.

The SLO is a target percentage, and the error budget equals 100% minus the SLO. For example, assume that during 30 days there were one million tunnel health checks in your account, and your SLO is set to 99.9%. The error budget for this case would be:

```txt
number of events x (1 - SLO) = 1,000,000 x (1-0.999) = 1,000
```

This means the SLO allows for 1,000 unsuccessful tunnel health checks over the course of 30 days. However, what happens if all errors happen in one hour instead of 30 days? This leads to the concept of burn rate.

### Burn rate

The burn rate measures how fast you expend the error budget over a given time window relative to the SLO window. In the example, an SLO of 99.9% means you can observe 1,000 tunnel health check failures over the course of 30 days. However, those same 1,000 health check failures are not acceptable during one hour.

## When Cloudflare alerts you

To determine when to send Tunnel health alerts, Cloudflare relies on a multi-window, multi-burn rate approach. Every five minutes, Cloudflare analyzes the last hour and the last five minutes of data. Cloudflare calculates the SLI for the short window (five minutes) and long window (one hour) of data.

Cloudflare only alerts you when both the short and long windows fall short of the configured threshold. This means both windows must fail the threshold for an alert to trigger. For example, if you defined a threshold of 99%:

- Short window: 99.2%, Long window: 99%. Cloudflare would not trigger an alert because the short window exceeds the 99% threshold.
- Short window: 98%, Long window: 98%. Cloudflare would trigger an alert because both windows fall short of the 99% threshold.

# Reference

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Maximum transmission unit and maximum segment size

Because Cloudflare WAN wraps your traffic in additional headers (encapsulation), the effective space available for your original data in each packet is reduced. If you do not account for this overhead, packets may be too large for the network path and will be dropped or fragmented — leading to performance loss or failed connections. This page explains the two key values you need to configure: maximum transmission unit (MTU) and maximum segment size (MSS).

## MTU and MSS

The [maximum transmission unit (MTU)](https://www.cloudflare.com/learning/network-layer/what-is-mtu/) is a measurement representing the largest data packet that a network-connected device will accept. The MTU almost always applies to Layer 3 of the Open Systems Interconnection (OSI) model in networking and includes the entire packet, including all headers (Transmission Control Protocol (TCP), Internet Protocol (IP), etc.) and the data (payload) itself. For example, packets must not exceed 1,500 bytes to route through the Internet.

The [maximum segment size (MSS)](https://www.cloudflare.com/learning/network-layer/what-is-mss/) refers to the amount of data that you can send in a single TCP datagram packet. You determine this value by subtracting the size of the IP and TCP headers from the MTU, which instructs the router how large the payload can be. It applies to Layer 4 of the OSI model in networking.

One common misconception about MSS/MTU is that setting these values negatively impacts performance. While there is a slight performance penalty, it is worse not to configure these values to account for the specificities of your network.

## Encapsulation

Since Cloudflare WAN uses encapsulation to deliver its services, it is also important to understand why MTU and MSS matter in this case.

Encapsulation adds bytes to the packet because Cloudflare adds a new IP header and (often) some sort of encapsulating header to every packet. For example, in the case of Generic Routing Encapsulation (GRE) for Internet Protocol version 4 (IPv4), encapsulation adds 24 bytes — 20 bytes for the IPv4 header and 4 bytes for the GRE tunnel header.

A network interface that performs GRE encapsulation needs to account for the added overhead by reducing its MTU. Since the MTU maximum size is 1,500 bytes, for IPv4 this means the MTU becomes 1,476 bytes (the original 1,500 bytes minus the 24 bytes from the GRE encapsulation). This reduced MTU defines the maximum size of the IP packet that GRE can encapsulate.

## Fragmentation

If the data packet is larger than what the network interface can accept, the network must either drop or fragment it into smaller packets. When fragmentation occurs, Cloudflare only accepts data packets that it can completely reassemble. If some fragments are missing, Cloudflare discards all received fragments. Cloudflare does not forward incomplete packets to the customer.

Setting the do not fragment (DF) bit in the TCP header to `1` denotes that the network must drop the packet rather than fragment it if the packet is larger than the MTU that intermediary network devices can accept. Most TCP implementations set the DF bit to `1` to avoid the potential issues that fragmentation causes.

If you experience issues with fragmentation and cannot set an MSS clamp, Cloudflare can clear the DF bit for you. When you enable this option, Cloudflare fragments packets greater than 1,500 bytes, and your infrastructure reassembles the packets after decapsulation. Use this as a last resort option. Contact your account team for more information.

### Fragmentation in Cloudflare WAN

Consider a UDP datagram of size 3,000 bytes (8 bytes for the UDP header + 2,992 bytes for the UDP data). To fit within a standard 1,500-byte MTU, this UDP datagram would be fragmented across three IP packets as follows:

![A diagram showing a UDP datagram and its various components.](~/assets/images/magic-transit/mtu-mss/udp-datagram.png)

Suppose that the UDP datagram has source port `389` and is destined for a Cloudflare WAN customer IP address. Suppose also that the Cloudflare WAN customer has a firewall rule in place that drops UDP traffic with source port `389`, a common [Connectionless Lightweight Directory Access Protocol (CLDAP)](https://blog.cloudflare.com/reflections-on-reflections) reflection attack vector.

The three preceding packet fragments will arrive at Cloudflare, but only the first fragment contains a UDP header with source port information. The second and third fragments contain UDP data but do not have UDP header information.

So the question is: which of these fragments does Cloudflare drop and which does it deliver to the customer? If Cloudflare only drops the first parts of fragmented packets, the remaining parts could still generate a large amount of traffic during a Denial of Service (DoS) attack.

### How Cloudflare handles fragments

The following diagram shows how the three UDP fragments in the preceding example flow through Cloudflare and Cloudflare WAN. The main takeaways are:

- **Cloudflare never sends incomplete packets to customers**: If Cloudflare does not see all parts of a packet required to fully reassemble that packet, Cloudflare will not send the partial data fragments to the customer.
- **Cloudflare Network Firewall operates on fully reassembled packets, not individual fragments**: This means that filters that match on UDP/TCP header information, for example, apply to the fully reassembled packet, not just the initial fragment. Cloudflare will not leak non-initial fragments to customers.
- **Customers can still see fragmented packets**: By default (without `clear_dont_fragment_bit` set), Cloudflare fragments packets to fit within the configured MTU of the tunnel before sending the data to the customer. If a packet is larger than 1,476 bytes, Cloudflare will fragment it and send those fragments to the customer for reassembly.

In all cases, Cloudflare sends all fragments to the customer.

![A diagram showing how Cloudflare handles fragmentation.](~/assets/images/magic-transit/mtu-mss/fragmentation.png)

## MSS clamping

Maximum segment size (MSS) is a TCP setting that limits the size of TCP segments. The SYN packets set this option during the three-way handshake.

By default, a TCP endpoint sets its MSS value based on its local network interface MTU. For example, for IPv4, if the MTU is 1,500 bytes then MSS becomes 1,460 bytes (1,500 bytes minus 20 bytes from the IPv4 header minus 20 bytes from the TCP header).

MSS is a tool that you can use to configure TCP packet size behavior. If a TCP endpoint sits behind a network with reduced MTU, changing the MSS value to match the actual path MTU value forces remote endpoints to send packets that fit within the specified MTU. So, if an IPv4 TCP endpoint sits behind a GRE tunnel with an MTU of 1,476 bytes, the MSS value in its TCP SYN packets should be 1,436 bytes - 1,476 bytes minus the 20 bytes from the IPv4 header, minus the 20 bytes from the TCP header.

One way to modify the MSS setting is by changing the MTU of the network interface in the router's WAN interface to match the path MTU. Another way to modify MSS is by applying an MSS clamp, where you configure an intermediary network device - such as a router - to modify the MSS TCP option on-the-fly when packets pass through it. Note that changing the MTU on the interface of an intermediary network device is not the same as applying an MSS clamp, and it does not change the TCP MSS value.

Refer to [MSS clamping recommendations](#mss-clamping-recommendations) for information on what you should set your MSS clamping to, depending on the type of tunnel.

:::caution
Cloudflare only recommends applying a MSS clamp to adjust the size of TCP packets. Changing the MTU of a network interface is not recommended as this might have unforeseen impacts on traffic.
:::

## MSS clamping recommendations

### GRE tunnels as off-ramp

The MSS value depends on how your network is set up.

- **On your edge router**: Apply the clamp to the GRE tunnel internal interface (meaning where the egress traffic will traverse). Set the MSS clamp to 1,436 bytes. Your devices may do this automatically once the tunnel is configured, but it depends on your devices.

### IPsec tunnels

For IPsec tunnels, the value you need to specify depends on how your network is set up. The MSS clamping value is lower than for GRE tunnels because the physical interface sees IPsec-encrypted packets, not TCP packets, and MSS clamping does not apply to those.

- **On your edge router**: Apply this on your IPsec tunnel internal interface (meaning where the egress traffic will traverse). Your devices may do this automatically once the tunnel is configured, but it depends on your devices. Set the TCP MSS clamp to 1,360 bytes maximum.

# Traffic steering

import { AnchorHeading, Aside, DashButton } from "~/components";

<AnchorHeading depth={2} title="Cloudflare Virtual Network routing table" />

When traffic enters Cloudflare's network, it needs to reach the correct destination in your infrastructure — a specific data center, office, or cloud environment. Traffic steering controls how Cloudflare makes these routing decisions.

The Cloudflare Virtual Network is a virtual network overlay, private to your account, that spans all Cloudflare data centers globally. This overlay network provides:

- Magic Transit delivery for [Denial of Service (DoS)](/ddos-protection/) and [Cloudflare Network Firewall](/cloudflare-network-firewall/) filtered Internet traffic, from the entry data center where the traffic ingressed, to your publicly addressed edge/border network.
- Cloudflare WAN packet transport between IPsec/GRE tunnels, interconnects, [Cloudflare Load Balancer](/load-balancing/), and [Zero Trust](/cloudflare-one/) connections such as [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), [Remote Browser Isolation](/cloudflare-one/remote-browser-isolation/), [Access](/cloudflare-one/access-controls/policies/), and [Gateway](/cloudflare-one/traffic-controls/).

The Cloudflare Virtual Network supports routing the Cloudflare WAN traffic through anycast tunnels using [GRE and Internet Protocol Security (IPsec)](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/) or [CNI with Dataplane v2](/network-interconnect/). You can add entries to the Cloudflare Virtual Network routing table through static route configuration or through routes learned through BGP peering. Traffic can also be routed automatically according to tracked flow state.

<Aside>
	For a conceptual overview of virtual networks, including how they work across
	Cloudflare products, refer to [Virtual
	networks](/cloudflare-one/networks/virtual-networks/). Cloudflare WAN
	connections (IPsec, GRE, CNI) currently use only the default virtual network.
</Aside>

### Allowed IP ranges

The following IPv4 address ranges are allowed in the Cloudflare Virtual Network routing table:

- [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) address space, specifically `10.0.0.0/8`, `172.16.0.0/12`, and `192.168.0.0/16`.

When using Cloudflare WAN and Cloudflare Tunnel together, consider the IP ranges utilized in the static routes of Cloudflare Tunnel when selecting static routes for Cloudflare WAN. For more information, refer to [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-tunnel/).

For prefixes outside RFC 1918, contact your Cloudflare customer service manager.

### Default routing

If traffic does not match any route you have configured in the virtual network, Cloudflare applies default behavior based on the destination address type:

- **Public (Internet-routable) addresses**: Traffic exits to the Internet.
- **Private addresses** ([RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) or [CGNAT/RFC 6598](https://datatracker.ietf.org/doc/html/rfc6598)): Traffic is dropped (null routed), because private addresses are not routable on the public Internet and Cloudflare has no path to deliver them without a matching route.

### Route prioritization

Cloudflare WAN steers traffic along tunnel routes based on route entry priorities.

- Lower values have greater priority.
- When the priority values for prefix entries match, Cloudflare uses [equal-cost multi-path (ECMP)](#equal-cost-multi-path-routing) packet forwarding to route traffic. You can apply an optional weight value to static routes to [modify ECMP tunnel distribution](#set-priority-and-weights-for-static-routes).
- Cloudflare routing applies longest-prefix match. A more specific static route (like `/30`) always takes precedence over a less specific one (like `/29`), regardless of tunnel priority — unless you remove the more specific route.
- When BGP and static routes have the same prefix and priority, Cloudflare enforces priority by preferring static routes over BGP routes. This ensures that manually configured static routes take precedence unless you explicitly deprioritize them.

### Set priority and weights for static routes

The priority value for static routes is directly configured as part of the route object in the Cloudflare [dashboard or through the API](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route). For example:

| Prefix            | NextHop        | Priority |
| ----------------- | -------------- | -------- |
| `10.10.10.100/24` | `TUNNEL_1_IAD` | `200`    |
| `10.10.10.100/24` | `TUNNEL_2_IAD` | `200`    |
| `10.10.10.100/24` | `TUNNEL_3_ATL` | `100`    |
| `10.10.10.100/24` | `TUNNEL_4_ATL` | `100`    |

In this example, tunnels with priority of `100` are preferred to tunnels with priority of `200` because lower numbers have greater priority.

Optionally, you can assign weights to distribute traffic more effectively among multiple tunnels. Weight values determine traffic proportion, with higher weights receiving more traffic. The maximum weight value is `256`.

In the following example, `TUNNEL_2_IAD` is likely to receive twice as much traffic as `TUNNEL_1_IAD`.

| Prefix            | NextHop        | Priority | Weight |
| ----------------- | -------------- | -------- | ------ |
| `10.10.10.100/24` | `TUNNEL_1_IAD` | `100`    | `64`   |
| `10.10.10.100/24` | `TUNNEL_2_IAD` | `100`    | `128`  |
| `10.10.10.100/24` | `TUNNEL_3_ATL` | `100`    | `192`  |
| `10.10.10.100/24` | `TUNNEL_4_ATL` | `100`    | `255`  |

Aside from priority, scoping static routes to specific geographic regions also impacts how traffic is steered. Refer to [Scoping routes to specific regions](#scoping-routes-to-specific-regions) for more details.

### Set priority for BGP routes

When BGP advertises a route, Cloudflare automatically adds it to the Cloudflare Virtual Network routing table with a default priority of `100` which applies to [all regions](#scoping-routes-to-specific-regions). However, if a static route exists with the same prefix and priority, the static route always takes precedence over the BGP route. Set a different priority for static routes (more or less than `100`) depending on which you want to prioritize. Lower values have greater priority.

Additionally, when multiple BGP routes exist with the same prefix length and priority, ECMP distributes traffic across them using [equal-cost multi-path (ECMP) routing](#equal-cost-multi-path-routing).

### Change route priorities with BGP attributes

Cloudflare supports traffic engineering through BGP communities and AS prepending. You can use these traffic routing techniques to set route priorities and perform traffic engineering across multiple interconnects.

#### BGP communities for setting route priority

The default BGP route priority is `100`. This base priority can be adjusted using communities. For example, when a route is tagged with the community `13335:60010` its priority is set to `10`. This makes it a higher priority than the default of `100` because lower numeric priorities are preferred.

The community values supported for setting base route priority are:

- `13335:60010`: Set base route priority to `10`
- `13335:60050`: Set base route priority to `50`
- `UNSET`: Set base route priority to `100`
- `13335:60150`: Set base route priority to `150`
- `13335:60200`: Set base route priority to `200`
- `13335:60901`: Set base route priority to `501000`
- `13335:60902`: Set base route priority to `1001000`

Setting multiple base priority communities in the same prefix update message is a misconfiguration. In this situation, Cloudflare prefers the highest priority (lowest integer value).

#### AS path prepending for adjusting route priority

For each additional mention of your ASN in the received AS path, Cloudflare adds `10` to the route's base priority. By increasing the priority number, the route becomes less preferred.

For example, if your ASN is `65000` then the `BGP UPDATE` to Cloudflare will be:

```txt
# No change to base priority.
AS_PATH: 65000 65200

# Add 10 to base priority for 1 prepend of 65000
AS_PATH: 65000 65000 65200

# Add 20 to base priority for 2 prepend of 65000
AS_PATH: 65000 65000 65000 65200
```

#### How communities and prepends work together

Cloudflare adjusts route priority when using AS prepending with communities. For example, if a route is tagged with `13335:60150`, the base priority is set to `150`. If you prepend your ASN twice, Cloudflare adds `10` for each prepend, increasing the route priority to `180`.

<AnchorHeading title="Automatic Return Routing" depth={2} />

Automatic Return Routing (ARR) allows Cloudflare to track network flows from your Cloudflare WAN (formerly Magic WAN) connected locations, ensuring return traffic is routed back to the connection where it was received without requiring static or dynamic routes. This functionality requires [Unified Routing mode](#unified-routing).

Instead of relying on static or dynamic routes for the return path, Cloudflare WAN learns flows and remembers which connection a given flow arrived on. For any matching return traffic, Cloudflare WAN uses this learned state to choose the next hop. This simplifies configuration, reduces the number of routes you must manage, and helps preserve symmetry for stateful traffic.

ARR provides the following benefits:

- **Removes the need for return routes**: For supported traffic types like new TCP connections (TCP SYN), UDP, and ICMP echo traffic, Cloudflare WAN no longer requires a routing table entry to return traffic to the originating tunnel or interconnect.
- **Maintains symmetric routing for flows**: Responses to a given flow (for example, a TCP session) return over the same Cloudflare WAN connection that carried the initial request — important for stateful firewalls and middleboxes.
- **Supports overlapping IP space**: Because the return path is tied to the learned connection state instead of a destination prefix in the routing table, Automatic Return Routing can support scenarios where different sites use overlapping private address space.
- **Operates per connection**: You decide which IPsec / GRE tunnels or network interconnects should use this behavior by enabling the feature on each connection.

<AnchorHeading title="How ARR works" depth={3} />

When traffic that is eligible for Automatic Return Routing (ARR) arrives on a connection with ARR enabled, Cloudflare WAN creates a flow entry that records:

- The source and destination IP addresses
- The relevant ports or identifiers, depending on the protocol
- The connection (tunnel or interconnect) that the traffic arrived on

For any subsequent packets that match this flow and require a next hop, Cloudflare WAN:

1. Checks for a matching Automatic Return Routing flow.
2. If a match exists, routes the packet back to the same connection where the flow was learned, instead of consulting the Cloudflare Virtual Network routing table.

The initial request from your network to the Internet still uses your configured static or BGP routes. ARR only affects the return path for supported traffic after the flow is learned.

<AnchorHeading title="Traffic and destinations affected" depth={3} />

Automatic Return Routing applies when:

- Traffic is received on a tunnel or network interconnect where the feature is enabled.
- The received traffic is one of:
  - New TCP connections (TCP SYN)
  - UDP
  - ICMP echo (ping) requests
- The traffic is destined for:
  - Internet egress through Cloudflare
  - A Cloudflare One Client
  - A private network connected to Cloudflare through Cloudflare Tunnel
  - A private network connected to Cloudflare through Cloudflare Mesh

ARR does not change routing for traffic between Cloudflare WAN connections (for example, traffic from one IPsec/GRE tunnel or interconnect to another). That traffic continues to follow your configured Cloudflare WAN routes.

<AnchorHeading title="Unified Routing mode" slug="unified-routing" depth={2} />

Unified Routing mode is the Cloudflare One data plane that uses a single routing fabric for all supported connection types. It routes traffic across the Cloudflare One Client, Cloudflare Tunnel, IPsec, GRE, and Cloudflare Network Interconnect (CNI) in one system.

In the Cloudflare WAN dashboard, routing mode appears where you manage routes:

- **Routing mode: Unified** — your account is on the unified data plane and supports the new routing features.
- **Routing mode: Legacy** — your account uses the previous data plane and does not support all unified routing features.

### Compare routing modes

Unified Routing is generally available and is our recommended routing mode for all new accounts.

| Area                               | Legacy Routing                                                                                                                                            | Unified Routing                                                                                                               |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Routing fabric                     | Uses separate routing systems for Zero Trust and WAN routes.                                                                                              | Uses one routing fabric across all supported connection types.                                                                |
| Route selection                    | Applies longest-prefix match within each routing system. For cross-system traffic, a Zero Trust route can take precedence over a more specific WAN route. | Applies longest-prefix match across supported connection types. When prefix lengths match, Zero Trust routes take precedence. |
| Cloudflare service source IPs      | Uses public Cloudflare source IPs for traffic sent to private networks.                                                                                   | Uses a dedicated, non-Internet-routable private source range. You can configure the IPv4 range.                               |
| Cloudflare Mesh and Cloudflare WAN | Does not support traffic between Mesh and WAN connections.                                                                                                | Supports Mesh and WAN connections in the same account.                                                                        |
| Load Balancing with WAN origins    | Supported. Traffic uses public Cloudflare source IPs.                                                                                                     | Generally available. Traffic uses dedicated private Cloudflare source IPs.                                                    |
| BGP over CNI                       | Closed beta and not available to new customers.                                                                                                           | Closed beta and not available to new customers.                                                                               |
| IPv6                               | Beta for Magic Transit only.                                                                                                                              | Beta for Cloudflare WAN and Magic Transit.                                                                                    |

#### Use features available only with Unified Routing

The following features require Unified Routing.

| Feature                                                                                                                                                   | Availability with Unified Routing                                                                  |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [Automatic Return Routing](#automatic-return-routing)                                                                                                     | Generally available for Internet return traffic. Routing between WAN connections is not available. |
| [BGP over IPsec and GRE](#release-status)                                                                                                                 | Generally available.                                                                               |
| [Cloudflare source IPs](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-cloudflare-source-ips/)                         | Generally available.                                                                               |
| [Custom Cloudflare One Client device subnets](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/) with Cloudflare WAN | Generally available.                                                                               |
| [Cloudflare Mesh](/mesh/) and Cloudflare WAN interoperability                                                                                             | Generally available.                                                                               |

#### Check feature availability before upgrading

Features outside Cloudflare Network Firewall have the following availability:

| Feature                                                                                        | Availability with Unified Routing                                                                                       |
| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [Sample packet captures](/cloudflare-network-firewall/packet-captures/#sample-packet-captures) | Generally available.                                                                                                    |
| [Full packet captures](/cloudflare-network-firewall/packet-captures/#full-packet-captures)     | Not available.                                                                                                          |
| [Gateway filtering](/cloudflare-one/traffic-controls/)                                         | Generally available. Supports WAN-to-Internet and WAN-to-WAN filtering. Contact your account team to enable WAN-to-WAN filtering. |
| [Load Balancing](/load-balancing/private-network/)                                             | Generally available.                                                                                                    |
| [Workers VPC](/workers-vpc/)                                                                   | Generally available.                                                                                                    |
| Application Services                                                                           | Generally available.                                                                                                    |
| [Network Analytics](/analytics/network-analytics/)                                             | Generally available.                                                                                                    |

[Cloudflare Network Firewall](/cloudflare-network-firewall/) features have the following availability:

| Feature                          | Availability with Unified Routing |
| -------------------------------- | --------------------------------- |
| Basic firewall rules             | Generally available.              |
| GeoIP country rules              | Beta.                             |
| ASN lists                        | Beta.                             |
| Account IP lists                 | Beta.                             |
| Threat Intel Lists               | Beta.                             |
| SIP rules                        | Beta.                             |
| Intrusion detection system (IDS) | Beta for Cloudflare WAN only.     |
| Rate limiting                    | Not available.                    |
| Managed rulesets                 | Beta.                             |

#### Evaluate performance

Unified Routing performance can differ from Legacy Routing for the same traffic profile. Throughput depends on traffic distribution, packet sizes, tunnel settings, encryption, and customer equipment.

Before upgrading, establish a Legacy Routing baseline and run a controlled Unified Routing pilot with representative traffic. Test expected and peak throughput, packet loss, latency, tunnel failover, and your existing IPsec settings.

### How to upgrade to Unified Routing

If your account uses Legacy Routing, follow these steps to upgrade:

1. Evaluate Unified Routing features and performance against your current needs. Review the [routing mode comparison](#compare-routing-modes), [feature availability](#check-feature-availability-before-upgrading), and [performance guidance](#evaluate-performance).
2. Identify suitable times for the upgrade. During the upgrade, Cloudflare One Client users can experience a remote access outage of up to three minutes.
3. Contact your account team to request the change and provide a range of acceptable times.

<AnchorHeading depth={2} title="Route evaluation with Zero Trust connections" />

When your account uses both Zero Trust routes (Cloudflare Tunnel, Cloudflare Mesh) and WAN routes (IPsec, GRE, CNI), route selection behavior depends on your [routing mode](#unified-routing).

<AnchorHeading depth={3} title="Terminology" />

| Route type        | Connection methods                 |
| ----------------- | ---------------------------------- |
| Zero Trust routes | Cloudflare Tunnel, Cloudflare Mesh |
| WAN routes        | IPsec, GRE, and CNI                |

<AnchorHeading depth={3} title="Unified Routing mode" />

Unified Routing uses a single routing fabric for all connection types. Route selection applies longest-prefix-match consistently across all traffic types and connection methods.

| Zero Trust route | WAN route      | Traffic destination | Selected route                  |
| ---------------- | -------------- | ------------------- | ------------------------------- |
| `10.0.0.0/24`    | `10.0.0.64/28` | `10.0.0.70`         | WAN (more specific)             |
| `10.0.0.0/28`    | `10.0.0.0/24`  | `10.0.0.10`         | Zero Trust (more specific)      |
| `10.0.0.0/24`    | `10.0.0.0/24`  | `10.0.0.10`         | Zero Trust (same prefix length) |

When routes have the same prefix length, Zero Trust routes take precedence over WAN routes.

For scenarios with overlapping IP space across sites, enable [Automatic Return Routing](#automatic-return-routing) to ensure return traffic reaches the correct origin.

<AnchorHeading depth={3} title="Legacy Routing mode" />

For accounts using Legacy Routing, route selection depends on the traffic source.

<AnchorHeading depth={4} title="Cloudflare One Client to private network" />

For accounts using only Zero Trust, Cloudflare One Client traffic is routed using the Zero Trust IP routing table only, following longest-prefix-match logic.

If your account has Cloudflare WAN enabled, traffic from Cloudflare One Client follows the same route selection behavior as [site-to-site traffic with Gateway](#site-to-site-traffic-with-gateway). Contact your account team if you want Cloudflare One Client to continue to behave as if WAN is not enabled.

<AnchorHeading depth={4} title="Site-to-site traffic (WAN to WAN)" />

For traffic between WAN connections (IPsec to IPsec, GRE to GRE, and CNI to CNI) that does not require Gateway filtering, longest-prefix-match applies within the WAN routing table. This traffic does not interact with Zero Trust routing.

<AnchorHeading depth={4} title="Site-to-site traffic with Gateway" />

When [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) are applied to site-to-site WAN traffic, route selection follows these rules:

| Scenario                                      | Behavior                                                                              |
| --------------------------------------------- | ------------------------------------------------------------------------------------- |
| More specific Zero Trust route than WAN route | **Works** — longest-prefix-match honored for both inbound and outbound traffic        |
| More specific WAN route than Zero Trust route | **Not guaranteed** — Zero Trust route can take precedence regardless of prefix length |
| Equal prefix length                           | Zero Trust route wins (by design)                                                     |

<Aside type="note">
	If you need consistent longest-prefix-match across all scenarios, follow [How
	to upgrade to Unified Routing](#how-to-upgrade-to-unified-routing).
</Aside>

<AnchorHeading
	depth={4}
	title="Cross-system traffic (WAN to Zero Trust or Zero Trust to WAN)"
/>

Legacy Routing uses two routing components:

- **Zero Trust routing** (handles Cloudflare One Client, Cloudflare Tunnel, and Cloudflare Mesh)
- **WAN routing** (handles IPsec, GRE, and CNI)

Cross-system traffic follows the same rules as [site-to-site traffic with Gateway](#site-to-site-traffic-with-gateway). A more specific Zero Trust route works correctly; a more specific WAN route is not guaranteed to be selected.

**Recommendation:** If overlap is required, follow [How to upgrade to Unified Routing](#how-to-upgrade-to-unified-routing).

<AnchorHeading depth={3} title="Check your routing mode" />

To determine the routing mode for your account:

1. Go to **Routes**.

<DashButton url="/?to=/:account/magic-networks/routes" />

2. Check the banner at the top of the page:
   - **Your account is using Unified Routing mode.** — Your account uses Unified Routing.
   - **Unified routing is available.** — Your account uses Legacy Routing.

If your account uses Legacy Routing, follow [How to upgrade to Unified Routing](#how-to-upgrade-to-unified-routing).

## Scoping routes to specific regions

If you have multiple connectivity paths to a network segment and want to apply different route prioritization based on where traffic arrives at the Cloudflare network, you can scope routes to specific Cloudflare data center regions. This is useful if you run your own anycast network and want your end-user traffic to arrive at your network location closest to the user.

When you scope a route to a Cloudflare data center region, it only shows up in the Cloudflare Virtual Network routing table in that region, along with all global routes that do not have any region scope. Route prioritization and ECMP logic apply across both region-scoped and global routes.

:::note
Scoping routes to specific regions is not supported with BGP peering, and is only available to statically configured routes at this time.
:::

When using region-scoped routes, ensure that all prefixes have routes covering all regions. Otherwise, traffic may arrive at a Cloudflare region that is not covered by any route, in which case Cloudflare drops the traffic.

The following table exemplifies how to use geographic scoping for routes:

| Prefix            | NextHop        | Priority | Region code |
| ----------------- | -------------- | -------- | ----------- |
| `10.10.10.100/24` | `TUNNEL_1_IAD` | `100`    | `AFR`       |
| `10.10.10.100/24` | `TUNNEL_2_IAD` | `100`    | `EEUR`      |
| `10.10.10.100/24` | `TUNNEL_3_ATL` | `100`    | `ENAM`      |
| `10.10.10.100/24` | `TUNNEL_4_ATL` | `100`    | `ME`        |
| `10.10.10.100/24` | `TUNNEL_5_ATL` | `100`    | `WNAM`      |
| `10.10.10.100/24` | `TUNNEL_4_ATL` | `100`    | `ENAM`      |

When there are multiple routes to the same prefix with equal priority, and those routes are assigned to different geographic regions (like WNAM and ENAM), traffic entering the network in a specific region — for example, WNAM — egresses through the route associated with that same region.

### Region codes and associated regions

Cloudflare has nine geographic regions:

| Region code | Region                |
| ----------- | --------------------- |
| `AFR`       | Africa                |
| `APAC`      | Asia Pacific          |
| `EEUR`      | Eastern Europe        |
| `ENAM`      | Eastern North America |
| `ME`        | Middle East           |
| `OC`        | Oceania               |
| `SAM`       | South America         |
| `WEUR`      | Western Europe        |
| `WNAM`      | Western North America |

Configure scoping for your traffic in the **Region code** section when adding or editing a static route. Refer to [Create a static route](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#create-a-static-route) and [Edit a static route](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#edit-a-static-route) for more information.

## Equal-cost multi-path routing

Equal-cost multi-path routing uses hashes calculated from [packet](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) data to determine the route chosen. The hash always uses the source and destination IP addresses. For TCP and UDP packets, the hash includes the source and destination ports as well. The ECMP algorithm divides the hash for each packet by the number of equal-cost next hops. The modulus (remainder) determines the route the packet takes.

Using ECMP has a number of consequences:

- Routing to equal-cost paths is probabilistic.
- Packets in the same session with the same source and destination have the same hash. The packets also use the same next hop.
- Routing changes in the number of equal-cost next hops can cause traffic to use different tunnels. For example, dynamic reprioritization triggered by health check events can cause traffic to use different tunnels.

As a result, ECMP provides load balancing across tunnels with the same prefix and priority.

:::note
Packets in the same flow use the same tunnel unless the tunnel priority changes. Packets for different flows can use different tunnels depending on which tunnel the flow's 4-tuple — source and destination IP and source and destination port — hash to.
:::

### Examples

This diagram illustrates how ECMP distributes traffic equally across two paths with the same prefix and priority.

#### Normal traffic flow

```mermaid
flowchart LR
accTitle: Tunnels diagram
accDescr: This example has three tunnel routes, with traffic equally distributed across two paths.

subgraph Cloudflare
direction LR
B[Cloudflare <br> data center]
C[Cloudflare <br> data center]
D[Cloudflare <br> data center]
end

Z("Load balancing for some <br> priority tunnels uses ECMP <br> (hashing on src IP, dst IP, <br> scr port, dst port)") --- Cloudflare
A((User)) --> Cloudflare --- E[Anycast IP]
E[Anycast IP] --> F[/"GRE Tunnel 1 / <br> priority 1 / <br> ~50% of flows"/] --> I{{Customer <br> data center/ <br> network 1}}
E[Anycast IP] --> G[/"GRE Tunnel 2 / <br> priority 1 / <br> ~50% of flows"/] --> J{{Customer <br> data center/ <br> network 2}}
E[Anycast IP] --> H[/GRE Tunnel 3 / <br> priority 2 / <br> 0% of flows/] --o K{{Customer <br> data center/ <br> network 3}}
```

#### Failover traffic flow: Scenario 1

**Customer router failure**

When Cloudflare WAN health checks determine that Tunnel 2 is unhealthy, Cloudflare WAN dynamically de-prioritizes that route, leaving Tunnel 1 as the sole top-priority route. As a result, Cloudflare WAN steers traffic away from Tunnel 2, and all traffic flows to Tunnel 1.

```mermaid
flowchart LR
accTitle: Tunnels diagram
accDescr: This example has Tunnel 2 unhealthy, and all traffic prioritized to Tunnel 1.

subgraph Cloudflare
direction LR
B[Cloudflare <br> data center]
C[Cloudflare <br> data center]
D[Cloudflare <br> data center]
end

Z(Tunnel health is <br> determined by <br> health checks that <br> run from all Cloudflare <br> data centers) --- Cloudflare
A((User)) --> Cloudflare --- E[Anycast IP]
E[Anycast IP] --> F[/"Tunnel 1 / <br> priority 1 / <br> ~100% of flows"/]:::green --> I{{Customer <br> data center/ <br> network 1}}
E[Anycast IP] --> G[/Tunnel 2 / <br> priority 3 / <br> unhealthy / 0% of flows/]:::red --x J{{Customer <br> data center/ <br> network 2}}
E[Anycast IP] --> H[/Tunnel 3 / <br> priority 2 / <br> 0% of flows/] --o K{{Customer <br> data center/ <br> network 3}}
classDef red fill:#EE4B2B,color: black
classDef green fill:#00FF00,color: black
```

#### Failover traffic flow: Scenario 2

**Intermediary Internet Service Provider (ISP) failure**

When Cloudflare WAN determines that Tunnel 1 is unhealthy as well, that route is also de-prioritized, leaving Tunnel 3 with the top priority route. In that case, all traffic flows to Tunnel 3.

```mermaid
flowchart LR
accTitle: Tunnels diagram
accDescr: This example has Tunnel 1 and 2 unhealthy, and all traffic prioritized to Tunnel 3.

subgraph Cloudflare
direction LR
B[Cloudflare <br> data center]
C[Cloudflare <br> data center]
D[Cloudflare <br> data center]
end

Z(Lower-priority tunnels <br> are used when <br> higher-priority tunnels <br> are unhealthy) --- Cloudflare
A((User)) --> Cloudflare --- E[Anycast IP]
E[Anycast IP]  -- Intermediary <br> network issue -->  F[/Tunnel 1 / <br> priority 3 / <br> unhealthy / 0% of flows/]:::red --x I{{Customer <br> data center/ <br> network 1}}
E[Anycast IP]  -- Intermediary <br> network issue -->  G[/Tunnel 2 / <br> priority 3 / <br> unhealthy / 0% of flows/]:::red --x J{{Customer <br> data center/ <br> network 2}}
E[Anycast IP] -->  H[/Tunnel 3 / <br> priority 2 / <br> 100% of flows/]:::green --> K{{Customer <br> data center/ <br> network 3}}
classDef red fill:#EE4B2B,color: black
classDef green fill:#00FF00,color: black
```

When Cloudflare WAN determines that Tunnels 1 and 2 are healthy again, it re-prioritizes those routes, and traffic flow returns to normal.

### ECMP and bandwidth utilization

Because ECMP is probabilistic, the algorithm routes roughly the same number of flows through each tunnel. However, it does not consider the amount of traffic already sent through a tunnel when deciding where to route the next packet.

For example, consider a scenario with many very low-bandwidth TCP connections and one very high-bandwidth TCP connection. Packets for the high-bandwidth connection have the same hash and thus use the same tunnel. As a result, that tunnel utilizes greater bandwidth than the others.

:::note
Cloudflare WAN supports a weight field that you can apply to a route so that a specified percentage of traffic uses a certain tunnel rather than other equal-cost tunnels. Refer to [Route prioritization](#route-prioritization) for more information.

For example, in a scenario where you want to route 70% of your traffic through ISP A and 30% through ISP B, you can use the weight field to help achieve that.

Because ECMP balances flows probabilistically, the use of weights is only approximate.

For more on Cloudflare WAN tunnel weights, contact your Cloudflare customer service manager.
:::

## BGP information

Using BGP peering with your Cloudflare One or Magic Transit network routing table allows you to automate the process of adding or removing networks and subnets. BGP does not replace tunnel health checks. Refer to [Tunnel health checks](#tunnel-health-checks).

With this functionality, you can:

- Establish an eBGP session between your devices and the Cloudflare WAN service when connected through CNI, GRE or IPsec tunnels.
- Secure the session by MD5 authentication to prevent misconfigurations.
- Exchange routes dynamically between your devices and your Cloudflare Virtual Network routing table.

### Release status

The following table outlines the current availability and recommended use cases for BGP across different connectivity methods.

| Feature                        | Release stage       | Recommended use                                            | Prerequisites                                                                         |
| ------------------------------ | ------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| **BGP over Anycast IPsec/GRE** | Generally available | Production workloads                                       | Available to all [Unified Routing](#unified-routing) accounts; no enablement required |
| **BGP over CNI**               | Closed beta         | Not available to new customers — contact your account team | Cloudflare Network Interconnect (CNI) v2                                              |

### BGP architecture

#### Global routing and anycast edge

Cloudflare Virtual Network makes a one-pass, per-packet routing decision at the Cloudflare data center that first processes the packet (the ingress node). This ensures that even when a packet traverses multiple nodes within the Cloudflare backbone, its path is determined at the point of entry for maximum efficiency.

Your BGP session over IPsec, GRE, or CNI is established with the Cloudflare data center closest to your BGP peer device. Routes learned here must propagate to Cloudflare's global edge to govern how traffic is routed across the entire network.

- **Convergence time**: Global route convergence typically completes within 20 seconds.
- **Visibility**: You can monitor learned routes and their propagation status through the Cloudflare dashboard or API.

#### Centralized route propagation

Cloudflare Virtual Network uses a centralized control plane for route propagation, functioning similarly to a BGP Route Reflector. This architecture decouples the physical BGP session from global route distribution:

- **Session termination**: BGP peering sessions are terminated at the Cloudflare edge location closest to your router.
- **SDN conversion**: Ingress BGP updates are converted into Software-Defined Networking (SDN) state and transmitted to a centralized relay function.
- **Global dissemination**: The relay propagates these instructions to every Cloudflare data center globally, updating the local Forwarding Information Base (FIB) at each site.

#### Edge Resiliency Mode (Non-Stop Forwarding)

Cloudflare's data plane is designed for high availability. If the edge location loses communication with the centralized relay, the system enters Edge Resiliency Mode, mimicking Non-Stop Forwarding (NSF) behavior:

- **Forwarding continuity**: Edge locations continue to route traffic using the last-known-good forwarding table (FIB). Data plane traffic remains uninterrupted.
- **Stale path retention**: Because the FIB is frozen during this mode, forwarding decisions remain active even if the underlying BGP session with your router flaps or resets.
- **Continuous health monitoring**: While BGP updates are frozen, tunnel health checks remain active. These are sent from all Cloudflare data centers, allowing the edge at any ingress node to detect if a physical connection to your router has failed. If a health check fails, the ingress node at the edge will deprioritize that specific path, preventing traffic from being sent into a black hole despite the frozen routing state.
- **Update freeze**: During this state, the global control plane is frozen. New BGP updates received from your router will be held locally at the edge and will not propagate globally until connectivity to the centralized relay is restored.

:::caution[Traffic persistence during BGP resets]
In Edge Resiliency Mode, Cloudflare prioritizes forwarding continuity. If your on-premises router resets or the BGP session flaps, the edge will continue to forward traffic toward your peer device based on the last known valid routing state — provided that the underlying tunnel health checks remain successful.

If the BGP session resets **and** the tunnel health checks fail (for example, your router is completely offline), the edge will typically take alternate paths until connectivity is restored.
:::

#### System recovery and re-synchronization

Once connectivity between the Cloudflare edge and the centralized relay is restored, the system automatically exits Edge Resiliency Mode and performs a stateful re-synchronization:

1. **RIB-to-relay sync**: The edge pushes all currently held BGP updates (the current RIB state) to the relay.
2. **Global update**: The relay reconciles these updates and propagates any changes to the rest of the Cloudflare global network.
3. **FIB unfreeze**: The local forwarding tables at the edge are unfrozen and updated with the latest validated routing instructions.

<AnchorHeading
	depth={3}
	title="BGP peering with the Cloudflare Virtual Network routing table"
/>

Cloudflare WAN BGP peering is with the Cloudflare Virtual Network routing table (as opposed to peering with the Cloudflare Internet global network). BGP peers configured by following this guide will receive advertisements for all prefixes in the Cloudflare Virtual Network routing table plus any additional prefixes configured in the on-ramp [Advertised prefix list](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/#set-up-bgp-peering).

If instead you are seeking to do public peering with the Cloudflare ASN 13335 at one of the Cloudflare data centers, refer to [PNI and peering setup](/network-interconnect/). It is not currently possible to share Cloudflare Virtual Network BGP peering and PNI on the same physical interconnect port.

### BGP route distribution and convergence

Cloudflare redistributes routes received from your device into the Cloudflare Virtual Network routing table, which both Cloudflare WAN and Magic Transit use.

All routes in the Cloudflare Virtual Network routing table are advertised to BGP peers. Each BGP peer receives each prefix route along with the full `AS_PATH`, with the selected Cloudflare side [ASN](https://www.cloudflare.com/learning/network-layer/what-is-an-autonomous-system/) prepended. This is so that the peer can accurately perform [loop prevention](https://datatracker.ietf.org/doc/html/rfc4271#section-9.1.2).

BGP peering sessions can advertise reachable prefixes to a peer and withdraw previously advertised prefixes. This propagation takes no more than a few minutes.

### BGP timers and settings

Cloudflare uses the following timers, which are not configurable:

| Setting              | Description                                                                                                                                                                                                                       |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Hold timer**       | 240 seconds<br /> _(To establish a session, Cloudflare compares its hold timer and the peer's hold timer, and uses the smaller of the two values to establish the BGP session.)_ |
| **Keepalive timer**  | One third of the hold timer.                                                                                                                                                                                                      |
| **Graceful restart** | Helper mode only. Cloudflare advertises a restart time of 120 seconds and honors a peer restart time of up to 90 seconds.                                                                                                         |

- **Hold timer**: Specifies the maximum amount of time that a BGP peer waits to receive a KEEPALIVE, UPDATE, or NOTIFICATION message before declaring the BGP session down. Cloudflare uses the smaller of this default hold timer and that received from the peer in the OPEN message.
- **Keepalive timer**: BGP systems exchange keepalive messages to determine whether the peer router is reachable. If keepalive messages are not received within the hold timer, the session is assumed to be down, indicating that the peer is no longer reachable at the BGP protocol level.
- **Graceful restart timer**: Tracks how long a router waits for a peer to re-establish a BGP session after the peer initiates a graceful restart. If the peer does not reconnect within this time, the router declares the session down and removes stale routes.

### BGP capabilities and limitations

BGP multipath is supported. If BGP learns the same prefix on two different interconnects, Cloudflare distributes traffic destined for that prefix across each interconnect according to the usual ECMP behavior.

Cloudflare supports BGP Graceful Restart ([RFC 4724](https://datatracker.ietf.org/doc/html/rfc4724)) in helper mode only. Cloudflare never acts as the restarting speaker, because your device always initiates the BGP session.

If your device negotiates graceful restart and then restarts, Cloudflare keeps its routes as stale while it reconnects. Cloudflare removes stale routes after your device sends End-of-RIB, or after 90 seconds.

BGP support has the following limitations:

- The Cloudflare account ASN and your device ASN must be different. Only eBGP is supported.
- Cloudflare always injects routes with a priority of `100`.
- Bidirectional Forwarding Detection (BFD) is not supported.

### Tunnel health checks

You need to enable [legacy health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/#legacy-bidirectional-health-checks) alongside BGP. This is essential to determine if a specific Cloudflare data center is reachable from your device. [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/) modify the route priorities for dynamically learned BGP routes.

<AnchorHeading title="Application-aware policies" depth={2} />

By default, Cloudflare balances and steers traffic based on network-layer characteristics (IP, port etc). If you are using the Cloudflare WAN Connector, you can also steer traffic based on well-known applications. Application-aware policies provide easier management and more granularity over traffic flows.

For more information, refer to [Applications and app types](/cloudflare-one/traffic-controls/application-app-types/).

# Tunnel health checks

import { GlossaryTooltip } from "~/components";

Cloudflare continuously monitors whether each tunnel connecting your network to Cloudflare is reachable and performing well. When a tunnel becomes unhealthy, Cloudflare automatically steers traffic to an alternate path — without requiring manual intervention. This monitoring relies on tunnel health check probes.

A tunnel health check probe consists of an [ICMP (Internet Control Message Protocol)](https://www.cloudflare.com/learning/ddos/glossary/internet-control-message-protocol-icmp/) payload encapsulated in the protocol of the tunnel being tested. For example, if the tunnel is an Internet Protocol Security (IPsec) tunnel, the ICMP [packet](https://www.cloudflare.com/learning/network-layer/what-is-a-packet/) is encrypted within the Encapsulating Security Payload (ESP) packet of the tunnel.

A tunnel health check probe travels from Cloudflare to the tunnel origin, then returns a response to Cloudflare. Cloudflare uses this response to determine the probe outcome and calculate the tunnel state (the following sections explain this in greater detail).

:::note
Cloudflare WAN customers with [Customer Metadata Boundary](/data-localization/metadata-boundary/) enabled for the European Union can access GRE, IPsec, and CNI (Cloudflare Network Interconnect) health check and traffic volume data in the Cloudflare dashboard and through the API. This ensures that customers who need to be General Data Protection Regulation (GDPR) compliant can access all Cloudflare WAN features.
:::

## Types of health checks

Cloudflare WAN uses two types of health checks:

### Tunnel health checks

Tunnel health checks monitor the status of the tunnels that route traffic from Cloudflare to your origin network. Cloudflare WAN relies on these checks to steer traffic to the best available routes. During onboarding, you [specify the tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/) or tunnel health check targets the tunnel probes originating from Cloudflare's global network will target.

You can access tunnel health check results [through the API](/analytics/graphql-api/tutorials/querying-magic-transit-tunnel-healthcheck-results/). Cloudflare aggregates these results from individual health check results from Cloudflare servers.

### Endpoint health checks

Endpoint health checks evaluate connectivity from Cloudflare distributed data centers to your origin network. Unlike tunnel health checks, endpoint probes are designed to provide a broad picture of Internet health between Cloudflare and your network. They flow over available tunnels but do not inform tunnel selection or steering logic.

Cloudflare global network servers issue endpoint health checks outside of customer network namespaces and typically target endpoints beyond the tunnel-terminating border router. During onboarding, you specify IP addresses to configure endpoint health checks.

## Tunnel health check attributes

A tunnel health check probe has the following attributes.

### Target

A tunnel health check probe tests whether Cloudflare can successfully connect to a specific address or endpoint through the tunnel. The target is the address you want to verify is reachable. It is optional, and defaults vary depending on the direction of the health check (refer to [Direction](#direction) for more information).

### Direction

A tunnel health check probe can have two possible directions — unidirectional and bidirectional.

#### Unidirectional

A unidirectional health check probe stays encapsulated in one direction and comes into the origin through the tunnel (from Cloudflare to the origin). The response comes back to Cloudflare unencapsulated and routes outside of the tunnel following standard Internet [routing](https://www.cloudflare.com/learning/network-layer/what-is-routing/).

The target defaults to the publicly routable origin specified as the `customer_endpoint` on the tunnel, if present. Otherwise, you can use a custom target.

#### Bidirectional

A bidirectional probe stays encapsulated in both directions. The probe comes in through the tunnel and the response also leaves encapsulated through the tunnel. The ICMP reply from your router destined for the anycast IP address on Cloudflare's network arrives at the closest Cloudflare data center and lands on one of the servers using Equal-Cost Multi-Path (ECMP), ensuring the response takes the most efficient path.

**Default packet addressing**

By default, Cloudflare destinations these packets for the Cloudflare side of the interface address field set on the tunnel, and sources them from the client side of the tunnel. For example, if the interface address is `10.100.0.8/31`, Cloudflare destinations the packet for `10.100.0.9` and sources it from `10.100.0.8`.

**Interface address ranges**

The interface address field uses either a `/30` or `/31` CIDR range:

- **`/31` range**: The IP you provide is the Cloudflare side, and the other IP is the client side. For example, if the interface address is `10.100.0.8/31`, then `10.100.0.8` is the Cloudflare side and `10.100.0.9` is the client side.
- **`/30` range**: The IP you provide is the Cloudflare side, and the other IP (excluding the broadcast and network identifier) is the client side. For example, if the interface address is `10.100.0.9/30`, then `10.100.0.9` is the Cloudflare side and `10.100.0.10` is the client side.

You can also configure a bidirectional health check with a custom public target, which is the recommended approach for an Azure Active Standby tunnel setup.

These packets flow to and from Cloudflare over the tunnels you have configured to provide full visibility into the traffic path between Cloudflare's network and your sites. You need to configure traffic selectors to accept the health check packets for IPsec tunnels.

Refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) to learn how to configure bidirectional or unidirectional health checks.

#### Legacy bidirectional health checks

For customers using the legacy health check system with a public IP range, Cloudflare recommends:

- Configuring the tunnel health check target IP address to one within the `172.64.240.252/30` prefix range.
- Applying a policy-based route that matches packets with a source IP address equal to the configured tunnel health check target (for example `172.64.240.253/32`), and route them over the tunnel back to Cloudflare.

### Type

A tunnel health check probe can have two possible types: request and reply. For each type, the source and destination address depends on the direction. Refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels) to learn how to change this setting.

#### Request style

In a request style health check the payload probe is an ICMP request.

For a unidirectional probe, the source address is the Cloudflare side of the tunnel (a publicly routable address) and the destination is the origin router (also publicly routable). The origin router receives the probe and produces an ICMP response with the opposite source and destination, and sends it outside of the tunnel.

For a bidirectional probe, the source address is the interface address of the Cloudflare side of the tunnel (a privately routable address) and the destination is the interface address of the tunnel (also privately routable). The origin router receives the probe and produces an ICMP response with the opposite source and destination and sends it into the tunnel.

#### Reply style

In a reply style health check the payload probe is an ICMP response.

For a unidirectional probe, the destination address is the Cloudflare side of the tunnel (a publicly routable address) and the source is the origin router (also publicly routable). The origin router receives the probe and sends it back as the response, unchanged, outside of the tunnel.

For a bidirectional probe, the destination address is the interface address of the Cloudflare side of the tunnel (a privately routable address) and the source is the interface address of the tunnel (also privately routable). The origin router receives the probe packet and sends the probe packet back as the response (unchanged) into the tunnel because the destination routes through the tunnel.

:::note
To avoid control plane policies enforced by the origin network, you can set tunnel health checks to use a request style health check if your network drops reply style health checks.
:::

### Summary table with tunnel health check probe types

| Attribute 					| Type 					| Unidirectional health checks | Bidirectional health checks |
| :---: 							| :---: 				| :---: 											 | :---: 											 |
| Source Address 			| Request Style | Cloudflare Address (Publicly Routable) | Cloudflare Interface Address (Privately Routable) |
| Destination Address | Request Style | Origin Tunnel Endpoint (Publicly Routable) |Origin Interface Address (Privately Routable) / Custom Target |
| Source Address 			| Reply Style		| Origin Tunnel Endpoint (Publicly Routable) | Origin Interface Address (Privately Routable) / Custom Target |
| Destination Address | Reply Style 	| Cloudflare Address (Publicly Routable) | Cloudflare Interface Address (Privately Routable) |

### Graphics summarizing health check types

#### Bidirectional request style

```mermaid
flowchart TB
accTitle: Bidirectional request style
accDescr: Shows the flow of a bidirectional request-style tunnel health check probe and response between Cloudflare and the origin.
   subgraph Tunnel Healthcheck Probe
   cloudflare(Cloudflare) --- bare_echo_request([ICMP Echo Request])
   bare_echo_request --> tunnel[Tunnel]
   tunnel --- encapsulated_echo_request([Tunnel Protocol < ICMP Echo Request >])
   encapsulated_echo_request --> Internet([Internet])
   Internet --- encapsulated_echo_request_2([Tunnel Protocol < ICMP Echo Request >])
   encapsulated_echo_request_2 --> origin_tunnel(Tunnel)
   origin_tunnel --- received_bare_echo_request([ICMP Echo Request])
   received_bare_echo_request --> origin(Origin)
   end
   subgraph Tunnel Healthcheck Response
   origin --> bare_echo_reply([ICMP Echo Reply])
   bare_echo_reply --- origin_tunnel_2(Tunnel)
   origin_tunnel_2 --- encapsulated_echo_reply([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_reply --- Internet_2([Internet])
   Internet_2 --> encapsulated_echo_reply_2([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_reply_2 --> tunnel_2[Tunnel]
   tunnel_2 --> bare_echo_reply_2([ICMP Echo Reply])
   bare_echo_reply_2 --> cloudflare
   end
```

#### Bidirectional reply style

```mermaid
flowchart TB
accTitle: Bidirectional reply style
accDescr: Shows the flow of a bidirectional reply-style tunnel health check probe and response between Cloudflare and the origin.
   subgraph Tunnel Healthcheck Probe
   cloudflare(Cloudflare) --- bare_echo_probe([ICMP Echo Reply])
   bare_echo_probe --> tunnel[Tunnel]
   tunnel --- encapsulated_echo_probe([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_probe --> Internet([Internet])
   Internet --- encapsulated_echo_probe_2([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_probe_2 --> origin_tunnel(Tunnel)
   origin_tunnel --- received_bare_echo_reply([ICMP Echo Reply])
   received_bare_echo_reply --> origin(Origin)
   end
   subgraph Tunnel Healthcheck Response
   origin --> bare_echo_reply([ICMP Echo Reply])
   bare_echo_reply --- origin_tunnel_2(Tunnel)
   origin_tunnel_2 --- encapsulated_echo_reply([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_reply --- Internet_2([Internet])
   Internet_2 --> encapsulated_echo_reply_2([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_reply_2 --> tunnel_2[Tunnel]
   tunnel_2 --> bare_echo_reply_2([ICMP Echo Reply])
   bare_echo_reply_2 --> cloudflare
   end
```

#### Unidirectional echo request

```mermaid
flowchart TB
accTitle: Unidirectional echo request
accDescr: Shows the flow of a unidirectional echo request health check from Cloudflare to the origin and back.
   cloudflare(Cloudflare) --- bare_echo_probe([ICMP Echo Request])
   bare_echo_probe --> tunnel[Tunnel]
   tunnel --- encapsulated_echo_probe([Tunnel Protocol < ICMP Echo Request >])
   encapsulated_echo_probe --> Internet([Internet])
   Internet --- encapsulated_echo_probe_2([Tunnel Protocol < ICMP Echo Request >])
   encapsulated_echo_probe_2 --> origin_tunnel(Tunnel)
   origin_tunnel --- received_bare_echo_reply([ICMP Echo Request])
   received_bare_echo_reply --> origin(Origin)
   origin --- received_bare_echo_reply_2([ICMP Echo Reply])
   received_bare_echo_reply_2 --> Internet_2([Internet])
   Internet_2 --> cloudflare
```

#### Unidirectional echo reply

```mermaid
flowchart TB
accTitle: Unidirectional echo reply
accDescr: Shows the flow of a unidirectional echo reply health check from Cloudflare to the origin and back.
   cloudflare(Cloudflare) --- bare_echo_probe([ICMP Echo Reply])
   bare_echo_probe --> tunnel[Tunnel]
   tunnel --- encapsulated_echo_probe([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_probe --> Internet([Internet])
   Internet --- encapsulated_echo_probe_2([Tunnel Protocol < ICMP Echo Reply >])
   encapsulated_echo_probe_2 --> origin_tunnel(Tunnel)
   origin_tunnel --- received_bare_echo_reply([ICMP Echo Reply])
   received_bare_echo_reply --> origin(Origin)
   origin --- received_bare_echo_reply_2([ICMP Echo Reply])
   received_bare_echo_reply_2 --> Internet_2([Internet])
   Internet_2 --> cloudflare
```

### Rate

:::caution
Cloudflare Network Firewall rules apply to Internet Control Message Protocol (ICMP) traffic. If you enable Cloudflare Network Firewall, ensure your rules allow ICMP traffic sourced from Cloudflare public IPs. Otherwise, <GlossaryTooltip term="tunnel health-check">health checks</GlossaryTooltip> will fail. Refer to [Cloudflare Network Firewall rules](/cloudflare-network-firewall/about/ruleset-logic/#cloudflare-network-firewall-rules-and-magic-transit-endpoint-health-checks) for more information.
:::

Every Cloudflare data center configured to process your traffic sends tunnel health check probes. The rate at which Cloudflare sends these probes varies based on tunnel and location. You can tune this rate on a per-tunnel basis by modifying the `health_check` rate with the [API or the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/update-tunnel-health-checks-frequency/). You can set the rate as _low_, _mid_, or _high_, with _mid_ being the default.

The actual rate formula considers the number of servers in a Cloudflare data center or the number of servers with the customer namespace provisioned on them for dynamically provisioned namespaces. The rate is dynamic and depends on the size of Cloudflare's network.

When a probe attempt fails for a [healthy tunnel](#health-state-and-prioritization), each server detecting the failure quickly probes up to two more times to obtain an accurate result. Cloudflare does the same if a tunnel has been down and probes start returning success. Because Cloudflare global network servers send probes up to every second, your network will receive several hundred health check packets per second. Each Cloudflare data center sends only one health check packet as part of a probe, representing a relatively trivial amount of traffic.

## Health state and prioritization

There are three tunnel health states: healthy, degraded, and down.

Healthy tunnels are preferred to degraded tunnels, and degraded tunnels are preferred to those that are down.

Cloudflare WAN steers traffic to tunnels based on priorities you set when you [assign tunnel route priorities during onboarding](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/). Tunnel routes with lower values have priority over those with higher values.

:::note
Cloudflare global network servers may reach the origin infrastructure from some locations but not others. This occurs because Cloudflare does not synchronize health checks among global network servers and because the Internet is not homogeneous. Therefore, tunnel health may be in different states in different parts of the world at the same time.
:::

## Tunnel state determination

### Degraded

- When at least 0.1% of tunnel health checks fail in the previous five minutes (with at least two failures), Cloudflare WAN considers the link lossy and sets the tunnel state to degraded (assuming the tunnel is not down).
- Cloudflare WAN requires two failures so that a single lost packet does not trigger a penalty.
- Cloudflare WAN then immediately sets the tunnel status to degraded and applies a priority penalty.

### Down

- When all health checks of at least three samples in the last one second fail, Cloudflare WAN immediately transitions the tunnel from healthy or degraded to down, and applies a priority penalty to routes through that tunnel.
- A down state determination takes precedence over a degraded state determination. This means that a tunnel can only be one of the following: down, degraded, or healthy.

When Cloudflare WAN identifies a route that is not healthy, it applies these penalties:

- **Degraded**: Add `500,000` to priority.
- **Down**: Add `1,000,000` to priority.

The values for failure penalties are intentionally extreme so that they always exceed the priority values assigned during [routing configuration](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/).

Applying a penalty instead of removing the route altogether preserves redundancy and maintains options for customers with only one tunnel. Penalties also support the case when multiple tunnels are unhealthy.

## Cloudflare data centers and tunnels

In the event a Cloudflare data center is down, Cloudflare's global network does not advertise your prefixes, and Cloudflare routes your packets to the next closest data center. To check the system status for Cloudflare's global network and dashboard, refer to [Cloudflare System Status](https://www.cloudflarestatus.com/).

## Recovery

Once a tunnel is in the down state, global network servers continue to emit probes according to the cadence described earlier. When a probe returns healthy, the global network server that received the healthy packet immediately sends two more probes. If the two probes return healthy, Cloudflare WAN sets the tunnel status to degraded (as three consecutive successful probes no longer satisfy the condition for a down state).

Tunnels in a degraded state transition to healthy when the failure rate for the previous 30 probes is less than 0.1%. This transition may take up to 30 minutes.

Cloudflare WAN's tunnel health check system allows a tunnel to quickly transition from healthy to degraded or down, but transitions slowly from degraded or down to healthy. This behavior is called hysteresis and prevents routing changes caused by flapping and other intermittent network failures.

:::note
Cloudflare always attempts to send traffic over available tunnel routes with the highest priority (lowest route value), even when all configured tunnels are in an unhealthy state.
:::

## Example

Consider two tunnels and their associated routing priorities. Remember that lower route values have priority.

- Tunnel 1, route priority `100`
- Tunnel 2, route priority `200`

When both tunnels are in a healthy state, routing priority directs traffic exclusively to Tunnel 1 because its route priority of `100` beats that of Tunnel 2. Tunnel 2 does not receive any traffic, except for tunnel health check probes. Endpoint health checks only flow over Tunnel 1 to their destination inside the origin network.

### Failure response

If the link between Tunnel 1 and Cloudflare becomes unusable, Cloudflare global network servers discover the failure on their next health check probe, and immediately issue two more probes (assuming the tunnel was initially healthy).

When a global network server does not receive the proper ICMP reply packets from these two additional probes, the global network server labels Tunnel 1 as down, and downgrades Tunnel 1 priority to `1,000,100`. The priority then shifts to Tunnel 2, and Cloudflare WAN immediately steers packets arriving at that global network server to Tunnel 2.

### Recovery response

Suppose the connectivity issue that set Tunnel 1 health to down becomes resolved. At the next health check interval, the issuing global network server receives a successful probe and immediately sends two more probes to validate tunnel health.

When all three probes return successfully, Cloudflare WAN transitions the tunnel from down to degraded. As part of this transition, Cloudflare reduces the priority penalty for that route so that its priority becomes `500,100`. Because Tunnel 2 has a priority of `200`, traffic continues to flow over Tunnel 2.

Global network servers continue probing Tunnel 1. When the health check failure rate drops below 0.1% for a five-minute period, Cloudflare WAN sets tunnel status to healthy. Cloudflare fully restores Tunnel 1's routing priority to `100`, and traffic steering returns the data flow to Tunnel 1.

## Troubleshooting

For help resolving tunnel health issues, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/).

# Troubleshoot connectivity

import { DashButton } from "~/components";

This guide helps you determine whether a tunnel health alert is actually affecting your traffic. A degraded or down tunnel only matters if your traffic is currently routing through the Cloudflare data center where that tunnel is unhealthy.

:::note
Cloudflare does not synchronize health checks among global network servers. A tunnel can be healthy in one data center and degraded in another at the same time. This is normal behavior, not an outage.
:::

## Before you begin

Understand how Cloudflare WAN health checks and traffic routing work:

- Health checks run independently from every Cloudflare data center.
- Each data center evaluates tunnel health based on its own probes.
- Traffic enters Cloudflare at the data center closest to the source (anycast routing).
- A degraded tunnel in a data center that is not handling your traffic has no impact on your connectivity.

If you are experiencing actual tunnel health issues (tunnels flapping, all tunnels down, or IPsec errors), refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/) instead.

## Diagnostic flowchart

Use this flowchart to determine whether a tunnel health alert requires action.

```mermaid
flowchart TD
accTitle: Connectivity troubleshooting flowchart
accDescr: A decision tree to determine whether a degraded tunnel alert is affecting your traffic.

A["You received a tunnel<br>health alert"] --> B{"Is your traffic<br>affected?"}
B -- "Yes, I have<br>connectivity issues" --> C["Identify your ingress<br>data center and check<br>tunnel health there"]
B -- "No, traffic<br>flows normally" --> D{"Does the alert match<br>a data center carrying<br>your traffic?"}
D -- "No" --> E["No action required.<br>The degraded tunnel is in<br>a data center not serving<br>your traffic."]
D -- "Yes" --> C
C --> G{"Are tunnels healthy<br>at your ingress<br>data center?"}
G -- "Yes" --> H["The issue is not<br>tunnel-related. Check<br>Cloudflare Status and<br>your origin network."]
G -- "No" --> I["Tunnels at your ingress<br>data center are unhealthy.<br>Refer to Troubleshoot<br>tunnel health."]
```

## 1. Identify your ingress data center

Determine which Cloudflare data center your traffic is entering. This is the only data center whose tunnel health status matters for your current connectivity.

### Use traceroute

Run a `traceroute` from the source network to your Cloudflare WAN prefix. Look for the Cloudflare data center hostname in the trace output, which contains a three-letter [IATA airport code](https://en.wikipedia.org/wiki/IATA_airport_code) that identifies the data center.

```sh
traceroute 203.0.113.1
```

```txt
 1  192.168.1.1 (192.168.1.1)  1.234 ms
 2  10.0.0.1 (10.0.0.1)  5.678 ms
 3  198.51.100.1 (198.51.100.1)  10.123 ms
 4  198.51.100.10 (198.51.100.10)  12.345 ms
 5  lhr01.cf (198.51.100.11)  15.678 ms
```

In this example, `lhr` indicates that traffic enters Cloudflare at the London (Heathrow) data center.

### Use the Cloudflare dashboard

You can identify which data centers handle your traffic by using **Network Analytics**.

1. Go to the **Network Analytics** page.

	<DashButton url="/?to=/:account/networking-insights/analytics/network-analytics/transport-analytics" />

2. Select **Add filter** and filter traffic by your source IP addresses to isolate your traffic.
3. Under **Packets summary**, select the **Source data center** tab. If the tab is not visible, select the three-dot menu (`...`) to reveal additional view options and select **Source data center**.
4. Review the per-data-center traffic breakdown to identify which Cloudflare data centers are handling your traffic.
5. Cross-reference these data centers with the tunnel health status on the [**Connector health** page](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/). If tunnels are healthy at the data centers carrying your traffic, a degraded tunnel alert for a different data center is not the cause of your connectivity issue.

## 2. Correlate with Cloudflare status

If your tunnels are healthy at the relevant data center but you still experience connectivity issues, check for broader platform issues.

1. Go to [Cloudflare Status](https://www.cloudflarestatus.com/).
2. Look for any active incidents or maintenance at the data center you identified.
3. Check for any incidents that might affect your traffic, such as outages related to networking, BYOIP, or the services your configuration depends on.

## 3. Gather information for support

If you have worked through this guide and cannot resolve the issue, gather the following information before contacting Cloudflare support.

### Required information

1. **Account ID** and **tunnel name(s)** affected
2. **Timestamps** (in UTC) when the issue started
3. **Ingress data center** you identified (airport code, for example `LHR`, `IAD`)
4. **Symptoms observed:**
   - Whether user traffic is affected or only health check alerts fired
   - Which tunnels and data centers show degraded or down status
   - Whether the issue is intermittent or persistent

### Helpful diagnostic data

- **Traceroute output** from your source network to your Cloudflare WAN prefix
- **Dashboard screenshots** showing tunnel health at the relevant data center
- **Distributed traceroutes** using tools like [ping.pe](https://ping.pe) to test reachability from multiple global locations
- **Packet captures** from your router if traffic loss is confirmed

## Related resources

- [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/): Resolve common tunnel health issues (flapping, IPsec errors, stateful firewall drops).
- [Troubleshoot routing and BGP](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/routing-and-bgp/): Diagnose routing and BGP issues that affect traffic delivery.
- [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/): Monitor tunnel status per data center.
- [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/): Technical details on how health checks work.
- [Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/): Analyze traffic patterns over time.

# Troubleshooting

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Troubleshoot IPsec tunnels

import { DashButton } from "~/components";

This guide helps you diagnose IPsec tunnel issues (also called connectors in the Cloudflare dashboard), from initial establishment through ongoing operation. Use the following sections to identify your symptom and find the appropriate solution.

## Tunnel never establishes (IKE negotiation fails)

### Symptoms

- Tunnel status shows `Down` and never becomes healthy
- No traffic passes through the tunnel
- Tunnel endpoint logs show IKE negotiation errors or retransmissions

### Possible causes and solutions

#### Firewall blocking IKE traffic

Your edge firewall may be blocking the traffic required for IPsec tunnel establishment. Verify your firewall permits:

- UDP port `500` (IKE)
- UDP port `4500` (IKE NAT-T)
- IP protocol `50` (ESP)

#### Crypto parameter mismatch

IKE negotiation fails when Phase 1 (IKE) or Phase 2 (IPsec) parameters do not match between your tunnel endpoint and Cloudflare. Common symptoms include "no proposal chosen" errors in your device logs.

Verify your parameters match Cloudflare's supported values. For the complete list, refer to [Supported configuration parameters](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters).

#### Pre-shared key (PSK) mismatch

Authentication failures in Phase 1 indicate a PSK mismatch. To resolve:

1. Go to **Connectors** and select your tunnel.

	<DashButton url="/?to=/:account/magic-networks/connections" />

2. Select **Generate new PSK**.
3. Copy the new PSK exactly — do not add extra spaces or characters.
4. Update your tunnel endpoint with the new PSK.

#### IKE ID format mismatch

Cloudflare uses FQDN format for the IKE ID. If your tunnel endpoint expects a different peer identity format (such as an IP address), authentication fails even when the PSK is correct.

Ensure your tunnel endpoint is configured to accept an FQDN peer identity. To find your tunnel's FQDN, go to **Connectors**, select your tunnel, and check the tunnel details.

---

## Tunnel establishes but health checks fail

### Symptoms

- IKE negotiation completes successfully
- Tunnel shows `Down` or `Degraded` in the dashboard
- User traffic may still pass through the tunnel

### Possible causes and solutions

#### Anti-replay protection enabled on tunnel endpoint

This is the most common IPsec issue. Anti-replay protection expects packets to arrive in sequence from a single sender. Cloudflare's anycast architecture means tunnel traffic originates from thousands of servers, each with its own sequence counter. This causes your tunnel endpoint to drop packets as out-of-order.

Disable anti-replay protection on your tunnel endpoint, or set the replay window to `0`. For a detailed explanation, refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/).

#### Health check type incompatible with stateful firewall

Stateful firewalls (such as Palo Alto Networks, Check Point, Cisco, and Fortinet) drop the default *Reply* health check packets because no matching ICMP request exists in their session table.

Change the health check type from *Reply* to *Request*. For detailed steps, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/).

#### ISP blocking health check return path

With unidirectional health checks, Cloudflare sends probes through the tunnel, but responses return via the public internet (direct server return). If your ISP blocks ICMP reply packets destined for Cloudflare, health checks fail even though tunnel traffic works normally.

If you have egress traffic enabled, consider switching to bidirectional health checks so that both the probe and response traverse the tunnel. For configuration details, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/).

#### Policy-based VPN health check failures

If you use a policy-based VPN (where traffic selectors define specific prefixes rather than `0.0.0.0/0`), Reply-style health checks do not work. Reply health checks are self-addressed to Cloudflare IP addresses, which fall outside your tunnel's traffic selectors.

Use Request-style health checks instead. Configure a loopback address on your tunnel endpoint as the health check target. The target must be routable and covered by the tunnel's traffic selectors (encryption domain). For more details, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/).

---

## Health checks fail at a low, steady rate {/* anti-replay-mismatch */}

### Symptoms

All of the following apply:

- The tunnel is an IPsec tunnel with **Replay protection** turned off in Cloudflare. This is the default.
- Tunnel health checks are turned on.
- The health check pass rate stays between 95% and 100%. Failures are small and steady, not a full outage.
- Most traffic passes through the tunnel, with occasional packet loss.

### Cause

The anti-replay setting on your tunnel endpoint does not match the replay protection setting on your Cloudflare tunnel. Your device still enforces anti-replay protection and drops some Encapsulating Security Payload (ESP) packets as replays.

Cloudflare sends tunnel traffic from many anycast servers, each with its own sequence counter. Packets therefore arrive at your device out of order, and the device drops the packets that fall outside its replay window.

This can happen even if you have turned off anti-replay on your device. On some devices, the option to turn it off does not take effect or reverts to its previous value.

To confirm the cause, check your device logs for packets dropped as replays. The message depends on the vendor. For example, Check Point logs the following:

```txt
Encryption failure: Warning: possible replay attack
```

Other devices may log messages such as `replay check failed` or `invalid sequence number`.

### Solution

Make the anti-replay setting on your device match the replay protection setting on your Cloudflare tunnel. Choose one of the following options:

- **Turn off anti-replay on your device (recommended).** Fully disable anti-replay protection, or set the replay window to `0`. After you make the change, check your device logs to confirm the device no longer drops packets as replays. For vendor-specific steps, refer to [Fortinet](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/fortinet/#disable-anti-replay-protection), [Sophos Firewall](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/sophos-firewall/), [SonicWall](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/sonicwall/), [Palo Alto Networks](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/palo-alto/), or other [third-party integration guides](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/third-party/).
- **Turn on replay protection on your Cloudflare tunnel.** Use this option if your device cannot reliably turn off anti-replay. For dashboard and API steps, refer to [Add tunnels](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels).

:::caution
Turning on replay protection currently reduces throughput for Cloudflare IPsec tunnels. For more information, refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/).
:::

---

## Tunnel works intermittently (flapping)

### Symptoms

- Tunnel alternates between healthy and unhealthy states
- Intermittent packet loss on the tunnel
- Traffic works for a period then stops without configuration changes

### Possible causes and solutions

#### Anti-replay protection dropping out-of-order packets

Cloudflare's anycast architecture means packets arrive from many servers with different sequence counters. Anti-replay protection interprets this as a replay attack and drops packets intermittently.

Disable anti-replay protection on your tunnel endpoint, or set the replay window to `0`. For a detailed explanation, refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/).

#### Rekey events causing brief disruption

When your tunnel endpoint initiates an IPsec rekey, new Security Associations (SAs) must propagate across Cloudflare's network. Rekey propagation delays have been significantly reduced and are uncommon in most deployments. However, brief tunnel degradation during rekeys can still occur in some configurations.

Cloudflare never initiates rekey — only responds. All rekey attempts must come from your tunnel endpoint. If your device receives a TEMPORARY_FAILURE response during rekey, configure Dead Peer Detection (DPD) with a "restart" action so the device re-establishes the IKE session automatically. Without DPD restart, the device can get stuck in a loop of failed rekeys.

To minimize any impact from rekeys, increase SA lifetimes on your tunnel endpoint to reduce rekey frequency. Common values are 8-24 hours for IKE SA and 1-8 hours for IPsec SA. For more details, refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/).

#### MTU issues

Packets exceeding the tunnel MTU are fragmented or dropped, causing intermittent connectivity issues. Verify MTU is set correctly — typically `1476` for GRE tunnels and `1400`-`1450` for IPsec tunnels. For detailed guidance, refer to [MTU and MSS](/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/).

---

## Monitor with IPsec logs

Use IPsec logs to monitor tunnel activity during the key-exchange phase of the IPsec negotiation. Configure a Logpush job to forward these logs to your preferred storage service for analysis.

### Set up an IPsec Logpush job

1. Go to the **Logpush** page.

	<DashButton url="/?to=/:account/logs" />

2. Select **Create a Logpush job**.
3. Select **IPsec logs** as your dataset.

Refer to the [Logpush documentation](/logs/logpush/) for more information about features, including the [available fields](/logs/logpush/logpush-job/datasets/account/ipsec_logs/) in the dataset.

# Troubleshoot routing and BGP

This guide helps you diagnose and resolve common routing and BGP issues with Cloudflare WAN. These issues can affect traffic delivery, cause unexpected latency, or result in connectivity loss.

## Quick diagnostic checklist

If you are experiencing routing or BGP issues, check these items first:

1. **BGP session state**: Verify session is **Established**, not stuck in **Connect** or **Active**.
2. **Firewall rules**: Ensure TCP port `179` is permitted bidirectionally between your router and Cloudflare.
5. **Tunnel or CNI health**: Check that underlying connectivity is healthy. Degraded tunnels affect route priority.
6. **Static route conflicts**: Static routes take precedence over BGP routes at equal priority.

## Resolve common issues

### BGP session not establishing

This section covers BGP peering sessions between your network and Cloudflare, established over [CNI](/network-interconnect/) or tunnels.

#### Symptoms

- BGP session never reaches **Established** state
- No routes being advertised or received
- Router logs show repeated connection attempts

#### BGP session states

| State | Meaning | Action |
| :--- | :--- | :--- |
| **Established** | Session up, exchanging routes | Normal operation |
| **Active** | Attempting to initiate connection | Check firewall rules, verify neighbor IP |
| **Connect** | TCP connection in progress | Check port `179` access, verify peering IP |
| **Idle** | Session down, no connection attempts | Check configuration, verify BGP is enabled |

#### Solution

1. Verify your firewall permits TCP port `179` bidirectionally between your router and the Cloudflare peering address.
2. Confirm the neighbor IP matches the Cloudflare-provided peering address exactly.
3. Verify your ASN configuration matches the dashboard settings. Only eBGP is supported, so your ASN must differ from the Cloudflare account ASN.
4. If using MD5 authentication, verify the password matches on both sides.

### Unexpected traffic routing or latency

#### Symptoms

- Traffic from specific regions routed through distant data centers
- Higher than expected latency for regional users
- Traffic not using the closest tunnel or CNI

#### Causes

- Tunnel health degradation causing route deprioritization
- Regional route scoping misconfiguration
- BGP route priorities not set as expected
- Static routes overriding BGP routes

#### Solution

1. **Check tunnel health**: Degraded tunnels have 500,000 added to their route priority. Down tunnels have 1,000,000 added. Traffic shifts to healthier paths, which may be in different regions. Refer to [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/) for diagnostic steps.

2. **Review route priorities**: Lower priority values indicate higher preference. Verify your routes have the expected priority configuration.

   - Default BGP route priority: `100`
   - Static routes at priority `100` take precedence over BGP routes at `100`

3. **Check regional scoping**: If you use region-scoped routes, ensure all regions have route coverage. Traffic arriving at a region without a matching route is dropped.

4. **Use Network Analytics**: Review traffic patterns to identify where traffic is landing and which paths it follows. Refer to [Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/) for usage instructions.

### CNI link failures

#### Symptoms

- CNI shows down in dashboard
- BGP session over CNI drops
- Traffic fails over to tunnels or alternate CNIs

#### CNI issue layers

CNI issues can occur at multiple layers:

| Issue type | Impact | What to check |
| :--- | :--- | :--- |
| Physical link down | All traffic over that CNI affected | Light levels, cross-connect status |
| BGP session down | Dynamic routes withdrawn | BGP neighbor state on your router |
| Prefixes withdrawn | Specific routes unavailable | BGP advertised and received routes |

A healthy physical link can still have BGP issues. A healthy BGP session can exist while specific prefixes are withdrawn.

#### Solution

**Check physical layer (your side):**

:::note
In the case of interconnects provisioned by third parties, you may need to request that your provider carry these steps out.
:::

1. Verify the interface is administratively up on your router.
2. Check optical light levels (Tx/Rx dBm). Abnormal readings indicate fiber or transceiver issues.
3. If light levels are low or absent on your receive side, contact your data center to verify cross-connect status.

**Check BGP session:**

1. Verify BGP neighbor state on your router shows **Established**.
2. Check for MD5 authentication mismatches if authentication is configured.
3. Review BGP logs for error messages indicating why the session may have dropped.

**Check for maintenance:**

1. Review [Cloudflare Status](https://www.cloudflarestatus.com/) for scheduled maintenance affecting your CNI location.
2. Some maintenance events may temporarily affect CNI connectivity even when marked as non-disruptive.

Refer to [Network Interconnect](/network-interconnect/) for CNI configuration and setup information.

### Static and BGP route conflicts

#### Symptoms

- BGP routes not being used despite being learned
- Traffic not following expected BGP path
- Route changes not taking effect as expected

#### Cause

Cloudflare prefers static routes when static and BGP routes share the same prefix and priority. This ensures manually configured routes take precedence unless explicitly deprioritized.

#### Solution

Adjust route priorities based on your preference:

- **To prefer BGP routes**: Set static route priority to a higher number (for example, `150` or `200`). Higher numbers indicate lower preference.
- **To prefer static routes**: Keep static route priority at or below `100`. BGP routes default to priority `100`.

| Route type | Prefix | Priority | Selected |
| :--- | :--- | :--- | :--- |
| Static | `10.0.0.0/24` | `100` | Yes (static wins ties) |
| BGP | `10.0.0.0/24` | `100` | No |

To make the BGP route preferred in this example, change the static route priority to `150` or higher, or remove the static route entirely.

Refer to [Route prioritization](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#route-prioritization) for detailed information on how priorities work.

## CNI, tunnel, and BGP health

Understanding the relationship between these components helps diagnose routing issues:

| Component | What it monitors | Impact when unhealthy |
| :--- | :--- | :--- |
| **CNI health** | Physical or virtual interconnect link status | BGP session may drop. All traffic over that CNI is affected. |
| **Tunnel health** | Logical GRE or IPsec tunnel through health check probes | Route priority penalized. Traffic steers to healthier tunnels. |
| **BGP session** | Control plane connectivity for dynamic routing | Dynamic routes withdrawn. Static routes remain unaffected. |

A healthy CNI can have an unhealthy tunnel if health check probes are blocked or misconfigured. BGP routes can be withdrawn even when the underlying physical link is operational.

## Gather information for support

If you have worked through this guide and still experience routing issues, gather the following information before contacting Cloudflare support.

### Required information

1. **Account ID** and affected prefix(es), tunnel name(s), or CNI identifier(s)
2. **Timestamps** (in UTC) when the issue occurred
3. **BGP configuration details:**
   - Your ASN and Cloudflare peering ASN
   - Neighbor IP addresses
   - Sanitized router configuration (remove passwords and keys)
4. **Current state information:**
   - BGP session state from your router
   - Dashboard screenshots showing prefix, route, or tunnel status

### Helpful diagnostic data

- **Router logs**: BGP neighbor logs covering the incident timeframe
- **Traceroute results**: From affected source networks to your prefix
- **For CNI issues**: Optical light level readings from your equipment

### Router diagnostic commands

Collect output from these commands (syntax varies by vendor):

```sh
# Show BGP neighbor status
show bgp neighbors

# Show BGP summary
show bgp ipv4 unicast summary

# Show specific prefix in BGP table
show bgp ipv4 unicast <YOUR_PREFIX>

# Show interface status (for CNI)
show interface <YOUR_INTERFACE_NAME>

# Show received and advertised routes
show bgp ipv4 unicast neighbors <YOUR_NEIGHBOR_IP> routes
show bgp ipv4 unicast neighbors <YOUR_NEIGHBOR_IP> advertised-routes
```

## Resources

- [Traffic steering](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#route-prioritization): Route prioritization, BGP communities, and ECMP behavior
- [Configure routes](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/): Static route configuration
- [Network Interconnect](/network-interconnect/): CNI setup and BGP peering
- [Troubleshoot tunnel health](/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/): Tunnel-specific diagnostic steps
- [Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/): Traffic analysis and monitoring
- [Cloudflare Status](https://www.cloudflarestatus.com/): Maintenance and incident notifications

# Troubleshoot tunnel health

import { DashButton } from "~/components";

This guide helps you diagnose and resolve common tunnel health issues with Cloudflare WAN. Tunnel health checks monitor your GRE and IPsec tunnel endpoints (also called connectors in the Cloudflare dashboard) and steer traffic to the best available routes.

## Quick diagnostic checklist

Use the following table to match your symptom to the most likely cause and first action:

| Symptom | Most likely cause | First action |
| :------ | :---------------- | :----------- |
| Tunnel shows Down, never becomes healthy | Configuration mismatch or firewall blocking IKE | Check IPsec parameters and firewall rules. Refer to [IPsec tunnel establishment failures](#ipsec-tunnel-establishment-failures). |
| Dashboard shows "100% degraded" for some colos | Normal — this is a state indicator, not packet loss | Check if affected colos carry your traffic. Refer to [Understanding degraded status](#understanding-degraded-status-in-the-dashboard). |
| Tunnel flaps between healthy and unhealthy | Anti-replay protection or rekey disruption | Disable anti-replay protection on your router. Refer to [IPsec tunnel instability](#ipsec-tunnel-instability-or-packet-drops). |
| Health checks fail but traffic flows normally | Stateful firewall dropping health check probes | Change health check type from *Reply* to *Request*. Refer to [Tunnel shows Down but traffic is flowing](#tunnel-shows-down-but-traffic-is-flowing). |
| Health checks fail on policy-based VPN tunnels | Reply health checks fall outside tunnel traffic selectors | Use Request-style health checks with a loopback target. Refer to [Policy-based VPN health check failures](#policy-based-vpn-health-check-failures). |
| All tunnels degraded or down in a specific region | Network path issue between that region and your network | Check ISP connectivity. Use traceroute or MTR from your tunnel endpoint toward Cloudflare. |
| All tunnels degraded or down globally | Issue at your network edge | Check your tunnel endpoint router and upstream connectivity. |

### What you can check

- **Dashboard**: Tunnel health status per data center and traffic volume per tunnel (Go to **Insights** > **Network health** > **Network health**)
- **API**: Tunnel health status via the [Cloudflare WAN tunnel health API](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/)
- **Network Analytics**: Traffic volume, packet counts, and protocol distribution through [Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/)
- **From your network**: Traceroute and MTR from your tunnel endpoint toward Cloudflare. Since Cloudflare endpoints use anycast, this tests the path to the nearest data center only. To test specific regions, use the [Cloudflare Traceroute API](/api/resources/diagnostics/subresources/traceroutes/methods/create/) to run traceroutes from specific Cloudflare locations to your network.

### What you cannot check (current limitations)

- Correlation between tunnel health events and Cloudflare network incidents
- Per-packet forwarding decisions (which data center forwarded which packet through which tunnel)
- Historical health check probe data beyond the dashboard retention period

### Common fixes checklist

If you are experiencing tunnel health issues, check these items first:

1. **Health check type**: If using a stateful firewall (such as Palo Alto Networks, Check Point, Cisco, or Fortinet), change health check type from *Reply* to *Request*.
2. **Anti-replay protection**: Disable anti-replay protection on your router, or set the replay window to `0`.
3. **MTU settings**: Verify MTU is set correctly (typically `1476` for GRE, `1400`-`1450` for IPsec).
4. **IPsec parameters**: Confirm your cryptographic parameters match [Cloudflare's supported configuration](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters).
5. **Health check direction**: Cloudflare WAN defaults to _Bidirectional_.
6. **Cloudflare Network Firewall rules (less common)**: Ensure ICMP traffic from [Cloudflare IP addresses](https://cloudflare.com/ips/) is allowed.

---

## Tunnel health states

The [Network health](https://dash.cloudflare.com/?to=/:account/networking-insights/health) page in the Cloudflare dashboard displays three tunnel health states:

| State | Dashboard display | Technical threshold |
| :---- | :---- | :---- |
| **Healthy** | More than 80% of health checks pass | Less than 0.1% failure rate |
| **Degraded** | Between 40% and 80% of health checks pass | At least 0.1% failures in last five minutes (minimum two failures) |
| **Down** | Less than 40% of health checks pass | All health checks failed (at least three samples in last second) |

The dashboard shows tunnel health as measured from each Cloudflare data center where your traffic lands. It is normal to see some locations reporting degraded status due to Internet path issues. Focus on locations that show traffic in the **Traffic volume (1h)** column.

:::note[Probe retry behavior]
When a health check probe fails, Cloudflare sends two additional probes to confirm the failure. A tunnel is only marked as unhealthy if all three probes fail. This retry behavior provides resilience against random packet loss.
:::

### Understanding degraded status in the dashboard

The tunnel health dashboard reports health state per data center per tunnel. Each Cloudflare data center independently tracks the health of each tunnel.

A common source of confusion is seeing "100% degraded" in the dashboard and misinterpreting it as 100% packet loss. Note that these are different.

:::note[100% degraded is a state, not a packet loss percentage]
Each Cloudflare data center is a single tracking instance for a tunnel. When a tunnel enters the degraded state in a data center, the dashboard reports that data center as "100% degraded" for that tunnel. The actual packet loss that triggered the state change may be very small — even a brief period of intermittent loss that does not noticeably affect applications can trigger the degraded state.
:::

**How degraded state is triggered:**

When a health check probe fails, Cloudflare sends two additional probes. If some probes succeed and some fail, the tunnel enters degraded state for that data center. A few seconds of intermittent packet loss is enough to trigger this transition.

**What to check:**

Focus on data centers that show traffic in the **Traffic volume (1h)** column. A data center showing degraded status with zero or minimal traffic is informational — it indicates a path issue between that specific Cloudflare data center and your network, but it does not affect your traffic if no traffic routes through that data center.

**Recovery timing:**

Tunnels remain in degraded state for at least five minutes, even if health checks start succeeding immediately. Recovery from degraded to healthy requires consistently passing health checks over a sustained period and can take up to 30 minutes. For details on how tunnels transition between states, refer to [Recovery behavior](#recovery-behavior) below.

### Routing priority penalties

When a tunnel becomes unhealthy, Cloudflare applies priority penalties to routes through that tunnel:

- **Degraded**: Adds `500,000` to route priority
- **Down**: Adds `1,000,000` to route priority

These penalties shift traffic to healthier tunnels while maintaining redundancy. Cloudflare never completely removes routes, preserving failover options even when all tunnels are unhealthy.

### Recovery behavior

Tunnels transition between states asymmetrically to prevent flapping:

- **Healthy to Degraded/Down**: Transitions quickly when failures are detected. A tunnel can go directly from Healthy to Down if all probe retries fail.
- **Down to Degraded**: Requires three consecutive successful health check probes.
- **Degraded to Healthy**: Requires failure rate below 0.1% over 30 consecutive probes.

:::note[Minimum state duration]
Tunnels remain in a degraded or down state for at least five minutes, even if health checks start succeeding immediately. This minimum duration prevents rapid flapping when there is intermittent packet loss. Additionally, a tunnel recovering from `Down` must always transition through `Degraded` before returning to `Healthy`.

Recovery from degraded to healthy can take up to 30 minutes. This intentional slow recovery behavior (called hysteresis) prevents rapid state changes caused by intermittent network issues or tunnel flapping.
:::

For instructions on monitoring tunnel status, refer to [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/).

### Health check types and directions

**Health check type:**

| Type | Behavior | When to use |
| :---- | :---- | :---- |
| **Reply** (default) | Cloudflare sends an ICMP reply packet | Simple networks without stateful firewalls |
| **Request** | Cloudflare sends an ICMP echo request | Networks with stateful firewalls (recommended for most deployments) |

**Health check direction:**

| Direction | Behavior | Default for |
| :---- | :---- | :--- |
| **Bidirectional** | Probe and response both traverse the tunnel | Cloudflare WAN (formerly Magic WAN) |
| **Unidirectional** | Probe traverses tunnel; response returns via Internet | Magic Transit (direct server return) |

:::note
Unidirectional health checks can be unreliable because intermediate network devices may drop ICMP reply packets. If you have egress traffic enabled, consider switching to bidirectional health checks.
:::

---

## Resolve common issues

### Tunnel shows `Down` but traffic is flowing

#### Symptoms

- Dashboard shows tunnel as `Down` or `Degraded`
- Actual user traffic passes through the tunnel successfully
- Health check failure rate is 100% despite working connectivity

#### Cause

Stateful firewalls (such as Palo Alto Networks, Check Point, Cisco, and Fortinet) drop the health check packets. By default, Cloudflare sends ICMP *Reply* packets as health check probes.

Stateful firewalls inspect these packets and look for a matching ICMP *Request* in their session table. When no matching request exists, firewalls drop the reply as "out-of-state".

#### Solution

Change the health check type from *Reply* to *Request*:

1. Go to the **Connectors** page.

	<DashButton url="/?to=/:account/magic-networks/connections" />

2. In **IPsec/GRE tunnels**, select **Edit** on the affected tunnel.
3. Under **Health check type**, change from *Reply* to *Request*.
4. Select **Update tunnel**.

When you use *Request* style health checks, Cloudflare sends an ICMP echo request. Your firewall's stateful inspection engine recognizes this as a legitimate request and automatically permits the ICMP reply response.

:::note
If your firewall drops ICMP request packets as well, verify that your firewall policy permits ICMP traffic on the tunnel interface.
:::

---

### Health check failures with Cloudflare Network Firewall

#### Symptoms

- Tunnels were healthy before enabling Cloudflare Network Firewall
- After adding Cloudflare Network Firewall rules, health checks fail
- Blocking ICMP traffic causes immediate health check failures

#### Cause

Cloudflare Network Firewall processes all traffic, including Cloudflare's health check probes. If you create a rule that blocks ICMP traffic, you also block the health check packets that Cloudflare sends to monitor tunnel status.

#### Solution

Add an allow rule for ICMP traffic from Cloudflare IP addresses *before* any block rules:

1. Go to the **Firewall policies** page.

	<DashButton url="/?to=/:account/network-security/magic_firewall" />

2. Create a new policy with the following parameters:

| Field | Value |
| :---- | :---- |
| **Action** | Allow |
| **Protocol** | ICMP |
| **Source** | [Cloudflare IP ranges](https://cloudflare.com/ips/) |

3. Position this rule *before* any rules that block ICMP traffic.

For more information, refer to [Cloudflare Network Firewall rules and endpoint health checks](/cloudflare-network-firewall/about/ruleset-logic/#cloudflare-network-firewall-rules-and-magic-transit-endpoint-health-checks).

---

### IPsec tunnel instability or packet drops

#### Symptoms

- IPsec tunnel frequently flaps between healthy and down states
- Intermittent packet loss on the tunnel
- Traffic works for a period then stops without configuration changes
- Router logs show packets dropped due to:
  - "replay check failed"
  - "invalid sequence number"
  - "invalid SPI" (Security Parameter Index)

#### Cause

Anti-replay protection is enabled on your router. IPsec anti-replay protection expects packets to arrive in sequence from a single sender.

Cloudflare's anycast architecture means your tunnel traffic can originate from thousands of servers across hundreds of data centers. Each server maintains its own sequence counter, causing packets to arrive out-of-order from your router's perspective.

#### Solution

Disable anti-replay protection on your router:

**For most routers:**

Locate the anti-replay or replay protection setting in your IPsec configuration and disable it.

**If you can only set a replay window size:**

Set the replay window to `0` to effectively disable the check.

**For devices that do not support disabling anti-replay:**

Enable replay protection in the Cloudflare dashboard. This routes all tunnel traffic through a single server, maintaining proper sequence numbers at the cost of losing anycast benefits.

1. Go to the **Connectors** page.

	<DashButton url="/?to=/:account/magic-networks/connections" />

2. In **IPsec/GRE tunnels**, select **Edit** on your IPsec tunnel.
3. Enable **Replay protection**.
4. Select **Update tunnel**.

**For Cisco IOS/IOS-XE routers experiencing "invalid SPI" errors:**

Enable ISAKMP invalid SPI recovery to help the router resynchronize Security Associations:

```txt
configure terminal
crypto isakmp invalid-spi-recovery
exit
```

:::caution
Enabling replay protection in Cloudflare reduces the performance and resilience benefits of the anycast architecture. Only use this option when your device does not support disabling anti-replay protection.
:::

For a detailed explanation of why this setting is necessary, refer to [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/).

---

### Tunnel degraded after rekey events

#### Symptoms

- Tunnel health drops to `Degraded` or `Down` periodically
- Issues coincide with IPsec rekey intervals (typically every few hours)
- Tunnel recovers automatically after 1-3 minutes
- Router logs show successful rekey completion

#### Cause

When your tunnel endpoint initiates an IPsec rekey, new Security Associations (SAs) must propagate across Cloudflare's network. Rekey propagation delays have been significantly reduced and are uncommon in most deployments. However, brief tunnel degradation during rekeys can still occur in some configurations.

Cloudflare never initiates rekey — only responds. All rekey attempts must come from your tunnel endpoint. If your device receives a TEMPORARY_FAILURE response during rekey, it must re-establish the IKE session to recover.

#### Solution

This behavior is expected and the tunnel will automatically recover. To minimize impact:

1. **Configure Dead Peer Detection (DPD) with restart**: Set your tunnel endpoint's DPD action to "restart" so it automatically re-establishes the IKE session if a rekey fails with TEMPORARY_FAILURE. Without DPD restart, the device can get stuck in a loop of failed rekeys.

2. **Increase rekey intervals**: Configure longer SA lifetimes on your tunnel endpoint to reduce rekey frequency. Common values are 8-24 hours for IKE SA and 1-8 hours for IPsec SA.

3. **Adjust health check sensitivity**: If brief degradation during rekeys triggers alerts, consider lowering the health check rate:

   1. Go to the **Connectors** page.

	<DashButton url="/?to=/:account/magic-networks/connections" />

   2. In **IPsec/GRE tunnels**, select **Edit** on the tunnel.
   3. Change **Health check rate** to *Low*.

4. **Stagger rekey times**: If you have multiple tunnels, configure different SA lifetimes so they do not rekey simultaneously.

---

### Bidirectional health check failures

#### Symptoms

- Health checks configured as bidirectional fail consistently
- Unidirectional health checks work correctly
- Traffic flows through the tunnel normally

#### Cause

Bidirectional health checks require both the probe and response to traverse the tunnel. Your router must:

1. Accept ICMP packets destined for the tunnel interface IP addresses
2. Route the ICMP response back through the tunnel to Cloudflare

If traffic selectors or firewall rules do not permit this traffic, bidirectional health checks fail.

#### Solution

**For IPsec tunnels:**

Configure traffic selectors to accept packets for the tunnel interface addresses. For example, if your tunnel interface address is `10.252.2.27/31`:

- Permit traffic to/from `10.252.2.26` (Cloudflare side)
- Permit traffic to/from `10.252.2.27` (your side)

**For all tunnel types:**

Ensure your firewall permits ICMP traffic on the tunnel interface. Many firewalls require explicit rules to allow management traffic (including ping) on tunnel interfaces.

For detailed information on how bidirectional health checks work, refer to [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/).

---

### IPsec tunnel establishment failures

#### Symptoms

- Tunnel status shows `Down` and never becomes healthy
- No traffic passes through the tunnel
- Router logs show IKE negotiation failures

#### Cause

IPsec tunnel establishment can fail due to several configuration mismatches:

| Issue | Symptom |
| :---- | :---- |
| **Crypto parameter mismatch** | IKE negotiation fails with "no proposal chosen" |
| **Incorrect PSK** | Authentication failures in Phase 1 |
| **Wrong IKE ID format** | Authentication failures despite correct PSK |
| **Firewall blocking IKE** | No IKE traffic reaches Cloudflare |

#### Solution

1. **Verify crypto parameters match Cloudflare's supported configuration:**

   **Phase 1 (IKE)**


| Parameter | Supported values |
| :---- | :---- |
| IKE version | IKEv2 only |
| Encryption | AES-GCM-16, AES-CBC-256 |
| Authentication | SHA-256, SHA-384, SHA-512 |
| DH Group | DH group 14, 15, 16, 19, 20 |

   **Phase 2 (IPsec)**

| Parameter | Supported values |
| :---- | :---- |
| Encryption | AES-GCM-16, AES-CBC-256 |
| Authentication | SHA-256, SHA-512 |
| PFS Group | DH group 14, 15, 16, 19, 20 |

2. **Verify the Pre-Shared Key (PSK):**

   - Regenerate the PSK in the Cloudflare dashboard
   - Copy the new PSK exactly (no extra spaces or characters)
   - Update your router with the new PSK

3. **Check the IKE ID format:** Cloudflare uses FQDN format for the IKE ID. Ensure your router is configured to accept an FQDN peer identity. The FQDN is displayed in the tunnel details in the Cloudflare dashboard.

4. **Verify firewall rules:** Ensure your edge firewall permits:

   - UDP port `500` (IKE)
   - UDP port `4500` (IKE NAT-T)
   - IP protocol `50` (ESP)

For the complete list of supported parameters, refer to [Supported configuration parameters](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters).

---

### Policy-based VPN health check failures

#### Symptoms

- Health checks fail consistently on policy-based IPsec tunnels
- Traffic matching the tunnel's traffic selectors (encryption domain) flows normally
- Route-based tunnels on the same device work correctly

#### Cause

Policy-based IPsec tunnels use traffic selectors to define which prefixes are permitted in the tunnel. Reply-style health checks are self-addressed to Cloudflare IP addresses. These addresses fall outside the tunnel's traffic selectors (which only permit customer network destinations), so the tunnel endpoint drops the health check packets.

Additionally, some firewalls (such as Check Point) may flag Reply-style health check packets as spoofed due to their self-addressed nature, even on route-based tunnels.

#### Solution

1. Change the health check type from *Reply* to *Request*.
2. Configure a loopback address on your tunnel endpoint as the health check target. The target must be:
   - Routable from the tunnel endpoint
   - Covered by the tunnel's traffic selectors (encryption domain)
3. For bidirectional health checks, ensure the health check source (the tunnel Interface Address configured in the Cloudflare dashboard) is also covered by a traffic selector.

:::note
Policy-based tunnels use a separate Child SA for each set of traffic selectors. There is a limit of approximately 100 Child SAs per tunnel. The health check traffic may use its own Child SA, which can go down independently from the Child SAs carrying your application traffic.
:::

---

## Vendor-specific guidance

### Common vendor-specific issues

| Vendor | Common issue | Solution |
| :---- | :---- | :---- |
| **Palo Alto Networks** | Health checks fail with default settings | Change health check type to *Request*; disable anti-replay |
| **Cisco Meraki** | Cannot disable anti-replay | Enable replay protection in Cloudflare dashboard |
| **AWS VPN Gateway** | Cannot disable anti-replay | Enable replay protection in Cloudflare dashboard |
| **VeloCloud** | Cannot disable anti-replay | Enable replay protection in Cloudflare dashboard |
| **Check Point** | Out-of-state packet drops | Change health check type to *Request* |

---

## Gather information for support

If you have worked through this guide and still experience tunnel health issues, gather the following information before contacting Cloudflare support:

### Required information

1. **Account ID** and **Tunnel name(s)** affected
2. **Timestamps** (in UTC) when the issue occurred
3. **Tunnel configuration details:**
   - Tunnel type (GRE or IPsec)
   - Health check type (Request or Reply)
   - Health check direction (Bidirectional or Unidirectional)
   - Health check rate (Low, Medium, or High)
4. **Router information:**
   - Vendor and model
   - Firmware/software version
   - IPsec configuration (sanitized to remove PSK)
5. **Symptoms observed:**
   - Dashboard tunnel health status
   - Whether user traffic is affected
   - Error messages from router logs

### Helpful diagnostic data

- **Packet captures** from your router showing tunnel traffic
- **Router logs** covering the time period of the issue
- **Traceroute** results from your network to Cloudflare endpoints
- **Screenshots** of the tunnel health dashboard
- **Distributed traceroutes** using tools like [ping.pe](https://ping.pe) to test reachability from multiple global locations

### Router diagnostic commands

Collect output from these commands (syntax varies by vendor):

- IPsec SA status: `show crypto ipsec sa`
- IKE SA status: `show crypto isakmp sa`
- Tunnel interface status: `show interface tunnel <number>`
- Routing table: `show ip route`

---

## Resources

- [Tunnel health checks](/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/): Technical details on health check behavior
- [Anti-replay protection](/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/): Why anti-replay must be disabled
- [Configure tunnel endpoints](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/): Tunnel setup instructions
- [Check tunnel health in the dashboard](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/): Dashboard navigation guide
- [Network Analytics](/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/): Traffic analysis tools

# Cloudflare Gateway

[Cloudflare Gateway](/cloudflare-one/traffic-controls/), our comprehensive Secure Web Gateway, allows you to set up policies to inspect DNS, network, HTTP, and egress traffic.

You can apply network and HTTP Gateway policies alongside [Cloudflare Network Firewall](/cloudflare-network-firewall/) policies (for L3/4 traffic filtering) to Internet-bound traffic or private traffic entering the Cloudflare network through Cloudflare WAN (formerly Magic WAN). Additionally, you can configure Gateway to [resolve DNS queries](#dns-filtering) from Cloudflare WAN.

## HTTPS filtering

To inspect HTTPS traffic, you need to install a Cloudflare root certificate on each client device. A certificate is required for Cloudflare to [decrypt TLS](/cloudflare-one/traffic-controls/http-policies/tls-decryption/).

### Installing certificates

You can use the [Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-one-client/) to [automatically install a Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/) on supported devices. If your device or application does not support certificate installation through the Cloudflare One Client, you can [manually install a certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/).

### Exempting traffic from inspection

If you cannot or do not want to install the certificate, you can create [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policies to exempt incompatible Cloudflare WAN traffic from inspection or to disable TLS decryption entirely.

Because Gateway cannot discern Cloudflare WAN traffic, you must use [Cloudflare One Client checks](/cloudflare-one/reusable-components/posture-checks/client-checks/) or the IP addresses associated with Cloudflare WAN to match traffic with Gateway policies.

For example, if your organization onboards devices to Cloudflare WAN using the Cloudflare One Client, you can exempt devices not running the Cloudflare One Client using [OS version checks](/cloudflare-one/reusable-components/posture-checks/client-checks/os-version/):

| Selector                     | Operator | Value                | Logic | Action         |
| ---------------------------- | -------- | -------------------- | ----- | -------------- |
| Passed Device Posture Checks | not in   | Windows (OS version) | Or    | Do Not Inspect |
| Passed Device Posture Checks | not in   | macOS (OS version)   | Or    | Do Not Inspect |
| Passed Device Posture Checks | not in   | Linux (OS version)   | Or    | Do Not Inspect |
| Passed Device Posture Checks | not in   | iOS (OS version)     | Or    | Do Not Inspect |
| Passed Device Posture Checks | not in   | Android (OS version) |       | Do Not Inspect |

If your organization onboards users to Cloudflare WAN using an [on-ramp other than the Cloudflare One Client](/cloudflare-one/networks/connectors/cloudflare-wan/on-ramps/), you can exempt devices from inspection using the IP addresses for your IPsec tunnels:

| Selector  | Operator | Value            | Action         |
| --------- | -------- | ---------------- | -------------- |
| Source IP | in       | `203.0.113.0/24` | Do Not Inspect |

## DNS filtering

You can configure the DNS resolver for your Cloudflare WAN networks to the shared IP addresses for the Gateway DNS resolver. The Gateway DNS resolver IPs are `172.64.36.1` and `172.64.36.2`.

When you resolve DNS queries from Cloudflare WAN through Gateway, Gateway will log the queries with the private source IP. You can use the private source IP to create [resolver policies](/cloudflare-one/traffic-controls/resolver-policies/) for queries intended for [internal DNS records](/cloudflare-one/traffic-controls/resolver-policies/#internal-dns).

The following diagram illustrates how DNS queries from Cloudflare WAN and Cloudflare Mesh flow through Gateway to your internal DNS:

<pre class="mermaid">
	{`
flowchart LR
accTitle: DNS query flow
accDescr: Shows how DNS queries from Cloudflare WAN and Cloudflare Mesh flow through Gateway to internal DNS.
 subgraph subGraph0["Data center"]
    direction TB
        InternalDNS(["Internal DNS"])
        ResolverPolicies["Resolver policies"]
        CloudflareGatewayDNSResolver["Gateway DNS resolver"]
  end
    ResolverPolicies -- Retain and use</br>Source Internal IP --> InternalDNS
    CloudflareGatewayDNSResolver -- <br> --> ResolverPolicies
    WarpConnector["Cloudflare Mesh"] -- DHCP/DNS resolver --> IPSecTunnel["IPsec tunnel"]
    CloudflareWAN[Cloudflare WAN] -- DHCP/DNS resolver --> IPSecTunnel
    IPSecTunnel -- Shared IP endpoints --> CloudflareGatewayDNSResolver
    ResolverPolicies@{ shape: proc}
    WarpConnector@{ shape: in-out}
    CloudflareWAN@{ shape: in-out}
	`}
</pre>

## Outbound Internet traffic

By default, the following traffic routed through IPsec/GRE tunnels and destined to public IP addresses is proxied/filtered through Cloudflare Gateway:

- TCP, UDP, and ICMP traffic sourced from [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) IPs or devices.
- TCP and UDP traffic sourced from [BYOIP](/byoip/) or [Leased IPs](/magic-transit/cloudflare-ips/) and destined to a well-known port (`0`-`1023`).

By default, traffic destined to public IPs will be routed over the public Internet. If you want to configure specific public IP ranges to be routed through your IPsec/GRE tunnels instead of over the public Internet after filtering, contact your account team.

This traffic will egress from Cloudflare according to the [egress policies](/cloudflare-one/traffic-controls/egress-policies/) you define in Cloudflare Gateway. By default, it will egress from a shared Cloudflare public IP range.

## Private traffic

By default, TCP, UDP, and ICMP traffic routed through IPsec/GRE tunnels and destined to routes behind [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) will be proxied/filtered through Cloudflare Gateway.

Contact your account team to enable Gateway filtering for traffic destined to routes behind IPsec/GRE tunnels.

### Default filtering criteria

When enabled, TCP/UDP traffic meeting **all** the following criteria will be proxied and filtered by Cloudflare Gateway:

- **Source and destination IPs**: Both must be part of [RFC1918](https://datatracker.ietf.org/doc/html/rfc1918) space, [WARP](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-one-client/), [BYOIP](/byoip/), or [Leased IPs](/magic-transit/cloudflare-ips/).
- **Source port**: Must be a client port strictly higher than `1023`.
- **Destination port**: Must be a well-known port (lower than `1024`).

### Custom filtering criteria

You can specify more specific matches to override the default criteria:

- **Source IP prefix**: A subset of RFC1918 space, [BYOIP](/byoip/), or [Leased IPs](/magic-transit/cloudflare-ips/).
- **Destination IP prefix**: A subset of RFC1918 space, [BYOIP](/byoip/), or [Leased IPs](/magic-transit/cloudflare-ips/).
- **Destination port**: Any port from `0` to `65535`.

:::note
Source ports are fixed to `1024`-`65535` and cannot be overridden.
:::

:::note[Run <code>traceroute</code>]
If you connect through [GRE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [CNI](/network-interconnect/), or [WARP](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-one-client/) and want to run `traceroute` to an endpoint behind a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), you need to change some settings.

Refer to [Run `traceroute`](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/traceroute/) for more information.
:::

## Test Gateway integration

To check if Gateway is working properly with your Cloudflare WAN connection, open a browser from a host behind your customer premise equipment, and browse to `https://ifconfig.me`.

If you are still testing Gateway and Cloudflare is not your default route, configure a policy-based route on your router to send traffic to Cloudflare Gateway first.

Confirm there is an entry for the test in [HTTP Gateway Activity Logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/#http-logs).

Verify the following details:
- **Destination IP**: Should be the public IP address of `ifconfig.me`.
- **Source IP**: Should be the private (WAN) address of the host with the browser.
- **Outbound connection**: Should be sourced from a Cloudflare WAN IP address, not any public IP address that Cloudflare might be advertising on your behalf.

This applies when using [Magic Transit With Egress Option](/reference-architecture/architectures/magic-transit/#magic-transit-with-egress-option-enabled) as well.

Additionally, test both `http://ifconfig.me` (non-TLS) and `https://ifconfig.me` (TLS) to ensure that your [TCP maximum segment size (MSS Clamping)](/cloudflare-one/networks/connectors/cloudflare-wan/get-started/#set-maximum-segment-size) has been set properly.

If the HTTPS query hangs or fails but HTTP works, the MSS value may be too high or not set. Reduce this value on your customer premise equipment to match the overhead introduced by your [IKE](/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters) and [ESP](https://en.wikipedia.org/wiki/IPsec#Encapsulating_Security_Payload) settings.

# WARP

import { GlossaryTooltip } from "~/components";

:::note
By default, Cloudflare WAN does not support direct Mesh connectivity for devices with the Cloudflare One Client enabled. Double encapsulation and asymmetric routing prevent these connections.

When a device is behind Cloudflare WAN, avoid enabling the Cloudflare One Client. Instead, access the device using its local LAN IP from remote systems, rather than relying on Mesh IPs.

If you do want to use the Cloudflare One Client on a device behind Cloudflare WAN and connect to its virtual IP (within the `100.96.0.0/12` range), you will need to adjust your Cloudflare One Client profiles. Specifically, exclude the `100.96.0.0/12` subnet from the on-premises Cloudflare One Client profiles, and include it in the off-premises profile.
:::

Use [WARP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) as an <GlossaryTooltip term="on-ramp">on-ramp</GlossaryTooltip> to Cloudflare WAN (formerly Magic WAN) and route traffic from user devices with the Cloudflare One Client installed to any network connected with Cloudflare Tunnel or IP-layer tunnels (<GlossaryTooltip term="anycast">anycast</GlossaryTooltip> [GRE, IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels), or [CNI](/network-interconnect/)). Take advantage of the integration between Cloudflare WAN and [Cloudflare Network Firewall](/cloudflare-network-firewall/) and enforce policies at Cloudflare's global network.

## Prerequisites

Before you can begin using the Cloudflare One Client as an on-ramp to Cloudflare WAN, you must set up your [Zero Trust account](/cloudflare-one/setup/#2-create-a-zero-trust-organization).

## IP ranges

When connecting a device to Cloudflare WAN, you will have virtual IP addresses from the Cloudflare One Client, in the `100.96.0.0/12` range.

---

## Set up the Cloudflare One Client with Cloudflare WAN

### 1. Route packets back to Cloudflare One Client devices

Route packets back to Cloudflare One Client devices from services behind an anycast GRE or other type tunnel. Complete this configuration before installing WARP. Otherwise, your infrastructure will not route packets correctly to Cloudflare global network and connectivity will fail.

Cloudflare will assign IP addresses from the virtual IP (VIP) space to your devices. To view your virtual IP address, go to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select **Zero Trust** > **My Team > Devices**.

All packets with a destination IP in the VIP space need to be routed back through the tunnel. For example, with a single GRE tunnel named `gre1`, in Linux, the following command would add a routing rule that would route such packets:

```sh
ip route add 100.96.0.0/12 dev gre1
```

:::note[Note]

After set up, **HTTP** and **Network logs** in Gateway will show the virtual IP address of your device as the **Source IP**. DNS logs will continue to show the original device IP because DNS traffic is sent over the public Internet to Cloudflare's public-facing resolver.
:::

### 2. Configure Split Tunnels

Configure [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) from your Zero Trust account to only include traffic from the private IP addresses you want to access.

Optionally, you can configure Split Tunnels to include IP ranges or domains you want to use for connecting to public IP addresses.

### 3. Install the Cloudflare One Client on your device

Refer to [Deploy the Cloudflare One Client to your organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) for more information on whether to choose a manual or managed deployment.

You can now access private IP addresses specified in the Split Tunnel configuration.

You must log out and log back in with at least one device to ensure the configuration updates on your device.

:::note[Run <code>traceroute</code>]
If you connect through [GRE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels), [IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/#add-tunnels), [CNI](/network-interconnect/), or [WARP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) and want to run `traceroute` to an endpoint behind a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), you need to change some settings.

Refer to [Run `traceroute`](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/traceroute/) for more information.
:::

## Double encapsulation

When a Cloudflare One Client user connects from a location (such as an office) with an IPsec/GRE tunnel already set up, Cloudflare One Client traffic is doubly encapsulated - first by the Cloudflare One Client and then by Cloudflare WAN. This is unnecessary, since each on-ramp method provides full Zero Trust protection.

Since Cloudflare One Client traffic is already protected on its own, set up Cloudflare WAN to exclude Cloudflare One Client traffic, sending it to the Internet through regular connections.

To learn which IP addresses and UDP ports you should exclude to accomplish this, refer to [Cloudflare One Client ingress IP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip).

### The Cloudflare One Client and Cloudflare One Appliance

If you have Cloudflare One Appliance (formerly Magic WAN Connector) and Cloudflare One Clients deployed in your premises, Cloudflare One Appliance automatically routes Cloudflare One Client traffic to the Internet rather than Cloudflare WAN IPsec tunnels. This prevents traffic from being encapsulated twice.

You may need to configure your firewall to allow this new traffic. Make sure to allow the following IPs and ports:

- **Destination IPs**: `162.159.193.0/24`, `162.159.197.0/24`
- **Destination ports**: `443`, `500`, `1701`, `2408`, `4443`, `4500`, `8095`, `8443`

Refer to [Cloudflare One Client with firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/) for more information on this topic.

## Test Cloudflare One Client integration

Before testing, [configure domain fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/#add-a-domain) for the server or service in the Cloudflare One Client settings. This is needed because by default Cloudflare Zero Trust excludes common top level domains used for local resolution from being sent to Gateway for processing.

If WARP integration has been enabled for the account within the last day, log off and on again in the Cloudflare One Client before testing.

To check if the Cloudflare One Client is working correctly as an on-ramp, you can do a resolution test on a [fully qualified domain name (FQDN)](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) for a server or service in the Cloudflare WAN. Test this from a user with a device.

For example:

```sh
nslookup <SERVER_BEHIND_CLOUDFLARE_WAN>
```

This DNS lookup should return a valid IP address associated with the server or service you are testing for.

Next, test with a browser that you can connect to a service on the WAN by opening a webpage that is only accessible on the WAN. Use the same server from the DNS lookup or another server in the WAN. Connecting using an IP address instead of a domain name should work.

# Cloudflare Tunnel

Cloudflare WAN (formerly Magic WAN) can work together with [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) to provide easy access between your networks and applications.

By default, [Cloudflare Gateway](/cloudflare-one/traffic-controls/) proxies and filters TCP, UDP, and ICMP traffic routed through IPsec/GRE tunnels and destined to routes behind Cloudflare Tunnel.

## Route evaluation and precedence

Cloudflare evaluates private network routes using longest-prefix-match. A prefix combines a base IP address with a prefix length that indicates how many bits define the network portion (for example, `192.168.0.0/24`). When multiple routes could match a destination IP, Cloudflare selects the route with the longest prefix (most specific match).

For example, if you have routes for both `10.0.0.0/16` and `10.0.1.0/24`, traffic destined for `10.0.1.50` matches the `/24` route because it is more specific.

### Route uniqueness

Within a [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/), each prefix can only appear once in the Zero Trust routing table. You cannot create two Zero Trust routes with the same prefix pointing to different tunnels in the same virtual network.

To route the same prefix to different destinations, use separate [virtual networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/).

### Reserved IP ranges

Cloudflare reserves the following IP ranges for Zero Trust services:

| IP range | Purpose |
| --- | --- |
| `100.64.0.0/12` | [Cloudflare Source IPs](/cloudflare-wan/configuration/how-to/configure-cloudflare-source-ips/) |
| `100.96.0.0/12` | [Device IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/) |
| `172.64.128.0/20` | [Initial resolved IPs](/cloudflare-one/traffic-controls/egress-policies/host-selectors/) |
| `100.112.0.0/16` | [Private Load Balancers](/load-balancing/private-network/) |

Do not configure routes that overlap with these reserved ranges.

### Interaction with WAN routes

If your account also uses WAN connections (IPsec, GRE, and CNI), route selection behavior depends on your routing mode.

For more information, refer to [Route evaluation with Zero Trust connections](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#route-evaluation-with-zero-trust-connections).

## Interaction with other route selection mechanisms

Longest-prefix-match routing is the default route selection method. Other mechanisms can bypass or augment route evaluation.

### Automatic Return Routing (ARR)

[Automatic Return Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#automatic-return-routing) bypasses route lookup for return traffic.

When ARR is enabled:

1. Cloudflare tags each flow with the source connection (tunnel or interconnect) when the flow is established.
2. For return traffic, Cloudflare routes packets back to the tagged source connection directly, bypassing the routing table.
3. This allows multiple sites to use identical private IP ranges without NAT or VRF configuration.

ARR requires Unified Routing mode. For more information, refer to [Automatic Return Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#automatic-return-routing).

### Hostname Routes (Initial resolved IPs)

[Hostname-based routing](/cloudflare-one/traffic-controls/egress-policies/host-selectors/) uses Gateway DNS to resolve hostnames to Initial resolved IPs, which then map to specific next hops.

When Hostname Routes are enabled:

1. Gateway DNS resolves the hostname to an Initial resolved IP (from `172.64.128.0/20` by default).
2. The client sends traffic to the Initial resolved IP.
3. Cloudflare looks up the Initial resolved IP to determine the real destination IP and the assigned next hop (specific tunnel or interconnect).
4. Traffic is forwarded to the assigned next hop, bypassing route evaluation for next-hop selection.

This enables hostname-based policies for non-HTTP traffic without requiring you to know destination IPs in advance.

## Test `cloudflared` tunnel integration

To verify that a `cloudflared` tunnel works correctly with your Cloudflare WAN connection:

1. From a host behind your customer premises equipment, open a browser.
2. Browse to an IP address or hostname that is reachable through a Cloudflare Tunnel private network route, such as the example destination `10.1.2.3`.
3. Confirm that the application loads as expected. If it does, Cloudflare Tunnel is handling the traffic as configured.

:::note[Run <code>traceroute</code>]
If you connect through [GRE](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [IPsec](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/), [CNI](/network-interconnect/), or [WARP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) and want to run `traceroute` to an endpoint behind a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/), you need to change some settings.

Refer to [Run `traceroute`](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/traceroute/) for more information.
:::

# Cloudflare One integration

import { Render } from "~/components";

<Render
	file="cloudflare-wan/zero-trust/overview"
	product="networking-services"
/>

# Secure WAN traffic

A key benefit of routing your network traffic through Cloudflare is that you can apply security policies without deploying additional hardware at each site. Once traffic reaches Cloudflare through WAN on-ramps (IPsec tunnels, GRE tunnels, CNI, or Appliance), multiple security services inspect it inline at the nearest Cloudflare data center. This page explains which services apply to WAN traffic, when to use each one, and how they work together.

## Traffic types

Cloudflare WAN carries three types of traffic, and different security services apply to each:

- **Outbound (site-to-Internet)**: Traffic from WAN-connected sites to the public Internet. For example, employees at a branch office browsing the web or accessing SaaS applications.
- **East-west (site-to-site)**: Traffic between WAN-connected locations routed through Cloudflare. For example, a branch office accessing an application hosted in a data center.
- **Inbound (Internet-to-site)**: Traffic from the Internet destined for customer networks. This typically applies to [Magic Transit](/magic-transit/) scenarios where you advertise your own IP prefixes (BYOIP) through Cloudflare.

## Security services

### Cloudflare Network Firewall

[Cloudflare Network Firewall](/cloudflare-one/traffic-controls/packet-filtering/network-firewall-overview/) provides packet-level filtering at layers 3 and 4. You define allow or block rules based on IP addresses, ports, and protocols.

- **Applies to**: inbound, outbound, and east-west traffic
- **Included with**: Cloudflare WAN by default for [standard features](/cloudflare-network-firewall/plans/)

Use Network Firewall when you need to control traffic at the packet level — for example, blocking specific IP ranges, restricting traffic to certain ports, or filtering protocols between sites.

### Gateway (Secure Web Gateway)

[Cloudflare Gateway](/cloudflare-one/traffic-controls/) inspects traffic at layers 4 through 7 and supports three policy types:

- **DNS policies**: Filter and log DNS queries from your sites. You configure the DNS resolver for your WAN networks to point to Gateway's resolver IPs.
- **Network policies**: Filter TCP, UDP, and ICMP traffic based on IP, port, protocol, and identity attributes.
- **HTTP policies**: Inspect HTTP and HTTPS traffic for threats, content categories, and application-level controls.

HTTP inspection requires TLS decryption and a Cloudflare root certificate installed on client devices. You must also enable the Gateway proxy for your WAN traffic.

- **Applies to**: outbound and east-west traffic

Gateway provides the deepest inspection for WAN traffic, covering DNS, network, and HTTP layers. For detailed setup instructions, refer to [Connect to Cloudflare Gateway with Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-gateway/).

### Browser Isolation

[Browser Isolation](/cloudflare-one/remote-browser-isolation/) runs web content in a remote browser on Cloudflare's network and streams a visual representation to the user's device. No web code executes locally.

- **Applies to**: outbound web traffic
- **Triggered by**: Gateway HTTP policies using the **Isolate** action

Use Browser Isolation when users at branch offices need to access untrusted or uncategorized websites without exposing local devices to web-based threats.

### Data Loss Prevention (DLP)

[Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/) scans HTTP uploads and downloads for sensitive data patterns such as Social Security numbers, credit card numbers, and custom regular expressions.

- **Applies to**: outbound HTTP traffic
- **Requires**: Gateway HTTP filtering with TLS decryption enabled

You define DLP profiles with detection rules and reference those profiles in Gateway HTTP policies. When a policy matches, Gateway can block, log, or allow the transfer.

### Cloud Access Security Broker (CASB)

[CASB](/cloudflare-one/cloud-and-saas-findings/) provides visibility and control over SaaS application usage through two modes:

- **Applies to**: outbound traffic to SaaS applications
- **API-based scanning**: Connects to your SaaS applications (Google Workspace, Microsoft 365, and others) to detect misconfigurations and security posture issues.
- **Inline remediation**: Gateway HTTP policies can block unsanctioned SaaS application usage detected by CASB — for example, preventing file uploads to unapproved cloud storage services.

### AI visibility

The [AI Security Report](/cloudflare-one/insights/analytics/ai-security/) provides visibility into AI application usage across your organization. It shows which AI tools employees are using, how frequently, and what data is being shared.

AI visibility is not a separate inline security service. It is an analytics feature powered by Gateway — it requires Gateway to be inspecting outbound traffic from your sites.

## Use-case mapping

| Traffic scenario | Recommended services |
|---|---|
| Block traffic between sites by IP, port, or protocol | Network Firewall |
| Filter DNS queries from branch offices | Gateway DNS policies |
| Block malware downloads from branch offices | Gateway HTTP policies |
| Prevent sensitive data uploads to the Internet | DLP (via Gateway HTTP policies) |
| Isolate risky web browsing from branch users | Browser Isolation (via Gateway HTTP policies) |
| Detect and block unsanctioned SaaS applications | CASB + Gateway HTTP policies |
| Monitor employee AI tool usage | AI Security Report (via Gateway) |
| Protect against DDoS on customer-owned IPs | Network Firewall (inbound) + [Magic Transit](/magic-transit/) |

## How services compose

Traffic on the Cloudflare network passes through a single-pass inspection pipeline. You do not need to backhaul traffic between services — all inspection happens at the nearest Cloudflare data center.

The evaluation order is:

1. **Network Firewall (L3/L4)**: Packet-level rules are evaluated first.
2. **Gateway (L4-L7 proxy)**: If traffic passes the Network Firewall, Gateway inspects it. Within Gateway, policies are evaluated in order: DNS → Network → HTTP.
3. **DLP, Browser Isolation, and CASB**: These services are triggered through Gateway HTTP policies. A single HTTP policy can reference a DLP profile, apply an Isolate action, or block a CASB-flagged application.

This means you can layer multiple security services on the same traffic flow without adding network hops or latency.

## Next steps

- [Connect to Cloudflare Gateway with Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/zero-trust/cloudflare-gateway/): Detailed setup guide for Gateway integration with WAN traffic.
- [Cloudflare Network Firewall](/cloudflare-one/traffic-controls/packet-filtering/network-firewall-overview/): Configure packet-level filtering rules.
- [SASE reference architecture](/reference-architecture/architectures/sase/): Explore the full architecture of Cloudflare One as a SASE platform.
- [WAN transformation](/cloudflare-one/networks/connectors/cloudflare-wan/wan-transformation/): Plan your migration from traditional WAN to Cloudflare.

# Add routes

import { Render, DashButton } from "~/components";

A route maps an IP address or hostname to a [Cloudflare One connector](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/#connectors) installed on your private network. When a user connects to that IP or hostname through Cloudflare's network, Cloudflare will route their traffic down a secure tunnel to the corresponding resource in your private network.

The dashboard **Routes** page is the single place to view and manage the routes for all of your connectors — Cloudflare Tunnel, Cloudflare Mesh, Cloudflare WAN, and Magic Transit — in one table. When you create a route, you choose its type, which determines the connector it uses.

:::note
When adding a CIDR, hostname, or WAN route, you can enter multiple destinations at once (separated by commas), or select **Add another** to queue up several routes — including different types or connectors — before creating them all together.
:::

## Add a CIDR route

CIDR routes define the IP network segments (such as `10.0.0.0/24`) that are reachable via a Cloudflare Tunnel or a Cloudflare Mesh node.

:::note[Prerequisites]
Before you add a CIDR route, ensure you have created a Cloudflare Tunnel using [`cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) or a [Cloudflare Mesh](/mesh/) node.
:::

To add a CIDR route:

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. From the **Routes** tab, select **Create route**, then choose **Tunnel CIDR** (for a `cloudflared` tunnel) or **Mesh CIDR** (for a Cloudflare Mesh node) as the route type.
3. For the connector, select the Cloudflare Tunnel or Cloudflare Mesh node that connects your private network to Cloudflare.
4. Enter the IP address or CIDR range that you wish to route through the connector (for example, `10.0.0.1` or `10.0.0.0/24`). This can be a private or public IP.
5. (Optional) Select a [virtual network](/cloudflare-one/networks/virtual-networks/) for this route. A virtual network is a private routing domain that provides routing isolation within your account. This step is only needed if the route's IP/CIDR range overlaps with another route in your account. If you do not select a virtual network, the route will be assigned to the `default` network.
		:::note
		Virtual networks are only supported for `cloudflared` tunnels.
		:::
6. Select **Create route**.

Cloudflare will now route requests to your private network. However, the route does not automatically capture traffic from end users. To enable client-side connectivity, refer to the [cloudflared](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/) or
[Cloudflare Mesh](/mesh/features/routes/) setup guides.

## Add a hostname route

Hostname routes steer traffic for a public or private hostname down a Cloudflare Tunnel. This allows users to access internal resources using familiar URLs (such as `wiki.internal.local`) rather than IP addresses.

:::note[Prerequisites]
Before you add a hostname route, ensure you have created a Cloudflare Tunnel using [`cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).
:::

To add a hostname route:

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. From the **Routes** tab, select **Create route**, then choose **Tunnel Hostname** as the route type.
3. For the connector, select the Cloudflare Tunnel that connects your private network to Cloudflare.
4. In **Hostname**, enter the private or public hostname that represents your application (for example, `wiki.internal.local` or `app.bank.com`).
5. Select **Create route**.

Cloudflare will now route requests to your private network. However, the route does not automatically capture traffic from end users. To enable client-side connectivity, refer to the [private hostname](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/) or [public hostname](/cloudflare-one/traffic-controls/egress-policies/egress-cloudflared/#3-route-network-traffic-through-the-cloudflare-one-client) setup guides.

## Add a published application route

Published application routes expose applications to the Internet via a domain that you have connected to Cloudflare. This allows users to access your applications without needing a VPN or specialized client software.

:::note[Prerequisites]
Before you publish an application, ensure you have:
- [Created a Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) using `cloudflared`.
- [Added a website to Cloudflare](/fundamentals/manage-domains/add-site/).
:::

To add a published application route to an existing tunnel:

1. In the Cloudflare dashboard, go to **Networking** > **Tunnels**, then select your tunnel.

   <DashButton url="/?to=/:account/tunnels" />

2. On the **Routes** tab, select **Add route**, then select **Published application**.

3. Enter a subdomain and select a **Domain** from the drop-down menu. Specify any subdomain or path information.
	:::note
	If you add a multi-level subdomain (more than one level of subdomain), you must [order an Advanced Certificate for the hostname](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/common-errors/#i-see-this-site-cant-provide-a-secure-connection).
	:::

4. In **Service URL**, enter the protocol and address of your application (for example, `http://localhost:8000`). Refer to [supported protocols](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/protocols/) for available options.

5. Select **Save**.

Anyone on the Internet can now access the application at the specified hostname. To allow or block specific users, [create an Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).

## Add a WAN route

WAN routes define the IP network segments (such as `10.0.0.0/24`) that are reachable via a GRE or IPsec tunnel. To add a WAN route, refer to the [WAN Connectors documentation](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/).

# Configure initial resolved IPs

import { CURL, DashButton, Render, Tabs, TabItem, GlossaryTooltip } from "~/components";

<GlossaryTooltip term="initial resolved IP">Initial resolved IPs</GlossaryTooltip> (also called token IPs) are ephemeral addresses that Gateway assigns to DNS queries so it can associate hostname-based traffic with the correct policy or tunnel at the network layer, where hostname information is not usually available. Refer to [Gateway initial resolved IPs](/cloudflare-one/networks/routes/reserved-ips/#gateway-initial-resolved-ips) for a list of features that depend on this range.

By default, initial resolved IPs are assigned from:

<Render file="gateway/egress-selector-cgnat-ips" product="cloudflare-one" />

The IPv6 range is not configurable.

:::caution
If you configure a custom IPv4 range within Carrier-Grade NAT (CGNAT) address space, this can lead to [Google Chrome's Local Network Access restrictions](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/#google-chrome-restricts-access-to-private-hostnames), which the public default range avoids.
:::

## Prerequisites

- You have the [Cloudflare One Networks Write](/fundamentals/api/reference/permissions/) permission (for API access), or dashboard access to **Networking** > **IP addresses** > **Address space** > **Custom IPs**.
- Your new range does not conflict with existing routes or other reserved [Cloudflare One subnets](/cloudflare-one/networks/routes/reserved-ips/) in your account.

## Check your current range

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Go to **Networking** > **IP addresses** > **Address space** > **Custom IPs**.

   <DashButton url="/?to=/:account/ip-addresses/address-space/custom-ips" />

2. Find the row where **Assign to** is **Initial Resolved IP** to see your account's current IPv4 range in the **Prefix** column.

</TabItem> <TabItem label="API">

Send a `GET` request to the [Get Initial Resolved IP Subnet](/api/resources/zero_trust/subresources/networks/subresources/subnets/subresources/initial_resolved_ip/methods/get/) endpoint for the address family you want to check:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/zerotrust/subnets/initial_resolved_ip/$ADDRESS_FAMILY"
	method="GET"
	headers={{
		Authorization: "Bearer $CLOUDFLARE_API_TOKEN",
	}}
/>

</TabItem> </Tabs>

## Update your range

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Go to **Networking** > **IP addresses** > **Address space** > **Custom IPs**.

   <DashButton url="/?to=/:account/ip-addresses/address-space/custom-ips" />

2. Find the row where **Assign to** is **Initial Resolved IP**, select the three dots menu, and select **Edit**.
3. Enter your new IPv4 range in **IP address**.
4. Select **Save**.

</TabItem> <TabItem label="API">

Send a `PUT` request to the [Update Initial Resolved IP Subnet](/api/resources/zero_trust/subresources/networks/subresources/subnets/subresources/initial_resolved_ip/methods/update/) endpoint with your desired network range:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/zerotrust/subnets/initial_resolved_ip/$ADDRESS_FAMILY"
	method="PUT"
	headers={{
		Authorization: "Bearer $CLOUDFLARE_API_TOKEN",
	}}
	json={{
		"comment": "example comment",
		"name": "IPv4 Gateway initial resolved IPs",
		"network": "172.64.128.0/20"
	}}
/>

</TabItem> </Tabs>

The new CIDR must not conflict with existing private routes or other reserved subnets in your account. If it does, the request fails and the response describes the conflicting route or subnet.

:::note
Only the IPv4 range is configurable. The IPv6 initial resolved IP range (`2606:4700:0cf1:4000::/64`) is fixed and does not need to be changed to resolve Chromium's Local Network Access restrictions, which do not affect IPv6.
:::

The default IPv4 range is [automatically routed through the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#automatically-managed-ranges) and does not require any Split Tunnel configuration. If you configure a custom range, update your [Split Tunnel configuration](/cloudflare-one/networks/routes/reserved-ips/#split-tunnel-configuration) so that traffic to the new range routes through the Cloudflare One Client, and remove the old range if it is no longer used by any other reserved IP purpose.

Initial resolved IPs have a TTL of approximately 10 minutes. DNS queries resolved before you change your range continue to use the previous range until that TTL expires. After that, new DNS queries receive an initial resolved IP from the new range.

# Routes

import { DirectoryListing } from "~/components";

Routes map IP addresses, hostnames, and published applications to Cloudflare One connectors on your private network.

<DirectoryListing />

# Reserved IP addresses

import SubtractIPCalculator from "~/components/SubtractIPCalculator.tsx";

Cloudflare reserves several IPv4 and IPv6 ranges for internal routing and service functionality. Most of these ranges are drawn from the CGNAT address space (`100.64.0.0/10`). [Gateway initial resolved IPs](#gateway-initial-resolved-ips) are the exception, using a public Cloudflare-owned range by default. To avoid routing conflicts, your Cloudflare Tunnel, Cloudflare Mesh, or WAN routes should not include subsets of these reserved ranges. Broader routes that contain a reserved range, such as `0.0.0.0/0`, are unaffected because longest-prefix match ensures the reserved ranges still take priority.

When planning your private network addressing and configuring [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) entries, use the tables below to identify which IP ranges Cloudflare has reserved and whether they can be reconfigured.

## IPv4 ranges

| Name                                                          | Default CIDR       | Configurable |
| ------------------------------------------------------------- | ------------------- | ------------ |
| [Cloudflare source IPs](#cloudflare-source-ips)               | `100.64.0.0/12`     | Yes          |
| [Gateway initial resolved IPs](#gateway-initial-resolved-ips) | `172.64.128.0/20`   | Yes          |
| [Device IPs](#device-ips)                           | `100.96.0.0/12`  | Yes          |
| [Private Load Balancer IPs](#private-load-balancer-ips)       | `100.112.0.0/16` | Yes          |

Unlike the other IPv4 ranges, Gateway initial resolved IPs are drawn from public Cloudflare address space rather than CGNAT (`100.64.0.0/10`) by default. If your account was created before this default changed, or if you configured a custom range, it may still fall within CGNAT space — refer to [Gateway initial resolved IPs](#gateway-initial-resolved-ips).

## IPv6 ranges

| Name                                                          | Default CIDR               | Configurable |
| ------------------------------------------------------------- | -------------------------- | ------------ |
| [Device IPs](#device-ips)                           | `2606:4700:0cf1:1000::/64` | No           |
| [Gateway initial resolved IPs](#gateway-initial-resolved-ips) | `2606:4700:0cf1:4000::/64` | No           |
| [Cloudflare source IPs](#cloudflare-source-ips)               | `2606:4700:0cf1:5000::/64` | No           |

## Cloudflare source IPs

Cloudflare source IPs are the source addresses used when a Cloudflare service sends traffic to your private networks. This range applies to customers using [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). Examples of requests that are sourced from this range include:

- [Load Balancing](/load-balancing/monitors/) — health check requests to private endpoints
- [Gateway DNS resolver](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/) — DNS resolution for private hostnames
- [Cloudflare Workers](/workers/) — requests from Workers to private origins

The default IPv4 range is `100.64.0.0/12`. You can change this to a different `/12` CIDR to avoid conflicts with your existing IP address management plan. For more information on affected services and configuration instructions, refer to [Configure Cloudflare source IPs](/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-cloudflare-source-ips/).

## Gateway initial resolved IPs

Gateway initial resolved IPs (also called token IPs) are ephemeral addresses used to map hostnames to destination IPs at the network layer, where hostname information is not usually available.

The following features use this range:

- [Private hostname routing](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/) — routes traffic to private applications behind Cloudflare Tunnel using their hostnames.
- [Public hostname routing](/cloudflare-one/traffic-controls/egress-policies/egress-cloudflared/) — egresses traffic through Cloudflare Tunnel to anchor source IPs for public destinations.
- [Egress policy host selectors](/cloudflare-one/traffic-controls/egress-policies/host-selectors/) — evaluates Gateway egress policies using hostname-based selectors.
- [Access private applications](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/) — manage access to private applications using their private hostnames.

Cloudflare assigns initial resolved IPs from the `172.64.128.0/20` (IPv4) or `2606:4700:0cf1:4000::/64` (IPv6) range by default. Unlike earlier CGNAT-based defaults, the IPv4 range is public Cloudflare address space, so it is not affected by [Google Chrome's Local Network Access restrictions](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/#google-chrome-restricts-access-to-private-hostnames).

You can configure a custom IPv4 range if the default conflicts with your existing network or private routes. The IPv6 range is not configurable. For instructions, refer to [Configure initial resolved IPs](/cloudflare-one/networks/routes/configure-initial-resolved-ips/).

## Device IPs

Device IPs (also called Mesh IPs in [Cloudflare Mesh](/mesh/)) are virtual addresses assigned to each Cloudflare One Client registration and each mesh node. These IPs identify and route traffic to specific devices for the following features:

- [Cloudflare Mesh](/mesh/) — mesh nodes and client devices communicate using their Mesh IPs for device-to-device, site-to-site, and mesh connectivity.
- [Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/) — on-ramps traffic from WAN tunnels to Cloudflare One Client devices.

The default IPv4 range is `100.96.0.0/12`. If this range conflicts with services on your private network, you can configure custom IPv4 subnets drawn from RFC 1918 or CGNAT address space. If your account uses [Cloudflare WAN](/cloudflare-wan/), custom subnets require [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). For configuration instructions, refer to [Device IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/).

## Private Load Balancer IPs

Private Load Balancer IPs are virtual addresses allocated to [Private Network Load Balancers](/load-balancing/private-network/). Each private load balancer receives a `/32` address from the `100.112.0.0/16` range by default, which serves as the load balancer's virtual IP for traffic distribution to private endpoints. Alternatively, you can configure a custom [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) `/32` address for each load balancer.

## Split Tunnel configuration

For deployments that use the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), ensure that the [reserved IP ranges](#ipv4-ranges) required by your deployment route through [WARP Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) to Cloudflare. Configuration depends on whether your [Split Tunnels mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#change-split-tunnels-mode) is set to **Exclude IPs and domains** or **Include IPs and domains**.

### Exclude mode (default)

In **Exclude IPs and domains** mode, default device profiles exclude the CGNAT range (`100.64.0.0/10`) from Cloudflare. To route a reserved range within CGNAT space through Cloudflare, delete or narrow the CGNAT exclusion. The Mesh setup wizard does this automatically for the device IP range used by Mesh.

[Gateway initial resolved IPs](#gateway-initial-resolved-ips) are the exception: the default range (`172.64.128.0/20`) is public Cloudflare address space, not CGNAT, so it is **not** excluded by default in Exclude mode. The Cloudflare One Client also automatically removes this range, along with the entire `2606:4700:0cf1::/48` IPv6 range (which also covers [device IPs](#device-ips) and [Cloudflare source IPs](#cloudflare-source-ips) on IPv6), from any exclusions you configure — refer to [Automatically managed ranges](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#automatically-managed-ranges). No Split Tunnel changes are required for these ranges. This only applies to the default IPv4 range: if you configured a custom initial resolved IP range within CGNAT space, treat it the same as the other CGNAT-based ranges below.

Cloudflare recommends excluding CGNAT IPs that are not used for Cloudflare One services. This reduces the risk of conflicts with private network configurations that use CGNAT address space.

Use the following calculator to determine which ranges you can exclude based on the Cloudflare One features you use. For example, if your deployment requires [Cloudflare source IPs](#cloudflare-source-ips) (`100.64.0.0/12`) and [device IPs](#device-ips) (`100.96.0.0/12`), exclude `100.80.0.0/12` and `100.112.0.0/12`.

<SubtractIPCalculator
	client:load
	defaults={{
		base: "100.64.0.0/10",
		subtract: ["100.64.0.0/12", "100.96.0.0/12"],
	}}
/>

### Include mode

In **Include IPs and domains** mode, the Cloudflare One Client sends only traffic for the included routes to Cloudflare. You must explicitly add the reserved IP ranges that your deployment depends on, except the default [Gateway initial resolved IP range](#gateway-initial-resolved-ips) (`172.64.128.0/20`) and the entire `2606:4700:0cf1::/48` IPv6 range (which also covers [device IPs](#device-ips) and [Cloudflare source IPs](#cloudflare-source-ips) on IPv6) — the Cloudflare One Client automatically includes these. Refer to [Automatically managed ranges](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#automatically-managed-ranges) for details. If you use [hostname routing or egress policy host selectors](#gateway-initial-resolved-ips) with a custom IPv4 initial resolved IP range, add that custom range to your Split Tunnels include list.

# Accessibility

Browser Isolation offers features to support users who have visual impairments or non-English language requirements.

## Screen reader

The isolated browser has a built-in screen reader which speaks out loud the content of the isolated page.

### Turn the screen reader on or off

To turn the built-in screen reader on or off, right-click on any isolated page and select **Accessibility** > **Enable** / **Disable screen reader**.

Alternatively, to use a keyboard shortcut, press `CTRL + ALT + Z`.

## Languages

The isolated browser supports keyboard inputs in all languages. Users can use their native keyboard to type in languages that use diacritics (for example, `á` or `ç`) or character-based scripts (for example, Chinese, Japanese, or Korean).

# Canvas Remoting

import { Details } from "~/components";

Canvas Remoting is a Browser Isolation capability that optimizes performance for web applications using the HTML5 Canvas API (a browser feature that allows web applications to draw graphics directly on the page). It sends vector draw commands to the client instead of rasterized bitmaps (pixel images), reducing bandwidth consumption and improving frame rates for productivity applications.

## How it works

Browser Isolation uses Network Vector Rendering (NVR) to send lightweight drawing instructions to the user's browser, rather than streaming rendered pixels or video of the page. However, HTML5 Canvas content previously required server-side rasterization (converting draw commands into pixel images), sending large bitmaps for every frame.

Canvas Remoting extends NVR to Canvas-based applications by:

1. Capturing draw commands made to the HTML5 Canvas element.
2. Converting and sending those commands to the client as NVR instructions.
3. Rendering the Canvas content on the client onto an offscreen texture (a hidden drawing surface used for intermediate rendering).
4. Compositing (layering) the texture into the final document output.

## Supported applications

Canvas Remoting improves performance for productivity applications that rely on the HTML5 Canvas API:

| Application                          | Improvement                                |
| ------------------------------------ | ------------------------------------------ |
| Microsoft Word                       | 10x bandwidth reduction                    |
| Microsoft Excel                      | Smooth scrolling and data entry            |
| Microsoft PowerPoint                 | Fluid animations                           |
| Google Sheets                        | Consistent 30fps rendering                 |
| Google Maps                          | Smooth panning and zooming                 |
| Web-based terminals and AI notebooks | Fast and responsive text input and display |

## Limitations

Canvas Remoting supports 2D Canvas contexts only. The following are not supported:

- WebGL and WebGPU contexts
- 3D graphics applications
- Advanced Canvas features requiring GPU acceleration

## Enable or disable Canvas Remoting

Canvas Remoting is on by default for all Browser Isolation customers. No configuration is required.

![Canvas Remoting context menu option](~/assets/images/cloudflare-one/remote-browser-isolation/canvas-remoting-context-menu.png)

### Disable Canvas Remoting for the current session

1. Right-click on the background of the isolated webpage.
2. Select **Disable Canvas Remoting** from the context menu.

### Re-enable Canvas Remoting

1. Right-click on the background of the isolated webpage.
2. Select **Enable Canvas Remoting** from the context menu.

## Troubleshooting

<Details header="Canvas content renders slowly">

If Canvas-based applications appear choppy or consume excessive bandwidth:

1. Verify Canvas Remoting is on by right-clicking the page background.
2. Check that the context menu shows **Disable Canvas Remoting** (indicating it is active).
3. If the issue persists, open a support case and provide the Ray ID from the error page.

</Details>

<Details header="Graphical glitches or missing elements">

If Canvas content displays incorrectly after reconnecting from a network interruption:

1. Refresh the isolated page.
2. If the issue persists, select **Disable Canvas Remoting** from the right-click menu.
3. Re-enable Canvas Remoting after the page reloads.

</Details>

# Extensions

Browser Isolation supports running native Chromium Web Extensions in the remote browser.

When a page is isolated, it runs in a remote browser — not in the user's local browser. Extensions installed locally cannot interact with isolated pages because the page content exists only on the remote side. This capability allows extending tools that require DOM access (the ability to read and modify page content and structure), such as password managers and ad blockers, to isolated pages.

## Install an extension inside the remote browser

### Prerequisite: Isolate Chrome Web Store

:::note

This step is not required when browsing via Clientless Web Isolation. You can access the Chrome Web Store at `https://<authdomain>.cloudflareaccess.com/browser/https://chromewebstore.google.com/`.
:::

Installing extensions requires Chrome Web Store isolation. Create an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to isolate the Chrome Web Store (chromewebstore.google.com).

### Install an extension

1. Go to `https://chromewebstore.google.com/` while isolated.
2. Choose your desired extension.
3. Select **Add to Chrome**. To confirm extension installation, select **Add extension**.

Remote browser extensions are automatically reinstalled across isolated sessions.

## Remove an extension from the remote browser

1. Go to any isolated webpage.
2. Right-click anywhere to open the context menu and select **Show isolation toolbar**.
3. Select the jigsaw icon in the isolation toolbar to open the extension manager.
4. Select the hamburger icon for the desired extension to open the extension controls.
5. Select **Remove from Chromium**. To confirm removal, select **Remove**.

## Useful extensions

### Modify remote browser user agent

[User-Agent Switcher for Chrome](https://chromewebstore.google.com/detail/user-agent-switcher-for-c/djflhoibgkdhkhhcedjiklpkjnoahfmg) enables controlling the User Agent sent from the remote browser to an isolated website.

### Control remote browser request headers

[ModHeader](https://chromewebstore.google.com/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj) enables controlling arbitrary request headers sent from the remote browser to an isolated website.

# Remote browser isolation

import { Render } from "~/components";

:::note
Remote browser isolation is available as an add-on to Zero Trust Pay-as-you-go and Enterprise plans.
:::

Cloudflare Browser Isolation complements the [Secure Web Gateway](/cloudflare-one/traffic-controls/) (which inspects and filters HTTP/HTTPS traffic) and [Zero Trust Network Access](/cloudflare-one/networks/connectors/cloudflare-tunnel/) (which controls access to private applications) by executing active webpage content — executable code such as JavaScript and plugins — in a secure isolated browser. Because active content executes remotely instead of on the user's device, Browser Isolation protects users from zero-day attacks (attacks that exploit vulnerabilities with no available patch) and malware.

Browser Isolation also protects users from phishing attacks by preventing user input on risky websites and controlling data transmission to sensitive web applications. You can further filter isolated traffic with Gateway [HTTP](/cloudflare-one/traffic-controls/http-policies/) and [DNS](/cloudflare-one/traffic-controls/dns-policies/) policies.

Remote browsing is invisible to the user who continues to use their browser normally without changing their preferred browser and habits. Every open tab and window is automatically isolated. When the user closes the isolated browser, their session is automatically deleted.

## Permissions

Browser Isolation is configured through Gateway [HTTP policies](/cloudflare-one/traffic-controls/http-policies/) with the _Isolate_ or _Do Not Isolate_ action. Gateway role-based access control (RBAC) covers isolation policy management, and there is no separate Browser Isolation permission surface.

You can use [account-level roles](/fundamentals/manage-members/roles/#account-scoped-roles) such as `Zero Trust HTTP Policies Admin` to grant access to all HTTP policies in the account. You can also use [resource-scoped roles](/cloudflare-one/traffic-controls/granular-permissions/) to delegate management of specific isolation policies to individual team members.

For details on available roles and how to assign granular permissions, refer to [Granular permissions for Gateway](/cloudflare-one/traffic-controls/granular-permissions/).

## Privacy

Cloudflare Browser Isolation is a security product. In order to serve transparent isolated browsing and block web based threats our network decrypts Internet traffic using the [Cloudflare root CA](/cloudflare-one/team-and-resources/devices/user-side-certificates/). Traffic logs are retained as per the [Zero Trust](/cloudflare-one/insights/logs/) documentation.

## Troubleshooting

For help resolving common issues with Browser Isolation, refer to [Troubleshoot Browser Isolation](/cloudflare-one/remote-browser-isolation/troubleshooting/).

# Isolation policies

import { Render, Badge, Tabs, TabItem, APIRequest } from "~/components";

With Browser Isolation, you can define policies to dynamically isolate websites based on identity, security threats, or content.

:::note
Isolation policies are Gateway HTTP policies. Gateway [account-level and resource-scoped roles](/cloudflare-one/traffic-controls/granular-permissions/) apply to isolation policies, so you can delegate management of specific isolation policies without granting access to all Gateway resources.
:::

## Isolate

When an HTTP policy applies the Isolate action, the user's web browser is transparently served an HTML compatible remote browser client. Isolation policies can be applied to requests that include `Accept: text/html*` (requests for web pages). This allows Browser Isolation policies to co-exist with API traffic.

The following example enables isolation for all web traffic:

| Selector | Operator      | Value | Action  |
| -------- | ------------- | ----- | ------- |
| Host     | matches regex | `.*`  | Isolate |

If instead you need to isolate specific pages, you can list the domains for which you would like to isolate traffic:

| Selector | Operator | Value                        | Action  |
| -------- | -------- | ---------------------------- | ------- |
| Domain   | In       | `example.com`, `example.net` | Isolate |

:::note[Isolate identity providers for applications]

Existing cookies and sessions from non-isolated browsing are not sent to the remote browser. Websites that implement single sign-on using third-party cookies will also need to be isolated.

For example, if `example.com` authenticates using Google Workspace, you will also need to isolate the top level [Google Workspace URLs](https://support.google.com/a/answer/9012184).

:::

## Do Not Isolate

You can choose to disable isolation for certain destinations or categories. The following configuration disables isolation for traffic directed to `example.com`:

| Selector | Operator | Value         | Action         |
| -------- | -------- | ------------- | -------------- |
| Host     | In       | `example.com` | Do Not Isolate |

## Policy settings

When you isolate a website, you can also restrict what users do on that site. The following optional settings appear in the Gateway HTTP policy builder when you select the _Isolate_ action. Configure these settings to [prevent data loss](https://blog.cloudflare.com/data-protection-browser/) when users interact with untrusted websites in the remote browser — for example, to stop a user from copying confidential data out of an isolated internal application.

### Copy (from remote to client)

```mermaid
    flowchart LR
			subgraph remotebrowser[Remote browser]
        siteA["Isolated
				website"]--Data-->remoteclip["Remote
				clipboard"]
      end
			subgraph client[Client]
        localclip["Local
				clipboard"]
      end
			remoteclip-->localclip
```

- _Allow_: (Default) Users can copy content from an isolated website to their local clipboard.
- _Allow only within isolated browser_: Users can only copy content from an isolated website to the remote clipboard. Users cannot copy content out of the remote browser to the local clipboard. You can use this setting alongside [**Paste (from client to remote)**: _Allow only within isolated browser_](/cloudflare-one/remote-browser-isolation/isolation-policies/#paste-from-client-to-remote) to only allow copy-pasting between isolated websites.
- _Do not allow_: Prohibits users from copying content from an isolated website.

### Paste (from client to remote)

```mermaid
    flowchart LR
			subgraph client[Client]
        localclip["Local
				clipboard"]
      end
			subgraph remotebrowser[Remote browser]
				remoteclip["Remote
				clipboard"]-->siteA["Isolated
				website"]
      end
			localclip--Data-->remoteclip
```

- _Allow_: (Default) Users can paste content from their local clipboard to an isolated website.
- _Allow only within isolated browser_: Users can only paste content from the remote clipboard to an isolated website. Users cannot paste content from their local clipboard to the remote browser. You can use this setting alongside [**Copy (from remote to client)**: _Allow only within isolated browser_](/cloudflare-one/remote-browser-isolation/isolation-policies/#copy-from-remote-to-client) to only allow copy-pasting between isolated websites.
- _Do not allow_: Prohibits users from pasting content into an isolated website.

### File downloads

- _Allow_: (Default) User can download files from an isolated website to their local machine.
- _Do not allow_: Prohibits users from downloading files from an isolated website to their local machine.
- _View in remote browser_: Users can open and view files in an isolated environment.

:::note
This option does not prevent files from being downloaded into the remote browser. To prevent files being downloaded into the remote browser, use HTTP Policies to block by [Download Mime Type](/cloudflare-one/traffic-controls/http-policies/#download-and-upload-mime-type).
:::

### File uploads

- _Allow_: (Default) Users can upload files from their local machine into an isolated website.
- _Do not allow_: Prohibits users from uploading files from their local machine into an isolated website.

:::note
This option does not prevent files being uploaded to websites from third-party cloud file managers or files downloaded into the remote browser download bar from other isolated websites. To prevent files being uploaded from the remote browser into an isolated website, use HTTP Policies to block by [Upload Mime Type](/cloudflare-one/traffic-controls/http-policies/#download-and-upload-mime-type).
:::

### Keyboard

- _Allow_: (Default) Users can perform keyboard inputs into an isolated website.
- _Do not allow_: Prohibits users from performing keyboard inputs into an isolated website.

:::note
Mouse input remains available to allow users to browse a website by following hyperlinks and scrolling. This does not prevent user input into third-party virtual keyboards within an isolated website.
:::

### Printing

- _Allow_: (Default) Users can print isolated web pages to their local machine.
- _Do not allow_: Prohibits users from printing isolated web pages to their local machine.

## Custom block dialog <Badge text="Beta" variant="note" />

With custom block dialogs, you can host a custom block page when users are blocked from taking specific actions, like [copying](/cloudflare-one/remote-browser-isolation/isolation-policies/#copy-from-remote-to-client), [pasting](/cloudflare-one/remote-browser-isolation/isolation-policies/#paste-from-client-to-remote), [downloading](/cloudflare-one/remote-browser-isolation/isolation-policies/#file-downloads), [uploading](/cloudflare-one/remote-browser-isolation/isolation-policies/#file-uploads), [performing keyboard inputs](/cloudflare-one/remote-browser-isolation/isolation-policies/#keyboard), or [printing](/cloudflare-one/remote-browser-isolation/isolation-policies/#printing), within an isolated browser session.

Administrators can configure custom block dialogs to explain the reason for the block, and guide the users on how to resolve their issue using the provided query parameters:

- `action`: copy, paste, download, upload, perform keyboard inputs, and print
- `cf_colo`: for example, `sea01`
- `client_url`: for example, `https://example.com`
- `policy_id`: 32-character id
- `rbi_debug_id`: 32-character id
- `user_id`: 32-character id

Custom block dialogs are still in beta. Contact your account team to start using custom block dialogs.

## Common policies

### Isolate all security threats

Isolate security threats such as malware and phishing.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector            | Operator | Value                | Action  |
| ------------------- | -------- | -------------------- | ------- |
| Security Categories | in       | _All security risks_ | Isolate |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Isolate all security threats",
		description: "Isolate security threats such as malware and phishing",
		enabled: true,
		action: "isolate",
		filters: ["http"],
		traffic:
			"any(http.request.uri.security_category[*] in {68 178 80 83 176 175 117 131 134 151 153})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Isolate high risk content

Isolate high risk content categories such as newly registered domains.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value            | Action  |
| ------------------ | -------- | ---------------- | ------- |
| Content Categories | in       | _Security Risks_ | Isolate |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Isolate high risk content",
		description:
			"Isolate high risk content categories such as newly registered domains",
		enabled: true,
		action: "isolate",
		filters: ["http"],
		traffic: "any(http.request.uri.content_category[*] in {32 169 177 128})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Isolate news and media

Isolate news and media sites, which are targets for malvertising attacks.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value            | Action  |
| ------------------ | -------- | ---------------- | ------- |
| Content Categories | in       | _News and Media_ | Isolate |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Isolate news and media",
		description:
			"Isolate news and media sites, which are targets for malvertising attacks",
		enabled: true,
		action: "isolate",
		filters: ["http"],
		traffic: "any(http.request.uri.content_category[*] in {122})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Isolate uncategorized content

Isolate content that has not been categorized by [Cloudflare Radar](/radar/).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value                    | Action  |
| ------------------ | -------- | ------------------------ | ------- |
| Content Categories | not in   | _All content categories_ | Isolate |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Isolate uncategorized content",
		description: "Isolate content not categorized by Cloudflare Radar",
		enabled: true,
		action: "isolate",
		filters: ["http"],
		traffic:
			"not(any(http.request.uri.content_category[*] in {2 67 125 133 3 75 183 89 182 6 90 91 144 150 7 70 74 76 79 92 96 100 106 107 116 120 121 122 127 139 156 164 99 9 101 137 10 103 146 11 12 77 98 108 110 111 118 126 129 172 168 113 33 179 166 15 115 119 124 141 161 17 85 87 102 157 135 138 180 162 140 142 32 169 177 128 22 73 82 88 148 23 24 181 71 72 173 78 84 86 94 97 104 105 114 174 93 130 132 136 147 149 154 158 152 26 69 184 81 95 109 123 145 155 159 160 163 165 167}))",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Isolate ChatGPT

Isolate the use of ChatGPT.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector    | Operator | Value     | Action  |
| ----------- | -------- | --------- | ------- |
| Application | in       | _ChatGPT_ | Isolate |

In **Configure policy settings**, you can customize restrictions for ChatGPT. For example, to prevent your users from inputting sensitive information, you can select **Disable copy / paste** and **Disable file uploads**.

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Isolate ChatGPT",
		description: "Isolate the use of ChatGPT",
		enabled: true,
		action: "isolate",
		filters: ["http"],
		traffic: "any(app.ids[*] in {1199})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

# Known limitations

import { Render } from "~/components";

Below, you will find information regarding the current limitations for Browser Isolation.

## Website compatibility

Our Network Vector Rendering (NVR) technology sends drawing instructions to the user's browser instead of streaming video of the page. This allows us to deliver a secure remote computing experience without the bandwidth limitations of video streams. While we expect most websites to work perfectly, some browser features and web technologies are unsupported and will be implemented in the future:

- Webcam and microphone support is unavailable.
- Websites that use WebGL (a browser technology for rendering 3D graphics) may not function. To turn off WebGL in the browser, refer to [WebGL Rendering Error](/cloudflare-one/remote-browser-isolation/troubleshooting/#webgl-rendering-error).
- Netflix and Spotify Web Player are unavailable.
- H.265/HEVC (a video compression format) is not a supported video format at this time.

## Single active window

Browser Isolation supports one active window at a time. All tabs and windows in the same local browser (for example, all Chrome tabs) share a single isolated session. Within that session, the remote browser actively renders and processes only the tab or window currently in focus. Browser Isolation deactivates background tabs and windows until you switch to them.

This means that workflows requiring simultaneous activity across multiple windows are unavailable.

For example:
- Audio or video playing in one isolated window will pause when you switch to a different isolated window.
- Real-time content (such as live dashboards or streaming media) will not update in background windows.
- Applications that rely on multi-window communication or synchronization are not supported.

## Browser compatibility

| Browser                                      | Compatibility |
| -------------------------------------------- | ------------- |
| Google Chrome                                | ✅            |
| Mozilla Firefox                              | ✅            |
| Safari                                       | ✅            |
| Microsoft Edge (Chromium-based)              | ✅            |
| Other Chromium-based browsers (Opera, Brave) | ✅            |
| Internet Explorer 11 and below               | ❌            |

### iOS

On iOS, Apple WebKit requires direct user interaction before the local browser can pass keyboard input into the remote browser. This means users should tap twice to begin entering text in an isolated session.

The first tap focuses the text field. The second tap starts text entry.

Browser Isolation shows an inline prompt over the focused text field when this interaction is required. If the text field is too small to display the full prompt, Browser Isolation displays a keyboard icon in the center of the field instead.

### Brave

Browser Isolation uses [WebRTC](/cloudflare-one/remote-browser-isolation/network-dependencies/#webrtc-channel) for low-latency communication between the local and remote browser. Brave's WebRTC IP Handling Policy can impact how Cloudflare RBI loads and functions. If the WebRTC IP Handling Policy is configured to **Disable Non-Proxied UDP**, RBI may fail to load correctly because Brave blocks the UDP connections that WebRTC requires.

To ensure RBI loads correctly, go to `brave://settings/privacy` in your Brave browser window, find **WebRTC IP Handling Policy**, and change the setting from **Disable Non-Proxied UDP** to one of the following:

- **Default**
- **Default Public and Private Interfaces**
- **Default Public Interface Only**

## Protocol support

Browser Isolation requires HTTPS. Websites served over unencrypted HTTP cannot be isolated.

## Virtual machines

Browser Isolation is not supported in virtualized environments (VMs).

## Gateway selectors

Certain selectors for Gateway HTTP policies bypass Browser Isolation, including:

- [Destination Continent IP Geolocation](/cloudflare-one/traffic-controls/http-policies/#destination-continent)
- [Destination Country IP Geolocation](/cloudflare-one/traffic-controls/http-policies/#destination-country)
- [Destination IP](/cloudflare-one/traffic-controls/http-policies/#destination-ip)

You cannot use these selectors to isolate traffic and isolation matches for these selectors will not appear in your Gateway logs. Additionally, you cannot apply other policies based on these selectors while in isolation. For example, if you have a Block policy that matches traffic based on destination IP, Gateway will not block the matching traffic if it is already isolated by an Isolate policy.

## Font rendering

The remote browser runs in a Linux-based container that does not include proprietary or licensed fonts such as Arial, Times New Roman, or other fonts typically bundled with Windows or macOS. If a website's CSS specifies one of these fonts without a generic fallback, the remote browser cannot match the named font and falls back to its own default, which may render text differently than expected.

For example, the following CSS will produce different results in the remote browser than on a local Windows machine:

```css
/* No generic fallback — text may render differently in Browser Isolation */
body {
  font-family: Arial;
}
```

To avoid font rendering differences, website owners should always include a generic fallback family at the end of their `font-family` declarations:

```css
/* Generic fallback ensures consistent rendering */
body {
  font-family: Arial, sans-serif;
}
```

Websites that load custom fonts as web fonts (for example, from Google Fonts or a self-hosted source) are not affected, because the remote browser downloads and uses the font file directly.

## File download size

When a user downloads a file within the remote browser, the file is held in memory and destroyed at the end of the remote browser session. Therefore, the total size of files downloaded per session is shared with the amount of memory available to the remote browser. We recommend a maximum individual file size of 512 MB.

## Multifactor authentication

[Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/) does not support Yubikey or WebAuthN (hardware security key authentication). These authentication technologies require the isolated website to use the same domain name as the non-isolated website. Clientless Web Isolation changes the URL by adding a prefix, which breaks this requirement. Therefore, Yubikey and WebAuthN will not work with prefixed Clientless Web Isolation URLs but will work normally for [in-line deployments](/cloudflare-one/remote-browser-isolation/setup/) such as [isolated Access applications](/cloudflare-one/access-controls/policies/isolate-application/).

## SAML applications

Cloudflare Remote Browser Isolation now [supports SAML applications that use HTTP-POST bindings](/cloudflare-one/changelog/browser-isolation/#2025-05-13). SAML is a protocol used for single sign-on (SSO), and some SAML implementations send login data via an HTTP POST request (HTTP-POST bindings). This resolves previous issues such as `405` errors and login loops during SSO authentication flows.

You no longer need to isolate both the Identity Provider (IdP) and Service Provider (SP), or switch to HTTP-Redirect bindings, to use Browser Isolation with POST-based SSO. Users can log in to internal or SaaS applications in the isolated browser securely and seamlessly.

[Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/) may still be preferred in some deployment models. Clientless Web Isolation implicitly isolates all traffic (both IdP and SP) and supports HTTP-POST SAML bindings.

## Browser Isolation is not compatible with private apps on non-`443` ports

<Render file="access/self-hosted-app/browser-isolation-non-443" product="cloudflare-one"/>

# Browser Isolation with firewall

If your organization uses a firewall or other policies to restrict Internet traffic, you may need to make a few changes to allow Browser Isolation to connect.

## Remoting client

Isolated pages are served by the remoting client — the software component in the user's browser that loads, displays, and communicates with the remote browser session. This client communicates to Cloudflare's network via HTTPS and WebRTC.

### Remoting Client (Services)

The remoting client provides static assets and API endpoints. For Browser Isolation to function, you must allow:

- HTTPS traffic to `*.browser.run` on port `443`

#### Clientless Web Isolation

Users connecting through Clientless Web Isolation also require connectivity to Cloudflare Access. For users to connect to Access, you must allow:

- HTTPS traffic to `https://<team-name>.cloudflareaccess.com` on port `443`

### WebRTC channel

Browser Isolation uses WebRTC (a real-time communication protocol) for low-latency communication between the local browser and the remote browser. WebRTC uses UDP rather than TCP, which means this traffic does not flow through standard HTTP/HTTPS proxy settings. The connecting device must have direct UDP connectivity to the IP ranges listed below.

In order to pass WebRTC traffic, the remoting client must be able to connect to the following IP addresses:

| IP range                                                                 | Port range    | Protocol |
| ------------------------------------------------------------------------ | ------------- | -------- |
| IPv4: `162.159.201.10 - 162.159.201.255` <br/> IPv4: `172.64.73.0 - 172.64.73.255` <br/> IPv6: `2606:4700:f2::/48` | 10000 - 59999 | UDP      |

Each remote browser instance is randomly assigned a port, and the port that a user is allocated to will change often and without notice.

:::note

WebRTC traffic does not flow through proxies specified in local browser HTTP/HTTPS proxy settings. The connecting device needs to be able to directly connect to the WebRTC IP ranges.
:::

# Troubleshoot Browser Isolation

import { Render } from "~/components";

<Render file="troubleshooting/browser-isolation" product="cloudflare-one" />

---

## How to contact Support

If you cannot resolve the issue, [open a support case](/support/contacting-cloudflare-support/). For RBI issues, it is helpful to provide the [Ray ID](/fundamentals/reference/cloudflare-ray-id/) from any error page and a description of the browser you are using.

# Clientless Web Isolation

import { GlossaryTooltip, Render } from "~/components";

Clientless Web Isolation allows users to securely browse high risk or sensitive websites in a remote browser without having to install the Cloudflare One Client on their device. Use Clientless Web Isolation when you need to provide isolated browsing to unmanaged devices (for example, contractor laptops or personal phones) where you cannot install software.

:::note
Clientless Web Isolation requires the [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/) add-on.
:::

## Set up Clientless Web Isolation

<Render file="clientless-browser-isolation" product="cloudflare-one" />

3. To configure permissions, in **Browser isolation** > **Browser isolation settings** > select **Manage** next to **Manage remote browser permissions**. You can add authentication methods and [rules](/cloudflare-one/access-controls/policies/) to control who can access the remote browser.

4. Under **Policies** > Access Policies > select **Create new policy**.

5. Name your policy and define who will have access to your isolated application. Refer to the [Access policy documentation](/cloudflare-one/access-controls/policies/#actions) to construct your policy.

6. Select **Save**.

7. Under **Policies** > Access Policies > select **Select existing policies** and select the policy or policies you created in the previous step > select **Confirm**.

8. At the bottom of the page, select **Save**.

Your application will now be served in an isolated browser for users matching your policies.

### Open links in Browser Isolation

To open links using Browser Isolation:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Browser isolation** > **Browser isolation settings**.
2. Turn on **Allow users to open a remote browser without the device client**.
3. In **Launch browser**, enter the URL link, and then select **Launch**. Your URL will open in a secure isolated browser.

## Filter DNS queries

When users browse through Clientless Web Isolation, their DNS queries (the lookups that translate domain names to IP addresses) are handled by Gateway. You can use [DNS policies](/cloudflare-one/traffic-controls/dns-policies/) to control which domains the remote browser can resolve. Enterprise users can resolve domains available only through private DNS servers by creating [resolver policies](/cloudflare-one/traffic-controls/resolver-policies/).

Gateway DNS and resolver policies will always apply to Clientless Web Isolation traffic, regardless of device configuration.

## Use the remote browser

Clientless Web Isolation is implemented through a prefixed URL — the target website's address is appended to a Cloudflare-hosted base URL. `<your-team-name>` is your organization's <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

```txt
https://<your-team-name>.cloudflareaccess.com/browser/<URL>
```

For example, to isolate `www.example.com`, users would visit `https://<your-team-name>.cloudflareaccess.com/browser/https://www.example.com/` in their preferred browser.

If `<url>` is not provided, users are presented with a Cloudflare Zero Trust landing page where they can input a target URL or search for a website.

## Optional configurations

### Allow or block websites

When users visit a website through the [Clientless Web Isolation URL](#use-the-remote-browser), the traffic passes through Cloudflare Gateway. This allows you to [apply HTTP policies](/cloudflare-one/traffic-controls/http-policies/) to control what websites the remote browser can connect to, even if the user's device does not have the Cloudflare One Client installed.

For example, if you use a third-party Secure Web Gateway to block `example.com`, users can still access the page in the remote browser by visiting `https://<your-team-name>.cloudflareaccess.com/browser/https://www.example.com/`. To block `https://<your-team-name>.cloudflareaccess.com/browser/https://www.example.com/`, create a Cloudflare Gateway HTTP policy to block `example.com`:

| Selector | Operator | Value         | Action |
| -------- | -------- | ------------- | ------ |
| Domain   | in       | `example.com` | Block  |

### Bypass TLS decryption

[TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) allows Gateway to inspect the contents of HTTPS traffic by decrypting it, applying policies, and re-encrypting it. If TLS decryption is turned on, Gateway will decrypt all sites accessed through the Clientless Web Isolation URL. Some sites are incompatible with this process (for example, sites that use certificate pinning). To connect to those sites, add a Do Not Inspect HTTP policy for the application or domain.

| Selector | Operator | Value        | Action         |
| -------- | -------- | ------------ | -------------- |
| Domain   | is       | `mysite.com` | Do Not Inspect |

:::note

Clientless Web Isolation can function without TLS decryption turned on. However, TLS decryption is required to apply [HTTP policies](/cloudflare-one/traffic-controls/http-policies/) to Clientless Web Isolation traffic, because Gateway must decrypt the traffic before it can inspect and filter the content.

:::

### Connect private networks

With Clientless Web Isolation, users can reach any internal web server you have connected through [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/). For more information, refer to [Connect private networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/).

For example, if you added `192.168.2.1` to your tunnel, users can connect to your application through the remote browser by going to `https://<your-team-name>.cloudflareaccess.com/browser/http://192.168.2.1`. Clientless Web Isolation also supports connecting over private ports, for example `https://<your-team-name>.cloudflareaccess.com/browser/http://192.168.2.1:7148`.

:::note

All users with access to your remote browser can access your Cloudflare Tunnel applications unless you create a Gateway HTTP policy to block them.
:::

### Disable remote browser controls

You can configure [remote browser controls](/cloudflare-one/remote-browser-isolation/isolation-policies/#policy-settings) such as disabling copy/paste, printing, or keyboard input. These settings display in the Gateway [HTTP policy builder](/cloudflare-one/traffic-controls/http-policies/) when you select the Isolate action.

### Sync cookies between local and remote browser

The Cloudflare One Chrome extension allows a user to seamlessly access isolated and non-isolated applications without needing to re-authenticate. The user can log in once to their identity provider (whether through a Clientless Web Isolation link or their local browser) and gain access to all applications behind the SSO login.

:::note
The Chrome extension is available in early access. To install, contact your account team.
:::

## Address bar

Clientless Web Isolation has an embedded address bar. This feature is designed to improve the user's experience while visiting isolated pages with prefixed URLs.

The clientless address bar has three views: hostname notch, full address bar and hidden. The user's selected view is remembered across domains and remote browsing sessions.

### Hostname notch view

By default the isolated domain name appears in the notch positioned at the top and center of an isolated page.

![Viewing hostname of an isolated page in the clientless remote browser](~/assets/images/cloudflare-one/policies/rbi-address-bar-notch.png)

Selecting **Expand** or the hostname text will expand the notch to the full address bar view. If isolated page content is obscured by the notch, expanding to the full address bar view will make the content accessible.

### Full address bar view

The full address bar allows users to search and go to isolated websites. Users can jump to the address bar at any time by pressing `CTRL + L` on the keyboard.

![Viewing full address of an isolated page in the clientless remote browser](~/assets/images/cloudflare-one/policies/rbi-address-bar-full.png)

### Hidden view

To turn on or off the address bar, users can right-click on any isolated page and select **Show / Hide address bar**.

## Logs

- **Authentication events**: User login events are available in [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/).
- **HTTP requests**: Traffic from the remote browser to the Internet is logged in [Gateway activity logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/).
- **DNS queries**: DNS queries from the remote browser are shown in [Gateway activity logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/).
- **Network sessions**: Egress traffic from the remote browser generates [Zero Trust Network Session Logs](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/), available via [Logpush](/cloudflare-one/insights/logs/logpush/) and [Log Explorer](/observability/logs/log-explorer/).
- **User actions**: Track copy/paste, download/upload, and print actions initiated by users in the remote browser (only available in [Logpush](/cloudflare-one/insights/logs/logpush/)).

## Redirect traffic to the remote browser

If you want to isolate a website without the Cloudflare One Client installed, you will need to redirect traffic to the Clientless Web Isolation [prefixed URL](#use-the-remote-browser). One way to do this is through a third-party Secure Web Gateway. To redirect users to the remote browser, you can implement a custom block page similar to the example shown below.

```html
<!DOCTYPE html>
<html>
	<head>
		<title>Redirecting website to a remote browser</title>
		<script>
			window.location.href =
				"https://<your-team-name>.cloudflareaccess.com/browser/<URL>}";
		</script>
		<noscript>
			<meta
				http-equiv="refresh"
				content="0; url=https://<your-team-name>.cloudflareaccess.com/browser/<URL>"
			/>
		</noscript>
	</head>
	<body>
		<p>
			This website is being redirected to a remote browser. Select
			<a href="https://<your-team-name>.cloudflareaccess.com/browser/<URL>"
				>here</a
			>
			if you are not automatically redirected.
		</p>
	</body>
</html>
```

## Troubleshooting

Review troubleshooting guidance related to Clientless Web Isolation.

- [Clientless Web Isolation is loading a blank screen on a Windows device](/cloudflare-one/remote-browser-isolation/troubleshooting/#blank-screen-on-windows)

# Set up Browser Isolation

Browser Isolation is enabled through [Secure Web Gateway HTTP policies](/cloudflare-one/traffic-controls/http-policies/). By default, no traffic is isolated until you have added an Isolate policy to your HTTP policies.

## 1. Connect devices to Cloudflare

Setup instructions vary depending on how you want to connect your devices to Cloudflare. Refer to the links below to view the setup guide for each deployment option.

| Connection                                                                                                | Mode         | Description                                                                                                          |
| --------------------------------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------- |
| [Traffic and DNS mode](/cloudflare-one/traffic-controls/get-started/http/)                                | In-line      | Apply identity-based HTTP policies to traffic proxied through the Cloudflare One Client.                                       |
| [Access](/cloudflare-one/access-controls/policies/isolate-application/)                                   | In-line      | Apply identity-based HTTP policies to Access applications that are rendered in a remote browser.                     |
| [Gateway proxy endpoint (source IP)](/cloudflare-one/remote-browser-isolation/setup/non-identity/)        | In-line      | Apply non-identity HTTP policies to traffic forwarded to a [source IP proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/#source-ip-endpoint).              |
| [Gateway proxy endpoint (authorization)](/cloudflare-one/traffic-controls/proxy-endpoints/#authorization-endpoint) | In-line      | Apply identity-based HTTP policies to traffic forwarded to an [authorization proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/#authorization-endpoint).    |
| [Cloudflare WAN](/cloudflare-one/remote-browser-isolation/setup/non-identity/)                            | In-line      | Apply non-identity HTTP policies to traffic connected through a GRE or IPsec tunnel (site-to-site encrypted connections to Cloudflare's network).                                 |
| [Clientless remote browser](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/) | Prefixed URL | Render web pages in a remote browser when users go to `https://<your-team-name>.cloudflareaccess.com/browser/<URL>`. |

**In-line** mode means traffic is inspected as it flows through Gateway — users browse to websites using normal URLs, not a special Cloudflare prefix. Some in-line methods require device or network configuration, such as installing the Cloudflare One Client or configuring a PAC file. **Prefixed URL** mode requires users to visit a Cloudflare-hosted URL that wraps the target website.

## 2. Build an Isolation policy

To configure Browser Isolation policies:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Traffic policies** > **Firewall policies** > **HTTP**.
2. Select **Add a policy** and enter a name for the policy.
3. Use the HTTP policy [selectors](/cloudflare-one/traffic-controls/http-policies/#selectors) and [operators](/cloudflare-one/traffic-controls/http-policies/#comparison-operators) to specify the websites or content you want to isolate.
4. For **Action**, choose either [_Isolate_](/cloudflare-one/remote-browser-isolation/isolation-policies/#isolate) or [_Do not Isolate_](/cloudflare-one/remote-browser-isolation/isolation-policies/#do-not-isolate).
5. (Optional) Configure [settings](/cloudflare-one/remote-browser-isolation/isolation-policies/#policy-settings) for an Isolate policy.
6. Select **Create policy**.

Next, [verify that your policy is working](#3-check-if-a-web-page-is-isolated).

## 3. Check if a web page is isolated

Users can see if a webpage is isolated by using one of the following methods:

- Select the padlock in the address bar and check for the presence of a Cloudflare Root CA.
- Right-click the web page and view the context menu options.

### Normal browsing

- A non-Cloudflare root certificate indicates that Cloudflare did not proxy this web page. The root certificate is the certificate authority (CA) that your browser trusts to verify the site's identity.

  ![Website does not present a Cloudflare root certificate](~/assets/images/cloudflare-one/rbi/non-cloudflare-root-ca.png)

- The right-click context menu will have all of the normal options.

  ![Normal right-click menu in browser](~/assets/images/cloudflare-one/rbi/non-isolated-browser.png)

### Isolated browsing

- A Cloudflare root certificate indicates traffic was proxied through Cloudflare Gateway.

  ![Website presents a Cloudflare root certificate](~/assets/images/cloudflare-one/rbi/cloudflare-gateway-root-ca.png)

- The right-click context menu will be simplified.

  ![Simplified right-click menu in browser](~/assets/images/cloudflare-one/rbi/isolated-browser.png)

#### Disconnect Browser Isolation

Cloudflare One Client users can temporarily disable remote browsing by [disconnecting the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#lock-device-client-switch).
Once the Cloudflare One Client is disconnected, a refresh will return the non-isolated page.

# Non-identity on-ramps

On-ramps are the methods used to route traffic from your network to Cloudflare for inspection. With Cloudflare One, you can isolate HTTP traffic from on-ramps such as [proxy endpoints](/cloudflare-one/traffic-controls/proxy-endpoints/) (which your browser connects to via PAC files to send traffic through Gateway) or [Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-gateway/) (formerly Magic WAN, which connects your network to Cloudflare through GRE or IPsec tunnels). Since these on-ramps do not require users to log in to the Cloudflare One Client, [identity-based policies](/cloudflare-one/traffic-controls/identity-selectors/) are not supported.

:::note

If you want to apply Isolate policies based on user identity, you will need to either install the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) or manually redirect users to the [Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/) URL.
:::

## Set up non-identity browser isolation

1. [Install a Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on your devices.
2. Connect your infrastructure to Gateway using one of the following on-ramps:
   - Configure your browser to forward traffic to a Gateway proxy endpoint with [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/) (Proxy Auto-Configuration files that tell the browser which traffic to route through the proxy).
   - Connect your enterprise site router to Gateway with the [anycast GRE or IPsec tunnel on-ramp to Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-gateway/) (site-to-site encrypted tunnels between your network and Cloudflare).
3. Enable non-identity browser isolation:
   1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Browser isolation** > **Browser isolation settings**.
   2. Turn on **Allow isolated HTTP traffic when user identity is unknown**.
4. Build a non-identity [HTTP policy](/cloudflare-one/remote-browser-isolation/isolation-policies/) to isolate websites in a remote browser.

# Reusable components

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Lists

import { Render } from "~/components";

With Cloudflare One, you can create lists of URLs, hostnames, or other entries to reference when creating [Gateway policies](/cloudflare-one/traffic-controls/) or [Access policies](/cloudflare-one/access-controls/policies/). This allows you to quickly create rules that match and take actions against several items at once.

Before creating a list, make note of the [limitations](#limitations).

:::note
The lists described in this page are not the same as [custom lists](/waf/tools/lists/custom-lists/) defined at the account level. The two types of lists support different data types and have different validation rules.
:::

## List types

Lists can contain a single type of data each. Supported data types include:

- URLs
- Hostnames or domains
- Serial numbers
- User email addresses
- IP addresses
- Device ID numbers
- AAGUIDs (used by [Access independent MFA](/cloudflare-one/access-controls/access-settings/independent-mfa/#restrict-authenticators-by-aaguid) to restrict the WebAuthn authenticators users can enroll)

<Render file="gateway/lists" product="cloudflare-one" />

## Edit a list

1. In the **Lists** page, locate the list you want to edit.

2. Select **Edit**. This will allow you to:
   - Edit list name and description by selecting on the three-dots menu to the right of your list's name.
   - Delete the list by selecting the three-dots menu to the right of your list's name.
   - Delete individual entries.
   - Manually add entries to your list.

3. Once you have edited your list, select **Save**.

## Limitations

### List size

Your lists can include up to 1,000 entries for Standard plans and 5,000 for Enterprise plans. An uploaded CSV file must be smaller than 2 MB.

### Wildcard entries

Hostname lists do not support wildcard entries (`*.example.com`). You will need to add domains as exact matches. Adding a wildcard to lists comprised of hostnames will return an error when you save.

### Non-Latin characters

Gateway supports non-Latin characters by converting all domains and hostnames to [Punycode](https://www.rfc-editor.org/rfc/rfc3492.txt). Once you save a list with non-Latin characters, Gateway will display the entry as Punycode.

### Duplicate entries

Lists cannot have duplicate entries. Because domains and hostnames are converted to [Punycode](#non-latin-characters), multiple list entries that convert to the same string will count as duplicates. For example, `éxàmple.com` converts to `xn—xmple-rqa5d.com`, so including both `éxàmple.com` and `xn—xmple-rqa5d.com` in a list will result in a duplicate error.

### URL slashes

<Render file="gateway/url-slash" product="cloudflare-one" />

### Extended email addresses

<Render file="gateway/extended-email" product="cloudflare-one" />

### API rate limit

You can send 600 requests to the [Gateway Lists](/api/resources/zero_trust/subresources/gateway/subresources/lists/) endpoint per minute. If you exceed the rate limit, Cloudflare will block subsequent requests for 120 seconds.

# Packet filtering (Cloudflare Network Firewall) fields

import { Type } from "~/components";

:::note
Some Cloudflare Network Firewall fields are available only to customers who purchased Cloudflare Network Firewall's advanced features. Refer to [Cloudflare Network Firewall plans](/cloudflare-network-firewall/plans/) for more information.
:::

## `cf.colo.name`

`cf.colo.name` <Type text='String' />

The data center that is handling this traffic.

Example value: `sfo06`

---

## `cf.colo.region`

`cf.colo.region` <Type text='String' />

Region of the data center that is handling this traffic.

Example value: `WNAM`

---

## `icmp`

`icmp` <Type text='String' />

The raw ICMP packet as a list of bytes. It should be used in conjunction with the bit_slice function when other structured fields are lacking.

---

## `icmp.type`

`icmp.type` <Type text='Number' />

The [ICMP type](https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#header_type). Only applies to ICMP packets.

Example value: `8`

---

## `icmp.code`

`icmp.code` <Type text='Number' />

The [ICMP code](https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#header_code). Only applies to ICMP packets.

Example value: `2`

---

## `ip`

`ip` <Type text='String' />

The raw IP packet as a list of bytes. It should be used in conjunction with the bit_slice function when other structured fields are lacking.

---

## `ip.dst`

`ip.dst` <Type text='IP address' />

The destination address as specified in the IP packet.

Example value: `192.0.2.2`

---

## `ip.dst.country`

`ip.dst.country` <Type text='String' />

Represents the 2-letter country code associated with the server IP address in [ISO 3166-1 Alpha 2](https://www.iso.org/obp/ui/#search/code/) format.

Example value: `GB`

For more information on the ISO 3166-1 Alpha 2 format, refer to [ISO 3166-1 Alpha 2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) on Wikipedia.

---

## `ip.src.country`

`ip.src.country` <Type text='String' />

Represents the 2-letter country code associated with the client IP address in [ISO 3166-1 Alpha 2](https://www.iso.org/obp/ui/#search/code/) format.

Example value: `GB`

For more information on the ISO 3166-1 Alpha 2 format, refer to [ISO 3166-1 Alpha 2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) on Wikipedia.

For Cloudflare Network Firewall, the `ip.geoip.country` field (which is deprecated) will match on either source or destination address. The `ip.geoip.country` field is still available for new and existing rules, but you should use the `ip.src.country` and/or `ip.dst.country` fields instead.

---

## `ip.hdr_len`

`ip.hdr_len` <Type text='Number' />

The length of the IPv4 header in bytes.

Example value: `5`

---

## `ip.len`

`ip.len` <Type text='Number' />

The length of the packet including the header.

Example value: `60`

---

## `ip.opt.type`

`ip.opt.type` <Type text='Number' />

The first byte of [IP options field](https://en.wikipedia.org/wiki/IPv4#Options), if the options field is set.

Example value: `25`

---

## `ip.proto`

`ip.proto` <Type text='String' />

The transport layer for the packet, if it can be determined.

Example values: `icmp`, `tcp`

---

## `ip.src`

`ip.src` <Type text='IP address' />

The source address of the IP Packet.

---

## `ip.src.country`

`ip.src.country` <Type text='String' />

Represents the 2-letter country code associated with the client IP address in [ISO 3166-1 Alpha 2](https://www.iso.org/obp/ui/#search/code/) format.

Example value: `GB`

For more information on the ISO 3166-1 Alpha 2 format, refer to [ISO 3166-1 Alpha 2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) on Wikipedia.

---

## `ip.ttl`

`ip.ttl` <Type text='Number' />

The time-to-live of the IP Packet.

Example values: `54`

---

## `sip`

`sip` <Type text='Boolean' />

Determines if packets are valid L7 protocol [SIP](https://datatracker.ietf.org/doc/html/rfc2543). Requires UDP packets to operate.

Use a guard clause as shown below to ensure the packet is UDP (wirefilter):

`ip.proto == "udp"`

---

## `tcp`

`tcp` <Type text='String' />

The raw TCP packet as a list of bytes. It should be used in conjunction with the bit_slice function when other structured fields are lacking.

---

## `tcp.flags`

`tcp.flags` <Type text='Number' />

The numeric value of the TCP flags byte.

---

## `tcp.flags.ack`

`tcp.flags.ack` <Type text='Boolean' />

TCP acknowledgment flag.

---

## `tcp.flags.cwr`

`tcp.flags.cwr` <Type text='Boolean' />

TCP congestion window reduced flag.

---

## `tcp.flags.ecn`

`tcp.flags.ecn` <Type text='Boolean' />

TCP ECN-Echo flag.

---

## `tcp.flags.fin`

`tcp.flags.fin` <Type text='Boolean' />

TCP flag indicating this is the last packet from sender.

---

## `tcp.flags.push`

`tcp.flags.push` <Type text='Boolean' />

TCP push flag.

---

## `tcp.flags.reset`

`tcp.flags.reset` <Type text='Boolean' />

TCP reset flag.

---

## `tcp.flags.syn`

`tcp.flags.syn` <Type text='Boolean' />

TCP synchronize flag.

---

## `tcp.flags.urg`

`tcp.flags.urg` <Type text='Boolean' />

TCP urgent flag.

---

## `tcp.srcport`

`tcp.srcport` <Type text='Number' />

Source port number of the IP packet. Only applies to TCP packets.

---

## `tcp.dstport`

`tcp.dstport` <Type text='Number' />

Destination port number of the IP packet. Only applies to TCP packets.

---

## `udp`

`udp` <Type text='String' />

The raw UDP packet as a list of bytes. It should be used in conjunction with the bit_slice function when other structured fields are lacking.

---

## `udp.dstport`

`udp.dstport` <Type text='Number' />

Destination port number of the IP packet. Only applies to UDP packets.

---

## `udp.srcport`

`udp.srcport` <Type text='Number' />

Source port number of the IP packet. Only applies to UDP packets.

---

_GeoIP is the registered trademark of MaxMind, Inc._

# Tags

import { Render } from "~/components";

<Render file="access/tags" product="cloudflare-one" />

# Use IP lists

[IP lists](/waf/tools/lists/custom-lists/#ip-lists) are a part of Cloudflare's custom lists. Custom lists contain one or more items of the same type — IP addresses, hostnames or ASNs — that you can reference in rule expressions.

IP lists are defined at the account level and can be used to match against `ip.src` and `ip.dst` fields. Currently, Cloudflare Network Firewall only supports IPv4 addresses in these lists, not IPv6.

To use this feature:

## 1. Create a [new IP list](/api/resources/rules/subresources/lists/methods/create/).

For example:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rules/lists \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '{
  "name": "iplist",
  "description": "This contains IPs that should be allowed.",
  "kind": "ip"
}'
```

## 2. Add IPs to the list

Next, [create list items](/api/resources/rules/subresources/lists/subresources/items/methods/create/). This will add elements to the current list.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rules/lists/{list_id}/items \
--header "X-Auth-Email: <EMAIL>" \
--header "X-Auth-Key: <API_KEY>" \
--header "Content-Type: application/json" \
--data '[
  {"ip":"10.0.0.1"},
  {"ip":"10.10.0.0/24"}
]'
```

## 3. Use the list in a rule

Finally, add a Cloudflare Network Firewall rule referencing the list into an existing ruleset:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets/{ruleset_id}/rules \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "action": "skip",
  "action_parameters": {
    "ruleset": "current"
  },
  "expression": "ip.src in $iplist",
  "description": "Allowed IPs from iplist",
  "enabled": true
}'
```

## Managed lists

:::note
Available for customers with a Cloudflare Network Firewall Advanced plan.
:::

You can create rules with managed lists. Managed IP Lists are [lists of IP addresses](/waf/tools/lists/managed-lists/#managed-ip-lists) maintained by Cloudflare and updated frequently.

You can access these managed lists when you create rules with either _IP destination address_ or _IP source address_ in the **Field** dropdown, and _is in list_ or _is not in list_ in the **Operator** dropdown.

For example:

| Field                    | Operator     | Value         |
| ------------------------ | ------------ | ------------- |
| _IP destination address_ | _is in list_ | _Anonymizers_ |

## List types

### Threat intelligence

Cloudflare handles millions of HTTP requests each second and blocks billions of cyber threats each day. Cloudflare uses that data to detect malicious actors on the Internet and turns that information into a list of known malicious IP addresses. Cloudflare also integrates with a number of third-party vendors to augment the coverage.

The threat intelligence feed categories are described in [Managed IP Lists](/waf/tools/lists/managed-lists/#managed-ip-lists). All of these lists are compatible with Cloudflare Network Firewall.

### IP lists

Use [IP lists](/waf/tools/lists/custom-lists/#ip-lists) to group services in networks, like web servers, or for lists of known bad IP addresses to make managing good network endpoints easier. IP lists are helpful for users with very expansive firewall rules with many IP lists. By default, you can add up to 10,000 IPs across all lists. Refer to [Use an IP list](/cloudflare-one/traffic-controls/packet-filtering/add-policies/#use-an-ip-list) to check an example of how to use an IP list.

### Geo-blocking

Geo-blocking enables you to selectively allow or block traffic to any country. Refer to [Block a country](/cloudflare-one/traffic-controls/packet-filtering/add-policies/#block-a-country) to check an example of how to block a country.

# Access custom block pages

import { Render } from "~/components";

:::note[Access custom pages]
To customize login, denial, and command-line approval pages with HTML and Liquid, follow [Access custom pages](/cloudflare-one/reusable-components/custom-pages/access-custom-pages/). This article describes legacy block-page templates.
:::

<Render file="access/block-page" product="cloudflare-one" />

# Access custom pages

import { DashButton, Steps } from "~/components";

Access custom pages let you customize the pages visitors see when they sign in, receive an Access policy denial, or approve a command-line login. Templates use HTML and Liquid to display request-specific information.

Custom pages change the presentation, not the Access decision. They do not grant access or replace your authentication and authorization policies.

## Page types

Create a separate template for each page type:

| Type                | Purpose                                                                                                         | Required control                                                         |
| ------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| **Login**           | Sign in to an Access application.                                                                               | Exactly one `<cf-access-authbox></cf-access-authbox>` element.           |
| **Identity denied** | Explain a denial after the visitor signs in. Successful authentication does not guarantee policy authorization. | No interactive control required.                                         |
| **Forbidden**       | Explain a non-identity policy denial, such as a request that fails a service authentication policy.             | No interactive control required.                                         |
| **Interstitial**    | Ask the visitor to approve or deny a command-line login request.                                                | Exactly one `<cf-access-cli-approval></cf-access-cli-approval>` element. |

Access inserts its own sign-in and approval controls into the required elements. Keep these elements visible and outside Liquid conditions or loops. They must render exactly once, not merely appear once in the template source. Do not replace them with your own forms, identity provider links, or approval buttons.

## Create a custom page

<Steps>

1. In Cloudflare One, go to **Reusable components** > **Custom pages**.

   <DashButton url="/?to=/:account/settings/custom_pages" zeroTrust />

2. On the **Access custom pages** card, select **Manage**.
3. Select **Create custom page**.
4. Enter a unique **Name** and select a **Type**. The editor loads a starter template for that type.
5. Edit the HTML and Liquid. Keep the required control for login or interstitial pages.
6. Use **Variables** to check the data available to the selected type. Review **Problems** for validation errors and warnings.
7. Resolve blocking errors, then select **Publish**. If validation is unavailable, select **Retry validation** before publishing.

</Steps>

Publishing saves a reusable template. To display it, assign it to an application or set it as an account default.

:::caution[Test the saved page]
Validation is not a rendered preview. Test the saved template against an isolated application before using it more broadly. Check the sign-in or denial flow, long text, mobile layouts, and any Liquid conditions. A clean **Problems** panel does not prove that every runtime condition renders correctly.
:::

### Login template example

This minimal example displays the application name, sign-in messages, and the Access sign-in control:

```html
<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<title>Sign in to {{ app_name }}</title>
	</head>
	<body>
		<main>
			<h1>Sign in to {{ app_name }}</h1>
			{% if message.text.size > 0 %}
			<p role="status">{{ message.text }}</p>
			{% endif %}
			<cf-access-authbox></cf-access-authbox>
		</main>
	</body>
</html>
```

Keep `message.text` visible so visitors can understand sign-in problems. Do not hardcode identity provider URLs or copy credentials into the template.

## Assign a page to an application

For a self-hosted application:

<Steps>

1. Go to **Access controls** > **Applications** and select the application.

   <DashButton url="/?to=/:account/access/apps" zeroTrust />

2. Open **Additional settings** and find **Custom block pages**. This section also contains the login and interstitial settings.
3. Choose the option for the page you want to replace:

   | Application setting                 | Option                   | Template type       |
   | ----------------------------------- | ------------------------ | ------------------- |
   | **Identity failure block page**     | **Custom page template** | **Identity denied** |
   | **Non-identity failure block page** | **Custom page template** | **Forbidden**       |
   | **Login page**                      | **Custom page**          | **Login**           |
   | **Interstitial page**               | **Custom page**          | **Interstitial**    |

4. Select a saved template from the dropdown. Each dropdown only offers matching page types.
5. Select **Save**, then exercise the application flow. Allow time for configuration changes to propagate before retesting.

</Steps>

To remove an application assignment, select **Cloudflare default** for that page type and save. If an account default exists, Access uses it when there is no application-specific template.

### Account defaults

The **Access custom pages** list offers **Set as default** and **Remove as default** actions. An application-specific assignment takes precedence over the account default for the same type. Without either assignment, Access uses its built-in page.

An account default affects applications that inherit that page type. Test an application-specific assignment before changing an account default.

## Template data

Select **Variables** in the editor for the selected type's variable names and descriptions. Do not assume that every page type has the same data.

| Page type                             | Examples of available data                                                                                                                |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Login**                             | `app_name`, `app_domain`, `org`, `idps`, `has_onetimepin`, and `message`. The `idps` list provides display information, not sign-in URLs. |
| **Identity denied** and **Forbidden** | `app`, and optional `ray_id`, `user`, and `policy_evaluation` data. User information may be absent before sign-in.                        |
| **Interstitial**                      | `app`, `requesting_tool`, and optional `user` data. Approval credentials belong to the Access control, not the template data.             |
| All types                             | `request` connection information and optional `locale`. Request fields may also be absent.                                                |

Guard optional data with Liquid conditions. For example, an identity-denied page can show an email address when available:

```html
<h1>Access denied</h1>
<p>You do not have access to {{ app.name }}.</p>
{% if user.email %}
<p>You are signed in as {{ user.email }}.</p>
{% endif %} {% if ray_id %}
<p>Share this Ray ID with your administrator: {{ ray_id }}</p>
{% endif %}
```

Liquid output is HTML-escaped. Missing variables render as empty text. Policy evaluation data is structured information, not a ready-made explanation of the denial. Choose what to display without exposing sensitive group names, claims, or policy details unnecessarily.

### Template restrictions

Templates run in a restricted Liquid environment:

- Customer JavaScript, inline event handlers, and custom authentication forms are not supported. Access owns the interactive sign-in and approval controls.
- The Liquid tags `include`, `render`, `layout`, `echo`, and `raw`, and the `raw` filter, are unavailable.
- HTML and CSS are sanitized. External assets must comply with the page's Content Security Policy (CSP). Use HTTPS image and font URLs, and do not depend on a third-party script to render the page.
- Parsing, rendering, and memory limits apply. Keep templates small and avoid expensive loops.

If a saved template is unavailable or cannot render safely, Access falls back to a built-in page. This fallback does not change the policy decision.

## Edit and maintain templates

Select a template from **Access custom pages** to edit it. Its type is fixed after creation. Review **Problems**, then select **Publish** to save changes.

A template can be used by multiple applications. Publishing an edit changes the shared template, so test with a separate template when you need to avoid affecting existing assignments. Before deleting a template, replace its application assignments and remove it as an account default.

Keep a copy of your HTML and Liquid outside the dashboard. The editor does not provide a rendered preview or a **Download** action.

# Access login page

import { Render } from "~/components";

You can customize the login page that is displayed to end users when they go to an Access application.

<Render file="access/login-page" product="cloudflare-one" />

# App Launcher customization

import { Render } from "~/components";

:::note

Only available on Pay-as-you-go and Enterprise plans.
:::

You can display your own branding, messages, and links to users when they open the [Access App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/).

To customize the App Launcher appearance:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Custom pages**.
2. Find the **App Launcher customization** setting and select **Manage**.
3. Give the App Launcher the look and feel of your organization by adding:
   - Your organization's name
   - A logo
   - A preferred background color for the header
   - A preferred background color for the page
   - A custom footer with links to your organization's help desk or other internal resources.

:::note

We recommend lighter background colors because the font defaults to black.
:::

4. Next, customize the landing page that users will see when they login to the App Launcher. Available properties include:
   - A custom title
   - A custom subtitle
   - An image
   - A preferred color for the **Log in** button
   - A preferred color for the **Log in** button text

   All of the properties configured in Step 3 will also apply to the landing page.

5. Once you are satisfied with your customization, select **Save**.

The App Launcher screens are now updated. To view your changes, select **Preview**.

# Block page

import { Render, Tabs, TabItem } from "~/components";

When Gateway blocks traffic with a [DNS](/cloudflare-one/traffic-controls/dns-policies/#block) or [HTTP Block policy](/cloudflare-one/traffic-controls/http-policies/#block), you can configure a block page to display in your users' browsers. You can provide a descriptive reason for blocking traffic and contact information, or you can redirect your users' browsers to another page. You can apply these customizations globally for every Block policy, or override the settings on a per-policy basis.

## Prerequisites

In order to display the block page as the URL of the blocked domain, your organization's devices must have a [Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/) installed. Enterprise users can also [deploy their own root CA certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/). If you do not install a certificate, the block page [will not display correctly](#certificate-error).

## Configure the block page

Gateway will display a global block page in the browser of any user whose traffic is blocked. By default, Gateway will display the block page for any DNS Block policies you turn it on for and all HTTP Block policies. You can [turn on or override the global setting](#configure-policy-block-behavior) on a per-policy basis.

To configure the global block page:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Reusable components** > **Custom pages**.
2. Under **Account Gateway block page**, Gateway will display the current block page setting. Select **Manage**.
3. Choose whether to use the [default Gateway block page](#use-the-default-block-page), a [URL redirect](#redirect-to-a-block-page), or a [custom Gateway block page](#customize-the-block-page).
4. Select **Save**.

### Use the default block page

When you choose **Default Gateway block page**, Gateway will display a [block page hosted by Cloudflare](https://blocked.teams.cloudflare.com/). This is the default option for all traffic blocked by Gateway.

### Redirect to a block page

Instead of displaying the Cloudflare block page, you can configure Gateway to return a `307` (Temporary Redirect) HTTP response code and redirect to a custom URL.

To redirect users to a non-Cloudflare block page:

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Reusable components** > **Custom pages**.
2. Under **Account Gateway block page**, select **Manage**.
3. Choose **URL redirect**.
4. Enter the URL you want to redirect blocked traffic to.
5. (Optional) Turn on **Send policy context** to send [additional policy context](#policy-context) to the redirected URL.
6. Select **Save**.

Gateway will now redirect users to a custom page when user traffic matches a Block policy with the block page configured.

To create an HTTP policy to redirect URLs, refer to the [Redirect action](/cloudflare-one/traffic-controls/http-policies/#redirect).

#### Policy context

<Render file="gateway/policy-context" product="cloudflare-one" />

#### Redirect precedence

Paths and queries in the redirect URL take precedence over the original URL. When you turn on **Send policy context**, Gateway will append context to the end of the redirected URL. For example, if the original URL is `example.com/path/to/page?querystring=X&k=1` and the redirect URL is `cloudflare.com/redirect-path?querystring=Y`, Gateway will redirect requests to:

```txt ins="&cf_user_email=user@example.com"
cloudflare.com/redirect-path?querystring=Y&cf_user_email=user@example.com
```

### Customize the block page

<Render file="gateway/customize-block-page" product="cloudflare-one" />

#### Add a logo image

<Render file="gateway/add-logo-image" product="cloudflare-one" />

#### Allow users to email an administrator

You can add a Mailto link to your custom block page, which allows users to directly email you about the blocked site. When users select **Contact your Administrator** on your block page, an email template opens with the email address and subject line you configure, as well as the following diagnostic information:

| Field        | Description                                                                                                                                                                                                                 |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Site URL     | The URL of the blocked page.                                                                                                                                                                                                |
| Rule ID      | The ID of the Gateway policy that blocked the page.                                                                                                                                                                         |
| Source IP    | The public source IP of the user device.                                                                                                                                                                                    |
| Account ID   | The Cloudflare account associated with the block policy.                                                                                                                                                                    |
| User ID      | The ID of the user who visited the page. Currently, User IDs are not surfaced in the dashboard and can only be viewed by calling the [API](/api/resources/zero_trust/subresources/access/subresources/users/methods/list/). |
| Device ID    | The ID of the device that visited the page. This is generated by the Cloudflare One Client.                                                                                                                                 |
| Block Reason | Your policy-specific block message.                                                                                                                                                                                         |

## Configure policy block behavior

For DNS Block policies, you will need to turn on the block page for each policy you want to display it. For HTTP Block policies, Gateway automatically displays your global block page setting by default. You can override your global block page setting for both policy types within each policy's settings.

To turn on the block page or override your global block page setting for an individual policy:

<Tabs>

<TabItem label="DNS policy">

<Render
	file="gateway/add-block-page"
	product="cloudflare-one"
	params={{
		firewallPolicyPath:
			"**Traffic policies** > **Firewall policies** > **DNS**",
		blockBehaviorAction: "turn on",
	}}
/>

</TabItem>
<TabItem label="HTTP policy">

<Render
	file="gateway/add-block-page"
	product="cloudflare-one"
	params={{
		firewallPolicyPath:
			"**Traffic policies** > **Firewall policies** > **HTTP**",
		blockBehaviorAction: "go to",
	}}
/>

</TabItem>

</Tabs>

## Limitations

### Certificate error

If your users receive a security risk warning in their browser when visiting a blocked page, check that you have correctly [installed a certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/) on their devices. If a certificate is not installed or the installed certificate is invalid or expired, your user's browser may:

- Display an **HTTP Response Code: 526** error page, indicating an insecure upstream.
- Close the connection and fail to display any pages.

For more information on fixing certificate issues, refer to [Troubleshooting](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#browser-and-certificate-issues).

### Incompatible DNS record types

<Render file="gateway/block-page-dns-records" product="cloudflare-one" />

### Third-party filtering conflict

<Render file="gateway/third-party-warning" product="cloudflare-one" />

### Data center and IP address matching

If an HTTP request that matches a block policy does not arrive at the same Cloudflare data center as its DNS query, Gateway will display the default block page instead of your custom block page.

This applies to DNS queries sent to any Gateway resolver endpoint, including those over IPv4, IPv6, and encrypted protocols like DoH (DNS over HTTPS) and DoT (DNS over TLS). If a DNS query is routed to a different Cloudflare data center than the corresponding HTTP request (for example, if DoH traffic is sent outside the WARP tunnel), Gateway cannot correlate the two requests and will display the default block page instead of your custom block page.

If the HTTP request comes from a different IP address than the DNS request, Gateway may not display the rule ID, custom message, or other fields on the block page. This can happen when a recursive DNS resolver's source IP address differs from the user device's IP address.

### Dual-stack networks

On dual-stack networks, the source IP address of the DNS query and the block page connection may differ. The DNS query may go over IPv4 while the browser connects to the block page over IPv6, or the other way around. When this happens, the block page may fail to load. It may also display without policy context (rule ID, block reason, or custom redirect parameters).

To resolve this issue, configure a [dedicated DNS resolver IP address](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#dedicated-dns-resolver-ip) for your DNS location. With dedicated IP addresses, the block server can identify the account based on the destination IP address regardless of the source address.

# Custom pages

import { DirectoryListing } from "~/components"

<DirectoryListing />

# Access integrations

The following device posture checks do not require the Cloudflare One Client and can only be used in [Cloudflare Access policies](/cloudflare-one/access-controls/policies/). They cannot be used in Gateway network policies.

## Supported operating systems

| Device posture check                                                                            | macOS | Windows | Linux | iOS | Android/ChromeOS |
| ----------------------------------------------------------------------------------------------- | ----- | ------- | ----- | --- | ---------------- |
| [Microsoft Entra ID Conditional Access](/cloudflare-one/tutorials/entra-id-conditional-access/) | ✅    | ✅      | ❌    | ❌  | ❌               |
| [Mutual TLS](/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/)    | ✅    | ✅      | ✅    | ✅  | ✅               |

# Posture checks

import { Render } from "~/components";

With Cloudflare Zero Trust, you can configure Zero Trust policies that rely on additional signals from the Cloudflare One Client or from third-party endpoint security providers. When device posture checks are configured, users can only connect to a protected application or network resource if they have a managed or healthy device.

## 1. Enable device posture checks

Setup instructions and requirements vary depending on the device posture attribute. Refer to the links below to view the setup guide for your provider.

- [Cloudflare One Client checks](/cloudflare-one/reusable-components/posture-checks/client-checks/) are performed by the Cloudflare One Client.
- [Service-to-service checks](/cloudflare-one/integrations/service-providers/) are performed by third-party device posture providers.
- [Access integration checks](/cloudflare-one/reusable-components/posture-checks/access-integrations/) are only configurable for Access applications. These attributes cannot be used in Gateway policies.

## 2. Verify device posture checks

Before integrating a device posture check in a Gateway or Access policy, verify that the Pass/Fail results match your expectations. To view the latest test results for a specific device:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**.
2. Select the device.
3. Select **View details**.
4. Select the **Posture checks** tab.

## 3. Build a device posture policy

You can now use your device posture check in an [Access policy](/cloudflare-one/access-controls/policies/) or a Gateway [network](/cloudflare-one/traffic-controls/network-policies/common-policies/#enforce-device-posture) or [HTTP](/cloudflare-one/traffic-controls/http-policies/common-policies/#check-device-posture) policy. In Access, the enabled device posture attributes will appear in the list of available [selectors](/cloudflare-one/access-controls/policies/#selectors). In Gateway, the attributes will appear when you choose the [Passed Device Posture Check](/cloudflare-one/traffic-controls/network-policies/#device-posture) selector.

:::caution[Gateway policy limitation]

Gateway does not support device posture checks for the [Tanium Access integration](/cloudflare-one/reusable-components/posture-checks/client-checks/tanium/).
:::

## 4. Ensure traffic is going through the Cloudflare One Client

[Cloudflare One Client](/cloudflare-one/reusable-components/posture-checks/client-checks/) and [service-to-service](/cloudflare-one/integrations/service-providers/) posture checks rely on traffic going through the Cloudflare One Client to detect posture information for a device. In your [Split Tunnel configuration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/), ensure that the following domains are included in the Cloudflare One Client:

- The IdP used to authenticate to Cloudflare Zero Trust if posture check is part of an Access policy.
- `<your-team-name>.cloudflareaccess.com` if posture check is part of an Access policy.
- The application protected by the Access or Gateway policy.

## Policy enforcement rate

Access detects changes in device posture at the same rate as the [polling frequency](#polling-frequency) configured for the posture check.

Because Gateway evaluates network and HTTP policies on every request, it maintains a local cache of posture results that is only updated every five minutes. Therefore, Gateway policies are subject to an additional five-minute delay. For example, if you set your polling frequency to 10 minutes, it may take up to 15 minutes for Gateway to detect posture changes on a device.

```mermaid
flowchart LR
accTitle: Device posture policy enforcement
A[Device] --schedule--> B[Cloudflare One Client]--> C((Cloudflare)) --> D[Access policy]
C --5 min--> E[Cache] --> F[Gateway policy]
A --> G[Service provider] --interval--> C
```

:::caution

Gateway does not terminate an [active session](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/#configure-warp-sessions-in-gateway) even if a subsequent posture check fails during that session. Gateway only evaluates posture checks at the beginning of a session, and ongoing sessions will remain uninterrupted.

For example, if you establish an SSH session based on a successful posture check, but a posture requirement fails after the session has started, the session will remain active.

:::

### Expiration

By default, the posture result on Cloudflare remains valid until it is overwritten by new data. You can specify an `expiration` time using our [API](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/update/). Cloudflare recommends setting the expiration to be at least double the [polling frequency](#polling-frequency). For example, if the posture check polling frequency is set to one hour, its expiration time should be set to two hours or greater.

### Polling frequency

#### Cloudflare One Client checks

By default, the Cloudflare One Client polls the device for status changes every five minutes. To modify the polling frequency, use the API to update the [`schedule`](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/update/) parameter.

#### Service provider checks

When setting up a [service-to-service integration](/cloudflare-one/integrations/service-providers/), you will choose a polling frequency to determine how often Cloudflare will query the third-party API. To set the polling frequency via the API, use the [`interval`](/api/resources/zero_trust/subresources/devices/subresources/posture/subresources/integrations/methods/edit/) parameter.

# Antivirus

import { Render } from "~/components";

The Antivirus device posture attribute checks if any antivirus software is installed and active on a device. The Cloudflare One Client queries the [Windows Security Center API](https://learn.microsoft.com/en-us/windows/win32/api/iwscapi/ne-iwscapi-wsc_security_product_state) to determine the state of registered security products. For the posture check to pass, Windows Security Center must report that a security product is turned on and up to date.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Enable the antivirus check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.
2. Go to **Cloudflare One Client checks** and select **Add a check**.
3. Select **Antivirus**.
4. Enter a descriptive name for the check.
5. Select your operating system.
6. (Optional) Set the maximum number of days allowed since the last antivirus signature update. If the device exceeds this limit (for example, you set 30 days but it has been 31 days since the last update), the device will fail the posture check.
7. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the antivirus check is returning the expected results.

## Validate antivirus status

You can use the following commands to validate if the posture check is working as expected.

### Windows

1. Open a PowerShell window.
2. List all installed antivirus products registered with Windows Security Center:

	```powershell
	Get-WmiObject -Namespace "root\SecurityCenter2" -ClassName "AntiVirusProduct"
	```

	```powershell output
	<redacted>
	displayName              : Windows Defender
	instanceGuid             : {00000000-0000-0000-0000-000000000000}
	pathToSignedProductExe   : windowsdefender://
	pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
	productState             : 397568
	timestamp                : Fri, 09 Jan 2026 12:00:00 GMT
	PSComputerName           : ENDPOINT-01
	```

3. Microsoft does not support decoding the `productState` from the `SecurityCenter2` namespace. To verify that an antivirus product is active, open the [Windows Security app](https://support.microsoft.com/en-us/windows/stay-protected-with-the-windows-security-app-2ae0363d-0ada-c064-8b56-6a39afb6a963). The **Virus & threat protection** panel should say `No action needed` with a green checkmark.

	To determine which antivirus product is running, select **Virus & threat protection** > **Manage providers**. You will see the name of the antivirus product (for example, `Windows Defender Antivirus`) and its current state.

4. If you configured a maximum antivirus signature age in your posture check, compare the `timestamp` in the PowerShell output against the current system time. If the difference exceeds the configured number of days, the posture check will fail.

# Application check

import { Render } from "~/components";

The Application Check device posture attribute checks that a specific application process is running on a device. You can create multiple application checks for each operating system you need to run it on, or if you need to check for multiple applications.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Configure an application check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

2. Go to **Cloudflare One Client checks** and select **Add a check**.

3. Select **Application Check**.

4. You will be prompted for the following information:
   1. **Name**: Enter a unique name for this device posture check.
   2. **Operating system**: Select your operating system.
   3. **Application path**: Enter the file path for the executable that will be running (for example, `C:\Program Files\myfolder\myfile.exe`).

		<Render file="posture/env-vars-in-file-paths" product="cloudflare-one" />

   :::note
   - Be sure to enter the binary file path, not the application launch path. When checking for an application on macOS, a common mistake is to enter `/Applications/ApplicationName.app`. This will not work as `ApplicationName.app` is a folder. The executable file that will be running is located within the folder, for example `ApplicationName.app/Contents/MacOS/ApplicationName`.
   - Some applications change their file path after an update. Ensure that the application is always in a stable location or use environment variables.
	 :::

5. **Signing certificate thumbprint (recommended)**: Enter the [thumbprint of the publishing certificate](#determine-the-signing-thumbprint) used to sign the binary. Adding this information will enable the check to ensure that the application was signed by the expected software developer.
6. **SHA-256 (optional)**: Enter the [SHA-256 value](#determine-the-sha-256-value) of the binary. This is used to ensure the integrity of the binary file on the device.

7. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the application check is returning the expected results.

## Determine the signing thumbprint

The process to determine the signing thumbprint of an application varies depending on the operating system. This is how you would look up the signing thumbprint of the Cloudflare One Client application on macOS and Windows.

:::note

When setting up new device posture checks, we recommend first testing them without setting certificate thumbprint or SHA256 checksum values.

:::

### macOS

1. Create a directory.

   ```bash
   ~/Desktop $ mkdir tmp

   ~/Desktop $ cd tmp
   ```

2. Run the following command to extract certificates for the Cloudflare One Client application:

   ```sh
   ~/Desktop/tmp $ codesign -d --extract-certificates "/Applications/Cloudflare WARP.app/Contents/Resources/CloudflareWARP"
   Executable=/Applications/Cloudflare WARP.app/Contents/Resources/CloudflareWARP
   ```

3. Next, run the following command to extract the SHA1 thumbprint:

   ```sh
   ~/Desktop/tmp $ openssl x509 -inform DER -in codesign0 -fingerprint -sha1 -noout | tr -d :
   SHA1 Fingerprint=FE2C359D79D4CEAE6BDF7EFB507326C6B4E2436E
   ```

### Windows

1. Open a PowerShell window.
2. Use the `Get-AuthenticodeSignature` command to find the thumbprint. For example:

   ```powershell
   Get-AuthenticodeSignature -FilePath c:\myfile.exe
   ```

## Determine the SHA-256 value

The SHA-256 value almost always changes between versions of a file/application.

### macOS

1. Open a Terminal window.
2. Use the `shasum` command to find the SHA256 value of the file. For example:

   ```sh
   shasum -a 256 myfile
   ```

### Windows

1. Open a PowerShell window.
2. Use the `get-filehash` command to find the SHA256 value of the file. For example:

   ```powershell
   get-filehash -path "C:\myfile.exe" -Algorithm SHA256 | format-list
   ```

## How WARP checks for an application

Learn how the Cloudflare One Client determines if an application is running on various systems.

### macOS

To get the list of active processes, run the following command:

```sh
ps -eo comm | xargs -I {} which "{}" | sort | uniq | xargs -I {} realpath "{}"
```

The application path must appear in the output for the check to pass.

### Linux

The Cloudflare One Client gets the list of running binaries by following the soft links in `/proc/<pid>/exe`. To view all active processes and their soft links:

```sh
ps -eo pid | awk '{print "/proc/"$1"/exe"}' | xargs readlink -f | awk '{print $1}' | sort | uniq
```

The application path must appear in the `/proc/<pid>/exe` output for the check to pass.

### Windows

To get the list of active processes, run the following command:

```powershell
Get-Process | Select-Object ProcessName, Path | Format-Table -AutoSize
```

The application path must appear in the output for the check to pass.

# Carbon Black

import { Render } from "~/components";

Cloudflare One can check if [Carbon Black](https://www.carbonblack.com/) is running on a device to determine if a request should be allowed to reach a protected resource.

## Prerequisites

- Carbon Black agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Configure the Carbon Black check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

2. Go to **Cloudflare One Client checks** and select **Add a check**.

3. Select **Carbon Black**.

4. You will be prompted for the following information:
   1. **Name**: Enter a unique name for this device posture check.
   2. **Operating system**: Select your operating system. You will need to configure one posture check per operating system (macOS and Windows currently supported).
   3. **Application Path**: Enter the full path to the Carbon Black process to be checked (for example, `c:\program files\CarbonBlack\CarbonBlack.exe`).
   4. **Signing certificate thumbprint (recommended)**: Enter the thumbprint of the publishing certificate used to sign the binary. This proves the binary came from Carbon Black and is the recommended way to validate the process.
   5. **SHA-256 (optional)**: Enter a SHA-256 value. This is used to validate the SHA256 signature of the binary and ensures the integrity of the binary file on the device. Note: do not fill out this field unless you strictly control updates to Carbon Black, as this will change between versions.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the Carbon Black check is returning the expected results.

# Client certificate

import { Details, Render, TabItem, Tabs } from "~/components";

The Client Certificate device posture attribute checks if the device has a valid client certificate signed by a trusted certificate. The trusted certificate is uploaded to Cloudflare and specified as part of the posture check rule. The client certificate posture check can be used in Gateway and Access policies to ensure that the user is connecting from a managed device.

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                 | All plans                                                     |

| System   | Availability | Minimum client version<sup>1</sup> |
| -------- | ------------ | -------------------------------- |
| Windows  | ✅           | 2024.6.415.0                     |
| macOS    | ✅           | 2024.6.416.0                     |
| Linux    | ✅           | 2024.6.497.0                     |
| iOS      | ❌           |                                  |
| Android  | ❌           |                                  |
| ChromeOS | ❌           |                                  |

<sup>1</sup> Client certificate checks that ran on an earlier Cloudflare One Client version will continue to work. To configure a new certificate check, update the Cloudflare One Client to the versions listed above.
</Details>

## Prerequisites

- A CA that issues client certificates for your devices. The Cloudflare One Client does not evaluate the certificate trust chain; this needs to be the issuing certificate.

  :::note[Upload the signing certificate that issued the client certificate]

  When uploading a certificate to use in posture checks, Cloudflare does not differentiate between root and intermediate certificates. You must upload the actual signing certificate - the one that directly signed the client certificate.

  If you upload a different certificate, even if it exists higher up in the trust chain (for example, the root that issued the signing certificate), the posture check will fail.

  :::

- Cloudflare One Client is [deployed](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on the device.
- A client certificate is [installed and trusted](#configure-the-client-certificate-check) on the device.

:::note

To generate a sample root CA for testing, refer to [Generate mTLS certificates](/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/#generate-mtls-certificates).
:::

## Configure the client certificate check

1. <Render
   	file="upload-mtls-cert"
   	product="cloudflare-one"
   	params={{
   		key: "The private key is only required if you are using this custom certificate for Gateway HTTPS inspection.",
   		cert: "signing certificate",
   	}}
   />

2. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

3. Go to **Cloudflare One Client checks** and select **Add a check**.

4. Select **Client certificate**.

5. You will be prompted for the following information:
   1. **Name**: Enter a unique name for this device posture check.
   2. **Operating system**: Select your operating system.
   3. **OS locations**: Specify the location(s) where the client certificate is installed.
      <Details header="Windows">
      	- Local machine trust store
        - User trust store
      </Details>
      <Details header="macOS">

      - System keychain

      </Details>
      <Details header="Linux">
      	- NSSDB (`/etc/pki/nssdb`) - To search a custom location, enter the
      	absolute file path(s) to the certificate and private key (for example
      	`/usr/local/mycompany/certs/client.pem` and
      	`/usr/local/mycompany/certs/client_key.pem`). The certificate and
      	private key must be in `PEM` format. They can either be in two different
      	files or the same file.
      </Details>
   4. **Certificate ID**: Enter the UUID of the signing certificate.
   5. **Common name**: (Optional) To check for a Common Name (CN) on the client certificate, enter a string with optional `${serial_number}` and `${hostname}` variables (for example, `${serial_number}_mycompany`). The Cloudflare One Client will search for an exact, case-insensitive match. If you do not specify a common name, the Cloudflare One Client will ignore the common name field on the certificate.
   6. **Check for Extended Key Usage**: (Optional) Check whether the client certificate has one or more attributes set. Supported values are **Client authentication** (`1.3.6.1.5.5.7.3.2`) and/or **Email** (`1.3.6.1.5.5.7.3.4`).
   7. **Check for private key**: (Recommended) When enabled, WARP checks that the device has a private key associated with the client certificate.
   8. **Subject Alternative Name**: (Optional) To check for a Subject Alternative Name (SAN) on the client certificate, enter a string with optional `${serial_number}` and `${hostname}` variables (for example, `${serial_number}_mycompany`). The Cloudflare One Client matches supported SAN types exactly, ignoring case. The client certificate posture check does not match `otherName` SAN entries. You can add multiple SAN values to the posture check. One value with a supported SAN type must match.

6. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the client certificate check is returning the expected results.

## Troubleshooting

You can use the following commands to check if a client certificate is properly installed and trusted on the device.

<Tabs>

<TabItem label="Windows">

1. Open a PowerShell window.
2. To search the local machine trust store for a certificate with a specific common name, run the following command:

```powershell
Get-ChildItem Cert:\LocalMachine\My\ | where{$_.Subject -like "*<COMMON_NAME>*"}
```

3. To search the user trust store for a certificate with a specific common name, run the following command:

```powershell
Get-ChildItem Cert:\CurrentUser\My\ | where{$_.Subject -like "*<COMMON_NAME>*"}
```

</TabItem>

<TabItem label="macOS">

1. Open Terminal.
2. To search System Keychain for a certificate with a specific common name, run the following command:

```sh
/usr/bin/security find-certificate -c "<COMMON_NAME>" -p /Library/Keychains/System.keychain
```

</TabItem>

<TabItem label="Linux">

1. Open Terminal.
2. To list all client certificates in NSSDB, run the following command:

```sh
certutil -L -d /etc/pki/nssdb
```

```sh output
Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

meow                                                         CTu,Cu,Cu
noPrivateKey                                                 CT,,

```

3. Open your desired certificate using its certificate nickname. The common name will appear in the line `Subject: "CN=123456.mycompany"`.

```sh
certutil -L -d /etc/pki/nssdb -n meow
```

```sh output
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 236 (0xec)
        Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption
        Issuer: "CN=123456.mycompany"
        Validity:
            Not Before: Tue Jul 02 17:20:40 2024
            Not After : Sun Jul 02 17:20:40 2034
        Subject: "CN=123456.mycompany"
        Subject Public Key Info:
            Public Key Algorithm: PKCS #1 RSA Encryption
            RSA Public Key:
                Modulus:
                    <redacted>
                Exponent: 65537 (0x10001)
    Signature Algorithm: PKCS #1 SHA-256 With RSA Encryption
    Signature:
        <redacted>
    Fingerprint (SHA-256):
        <redacted>
    Fingerprint (SHA1):
        <redacted>

    Mozilla-CA-Policy: false (attribute missing)
    Certificate Trust Flags:
        SSL Flags:
            Valid CA
            Trusted CA
            User
            Trusted Client CA
        Email Flags:
            Valid CA
            Trusted CA
            User
        Object Signing Flags:
            Valid CA
            Trusted CA
            User
```

</TabItem>

</Tabs>

For the posture check to pass, a certificate must appear in the output that validates against the uploaded signing certificate.

# Device serial numbers

import { Render } from "~/components";

Cloudflare One allows you to build Zero Trust rules based on device serial numbers. You can create these rules so that access to applications is granted only to users connecting from company devices.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Create a list of serial numbers

To create rules based on device serial numbers, you first need to create a [Gateway List](/cloudflare-one/reusable-components/lists/) of numbers.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Lists**.

2. Select **Create manual list** or **Upload CSV**. For larger teams, we recommend uploading a CSV or using Cloudflare's [API endpoint](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/).

3. Give your list a descriptive name, as this name will appear when configuring your policies.

4. Set **List Type** to _Serial numbers_.

5. Enter the serial numbers of the devices your team manages, or upload your CSV file.

6. Select **Save**.

You can now create an [Access policy](/cloudflare-one/access-controls/policies/) or a Gateway [network policy](/cloudflare-one/traffic-controls/network-policies/common-policies/#enforce-device-posture) that checks if the device presents a serial number on your list. In Access, the serial number check will appear as a _Device Posture - Serial Number List_ selector. In Gateway, your serial number list will appear in the **Value** dropdown when you choose the [Passed Device Posture Check](/cloudflare-one/traffic-controls/network-policies/#device-posture) selector.

## Validate the serial number

You can use the following commands to check the serial number of your device. The results can help you validate if the posture check is working as expected.

### macOS

1. Open a terminal window.
2. Use the `system_profiler` command to check for the value of `SPHardwareDataType` and retrieve the serial number.

   ```sh
   system_profiler SPHardwareDataType | grep 'Serial Number'
   ```

### Windows

1. Open a PowerShell window.
2. Use the `Get-CimInstance` command to get the SerialNumber property of the `Win32_BIOS` class.

   ```powershell
   Get-CimInstance Win32_BIOS
   ```

### Linux

1. Open a Terminal Window
2. Use the `dmidecode` command to get the version property `system-serial-number`.

   ```sh
   sudo dmidecode -s system-serial-number
   ```

### iOS, Android and ChromeOS

Serial number checks are not supported on mobile devices. You can identify mobile devices by a [unique client ID](/cloudflare-one/reusable-components/posture-checks/client-checks/device-uuid) instead of by serial number.

# Device UUID

import { Render } from "~/components";

Cloudflare One allows you to build Zero Trust rules based on device UUIDs supplied in an MDM file. You can create these rules so that access to applications is granted only to users connecting from company devices.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## 1. Assign UUIDs to devices

You will need to use a [managed deployment tool](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) to assign UUIDs. It is not possible to assign them manually.

1. Generate a unique identifier for each corporate device. For best practices on choosing UUIDs, refer to the [Android documentation](https://developer.android.com/training/articles/user-data-ids#best-practices-android-identifiers).

2. Enter the UUIDs into your MDM configuration file using the [`unique_client_id` key](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#unique_client_id).

## 2. Create a list of UUIDs

To create rules based on device UUIDs, you first need to create a [Gateway List](/cloudflare-one/reusable-components/lists/) of UUIDs.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Lists**.

2. Select **Create manual list** or **Upload CSV**. For larger teams, we recommend uploading a CSV or using Cloudflare's [API endpoint](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/).

3. Give your list a descriptive name, as this name will appear when configuring your policies.

4. Set **List Type** to _Device IDs_.

5. Enter the UUIDs of the devices your team manages, or upload your CSV file.

6. Select **Save**.

:::note

Hyphens are automatically stripped from UUIDs. For example, the posture check will match `123e4567-e89b-12d3-a456-426614174000` to `123e4567e89b12d3a456426614174000`.
:::

## 3. Enable the posture check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

2. Go to **Cloudflare One Client checks** and select **Add a check**.

3. Select **Unique Client ID**.

4. You will be prompted for the following information:
   - **Name**: Enter a unique name for this device posture check.
   - **Operating system**: Select the operating system of the device.
   - **List**: Select your [list of UUIDs](#2-create-a-list-of-uuids).

5. Select **Save**.

6. [Verify](/cloudflare-one/reusable-components/posture-checks/#2-verify-device-posture-checks) that the posture check is returning the expected results.

You can now create an Access or Gateway device posture policy that checks if the device presents a UUID on your list.

# Disk encryption

import { Render } from "~/components";

The Disk Encryption device posture attribute ensures that disks are encrypted on a device.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Enable the disk encryption check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.
2. Go to **Cloudflare One Client checks** and select **Add a check**.
3. Select **Disk Encryption**.
4. Enter a descriptive name for the check.
5. Select your operating system.
6. Either enable disk encryption for all volumes, or input the specific volume(s) you want to check for encryption (for example, `C`).
7. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the disk encryption check is returning the expected results.

## Validate disk encryption status

The following commands will return the disk encryption status on various operating systems. The results can help you validate if the posture check is working as expected.

### macOS

1. Open a terminal window.

2. Run the `/usr/sbin/system_profiler SPStorageDataType` command to return a list of drivers on the system and note the value of **Mount Point**.

   ```sh
   /usr/sbin/system_profiler SPStorageDataType
   ```

   ```sh output
   Storage:

      Data:

        Free: 428.52 GB (428,519,702,528 bytes)
        Capacity: 494.38 GB (494,384,795,648 bytes)
        Mount Point: /System/Volumes/Data
   ```

3. Run the `diskutil info` command for a specific **Mount Point** and look for the value returned for **FileVault**. It must show **Yes** for the disk to be considered encrypted.

   ```sh
   diskutil info /System/Volumes/Data | grep FileVault
   ```

   ```sh output
    FileVault:                 Yes
   ```

### Windows

1. Open a PowerShell window.
2. Run the `Get-BitLockerVolume` command to list all volumes detected on the system.
3. **Protection Status** must be set to **On** for the disk to be considered encrypted.

### Linux

List all hard drives on the system:

```sh
lsblk
```

```sh output
NAME                        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
nvme0n1                     259:0    0 476.9G  0 disk
├─nvme0n1p1                 259:1    0   512M  0 part  /boot/efi
├─nvme0n1p2                 259:2    0   488M  0 part  /boot
└─nvme0n1p3                 259:3    0   476G  0 part
  └─nvme0n1p3_crypt         253:0    0 475.9G  0 crypt
    ├─my--vg-root   253:1            0 474.9G  0 lvm   /
    └─my--vg-swap_1 253:2            0   976M  0 lvm   [SWAP]
```

On Linux, encryption is reported per mounted partition, not physical drive. In the example above, the root and swap partitions are considered encrypted because they are located within a `crypt` container. The `/boot` and `/boot/efi` partitions remain unencrypted.

### iOS, Android and ChromeOS

These platforms are always encrypted and so no disk encryption check is supported.

# Domain joined

import { Render } from "~/components";

The Domain Joined device posture attribute ensures that a user is a member of a specific Windows Active Directory domain.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Enable the Domain Joined check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.
2. Go to **Cloudflare One Client checks** and select **Add a check**.
3. Select **Domain Joined**.
4. Enter a descriptive name for the check.
5. Select your operating system.
6. Enter the domain you want to check for, such as `example.com`.
   :::note
   The **Domain** field is case-sensitive. If your domain is `example.com`, entering `Example.com` will fail the posture check.
   :::
7. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the Domain Joined check is returning the expected results.

## Validate the domain value

To check the domain value on your Windows device:

1. Open a PowerShell window.
2. Run the following command:

   ```powershell
   (Get-WmiObject Win32_ComputerSystem).Domain
   ```

The command will return the Active Directory domain to which your device belongs.

# File check

import { Render } from "~/components";

The File Check device posture attribute checks for the presence of a file on a device. You can create multiple file checks for each operating system you need to run it on, or if you need to check for multiple files.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Configure a file check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

2. Go to **Cloudflare One Client checks** and select **Add a check**.

3. Select **File Check**.

4. You will be prompted for the following information:
   1. **Name**: Enter a unique name for this device posture check.
   2. **Operating system**: Select your operating system.
   3. **File Path**: Enter a file path (for example, `c:\my folder\myfile.exe`).

		<Render file="posture/env-vars-in-file-paths" product="cloudflare-one" />
   4. **Signing certificate thumbprint (recommended)**: Enter the [thumbprint](/cloudflare-one/reusable-components/posture-checks/client-checks/application-check/#determine-the-signing-thumbprint) of the publishing certificate used to sign the file. Adding this information will enable the check to ensure that the file was signed by the expected software developer.
   5. **SHA-256 (optional)**: Enter the [SHA-256 value](/cloudflare-one/reusable-components/posture-checks/client-checks/application-check/#determine-the-sha-256-value) of the file. This is used to ensure the integrity of the file on the device.

5. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the file check is returning the expected results.

# Firewall

import { Render } from "~/components";

The Firewall device posture attribute ensures that a firewall is running on a device.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Enable the firewall check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.
2. Go to **Cloudflare One Client checks** and select **Add a check**.
3. Select **Firewall**.
4. Enter a descriptive name for the check.
5. Select your operating system.
6. Configure **Enable firewall check** based on your desired security policy:
	- **Enabled**: (Recommended) The posture check passes only if the firewall is running.
	- **Disabled**: The posture check passes only if the firewall is turned off.

	:::note
	The **Enable firewall check** toggle does not turn the posture check on or off; rather, the toggle determines whether the Cloudflare One Client looks for an active or inactive firewall.
	:::
7. Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the firewall check is returning the expected results.

## Validate firewall status

Operating systems determine firewall configuration in various ways. Follow the steps below to understand how the Cloudflare One Client determines if the firewall is enabled.

### On macOS

macOS has two firewalls: an application-based firewall and a port-based firewall. The Cloudflare One Client will report a firewall is enabled if either firewall is running.

#### Application-based firewall

1. Open **System Settings** and go to **Network**.
2. Verify that **Firewall** is `Active`.

#### Port-based firewall

1. Open Terminal and run:

   ```sh
   sudo /sbin/pfctl -s info
   ```

2. Verify that **Status** is `Enabled`.

### On Windows

1. Open PowerShell and run:

   ```powershell
   Get-NetFirewallProfile -PolicyStore ActiveStore -Name Public
   ```

2. Verify that **Enabled** is `True`.

# Cloudflare One Client checks

These device posture checks are performed by the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/). To use this feature, you must [deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) to your devices and enable the desired posture checks.

## Supported Client modes

* Traffic and DNS mode
* Traffic only mode
* Posture only mode

## Supported operating systems

| Device posture check                                                                          | macOS | Windows | Linux       | iOS | Android/ChromeOS |
| --------------------------------------------------------------------------------------------- | ----- | ------- | ----------- | --- | ---------------- |
| [Antivirus](/cloudflare-one/reusable-components/posture-checks/client-checks/antivirus/)           | ❌     | ✅       | ❌           | ❌   | ❌                |
| [Application check](/cloudflare-one/reusable-components/posture-checks/client-checks/application-check/)   | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Carbon Black](/cloudflare-one/reusable-components/posture-checks/client-checks/carbon-black/)             | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Client certificate](/cloudflare-one/reusable-components/posture-checks/client-checks/client-certificate/) | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Device serial numbers](/cloudflare-one/reusable-components/posture-checks/client-checks/corp-device/)     | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Device UUID](/cloudflare-one/reusable-components/posture-checks/client-checks/device-uuid/)               | ❌     | ❌       | ❌           | ✅   | ✅                |
| [Disk encryption](/cloudflare-one/reusable-components/posture-checks/client-checks/disk-encryption/)       | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Domain joined](/cloudflare-one/reusable-components/posture-checks/client-checks/domain-joined/)           | ❌     | ✅       | ❌           | ❌   | ❌                |
| [File check](/cloudflare-one/reusable-components/posture-checks/client-checks/file-check/)                 | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Firewall](/cloudflare-one/reusable-components/posture-checks/client-checks/firewall/)                     | ✅     | ✅       | ❌           | ❌   | ❌                |
| [OS version](/cloudflare-one/reusable-components/posture-checks/client-checks/os-version/)                 | ✅     | ✅       | ✅           | ✅   | ✅                |
| [Require Gateway](/cloudflare-one/reusable-components/posture-checks/client-checks/require-gateway/)       | ✅     | ✅       | ✅           | ✅   | ✅                |
| [Require WARP](/cloudflare-one/reusable-components/posture-checks/client-checks/require-warp/)             | ✅     | ✅       | ✅           | ✅   | ✅                |
| [SentinelOne](/cloudflare-one/reusable-components/posture-checks/client-checks/sentinel-one/)              | ✅     | ✅       | ✅           | ❌   | ❌                |
| [Tanium (legacy)](/cloudflare-one/reusable-components/posture-checks/client-checks/tanium/)                          | ✅    | ✅      | ✅    | ❌  | ❌  |

# OS version

import { Render, TabItem, Tabs } from "~/components";

The OS Version device posture attribute checks whether the version of a device's operating system matches, is greater than or lesser than the configured value.

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Enable the OS version check

1.  In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.
2.  Go to **Cloudflare One Client checks** and select **Add a check**.
3.  Select **OS version**.
4.  Configure the **Operating system**, **Operator**, and **Version** fields to specify the [OS version](#determine-the-os-version) you want devices to match.

    <Render file="warp/os-version-semver" product="cloudflare-one" />

5.  (Optional) Configure additional OS-specific fields:

		<Tabs>
		<TabItem label="Windows">
		To check that Windows devices have required security patches and features installed, include an Update Build Revision (UBR) number in the OS version check.
		- **Update Build Revision**: Enter the Windows UBR you want devices to match (for example, `3803`). The UBR is the fourth part of the full Windows version number (for example, in `10.0.19045.3803`, the UBR is `3803`).
		</TabItem>

		<TabItem label="macOS">
		- **Rapid Security Response Version**: Enter the macOS [Rapid Security Response (RSR)](https://support.apple.com/guide/deployment/rapid-security-responses-dep93ff7ea78/web) version you want devices to match (for example, `(a)`). Be sure to include the parenthesis around the letter.
		</TabItem>

		<TabItem label="Linux">
		- **Distro name** and **Distro revision**: Enter the Linux distribution you want devices to match (for example, `ubuntu 22.04`). The distro version always matches with an equal-to operator (==) regardless of the **Operator** setting.

		- **Patch Version**: Enter the [patch version](#linux) of the kernel. For example, if the kernel is `6.50.0-1007-oem`, enter `1007`.
		</TabItem>

		<TabItem label="iOS">
		- **Rapid Security Response Version**: Enter the iOS [Rapid Security Response (RSR)](https://support.apple.com/guide/deployment/rapid-security-responses-dep93ff7ea78/web) version you want devices to match (for example, `(a)`). Be sure to include the parenthesis around the letter.
		</TabItem>
		</Tabs>

6.  Select **Save**.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the OS version check is returning the expected results.

## Determine the OS version

Operating systems display version numbers in different ways. This section covers how to retrieve the version number in each OS, in a format matching what the OS version posture check expects.

### macOS

1. Open a terminal window.
2. Use the `defaults` command to check for the value of `SystemVersionStampAsString`.

   ```sh
   defaults read loginwindow SystemVersionStampAsString
   ```

### Windows

Windows version numbers consist of four parts: `Major.Minor.Build.UBR`. For example, `10.0.19045.3803` where:
- `10.0` is the **Version** (Major.Minor)
- `19045` is the **Build** number
- `3803` is the **UBR** (Update Build Revision)

To determine the Windows version on your device:

1. Open a PowerShell window.
2. Get the **Version** (Major.Minor.Build):

   ```bash
   (Get-CimInstance Win32_OperatingSystem).version
   ```

   This returns the version in the format `Major.Minor.Build` (for example, `10.0.19045`).

3. Get the **UBR** (Update Build Revision):

   ```bash
   (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name UBR).UBR
   ```

   This returns the UBR value (for example, `3803`).

### Linux

#### OS version

The Linux OS version check reads the system kernel version.

1. Open a Terminal window.

2. Run the `uname -r` command to get the complete kernel version. For example,

   ```sh
   $ uname -r
   5.14.0-25.el9.x86_64
   ```

3. **Version** is the first three numbers of the output in SemVer format (`5.14.0`).

4. **Patch Version** is the first number after the SemVer (`25`).

#### Distro version

The Cloudflare One Client reads **Distro name** and **Distro revision** from the `/etc/os-release` file. The name comes from the **ID** field, and the revision comes from the **VERSION_ID** field.

To determine the Linux distro version on your device:

1. Open a Terminal window.

2. Get the OS identification fields that contain `ID`:

   ```sh
   cat /etc/os-release | grep "ID"
   ```

3. If the output of the above command contained `ID=ubuntu` and `VERSION_ID=22.04`, **Distro name** would be `ubuntu` and **Distro revision** would be `22.04`. The Cloudflare One Client will check these strings for an exact match.

### ChromeOS

ChromeOS version numbers consist of [four parts](https://www.chromium.org/developers/version-numbers/): `MAJOR.MINOR.BUILD.PATCH`. The OS version posture check returns `MAJOR.MINOR.BUILD`.

To determine the ChromeOS version on your device:

1. Open Chrome browser and go to `chrome://system`.
2. Find the following values:
   | Property | OS version component |
   | ----------------------------------- | -------------------- |
   | `CHROMEOS_RELEASE_CHROME_MILESTONE` | `MAJOR` |
   | `CHROMEOS_RELEASE_BUILD_NUMBER` | `MINOR` |
   | `CHROMEOS_RELEASE_BRANCH_NUMBER` | `BUILD` |
3. The OS version in Semver format is `MAJOR.MINOR.BUILD` (for example, `103.14816.131`).

# Require Gateway

import { Render } from "~/components";

With Require Gateway, you can allow access to your applications only to devices enrolled in your Zero Trust organization. Unlike [Require WARP](/cloudflare-one/reusable-components/posture-checks/client-checks/require-warp/), which will check for any WARP instance (including the consumer version), Require Gateway will only allow requests coming from devices whose traffic is filtered by your organization's Cloudflare Gateway configuration. This policy is best used when you want to protect company-owned assets by only allowing access to employees.

## Check result per client mode

The check result depends on the [client mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) configured on the device.

| Client mode          | Result   | Reason                                                        |
| -------------------- | -------- | ------------------------------------------------------------- |
| Traffic and DNS mode | ✅ Pass  | Device traffic is routed through and filtered by Gateway |
| Traffic only mode    | ✅ Pass  | Device traffic is routed through Gateway except the DNS queries |
| DNS only mode        | ❌ Fail  | Only DNS queries are forwarded; network traffic does not flow through Gateway |
| Local proxy mode     | ⚠️ Partial | The check passes only for traffic explicitly configured to use the local SOCKS/HTTP proxy on port `40000`, as this traffic is tunneled through Gateway. Traffic not routed through the proxy bypasses Gateway and fails the check. |
| Posture only mode    | ❌ Fail  | The client is enrolled and collects posture data, but does not route traffic through Gateway |

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## 1. Enable the Gateway check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

2. Go to **Cloudflare One Client checks** and select **Add a check**.

3. Select **Gateway**, then select **Save**.

## 2. Add the check to an Access application

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Locate the application for which you want to require Gateway. Select **Configure**.

3. In the **Policies** tab, create a new Access policy or edit an existing policy.

4. In the policy builder, add an Include or Require rule which uses the _Gateway_ selector. Save the policy.

5. Save the Access application.

Before granting access to the application, the policy will check that the device is running the Cloudflare One Client and enrolled in your Zero Trust organization.

# Require WARP

import { Render } from "~/components";

:::note

This device posture attribute will check for all versions of WARP, including the consumer version.

:::

Cloudflare One enables you to restrict access to your applications to devices running the Cloudflare One Client. This allows you to flexibly ensure that a user's traffic is secure and encrypted before allowing access to a resource protected behind Cloudflare One.

## Check result per client mode

The check result depends on the [client mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) configured on the device.

| Client mode          | Result   | Reason                                                        |
| -------------------- | -------- | ------------------------------------------------------------- |
| Traffic and DNS mode | ✅ Pass  | An active Cloudflare One Client tunnel is established                          |
| Traffic only mode    | ✅ Pass  | An active Cloudflare One Client tunnel is established                          |
| DNS only mode        | ❌ Fail  | No Cloudflare One Client tunnel is established; only DNS is forwarded          |
| Local proxy mode     | ⚠️ Partial | The Cloudflare One Client is running and authenticated, and traffic explicitly routed through the local SOCKS/HTTP proxy on port `40000` is tunneled through Cloudflare. Traffic not routed through the proxy bypasses the client and fails the check. |
| Posture only mode    | ❌ Fail  | The client is enrolled and collects posture data, but does not establish a Cloudflare One Client tunnel |

## Prerequisites

- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## 1. Enable the WARP check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. Ensure that *Allow Secure Web Gateway to proxy traffic** is enabled.
3. Go to **Reusable components** > **Posture checks**.
4. In **Cloudflare One Client checks**, select **Add a check**.
5. Select **WARP**, then select **Save**.

## 2. Add the check to an Access policy

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Locate the application for which you want to require WARP. Select **Configure**.

3. In the **Policies** tab, create a new Access policy or edit an existing policy.

4. In the policy builder, add an Include or Require rule which uses the _WARP_ selector. Save the policy.

5. Save the Access application.

Before granting access to the application, the policy will check that the device is running the Cloudflare One Client.

# SentinelOne

import { Render } from "~/components";

Cloudflare One can check if [SentinelOne](https://www.sentinelone.com/) is running on a device to determine if a request should be allowed to reach a protected resource.

## Prerequisites

- SentinelOne agent is deployed on the device.
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Cloudflare One Client Checks",
  		link: "/cloudflare-one/reusable-components/posture-checks/client-checks/",
  	}}
  />

## Configure the SentinelOne check

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

2. Go to **Cloudflare One Client checks** and select **Add a check**.

3. Select **SentinelOne**.

4. You will be prompted for the following information:
   1. **Name**: Enter a unique name for this device posture check.
   2. **Operating system**: Select your operating system. You will need to configure one posture check per operating system.
   3. **Application Path**: Enter the full path to the SentinelOne process to be checked (for example, `C:\Program Files\SentinelOne\Sentinel Agent 21.7.4.1043\SentinelAgent.exe`).
      :::note
      The path of the SentinelOne process may change between updates. Make sure to edit **Application Path** to match the new path, or use `%PATH%` variables.
      :::

   4. **Signing certificate thumbprint (recommended)**: Enter the thumbprint of the publishing certificate used to sign the binary. This proves the binary came from SentinelOne and is the recommended way to validate the process.
   5. **SHA-256 (optional)**: Enter a SHA-256 value. This is used to validate the SHA256 signature of the binary and ensures the integrity of the binary file on the device. Note: do not fill out this field unless you strictly control updates to SentinelOne, as this will change between versions.

Next, go to **Insights** > **Logs** > **Posture logs** and verify that the SentinelOne check is returning the expected results.

# Tanium (legacy)

import { Render } from "~/components";

:::note
Not recommended for new deployments. We recommend using the [Tanium service-to-service integration](/cloudflare-one/integrations/service-providers/taniums2s/) to get device posture signals from Tanium.
:::

Cloudflare Access can use endpoint data from [Tanium™](https://www.tanium.com/) to determine if a request should be allowed to reach a protected resource. When users attempt to connect to a resource protected by Access with a Tanium rule, Cloudflare Access will validate the user's identity, and the browser will connect to the Tanium agent before making a decision to grant access.

:::caution[Gateway policy limitation]
The legacy Tanium integration cannot be used in [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/#device-posture). Only [Access policies](/cloudflare-one/access-controls/policies/) are supported.
:::

## Prerequisites

- Tanium Core Platform version 7.2 or later
- <Render
  	file="posture/prereqs-warp-is-deployed"
  	product="cloudflare-one"
  	params={{
  		name: "Access integrations",
  		link: "/cloudflare-one/reusable-components/posture-checks/access-integrations/",
  	}}
  />

## Integrate Tanium with Cloudflare Access

:::note

The integration does not currently support Safari.

:::

1. Configure your Tanium deployment using the [step-by-step documentation](https://docs.tanium.com/endpoint_identity/endpoint_identity/userguide.html) provided. You will need the public key to integrate your Tanium deployment with Cloudflare Access.

2. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Posture checks**.

3. Go to **Cloudflare One Client checks** and select **Add a check**.

4. Select **Tanium** from the list of providers.

5. Enter any **Name** for the integration.

6. For **Port**, enter `17472`.

   This is the default port used by the Tanium endpoints to communicate inbound and outbound with Cloudflare Access. You may need to modify it to reflect your organization's deployment.

7. Input the public certificate generated in Step 1.

   Adding the certificate allows Cloudflare to validate that the response from the Tanium agent is valid.

You can now build [Access policies](/cloudflare-one/access-controls/policies/) that check [device posture signals](#tanium-endpoint-signals) from the Tanium endpoint.

## Example Access policy

This example will only grant access to users who are part of your team's email domain and running the Tanium agent.

| Action | Rule type | Selector                | Value       |
| ------ | --------- | ----------------------- | ----------- |
| Allow  | Include   | Emails Ending in        | `@team.com` |
|        | Require   | Device Posture - Tanium | `Managed`   |

The Tanium rule will require that the device connecting is managed in your Tanium deployment and has checked into the Tanium server in the last 7 days.

## Tanium endpoint signals

| Signal  | Value   | Description                                                                 |
| ------- | ------- | --------------------------------------------------------------------------- |
| Managed | Boolean | Validates that the device is managed in your organization's Tanium account. |

# Get started

import { CardGrid, LinkTitleCard, Render } from "~/components";

Set up Cloudflare Zero Trust to protect your users, devices, and networks. Complete the prerequisites below, then choose a use case to get started.

## Prerequisites

Before you begin any use case, you need a Cloudflare account and a Zero Trust organization.

### 1. Create a Cloudflare account

Sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up) and enable two-factor authentication.

### 2. Create a Zero Trust organization

<Render file="choose-team-name" product="cloudflare-one" />

## What would you like to do?

These use cases match the guided onboarding in the [Cloudflare One dashboard](https://one.dash.cloudflare.com). To follow along in the dashboard, select **Get Started**.

<CardGrid>

<LinkTitleCard
	title="Replace your VPN"
	href="/cloudflare-one/setup/replace-vpn/"
	icon="seti:db"
>
	Give remote users, offices, and devices secure access to private networks and
	applications without a traditional VPN.
</LinkTitleCard>

<LinkTitleCard
	title="Secure access to private apps from any browser"
	href="/cloudflare-one/setup/secure-private-apps/"
	icon="laptop"
>
	Provide browser-based access to internal web applications, SSH servers, and
	RDP sessions without installing software on user devices.
</LinkTitleCard>

<LinkTitleCard
	title="Filter DNS to block threats"
	href="/cloudflare-one/traffic-controls/get-started/dns/"
	icon="magnifier"
>
	Set up DNS filtering to block malware, phishing, and unwanted content across
	your network in minutes.
</LinkTitleCard>

<LinkTitleCard
	title="Secure web traffic from threats"
	href="/learning-paths/secure-internet-traffic/concepts/"
	icon="seti:lock"
>
	Inspect and filter all Internet-bound traffic from your users to block
	threats, enforce acceptable use policies, and prevent data loss.
</LinkTitleCard>

</CardGrid>

:::note

For in-depth deployment guides that cover policy design and advanced configuration, refer to [Implementation guides](/cloudflare-one/implementation-guides/).
:::

# Device to device

import { DashButton } from "~/components";

Create a secure connection between two devices so they can communicate directly through Cloudflare's network, without needing to be on the same physical network. This is useful when you need to remotely access a specific device, for example connecting to a home computer from a laptop at a coffee shop.

To explore other connection scenarios, refer to [Replace your VPN](/cloudflare-one/setup/replace-vpn/).

## How it works

The [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) is an app that you install on each device you want to connect. When you enroll a device in your Cloudflare account, it is assigned a [Mesh IP](/cloudflare-one/networks/routes/reserved-ips/#device-ips).

Devices use their Mesh IPs to communicate with each other through Cloudflare's network. This works for most common types of network traffic, including web requests, remote desktop, file sharing, and ping.

Only devices enrolled in your Cloudflare account can reach these addresses, so they are not accessible to anyone outside your organization. No tunnel infrastructure or network configuration is required, and the connection does not disrupt existing traffic on your network.

For more details, refer to [Connect two devices](/mesh/guides/connect-client-devices/).

## Prerequisites

- A [Cloudflare account](https://dash.cloudflare.com/sign-up)
- Two Linux, Windows, macOS, Android, or iOS devices you want to connect together.

## Step 1: Enroll your first device

The Mesh dashboard provides the Cloudflare One Client installer and organization name.

1. In the Cloudflare dashboard, go to **Networking** > **Mesh**.

   <DashButton url="/?to=/:account/mesh" />

2. Select **Add participant** > **Add device**.
3. Select your platform and use the provided link or QR code to install the Cloudflare One Client.
4. Open the client and select **Cloudflare Zero Trust** when prompted for a connection type.
5. Enter the organization name displayed in the Mesh dashboard and sign in.

## Step 2: Enroll your second device

Both devices must be enrolled in your Cloudflare account for the connection to work.

1. Download the Cloudflare One Client on your second device.
2. Open the client, enter the same team name, and sign in.
3. The client should show as **Connected** on both devices.

## Step 3: Verify your connection

Both devices are now connected through Cloudflare's network using their assigned Mesh IPs.

To view your device's assigned Mesh IP:

1. In the Cloudflare dashboard, go to **Networking** > **Mesh**.

   <DashButton url="/?to=/:account/mesh" />

2. Your connected devices appear with their Mesh IPs.

To test connectivity, `ping` the Mesh IP of one device from the other.

## Recommended next steps

After verifying your connection, consider securing your connected devices with policies and access controls:

- **Set up Gateway policies**: By default, all enrolled devices can reach each other over the Mesh IP space. Gateway policies let you scan, filter, and log traffic between your devices. For more information, refer to [DNS policies](/cloudflare-one/traffic-controls/dns-policies/), [Network policies](/cloudflare-one/traffic-controls/network-policies/), and [HTTP policies](/cloudflare-one/traffic-controls/http-policies/).
- **Create an Access application**: Restrict access to specific destinations on enrolled devices with identity-based rules. For more information, refer to [Secure a private IP or hostname](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/).

For in-depth guidance on policy design and device posture checks, refer to the [Replace your VPN learning path](/learning-paths/replace-vpn/concepts/).

## Troubleshoot

If you have issues connecting, try these steps:

- **Windows users**: Windows Firewall blocks device-to-device traffic by default. You may need to add a firewall rule that allows incoming traffic from `100.96.0.0/12`. For details, refer to [Connect two devices](/mesh/guides/connect-client-devices/).
- [Troubleshoot the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/): resolve connection and enrollment issues.

# Device to network

import { GlossaryTooltip, Render } from "~/components";

Connect a remote device to a private network so your users can securely access internal applications and services from anywhere, without the security risks and performance bottlenecks of a traditional VPN.

To explore other connection scenarios, refer to [Replace your VPN](/cloudflare-one/setup/replace-vpn/).

This guide follows the same steps as the **Get Started** onboarding wizard in the [Cloudflare One dashboard](https://one.dash.cloudflare.com).

## How it works

[Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) is a network connector that creates an outbound-only connection between your private network and Cloudflare. No open inbound ports or firewall changes are required.

The <GlossaryTooltip term="Cloudflare One Client">[Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/)</GlossaryTooltip> is an app that you install on each user's device. It routes traffic through Cloudflare and into the tunnel, so users can reach internal resources from anywhere.

## Prerequisites

- A Cloudflare account with a Zero Trust organization. If you have not set this up, refer to [Get started](/cloudflare-one/setup/).
- A Linux, Windows, or macOS device on your private network to run the tunnel.
- A Linux, Windows, or macOS device to install the Cloudflare One Client on.

## Step 1: Assign a Tunnel

Cloudflare Tunnel establishes an outbound connection between your resources and Cloudflare. This is how new devices can reach your private network. You can install Tunnel on any Windows, Mac, or Linux device currently in your private network.

1. In [Cloudflare One](https://one.dash.cloudflare.com), select the **Get Started** tab.
2. For **Replace my client-based or site-to-site VPN**, select **Get started**.
3. For **Device to network**, select **Continue**.
4. On the **Connect a remote device to a private network** screen, select **Continue**.
5. On the **Assign a Tunnel** screen, use the dropdown to choose an existing tunnel or create a new one.
6. Select **Continue**.

## Step 2: Set your Tunnel's IP range

Add the IP range of your private network to the tunnel. This defines which internal resources your remote users can reach. Your tunnel accepts traffic to this range from devices enrolled in your Zero Trust organization.

1. Enter your IP range (for example, `10.0.1.0/24`).
2. Select **Continue**.

:::note
If you are not sure of your IP range, check your router or network settings.
:::

## Step 3: Deploy your Tunnel

Install the `cloudflared` connector on a device in your private network and run the tunnel. This service creates the secure connection between your network and Cloudflare.

1. Select your device's operating system and architecture.
2. Copy the install command and run it on your device. For Windows, open Command Prompt as an administrator. For all other operating systems, use a terminal window.

   For macOS, the command looks similar to:

   ```sh
   brew install cloudflared && sudo cloudflared service install <YOUR_TUNNEL_TOKEN>
   ```

   For Windows and Linux, the dashboard provides a download link and install command for your selected architecture. For more download options, refer to [Downloads](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/).

3. After `cloudflared` connects, the dashboard confirms the tunnel is active.
4. Select **Continue**.

## Step 4: Enroll your devices

Device enrollment controls which users can connect their devices to your private network through Cloudflare. In this step, you register your first device by providing an email address and installing the Cloudflare One Client.

1. Enter the email you want to use to enroll your first device.
2. Select your device's operating system.
3. Select **Download to continue** to download the Cloudflare One Client, or copy the download link to send to a different device.
4. Select **Continue**.

:::note
You can manage device enrollment permissions later in **Team & Resources** > **Devices**.
:::

## Step 5: Complete Cloudflare One Client setup

<Render
	file="warp/complete-warp-setup"
	product="cloudflare-one"
	params={{ device: "your" }}
/>

## Step 6: Verify your connection

The dashboard confirms that you are securely connected. You now have remote access between your device and your private network resources.

To verify connectivity, try reaching a resource on your private network (for example, `http://10.0.1.100` or `ssh 10.0.1.50`).

## Recommended next steps

After verifying your connection, consider securing your private network with policies and access controls:

- **Set up Gateway policies**: By default, all enrolled devices can reach your entire private network. Gateway policies let you scan, filter, and log traffic between your devices and your private network. For more information, refer to [DNS policies](/cloudflare-one/traffic-controls/dns-policies/), [Network policies](/cloudflare-one/traffic-controls/network-policies/), and [HTTP policies](/cloudflare-one/traffic-controls/http-policies/).
- **Create an Access application**: Restrict access to specific applications or hostnames on your private network with identity-based rules. For more information, refer to [Secure a private IP or hostname](/cloudflare-one/access-controls/applications/non-http/self-hosted-private-app/).
- **Explore more with Zero Trust**: Review your tunnel, policies, and connected devices in the [Cloudflare One dashboard](https://one.dash.cloudflare.com).

For in-depth guidance on policy design and device posture checks, refer to the [Replace your VPN learning path](/learning-paths/replace-vpn/concepts/).

## Troubleshoot

If you have issues connecting, refer to these resources:

- [Troubleshoot WARP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/): resolve Cloudflare One Client connection and enrollment issues.
- [Troubleshoot tunnels](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/): diagnose tunnel connectivity and routing problems.

# Replace your VPN

import { CardGrid, LinkTitleCard } from "~/components";

Cloudflare One uses Cloudflare's global network and Zero Trust Network Access (ZTNA) to replace traditional VPNs. After you securely connect your devices and resources to Cloudflare, you can set policies to verify every request based on identity and context, reducing your attack surface and improving performance for remote users. For more background, refer to [Why should you replace your VPN?](/learning-paths/replace-vpn/concepts/why-vpn/)

How you set this up depends on what needs to connect to what. Choose the scenario that matches your use case:

<CardGrid>

<LinkTitleCard
	title="Device to network"
	href="/cloudflare-one/setup/replace-vpn/device-to-network/"
	icon="laptop"
>
	Connect remote users to internal applications and services through a secure
	connection. Best for remote access to private networks.
</LinkTitleCard>

<LinkTitleCard
	title="Device to device"
	href="/cloudflare-one/setup/replace-vpn/device-to-device/"
	icon="laptop"
>
	Create secure, direct connections between two or more devices through
	Cloudflare's network using Mesh IPs. Best for device-to-device communication.
</LinkTitleCard>

<LinkTitleCard
	title="Network to network"
	href="/cloudflare-one/setup/replace-vpn/network-to-network/"
	icon="seti:db"
>
	Connect two or more private networks bidirectionally through Cloudflare. Best
	for linking offices, data centers, or cloud environments.
</LinkTitleCard>

</CardGrid>

:::note

For in-depth guidance on policy design and device posture checks, refer to the [Replace your VPN learning path](/learning-paths/replace-vpn/concepts/).
:::

# Connect two private networks with Cloudflare Mesh

import { Render } from "~/components";

<Render file="mesh/pages/guides/connect-sites" product="cloudflare-one" />

# Clientless SSH

import { Render } from "~/components";

Provide secure, in-browser command line access to an internal server without SSH client software on the user's device. This is useful when you need to give developers or IT staff remote access to servers for administration or troubleshooting from any browser.

To explore other access scenarios, refer to [Secure private apps](/cloudflare-one/setup/secure-private-apps/).

This guide follows the same steps as the **Get Started** experience in the [Cloudflare One dashboard](https://one.dash.cloudflare.com).

## How it works

[Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) connects your private network to Cloudflare without opening any ports on your network. You install `cloudflared`, a connector service that runs in the background, on a device that can reach your server. It creates a secure connection from your network out to Cloudflare, so no firewall changes are required.

[Cloudflare Access](/cloudflare-one/access-controls/) sits in front of the server and verifies who each user is before letting them through. Users sign in through a browser using an email one-time PIN or your identity provider, then interact with the server through an in-browser terminal.

For details on connection methods and advanced configuration, refer to [Connect to SSH in the browser](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-browser-rendering/).

## Prerequisites

- A Cloudflare account with a Zero Trust organization. If you have not set this up, refer to [Get started](/cloudflare-one/setup/).
- An [active domain on your Cloudflare account](/fundamentals/manage-domains/add-site/). A public subdomain is created on this domain for your application.
- A Linux, Windows, or macOS device on your private network that can reach the server. This is where you install the tunnel.
- A server on your private network with SSH enabled.

## Step 1: Define your application

In this step, you describe the internal server you want to make available through Cloudflare.

1. In [Cloudflare One](https://one.dash.cloudflare.com), select the **Get Started** tab.
2. For **Set up secure access to private apps from any browser**, select **Get started**.
3. For **Configure clientless SSH access to an internal service**, select **Continue**.
4. On the **Zero Trust SSH terminal directly from your browser** screen, select **Continue**.
5. Enter a name for your application.
6. Enter the hostname or IP address of the server. Use the IP address if you are not sure (for example, `10.10.1.25`).
7. Enter the SSH port (the default is `22`).
8. Select **Continue**.

<Render
	file="access/secure-private-apps-shared-steps"
	product="cloudflare-one"
/>

# In-browser remote desktop

import { Render } from "~/components";

Provide secure, in-browser remote desktop access to Windows hosts without Remote Desktop Protocol (RDP) client software on the user's device. This is useful when you need to give IT staff or support teams remote access to Windows machines for administration or troubleshooting from any browser.

To explore other access scenarios, refer to [Secure private apps](/cloudflare-one/setup/secure-private-apps/).

This guide follows the same steps as the **Get Started** experience in the [Cloudflare One dashboard](https://one.dash.cloudflare.com).

## How it works

[Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) connects your private network to Cloudflare without opening any ports on your network. You install `cloudflared`, a connector service that runs in the background, on a device that can reach the Windows host. It creates a secure connection from your network out to Cloudflare, so no firewall changes are required.

[Cloudflare Access](/cloudflare-one/access-controls/) sits in front of the host and verifies who each user is before letting them through. Users sign in through a browser using an email one-time PIN or your identity provider, then interact with the Windows desktop through an in-browser remote desktop session.

For details on supported operating systems, connection methods, and known limitations, refer to [Connect to RDP in a browser](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/rdp/rdp-browser/).

## Prerequisites

- A Cloudflare account with a Zero Trust organization. If you have not set this up, refer to [Get started](/cloudflare-one/setup/).
- An [active domain on your Cloudflare account](/fundamentals/manage-domains/add-site/). A public subdomain is created on this domain for your application.
- A Linux, Windows, or macOS device on your private network that can reach the Windows host. This is where you install the tunnel.
- A Windows host on your private network that accepts Remote Desktop connections.

## Step 1: Define your application

In this step, you describe the Windows host you want to make available through Cloudflare.

1. In [Cloudflare One](https://one.dash.cloudflare.com), select the **Get Started** tab.
2. For **Set up secure access to private apps from any browser**, select **Get started**.
3. For **Enable in-browser remote desktop sessions to Windows hosts**, select **Continue**.
4. On the **Zero Trust RDP client directly from your browser** screen, select **Continue**.
5. Enter a name for your application.
6. Enter the local IP address of the Windows host (for example, `10.10.1.25`).
7. Enter the RDP port (the default is `3389`).
8. Select **Continue**.

<Render
	file="access/secure-private-apps-shared-steps"
	product="cloudflare-one"
/>

# Secure private apps

import { CardGrid, LinkTitleCard } from "~/components";

Cloudflare Access lets users reach internal applications through a browser without a VPN or client software on their device. You connect your application to Cloudflare using a secure connection called a tunnel, then protect it with policies that control who can access it. For more background, refer to [What is clientless access?](/learning-paths/clientless-access/concepts/what-is-clientless-access/).

How you set this up depends on the type of application you are securing. Choose the scenario that matches your use case:

<CardGrid>

<LinkTitleCard
	title="Private web application"
	href="/cloudflare-one/setup/secure-private-apps/private-web-app/"
	icon="laptop"
>
	Connect an internal web application to Cloudflare and control who can access
	it. Best for applications like company intranets, internal wikis, or admin
	panels.
</LinkTitleCard>

<LinkTitleCard
	title="Clientless SSH"
	href="/cloudflare-one/setup/secure-private-apps/clientless-ssh/"
	icon="seti:shell"
>
	Provide in-browser command line access to an internal server without SSH
	client software on the user's device.
</LinkTitleCard>

<LinkTitleCard
	title="In-browser remote desktop"
	href="/cloudflare-one/setup/secure-private-apps/in-browser-rdp/"
	icon="seti:windows"
>
	Provide in-browser remote desktop access to Windows hosts without remote
	desktop client software on the user's device.
</LinkTitleCard>

</CardGrid>

:::note

For in-depth guidance on clientless access and advanced configuration, refer to the [Clientless access learning path](/learning-paths/clientless-access/concepts/what-is-clientless-access/).
:::

# Private web application

import { Render } from "~/components";

Connect a self-hosted web application to Cloudflare so authorized users can access it from a browser without a VPN. This is useful when you need to give employees or contractors secure access to applications like company intranets, internal wikis, or admin panels.

To explore other access scenarios, refer to [Secure private apps](/cloudflare-one/setup/secure-private-apps/).

This guide follows the same steps as the **Get Started** experience in the [Cloudflare One dashboard](https://one.dash.cloudflare.com).

## How it works

[Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) connects your private network to Cloudflare without opening any ports on your network. You install `cloudflared`, a connector service that runs in the background, on a device that can reach your application. It creates a secure connection from your network out to Cloudflare, so no firewall changes are required.

[Cloudflare Access](/cloudflare-one/access-controls/) sits in front of the application and verifies who each user is before letting them through. Users sign in through a browser using an email one-time PIN or your identity provider.

## Prerequisites

- A Cloudflare account with a Zero Trust organization. If you have not set this up, refer to [Get started](/cloudflare-one/setup/).
- An [active domain on your Cloudflare account](/fundamentals/manage-domains/add-site/). A public subdomain is created on this domain for your application.
- A Linux, Windows, or macOS device on your private network that can reach the application. This is where you install the tunnel.
- A running web application on your private network (for example, `http://10.10.1.25` or `http://grafana.local`).

## Step 1: Define your application

In this step, you describe the internal application you want to make available through Cloudflare.

1. In [Cloudflare One](https://one.dash.cloudflare.com), select the **Get Started** tab.
2. For **Set up secure access to private apps from any browser**, select **Get started**.
3. For **Connect a private web application**, select **Continue**.
4. On the **Connect and access private web applications** screen, select **Continue**.
5. Enter a name for your application (for example, `grafana-gcp`).
6. Enter the hostname or IP address where the application is running. Use the IP address if you are not sure (for example, `10.10.1.25`).
7. Select the protocol your application uses (HTTP or HTTPS).
8. Enter the port your application listens on. This is usually part of the URL you use to access the application locally (for example, the `80` in `http://10.10.1.25:80`).
9. Select **Continue**.

<Render
	file="access/secure-private-apps-shared-steps"
	product="cloudflare-one"
/>

# Team and resources

import { DirectoryListing } from "~/components";

<DirectoryListing />

# Custom applications

import { DashButton, Steps } from "~/components";

Create a custom application to identify an internal service or an application that is not in the Cloudflare-managed catalog. Custom applications belong to your account and appear alongside Cloudflare-managed applications in the App Library. You can use them in [DNS](/cloudflare-one/traffic-controls/dns-policies/#application), [HTTP](/cloudflare-one/traffic-controls/http-policies/#application), and [Network](/cloudflare-one/traffic-controls/network-policies/#application) policies with the existing **Application** selector.

## Create a custom application

<Steps>

1. In the Cloudflare dashboard, go to **Zero Trust** > **Team & Resources** > **Application library**.

   <DashButton
       url="/?to=/:account/team-resources/application-library"
       buttonName="Application library"
       zeroTrust
   />

2. Select **Add custom application**.
3. Enter an **Application name** and **Application ID**, and select a **Category**. The Application ID field contains a human-readable identifier generated from the name, which you can change before saving. Cloudflare also assigns a numeric application ID for use in Gateway rule expressions.
4. In **Network identifiers**, enter at least one **Hostname** or **IP subnet**. You can include both hostnames and IP subnets in the same application.
5. (Optional) Add **Support hostnames** for dependencies such as authentication or content delivery, and **Port protocols** to restrict matching to specific TCP or UDP ports. Refer to [custom application limits](#custom-application-limits) for formats and limits.
6. Select **Add custom application**.

</Steps>

Creating an application does not create a Gateway policy. To control its traffic, [reference the application in a policy](#use-a-custom-application-in-gateway).

## Edit a custom application

<Steps>

1. In **Application library**, find the custom application and open its actions menu.
2. Select **Edit custom app**.
3. Update its **Hostnames**, **Support hostnames**, **IP subnets**, or **Port protocols**. The application must retain at least one primary hostname or IP subnet.
4. Select **Save**.

</Steps>

The application name, human-readable Application ID, and category are fixed after creation. Editing the network identifiers preserves the numeric application ID, so Gateway policies that reference the application use its updated definition without changing their expressions. Allow time for the updated definition to propagate to Gateway.

You can also [change the application's review status](/cloudflare-one/team-and-resources/app-library/#review-applications) from its actions menu.

## Use a custom application in Gateway

<Steps>

1. In **Traffic policies**, create or edit a DNS, HTTP, or network policy.
2. For the traffic condition, select **Application** and choose the custom application by name. You can select custom and Cloudflare-managed applications in the same condition.
3. Choose the policy action and save the policy.

</Steps>

You can also start a policy from the application's actions menu by selecting **Create new Gateway policy** and choosing a policy type.

Custom applications use the same selectors and numeric IDs as other [applications in Gateway](/cloudflare-one/traffic-controls/application-app-types/#custom-applications). Use the assigned numeric ID when configuring a Gateway rule through the API or Terraform.

## Delete a custom application

You cannot delete a custom application while a Gateway rule or reusable profile references it. Remove the application from every referencing rule and profile before deleting it. Turning off a rule does not remove its reference.

<Steps>

1. In **Application library**, find the custom application and open its actions menu.
2. Select **Delete custom app**.
3. Confirm the deletion.

</Steps>

If the application is still referenced, deletion fails and the API returns HTTP `409` with the referencing resource IDs, names, and types. If Gateway cannot verify the references, deletion also fails and the application remains available.

## Required API token permissions

To create, edit, or delete custom applications with the API or Terraform, the API token must have at least one of the following permissions:

- _Account_ > _Zero Trust_ > _Edit_
- _Account_ > _Resource Library_ > _Edit_

To list or view applications and categories, the API token must have at least one of the following permissions:

- _Account_ > _Zero Trust_ > _Read_
- _Account_ > _Zero Trust_ > _Edit_
- _Account_ > _Resource Library_ > _Read_
- _Account_ > _Resource Library_ > _Edit_

## Custom application limits

For the number of custom applications you can create per account, refer to [Application Library account limits](/cloudflare-one/account-limits/#application-library).

The following limits apply to each custom application when creating or editing it.

| Field                           | Limit or supported format                                                                                                                                                                                                                                                                                            |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hostnames and support hostnames | 50 entries combined. For example, 40 primary hostnames leave room for 10 support hostnames.                                                                                                                                                                                                                          |
| IP subnets                      | 50 entries per application, separate from the hostname limit. Use CIDR notation. IPv4 prefix lengths must be `/8` through `/32`. IPv6 prefix lengths must be `/32` through `/128`. For a single address, use `/32` for IPv4 or `/128` for IPv6.                                                                      |
| Port protocols                  | TCP and UDP, in `protocol/port` or inclusive `protocol/start-end` format, such as `tcp/443` or `udp/10000-20000`. The dashboard accepts ports `1` through `65535`. The API also accepts port `0`. The start of a range must be less than or equal to its end. There is no separate entry-count limit for this field. |

At least one primary hostname or IP subnet is required. Support hostnames, ports, or a combination of support hostnames and ports cannot satisfy this requirement. You cannot create a custom application identified only by a port and protocol, such as `tcp/443`.

Hostnames and IP subnets are alternative ways to match the application. Port protocols, when configured, restrict those matches in HTTP and network policies. DNS policies do not check destination ports. Refer to [custom application matching](/cloudflare-one/traffic-controls/application-app-types/#custom-application-matching) for examples.

# Application Library

import { Render, GlossaryTooltip } from "~/components";

The Application Library allows users to manage their SaaS applications in Cloudflare One by consolidating views across all relevant products: [Gateway](/cloudflare-one/traffic-controls/), [Access](/cloudflare-one/access-controls/policies/), and [Cloud Access Security Broker (CASB)](/cloudflare-one/integrations/cloud-and-saas/). The App Library provides visibility and control for available applications, as well as the ability to view categorized hostnames and manage configuration for Access for SaaS and Gateway policies. For example, you can use the App Library to review how Gateway uses specific hostnames to match against application traffic.

To access the App Library in the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Application library**. Each application card will list the number of hostnames associated with the application, the supported Cloudflare One product usage, and the [app type](/cloudflare-one/traffic-controls/application-app-types/#app-types).

The App Library groups [Do Not Inspect applications](/cloudflare-one/traffic-controls/application-app-types/#do-not-inspect-applications) within the corresponding application. For example, the App Library will group _Google Drive (Do Not Inspect)_ under **Google Drive**. Traffic that does not match a known application will not be included in the App Library.

You can also [create custom applications](/cloudflare-one/team-and-resources/app-library/custom-applications/) to identify internal services or applications that are not in the Cloudflare-managed catalog. Use custom applications in [DNS](/cloudflare-one/traffic-controls/dns-policies/#application), [HTTP](/cloudflare-one/traffic-controls/http-policies/#application), and [Network](/cloudflare-one/traffic-controls/network-policies/#application) policies with the existing **Application** selector.

To manage applications with the API or Terraform, refer to [Required API token permissions](/cloudflare-one/team-and-resources/app-library/custom-applications/#required-api-token-permissions).

## View application details

Select an application card to view details about the application.

### Overview

The **Overview** tab shows details about an application, including:

- Name
- Shadow IT [review status](#review-applications)
- Number of hostnames
- [App type](/cloudflare-one/traffic-controls/application-app-types/#app-types)
- Supported Cloudflare One applications
- Application ID for use with the API and Terraform

### Findings

The **Findings** tab shows any connected [CASB integrations](/cloudflare-one/cloud-and-saas-findings/#manage-casb-integrations) for the selected application, as well as instances of any detected [posture findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#posture-findings) and [content findings](/cloudflare-one/cloud-and-saas-findings/manage-findings/#content-findings) for each integration.

### Policies

The **Policies** tab shows any [Gateway](/cloudflare-one/traffic-controls/) and [Access for SaaS](/cloudflare-one/access-controls/applications/http-apps/saas-apps/) policies related to the selected application.

### Usage

The **Usage** tab shows any logs for [Gateway traffic requests](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/), [Access authentication events](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/#authentication-logs), [Shadow IT Discovery user sessions](/cloudflare-one/insights/analytics/shadow-it-discovery/), and [generative AI prompt logs](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#view-prompt-logs) sent to the selected application. This section requires logs to be turned on for each feature.

The Shadow IT Discovery dashboard will provide more details for discovered applications. To access Shadow IT Discovery in the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Dashboards**, then select **Shadow IT: SaaS analytics** or **Shadow IT: Private Network analytics**.

## Review applications

The App Library synchronizes application review statuses with approval statuses from the [Shadow IT Discovery SaaS analytics](/cloudflare-one/insights/analytics/shadow-it-discovery/) dashboard.

<Render file="app-library-review-apps" product="cloudflare-one" />

## Application confidence scorecards

Application confidence scorecards provide automated risk assessment for AI and SaaS applications to help organizations make informed decisions about application approval and security policies. These scores bring scale and automation to the labor- and time-intensive task of evaluating generative AI and SaaS applications.

The scoring system evaluates applications across multiple security, compliance, and operational dimensions to generate two complementary scores: the Application Posture Score and the Generative AI Posture Score. These scores help security teams identify risks in Shadow AI and Shadow IT deployments without manual auditing of every application.

To view an application's confidence scorecard:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Application library**
2. Find the application you would like to review or search it by name.
3. Review the Application Posture Score and the Generative AI Posture Score which are generated on the application card.

### Scoring methodology

#### Application Posture Score (5 points)

The Application Posture Score evaluates SaaS providers across five major categories.

| Category                              | Points | Assessment Criteria                                                                                                                            | Scoring Logic                                                                                                                                                                 |
| ------------------------------------- | :----: | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Security and Privacy Compliance       |  1.2   | Presence of SOC 2 and ISO 27001 certifications, which signal operational maturity and adherence to security frameworks.                        | Full credit awarded for both certifications; partial credit for one certification; no credit if neither certification is present.                                             |
| Data Management Practices             |  1.0   | Data retention windows and whether the provider shares data with third parties.                                                                | Shorter retention periods and no third-party data sharing earn the highest marks. Applications with indefinite data retention or extensive data sharing receive lower scores. |
| Security Controls                     |  1.0   | Support for Multi-Factor Authentication (MFA), Single Sign-On (SSO), TLS 1.3, role-based access controls, and session monitoring capabilities. | These represent table stakes of modern SaaS security. Full credit requires comprehensive support across all controls; partial credit awarded for subset implementation.       |
| Security Reports and Incident History |  1.0   | Availability of trust or security pages, active bug bounty programs, incident response transparency, and recent breach history.                | Recent material breaches result in full point deduction. Proactive security measures like bug bounty programs and transparent incident reporting increase scores.             |
| Financial Stability                   |  0.8   | Company financial status, funding levels, and operational stability.                                                                           | Public companies and heavily capitalized providers score highest, while startups with limited funding or companies in financial distress receive lower scores.                |
| Total Points                          |  5.0   |                                                                                                                                                |                                                                                                                                                                               |

#### Generative AI Posture Score (5 points)

| Category                  | Points  | Assessment Criteria                                                                                                                    | Scoring Logic                                                                                                                                                                                 |
| ------------------------- | :-----: | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Compliance                |   1.0   | Presence of ISO 42001 certification for AI management systems.                                                                         | Full credit for ISO 42001 certification; no credit without this specialized AI governance certification.                                                                                      |
| Deployment Security Model |   1.0   | Whether application access requires authentication and implements rate limiting, or if services are publicly exposed without controls. | Authenticated access with proper rate limiting receives full credit; publicly exposed services without controls receive minimal scoring.                                                      |
| System Card               |   1.0   | Publication of model or system cards documenting safety evaluations, bias testing, and risk assessments.                               | Comprehensive system cards with detailed safety and bias documentation receive full credit; incomplete or missing documentation results in score reduction.                                   |
| Training Data Governance  |   2.0   | Whether user data is explicitly excluded from model training and availability of opt-in/opt-out controls for training data usage.      | Explicit exclusion of user data from training receives maximum points; opt-in/opt-out controls receive partial credit; no controls or guaranteed user data training receives minimal scoring. |
| **Total Points**          | **5.0** |                                                                                                                                        |                                                                                                                                                                                               |

### Automated scoring infrastructure

#### Web crawling and data extraction

The scoring system employs automated infrastructure to crawl and analyze public information sources.

- Data sources: Trust centers, privacy policies, security pages, compliance documents, and vendor documentation.
- Extraction process: Large language models parse documents to identify relevant information, with structured extraction methods to resist hallucinations and ensure accuracy.
- Validation requirements: Source validation and structured data extraction prevent false positives and ensure reliable scoring.

#### Human oversight and quality assurance

Automated results are supplemented with manual review to maintain transparency and ensure data integrity.

- Review process: Every automated score undergoes review and audit by Cloudflare analysts before publication in the Application Library.
- Validation methodology: Combination of automated crawling and extraction with human validation ensures comprehensive and trustworthy scoring.
- Update frequency: Scores update dynamically as vendors improve security and compliance postures, providing live assessment rather than static reports.

#### Report score inaccuracies

If you believe one of the Application confidence scores is incorrect or have additional evidence that should be considered in the scoring process, contact `app-confidence-scores@cloudflare.com`. Include relevant documentation or evidence that supports your assessment to help us review and update the score accordingly.

# Device registration

import { Render, TabItem, Tabs, APIRequest } from "~/components";

A device registration represents an individual session of the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on a physical device, linking a user (or service token) and the device to your [Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization). It is created the first time the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) authenticates on that device.

Each device registration includes a unique public key, [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/), and [virtual IP addresses](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/) (one IPv4 and one IPv6) that identify the device on your network.

A single physical device can have [multiple device registrations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-multiuser/), for example, if multiple users share a single laptop and each enrolls the Cloudflare One Client with their own credentials.

## Key concepts

| Concept                                                                              | Definition                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [User](/cloudflare-one/team-and-resources/users/seat-management/#manage-users)       | A person whose identity is verified through your identity provider (IdP) and who can enroll devices in your Zero Trust organization.                                                                                                                                                                                                                                            |
| [Seat](/cloudflare-one/team-and-resources/users/seat-management/)                    | A billable unit consumed when a user authenticates to your Zero Trust organization. Each user occupies one seat regardless of how many devices they enroll. Service tokens do not consume seats.                                                                                                                                                                                |
| [Service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) | Credentials used by automated systems to authenticate against your Cloudflare One policies.                                                                                                                                                                                                                                                                             |
| Device registration                                                                  |  An individual session of the Cloudflare One Client on a physical device, with its own public key, [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/), and [virtual IP addresses](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/) (one IPv4 and one IPv6). |
| [Session](/cloudflare-one/access-controls/access-settings/session-management/)       | A time-limited JSON Web Token (JWT) that controls how long a user can access an Access application before re-authenticating. Unlike sessions, a device registration is persistent — it does not expire and exists until you delete it.                                                                                                                                                                |

## Review device registration status

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

To review how many device registrations are associated with a device:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Teams & Resources** > **Devices**.
2. Select a device and select **View details**.
3. Scroll down to **Users** and review users who enrolled on this device.

To review a device registration's status:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources** > **Devices**.
2. Select the device and select **View details**.
3. Scroll down to **Users** and find the user associated with the device.
4. Review the status (such as `Active` or `Revoked`) of the device registration under **Status**.

</TabItem>
<TabItem label="API">

To get a list of all device registrations, including active and revoked registrations:

<APIRequest
	path="/accounts/{account_id}/devices/registrations"
	method="GET"
	parameters={{
  	status: "all",
  	per_page: "50"
}}
/>

```json output {12}
{
		"created_at": "2026-01-26T19:27:49.770372Z",
		"device": {
			"client_version": "2025.10.186",
			"id": "11ffb86f-3f0c-4306-b4a2-e62f872b166a",
			"name": "My Device"
		},
		"id": "11ffb86f-3f0c-4306-b4a2-e62f872b166a",
		"key": "<U+QTP50RsWfeLGHF4tlGDnmGeuwtsz46KCHr5OyhWq00Rsdfl45mgnQAuEJ6CO0YrkyTl9FUf5iB0bwYR3g4EEFEHhtu6jFaqfMrBMBSz6itv9HQXkaR9OieKQ==",
		"key_type": "secp256r1",
		"last_seen_at": "2026-01-29T00:57:57.925979Z",
		"revoked_at": "2026-01-29T00:58:16.704026Z",
		"tunnel_type": "masque",
		"updated_at": "2026-01-29T00:58:16.704026Z",
		"user": {
			"email": "user@example.com",
			"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
			"name": ""
		}
	},

```

A `revoked_at` timestamp indicates that the device registration has a [revoked status](#registration-status). If `revoked_at` is null or not present, it means the registration status is active.

</TabItem>
</Tabs>

A deleted device registration is permanently removed from the account and no longer appears in your device list. Deletion is permanent and requires re-registering the device.

:::caution[Device registrations will automatically re-register]

Deleting or revoking a device registration is not permanent without [removing user access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-user-access) and [removing service token access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-service-token-access).

If the user or service token can successfully re-authenticate, a new device registration will be automatically created following a deletion or revocation.

:::

### Registration status

Registrations can have the following statuses:

| Status | Description |
| --- | --- |
| **Active** | Registered and able to connect via the Cloudflare One Client. This is the expected operational state. |
| **Revoked** | The registration's public key is invalidated. Revocation does not release the assigned virtual IP addresses. |

## Manage device registrations

The following table summarizes the actions available for managing device registrations and devices. For all actions, if the user or service token can still re-authenticate, a new registration will be created automatically. To permanently remove access, refer to [Device management](#device-management).

| Action                                                 | What it does                                                                                             | Virtual IPs released?                                  | When to use                                                                                                                                                                                  |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Delete a registration](#delete-a-device-registration) | Permanently removes a single device registration and its configuration.                                  | Yes                                                    | You want to fully remove a user's enrollment from a device and free up the virtual IP.                                                                                                       |
| [Revoke a registration](#revoke-a-device-registration) | Invalidates the registration's public key, blocking it from connecting. The registration record remains. | No                                                     | You want to temporarily block a device from connecting but preserve the registration record. |
| [Delete a device](#delete-a-device)                    | Removes the physical device record and all its associated registrations.                                 | Yes | You want to fully remove a device and all associated registrations.                                                                                                                 |

### Delete a device registration

Devices can have multiple device registrations. Deleting one registration does not affect other registrations on the same device.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

To delete a device registration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources** > **Devices**.
2. Select the device > **View details**.
3. Go to **Users** and mark the checkbox next to the device registration you want to delete.
4. Select **Action** > _Delete access_.

</TabItem>
<TabItem label="API">

To delete a single device registration using the [API](/api/resources/zero_trust/subresources/devices/subresources/registrations/methods/delete/):

<APIRequest
	path="/accounts/{account_id}/devices/registrations/{registration_id}"
	method="DELETE"
/>

To bulk delete multiple device registrations:

```curl
curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/device/registrations/registrations?id=reg_id_1&id=reg_id_2&id=reg_id_3" \
	--request DELETE\
	--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

</TabItem>
</Tabs>

The device registration is now permanently deleted, and its virtual IP address is released back into the available pool for reassignment.

:::caution[Automatic device re-registration]

If you delete a device registration, but the Cloudflare One Client remains installed and is successfully able to re-authenticate, a new device registration will be created for the user or service token. For long-term, permanent denial of access, you should [remove the user from your device enrollment policies or your IdP](/cloudflare-one/team-and-resources/devices/device-registration/#remove-user-access) or [remove service token access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-service-token-access).

:::

### Revoke a device registration

Revoking a device registration invalidates its associated public key, which disallows the specific device registration from connecting to Cloudflare's network. Revoking a device registration does not release the virtual IPs that are assigned to the registration. Because virtual IPs are a finite resource, Cloudflare strongly advises deleting a registration rather than revoking it.

:::caution[Automatic unrevocation]

If a user re-authenticates while the device registration is revoked, a new device registration will be created for the user. For long-term, permanent denial of access, you should [remove the user from your device enrollment policies or your IdP](/cloudflare-one/team-and-resources/devices/device-registration/#remove-user-access) and [remove service token access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-service-token-access).

:::

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources** > **Devices**.
2. Select the device and select **View details**.
3. To revoke access, select **Revoke access**. This revokes access for all associated registrations on the device.
4. To unrevoke access, scroll down to the **Users** section and select one or more users using the checkbox. Select **Actions** > **Unrevoke access**.

### Delete a device

Deleting a device removes the physical device from your Cloudflare Zero Trust account. This action automatically deletes all associated device registrations.

Devices that have zero active registrations (because all registrations were deleted) are hidden by default in Cloudflare One > **Teams & Resources** > **Devices** table. You may need to adjust the filter to view devices with zero device registrations.

:::caution[Automatic device re-creation]

If you delete a device record, but the Cloudflare One Client remains installed and a user or service token is successfully able to re-authenticate, a device record will be automatically created in your dashboard with a new device registration. For permanent deletion, you should [remove service token access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-service-token-access).

:::

To delete a device:

1. In Cloudflare One > **Teams & Resources** > **Devices**.
2. Select the device and select **View details**.
3. Select **Delete**.

## Device management

Seat management (billing) and access management are separate processes. Deleting a device registration does not free up the user's seat or block them from accessing internal resources. To fully remove a user's access, you must take additional steps as described below.

### Remove user access

Deleting or revoking a registration will not be permanent if the user can re-authenticate. To prevent a user from re-authenticating and creating new device registrations, you must remove them from your [device enrollment policies](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) or from your Identity Provider (IdP).

- If your device enrollment policies allow a broad domain (for example, `@company.com`), remove the user from your IdP. This prevents the user from authenticating through Access, effectively blocking them from enrolling devices.
- If your device enrollment policies list specific user emails (for example, `sally@company.com`), you must remove that specific email from your device enrollment policies. Additionally, you can add an explicit Exclude rule for that user to the policy.

After you have removed user access, to fully decommission a device, [remove service token access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-service-token-access), if any exists. Devices with existing registrations will remain connected to Cloudflare until those specific device registrations are manually deleted.

### Remove service token access

If you delete a service token's device registration, a new device registration for the service token will be automatically created without user interaction. For device registration deletion to be permanent, you must update your device enrollment policies to remove the service token.

To block a service token from re-authenticating, you must either:

1. Delete the enrollment policy associated with the token, or modify the enrollment policy to no longer include the token (by removing its specific Include rule).
2. (Optional) [Delete the service token](/cloudflare-one/access-controls/service-credentials/service-tokens/).
      <br/> You cannot use this service token to create new registrations.
      <br/> You cannot delete a service token while it is attached to a device enrollment policy.
3. Delete the service token [device registration](/cloudflare-one/team-and-resources/devices/device-registration/#delete-a-device-registration).
4. (Optional) To fully decommission a device, [remove user access](/cloudflare-one/team-and-resources/devices/device-registration/#remove-user-access), if any exists. Devices with existing registrations will remain connected to Cloudflare until those specific device registrations are manually deleted.

:::caution[Impact on existing registrations]

If a single service token was used to enroll multiple devices, removing that token from your enrollment policies prevents all those devices from re-registering. Devices with existing registrations will remain connected to Cloudflare until those specific device registrations are manually deleted.

:::

### Seat management (billing)

Deleting a device or a device registration does not affect [seat usage](/cloudflare-one/team-and-resources/users/seat-management/). Seats are tied to the user identity, not to individual devices.

To stop a user from consuming a seat, you must remove the user from your Zero Trust Organization.

<Render file="users/remove-user-from-seat" product="cloudflare-one" />

# Devices

import { DirectoryListing, Render } from "~/components";

Configure devices to send DNS queries to Cloudflare, or proxy all traffic leaving the device through Cloudflare's network.

<DirectoryListing />

# Business Continuity Guide

This guide helps you build business continuity strategies for the Cloudflare One Client by documenting available disconnection mechanisms and providing decision guidance for handling service degradation or infrastructure unavailability.

## Current resilience posture

The Cloudflare One Client operates on Cloudflare's globally distributed network with 300+ points of presence (PoPs) worldwide. Anycast routing automatically directs client connections to the nearest healthy PoP without manual intervention. The client maintains locally cached policies and continues enforcing security controls even when unable to reach Cloudflare's management systems.

For detailed architecture information, refer to the [Cloudflare One Client documentation](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) and the [Cloudflare Network and Service Resilience Whitepaper](https://cf-assets.www.cloudflare.com/slt3lc6tev37/7ad0dpR3YyqxMlikPfbBgn/020b7450909f03ccf3c7dcfb0e99fc2e/Resilience_Whitepaper.pdf).

## Fail-open vs. fail-closed decisions

:::note

**Critical decision framework**

The Cloudflare One Client operates in **fail-closed mode by default**: if the client cannot reach Cloudflare, it remains connected and blocks traffic rather than failing open to unprotected Internet access. This protects your security posture but requires active decision-making during incidents.

**When to fail open** (Cloudflare One Client stops trying to connect and allows network connectivity without Cloudflare One Client protections):

- User productivity is critically impaired and business operations are at risk
- Emergency access to non-protected resources is required
- Forensic investigation requires raw traffic visibility

**When to fail closed** (Cloudflare One Client blocks network connectivity until it can re-establish a tunnel to Cloudflare):

- Cloudflare edge services are operational (traffic is processing normally)
- Only management dashboard is unavailable (policies continue enforcing)
- Regulatory/compliance requirements prohibit unfiltered Internet access
- Security incident requires maintaining visibility and control

:::

The mechanisms below help you execute fail-open decisions when needed. Document your decision criteria in advance and ensure appropriate stakeholders have authorization to trigger disconnection.

## Customer impact and decision guidance

<table>
  <thead>
    <tr>
      <th>Scenario</th>
      <th>Mechanism</th>
      <th>Guidance</th>
      <th>Prerequisites and limitations</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <p><strong>Complete unavailability during Cloudflare infrastructure outage</strong></p>
        <p>Example: Cloudflare management systems unreachable; Global Disconnection unavailable but users need Internet access to maintain business operations.</p>
      </td>
      <td>
        <p><strong><a href="/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect">External Emergency Disconnect</a></strong></p>
        <p>A customer-hosted HTTPS endpoint that clients poll for disconnect signals, operating independently of Cloudflare infrastructure.</p>
      </td>
      <td>
        <p><strong>Use when:</strong> Cloudflare's management systems are unreachable but you need to disconnect clients to restore Internet access.</p>
        <p><strong>Guidance:</strong> Pre-configure this mechanism before outages occur. During an incident, update your endpoint to return <code>{'{"emergency_disconnect": true}'}</code>.</p>
        <p><strong>Expected outcome:</strong> Clients disconnect within 1–2 polling intervals (configurable, default 60 seconds); users regain direct Internet access without security controls.</p>
      </td>
      <td>
        <p><strong>Prerequisites:</strong></p>
        <ul>
          <li>Customer-hosted HTTPS endpoint (IPv4/IPv6 address, not domain)</li>
          <li>SHA-256 fingerprint of TLS certificate</li>
          <li>MDM deployment for group-differentiated responses</li>
        </ul>
        <p><strong>Limitations:</strong></p>
        <ul>
          <li><strong>Not available on iOS, Android, or ChromeOS</strong></li>
          <li>Customer responsible for endpoint maintenance and certificate renewal</li>
          <li>No Cloudflare logging of disconnect events</li>
          <li>Split Tunnel configuration must not include the endpoint IP</li>
        </ul>
        <p><strong>Security impact:</strong> Loss of all Zero Trust controls (same as Global Disconnection).</p>
      </td>
    </tr>
    <tr>
      <td>
        <p><strong>Complete unavailability of client connectivity</strong></p>
        <p>Example: Client cannot establish secure tunnel; users unable to access protected applications or filtered Internet.</p>
      </td>
      <td>
        <p><strong>Global Disconnection</strong></p>
        <p>Instantly disconnect all Cloudflare One Clients from the secure tunnel via Dashboard or API.</p>
      </td>
      <td>
        <p><strong>Use when:</strong> You need immediate fleet-wide disconnection and Cloudflare's management systems are reachable.</p>
        <p><strong>Guidance:</strong> Check the <a href="https://www.cloudflarestatus.com/">Cloudflare status page</a> first. If Cloudflare infrastructure is experiencing issues, this mechanism may be unavailable — use <a href="/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect">External Emergency Disconnect</a> instead.</p>
        <p><strong>Expected outcome:</strong> All clients disconnect within seconds; users have direct Internet access without filtering, threat protection, or private application connectivity.</p>
      </td>
      <td>
        <p><strong>Prerequisites:</strong></p>
        <ul>
          <li>Dashboard or API access</li>
          <li>Account administrator permissions</li>
        </ul>
        <p><strong>Limitations:</strong></p>
        <ul>
          <li>Requires connectivity to Cloudflare's management systems</li>
          <li>Account-wide only (no group scoping)</li>
          <li>Unavailable during complete Cloudflare outages</li>
        </ul>
        <p><strong>Security impact:</strong></p>
        <ul>
          <li>Loss of web filtering and malware protection</li>
          <li>Loss of data loss prevention (DLP) inspection</li>
          <li>Loss of access to private applications</li>
          <li>Unencrypted DNS queries (potential privacy exposure)</li>
        </ul>
      </td>
    </tr>
    <tr>
      <td>
        <p><strong>Individual device issue requiring immediate local override</strong></p>
        <p>Example: Single user locked out due to policy misconfiguration; client switch disabled but user needs emergency access.</p>
      </td>
      <td>
        <p><strong>Admin Override Codes</strong></p>
        <p>Time-limited, single-use codes allowing IT administrators to temporarily unlock client settings on a specific device.</p>
      </td>
      <td>
        <p><strong>Use when:</strong> An individual device requires immediate attention. This is the only option for iOS and Android users when External Emergency Disconnect is unavailable.</p>
        <p><strong>Guidance:</strong> Generate the code in the Dashboard, provide it to the user over a secure channel, and have the user enter it locally to temporarily bypass the locked switch.</p>
        <p><strong>Expected outcome:</strong> Temporary local override allowing the user to disconnect the client for one hour.</p>
      </td>
      <td>
        <p><strong>Prerequisites:</strong></p>
        <ul>
          <li><strong>Lock client device switch</strong> policy enabled</li>
          <li>Dashboard access to generate codes</li>
          <li>Direct communication with the end user</li>
        </ul>
        <p><strong>Limitations:</strong></p>
        <ul>
          <li>One code per device per hour</li>
          <li>Manual IT intervention required</li>
          <li>Not scalable for fleet-wide scenarios</li>
          <li>Requires staffed IT during incidents</li>
        </ul>
        <p><strong>Security impact:</strong> Single device loses Zero Trust controls for one hour.</p>
      </td>
    </tr>
    <tr>
      <td>
        <p><strong>Degraded performance impacting user productivity</strong></p>
        <p>Example: High latency through client tunnel; intermittent connection drops affecting work quality.</p>
      </td>
      <td>
        <p><strong>Graduated response strategy</strong></p>
        <p>Use a combination of mechanisms based on scope and severity. Use <a href="/cloudflare-one/insights/dex/">Digital Experience (DEX)</a> to determine scope and severity.</p>
      </td>
      <td>
        <p><strong>Guidance by scope:</strong></p>
        <ul>
          <li><strong>Single device:</strong> Admin Override Code → manual disconnect</li>
          <li><strong>Group or department:</strong> External Emergency Disconnect with MDM-differentiated endpoints</li>
          <li><strong>Organization-wide:</strong> Global Disconnection (if Cloudflare reachable) or External Emergency Disconnect</li>
        </ul>
        <p><strong>Decision factors:</strong> Balance user productivity needs against security requirements. For regulated industries, consult your compliance team before disconnecting.</p>
        <p><strong>Expected outcome:</strong> Restored user productivity with a documented security trade-off.</p>
      </td>
      <td>
        <p><strong>Prerequisites:</strong></p>
        <ul>
          <li>Documented decision criteria for fail-open vs. fail-closed</li>
          <li>Pre-configured mechanisms before incidents occur</li>
          <li>Clear authorization matrix</li>
        </ul>
        <p><strong>Limitations:</strong></p>
        <ul>
          <li>Each mechanism has different infrastructure dependencies</li>
          <li>Mobile platforms have limited options</li>
        </ul>
        <p><strong>Security impact:</strong> Scope-dependent — refer to individual mechanism entries above.</p>
      </td>
    </tr>
    <tr>
      <td>
        <p><strong>Management dashboard unavailable, traffic processing normally</strong></p>
        <p>Example: Dashboard and API unreachable; edge services and client connections remain functional with cached policies.</p>
      </td>
      <td>
        <p><strong>No action required</strong></p>
        <p>Edge services continue operating using cached configurations. New configuration changes will be unavailable until management systems recover.</p>
      </td>
      <td>
        <p><strong>Use when:</strong> Cloudflare's management systems are unavailable but user traffic continues processing normally.</p>
        <p><strong>Guidance:</strong> Monitor the <a href="https://www.cloudflarestatus.com/">Cloudflare status page</a>. No customer action is typically required — edge services enforce cached policies until management systems recover.</p>
        <p><strong>Expected outcome:</strong> Existing configuration continues to apply; configuration changes resume when management systems recover.</p>
      </td>
      <td>
        <p><strong>Prerequisites:</strong></p>
        <ul>
          <li>Monitoring of the Cloudflare status page</li>
          <li>Understanding that traffic processing and management are separate systems</li>
        </ul>
        <p><strong>Limitations:</strong></p>
        <ul>
          <li>Cannot modify policies during the outage</li>
          <li>Cannot trigger Global Disconnection from Dashboard</li>
          <li>Real-time logs and analytics may be delayed</li>
        </ul>
        <p><strong>Security impact:</strong> None — security controls remain active.</p>
      </td>
    </tr>
  </tbody>
</table>

:::caution

**Mobile platform limitation**

External Emergency Disconnect is **not available on iOS, Android, or ChromeOS**. For mobile users during Cloudflare infrastructure outages when Global Disconnection is also unavailable, Admin Override Codes are the only option (requires individual IT intervention).

:::

## Additional considerations

### Prerequisites to validate before incidents

- Turn on the Global Disconnection feature in the Dashboard.
- Configure an External Emergency Disconnect endpoint and upload the certificate fingerprint.
- Test all mechanisms in a non-production environment.
- Document fail-open vs. fail-closed decision criteria and create an authorization matrix.
- Validate that IT and Security staff have backup mechanisms to access critical infrastructure.
- Practice using backup mechanisms regularly across departments and geographies.

**Access and credentials needed during incidents:**

- Cloudflare Dashboard administrator access
- API token with device settings permissions (for programmatic control)
- MDM administrator credentials (for group-differentiated responses)

### Testing recommendations

- Use a dedicated test organization or tenant for initial validation.
- Test with a small pilot group before fleet-wide deployment.
- Conduct quarterly testing of all three disconnection mechanisms.
- Run an annual full business continuity exercise including decision-making scenarios.

**Common testing issues:**

- External Emergency Disconnect changes take 1–2 polling intervals to take effect (default 60 seconds).
- Split Tunnel **Include** mode automatically excludes emergency endpoint IPs.
- Certificate fingerprint changes require MDM re-deployment to all affected devices.

### Integration dependencies

When you disconnect the Cloudflare One Client, the following controls are affected:

- **Web filtering and threat protection:** DNS and HTTP policies stop enforcing; users have direct, unfiltered Internet access.
- **Data loss prevention (DLP):** Content inspection stops; sensitive data uploads and downloads occur without DLP controls.
- **Private application access:** Connectivity to applications protected by Cloudflare Tunnel is lost. Consider alternative access methods such as a direct VPN for critical applications.
- **Gateway logging and analytics:** No visibility into user traffic during disconnection.

### When to contact Cloudflare support

**Contact support immediately if:**

- A suspected Cloudflare infrastructure issue is affecting multiple customers.
- You are unable to access the Dashboard during a critical security incident.
- An External Emergency Disconnect misconfiguration has caused a fleet-wide stuck state.

**Information to provide when opening a ticket:**

- Account ID and organization name
- Affected device count and platform distribution
- Results of your Cloudflare status page check
- Client diagnostic logs (`warp-diag`)
- Timeline and troubleshooting steps already taken

## Related resources

| Resource | Link |
|---|---|
| Product documentation | [Cloudflare One Client documentation](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) |
| API reference | [Zero Trust Devices Settings API](https://developers.cloudflare.com/api/resources/zero_trust/subresources/devices/subresources/settings/) |
| Global Disconnection | [Global Disconnection settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#disconnect-the-cloudflare-one-client-on-all-devices) |
| External Emergency Disconnect | [External Emergency Disconnect documentation](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect) |
| Admin Override Codes | [Admin Override Codes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-admin-override-codes) |
| MDM deployment | [MDM Deployment Guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) |
| Terraform provider | [Cloudflare Terraform Provider – Zero Trust Devices](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_settings) |
| Status page | [cloudflarestatus.com](https://www.cloudflarestatus.com/) |
| Troubleshooting | [Client Troubleshooting Guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/) |
| Resilience Whitepaper | [Cloudflare Network and Service Resilience Whitepaper](https://cf-assets.www.cloudflare.com/slt3lc6tev37/7ad0dpR3YyqxMlikPfbBgn/020b7450909f03ccf3c7dcfb0e99fc2e/Resilience_Whitepaper.pdf) |

# Cloudflare One Client

import { Render, Stream } from "~/components";

## About the Cloudflare One Client

The Cloudflare One Client (formerly WARP) securely and privately sends traffic from your devices to Cloudflare's global network, where [Cloudflare Gateway](/cloudflare-one/traffic-controls/) can apply advanced web filtering. The client also reports device health information — such as OS version, disk encryption status, and the presence of specific applications — so that you can enforce [device posture checks](/cloudflare-one/reusable-components/posture-checks/) in your Access and Gateway policies.

## How the Cloudflare One Client works

The Cloudflare One Client creates encrypted connections between your device and Cloudflare's network. It does this in two ways:

- **Proxy tunnel** — Encrypts and routes your device's internet and private network traffic through Cloudflare, using the [WireGuard](https://www.wireguard.com/) or [MASQUE](https://blog.cloudflare.com/zero-trust-warp-with-a-masque) protocol.
- **DNS proxy** — Sends your device's DNS queries to Cloudflare over an encrypted channel ([DNS-over-HTTPS](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/)), where [Gateway DNS policies](/cloudflare-one/traffic-controls/dns-policies/) can filter them.

The client runs on all major operating systems and can be deployed through common endpoint management tools (such as Intune, JAMF, or JumpCloud).

The Cloudflare One Client consists of:

- **Graphical User Interface (GUI):** A control panel that allows end users to view the client's [status](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/connectivity-status/) and perform actions such as connecting or disconnecting.
- **WARP daemon (or service):** The core background process responsible for establishing the encrypted connections described above and handling all client functionality on your device.

For more information on how the Cloudflare One Client routes traffic, refer to the [client architecture page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/) and watch the video below.

<Stream
	id="31178cc41d0ec56d42ef892160589635"
	title="Understand Cloudflare WARP basics"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/4fdd3005-7995-4876-8faa-4375f9236500/public"
	showMoreVideos={false}
	chapters={{
		"Introduction and WARP GUI Basics": "0s",
		"Consumer vs. Corporate WARP": "57s",
		"Device Profiles Explained": "1m35s",
		"WARP Operating Modes": "2m12s",
		"Split Tunneling": "3m44s",
		Conclusion: "4m56s",
	}}
/>

## Installation details

<Render file="warp/warp-installation-details" product="cloudflare-one" />

## Key benefits of using the Cloudflare One Client

Deploying the Cloudflare One Client significantly enhances your organization's security and visibility within Cloudflare Zero Trust:

- **Unified security policies everywhere**: With the Cloudflare One Client deployed in the Traffic and DNS mode, [Gateway policies](/cloudflare-one/traffic-controls/) are not location-dependent — they can be enforced anywhere.

- **Advanced web filtering and threat protection**: Activate Gateway features for your device traffic, including:

      - [Anti-Virus scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/)
      - [HTTP filtering](/cloudflare-one/traffic-controls/http-policies/)
      - [Browser Isolation](/cloudflare-one/traffic-controls/http-policies/#isolate)
      - [Identity-based policies](/cloudflare-one/traffic-controls/network-policies/)

- **Application and device-specific insights**: View which SaaS applications your users are accessing and review their approval status on the [Shadow IT Discovery](/cloudflare-one/insights/analytics/shadow-it-discovery/) page. Monitor device and network performance with [Digital Experience Monitoring (DEX)](/cloudflare-one/insights/dex/) to detect connectivity or performance issues before users report them.

- **Device posture checks**: The Cloudflare One Client provides advanced Zero Trust protection by making it possible to check for [device posture](/cloudflare-one/reusable-components/posture-checks/). By setting up device posture checks, you can build Access or Gateway policies that check for a device's location, disk encryption status, OS version, and more.

- **Secure private and infrastructure access**: Connect devices to internal networks and applications through [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/) without exposing them to the public internet. The client is also required for [Access for Infrastructure](/cloudflare-one/networks/connectors/cloudflare-tunnel/use-cases/ssh/ssh-infrastructure-access/), which provides SSH access using short-lived certificates and detailed audit logging.

## Client modes

The Cloudflare One Client offers flexible [operating modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) to suit your specific needs:

- **Traffic and DNS mode** (default) — Routes device traffic (by default, all ports and protocols) and DNS queries through Cloudflare for filtering, inspection, and policy enforcement. Traffic exclusions can be configured with [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/).
- **DNS-only mode** — Routes only DNS queries through Cloudflare. Use this mode if you only need DNS-level filtering without inspecting web or application traffic.

Other modes (Traffic only, Local proxy, Posture only) are also available. For details, refer to the [operating modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) page.

## Next steps

- Review the [first-time setup](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/set-up/) guide to [install](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) and [deploy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) the Cloudflare One Client on your corporate devices.
- Review possible [client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) and [settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/) to best suit your organization's needs.
- Explore [Cloudflare Gateway](/cloudflare-one/traffic-controls/) to enforce advanced DNS, network, HTTP, and egress policies with the Cloudflare One Client.

# First-time setup

import { GlossaryTooltip } from "~/components";

This guide walks you through setting up the Cloudflare One Client (formerly WARP) for your organization for the first time. After completing these steps, your devices will route traffic through Cloudflare's network, where you can apply security policies.

Choose a setup mode based on your needs:

- [**Traffic and DNS mode** (default)](#traffic-and-dns-mode-default) — Enables the full suite of security features, including HTTP inspection, identity-based policies, and device posture checks.
- [**DNS-only mode**](#dns-only-mode) — Filters only DNS queries. Does not inspect HTTP traffic or enforce device posture checks.

## Traffic and DNS mode (default)

This mode enables the complete suite of [device security features](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/).

### 1. Create a Cloudflare Zero Trust account.

The [Cloudflare One dashboard](https://dash.cloudflare.com/one/) will be your go-to place to check device connectivity data, as well as create Secure Web Gateway and Zero Trust policies for your organization.

As you complete the [Cloudflare Zero Trust onboarding](/cloudflare-one/setup/), you will be asked to create a <GlossaryTooltip term="team name">team name</GlossaryTooltip> for your organization. You will need the team name when you deploy the Cloudflare One Client on your devices; it will allow your users to connect to your organization's Cloudflare Zero Trust instance.

### 2. Set up a login method.

Configure [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) or connect a [third-party identity provider](/cloudflare-one/integrations/identity-providers/) in Zero Trust. This is the login method your users will utilize when authenticating to add a new device to your Cloudflare Zero Trust setup.

### 3. Define device enrollment permissions.

Create [device enrollment rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) to define which users in your organization should be able to connect devices to your organization's Cloudflare Zero Trust setup. As you create your rule, you will be asked to select which login method you would like users to authenticate with.

### 4. Install the Cloudflare root certificate on your devices.

Advanced security features including HTTP traffic inspection require users to install and trust the [Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on their machine or device. If you are installing certificates manually on all your devices, these steps will need to be performed on each new device that is to be subject to HTTP filtering.

### 5. Download and deploy the Cloudflare One Client to your devices.

Choose one of the [different ways](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) to deploy the Cloudflare One Client, depending on what works best for your organization.

### 6. Log in to your organization's Cloudflare Zero Trust instance from your devices.

Once the Cloudflare One Client is installed on the device, [log in to your Zero Trust organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/). The user is prompted to authenticate with one of your configured login methods. New organizations include the [Cloudflare identity provider](/cloudflare-one/integrations/identity-providers/cloudflare/) as the default, so users can sign in with their Cloudflare account credentials. You can also connect a [third-party identity provider](/cloudflare-one/integrations/identity-providers/) or enable a [one-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/).

Next, build [Secure Web Gateway policies](/cloudflare-one/traffic-controls/) to filter DNS, HTTP, and Network traffic on your devices.

## DNS only mode

This mode is best suited for organizations that only want to apply DNS filtering to outbound traffic from their company devices. It does not enable advanced HTTP filtering features such as HTTP policies, identity-based policies, device posture checks, or Browser Isolation.

### 1. Create a Cloudflare Zero Trust account.

Zero Trust will be your go-to place to check device connectivity data, as well as create Secure Web Gateway and Zero Trust policies for your organization.

As you complete the [Cloudflare Zero Trust onboarding](/cloudflare-one/setup/), you will be asked to create a <GlossaryTooltip term="team name">team name</GlossaryTooltip> for your organization. You will need the team name when you deploy the Cloudflare One Client on your devices; it will allow your users to connect to your organization's Cloudflare Zero Trust instance.

### 2. Set up a login method.

Configure [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) or connect a [third-party identity provider](/cloudflare-one/integrations/identity-providers/) in Zero Trust. This is the login method your users will utilize when authenticating to add a new device to your Cloudflare Zero Trust setup.

### 3. Define device enrollment permissions.

Create [device enrollment rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) to define which users in your organization should be able to connect devices to your organization's Cloudflare Zero Trust setup. As you create your rule, you will be asked to select which login method you would like users to authenticate with.

### 4. (Optional) Add a DNS location to Gateway.

By default, the Cloudflare One Client sends DNS queries to Cloudflare using an encrypted protocol called DNS-over-HTTPS (DoH). If you need to apply different DNS policies to different offices or network locations, [add a DNS location](/cloudflare-one/traffic-controls/resolver-endpoints/) to Gateway. Gateway will assign a unique <GlossaryTooltip term="DoH subdomain">DoH subdomain</GlossaryTooltip> to each location, which you provide as a parameter when deploying the Cloudflare One Client to your devices.

### 5. Download and deploy the Cloudflare One Client to your devices.

Choose one of the [different ways](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) to deploy the Cloudflare One Client, depending on what works best for your organization.

Next, create [DNS policies](/cloudflare-one/traffic-controls/dns-policies/) to control how DNS queries from your devices get resolved.

# Uninstall the Cloudflare One Client

The following procedures will uninstall the Cloudflare One Client (formerly WARP) from your device. If you used the Cloudflare One Client to deploy a root certificate, the certificate will also be removed.

## Windows

1. Go to Windows Settings (Windows Key + I).
2. Select **Apps**.
3. Select **Installed Apps**.
4. Scroll to find the Cloudflare One Client application, click the three dots (...), and select **Uninstall**.

:::note
If your organization turned on [uninstall protection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/uninstall-protection/), the installer prompts you for a device-specific uninstall code. Contact your administrator to get the code.
:::

## macOS

We include an uninstall script as part of the macOS package that you originally used.

1. To find and run the uninstall script, run the following commands:

```sh
cd /Applications/Cloudflare\ WARP.app/Contents/Resources
./uninstall.sh
```

2. If prompted, enter your admin credentials to proceed with the uninstall.

:::note

You can bypass the **Are you sure** prompt by passing `-f` as a parameter to the macOS uninstall command.

:::

## Linux

On CentOS 8, RHEL 8:

```sh
sudo yum remove cloudflare-warp
```

On Ubuntu 18.04, Ubuntu 20.04, Ubuntu 22.04, Debian 9, Debian 10, Debian 11:

```sh
sudo apt remove cloudflare-warp
```

## iOS and Android

1. Find the Cloudflare One Agent application (or the legacy 1.1.1.1 application) on the home screen.
2. Select and hold the application tile, and then select **Remove App**.
3. Select **Delete App**.

:::note

If you [manually deployed a Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/), remember to manually delete the certificate from the device.
:::

# Client sessions

import { Render, Badge } from "~/components";

Client sessions control how often users must re-authenticate with your identity provider (IdP) while using the Cloudflare One Client (formerly WARP). Unlike legacy VPNs, which enforce a single global session timeout, Cloudflare One allows you to set session timeouts per application or per policy. You can configure session timeouts for your [Access applications](#configure-warp-sessions-in-access) or as part of your [Gateway policies](#configure-warp-sessions-in-gateway).

<Render file="warp/warp-sessions-intro" product="cloudflare-one" />

## Prerequisites

Ensure that traffic can reach your IdP and `<your-team-name>.cloudflareaccess.com` through the Cloudflare One Client.

## Configure client sessions in Gateway

<Render file="warp/warp-sessions-gateway" product="cloudflare-one" />

:::note[Enforce a global timeout]

To enforce a global reauthentication event, set each of your Network or HTTP policies to the same device client session duration.
:::

## Configure client sessions in Access <Badge text="Beta" variant="caution" size="small" />

You can allow users to log in to Access applications using their device client session. **Authenticate with Cloudflare One Client** is only supported for Access applications protected by Allow or Block policies.

To configure device client sessions for Access applications:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. Enable **Authenticate with Cloudflare One Client**.
3. Under **Session duration**, choose a session timeout value of up to 90 days. This timeout will apply to all Access applications that have **Authenticate with Cloudflare One Client** enabled.

:::note

This timeout value does not apply to [device client session checks in Gateway policies](#configure-warp-sessions-in-gateway).
:::

4. (Optional) To enable **Authenticate with Cloudflare One Client** by default for all existing and new applications, select **Apply to all Access applications**. You can override this default setting on a per-application basis when you [create](/cloudflare-one/access-controls/applications/http-apps/) or modify an Access application.
5. Select **Save**.

Users can now authenticate once with the Cloudflare One Client and have access to your Access applications for the configured period of time. The session timer resets when the user re-authenticates with the IdP used to enroll in the Cloudflare One Client.

## Force user interaction with IdP

If the user has an active browser session with the IdP, the Cloudflare One Client will use the existing browser cookies to re-authenticate and the user will not be prompted to re-enter their credentials. You can override this behavior to require explicit user interaction in the IdP.

### Supported IdPs

- [Microsoft Entra ID](/cloudflare-one/integrations/identity-providers/entra-id/#force-user-interaction-during-warp-reauthentication)

## Manually reauthenticate

<Render file="warp/manually-reauth" product="cloudflare-one" />

## Limitations

- **Only one user per device** — If a device is already registered with User A, User B will not be able to log in on that device through the re-authentication flow. To switch the device registration to a different user, User A must first log out from Zero Trust (if [Allow device to leave organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-device-to-leave-organization) is enabled), or an admin can revoke the registration from **Team & Resources** > **Devices**. User B can then properly [enroll](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/).

- **Active connections are not terminated** — Active sessions such as SSH and RDP will remain connected beyond the timeout limit.
- **Binding Cookie is not supported** - **Authenticate with Cloudflare One Client** will not work for Access applications that have the [Binding Cookie](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/#binding-cookie) enabled.

## Related resources

- [Connectivity status](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/connectivity-status/) - Learn about the status messages displayed by the Cloudflare One Client during its connection process, and understand each stage as the client establishes a secure tunnel to Cloudflare.

# Device IPs

import { Render, Details } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                        | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.10.186.0          |
| macOS    | ✅           | 2025.10.186.0          |
| Linux    | ✅           | 2025.10.186.0          |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

A device IP identifies and routes traffic to a specific device in your Zero Trust organization. When a user registers the Cloudflare One Client (formerly WARP), Cloudflare assigns a virtual IPv4 and IPv6 address to the [device registration](/cloudflare-one/team-and-resources/devices/device-registration/). The Cloudflare One Client uses these IP addresses to create a [virtual network interface](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#ip-traffic) on the device, which allows your private network to reach the device via [Cloudflare Mesh](/mesh/) or [Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/) on-ramps.

You can verify device IPs and, if needed, reconfigure address pools to avoid overlapping IPs with existing internal resources.

## Default device IPs

By default, Cloudflare assigns device IPs from the following address space:

- Default IPv4: `100.96.0.0/12`
- Default IPv6: `2606:4700:0cf1:1000::/64`

If your organization already uses the default IPv4 range for internal networking, or if you require more granular IP assignments for firewall policy management, you can configure custom device IPv4 subnets. You can assign different IPv4 subnets to devices based on the user's identity.

The default IPv6 range is owned by Cloudflare and therefore should not conflict with services on your private network. The device IPv6 range is not configurable.

## Create an IP subnet

:::note
If your account uses [Cloudflare WAN](/cloudflare-wan/), custom device IP subnets require [Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#unified-routing). If your account uses Legacy Routing, follow [How to upgrade to Unified Routing](/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#how-to-upgrade-to-unified-routing).
:::

Create a custom IP subnet when the [default IPv4 range](#default-device-ips) conflicts with services on your private network.

To define a custom IPv4 subnet for device IPs:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles**.
2. Under **Device IP subnets**, select **Add new IP subnet**.
3. Enter any name for the subnet.
4. In **CIDR**, enter a valid IPv4 CIDR block from the supported private ranges:
   - `10.0.0.0/8`
   - `172.16.0.0/12`
   - `192.168.0.0/16`
   - `100.64.0.0/10`

   The configured CIDR block must be at least size `/24`.

   :::note[Avoid IP conflicts]
   Ensure that the chosen CIDR block does not overlap with existing internal routes, such as local office subnets or virtual private clouds (VPCs) in AWS or GCP.
   :::

5. Select **Add subnet** to save.

Next, [assign this subnet](#assign-device-ips) to a group of devices.

## Assign device IPs

Assign [custom IP subnets](#create-an-ip-subnet) to ensure devices are provisioned within a predictable address space based on specific user identity criteria.

### Prerequisites

- [**Assign a unique IP address to each device**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#assign-a-unique-ip-address-to-each-device) is enabled in your [general device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/).

### Create an IP profile

To assign IP subnets to your devices:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles**.
2. Under **Device IP profiles**, select **Add new IP profile**.
3. Enter a name for this group of devices (for example, `IT department`).
4. Create rules to define the users or devices that will receive these IPs. Learn more about the available [Selectors](#selectors), [Operators](#comparison-operators), and [Values](#value).
5. Choose an existing IPv4 subnet from the dropdown menu, or [create a new subnet](#create-an-ipv4-subnet).
6. Select **Assign IP address**.
7. (Optional) In the **Device IP profiles** table, change the [order of precedence](#order-of-precedence) of IP profiles.

Devices that match your rules are assigned a random IP from this address space upon registration. Only newly registered devices will receive a new IP; existing devices will not see any impact to connectivity. To assign a new IP to an existing device, you must [delete its registration](/cloudflare-one/team-and-resources/devices/device-registration/#delete-a-device-registration) and then [re-enroll the device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) in your Zero Trust organization.

Organizations are currently [limited](/cloudflare-one/account-limits/#warp) to 30 custom device IP profiles per account.

### Selectors

You can configure IP profiles to match against the following selectors or criteria. Identity-based selectors are only available if the user [enrolled the device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) by logging in to an identity provider (IdP).

#### User email

Apply a device profile based on the user's email.

<Render
	file="gateway/selectors/user-email"
	product="cloudflare-one"
	params={{ UIname: "User email" }}
/>

#### User group emails

Apply a device IP profile based on an [IdP group](/cloudflare-one/traffic-controls/identity-selectors/#idp-groups-in-gateway) email address of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-email"
	product="cloudflare-one"
	params={{ UIname: "User group emails" }}
/>

#### User group IDs

Apply a device IP profile based on an [IdP group](/cloudflare-one/traffic-controls/identity-selectors/#idp-groups-in-gateway) ID of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-ids"
	product="cloudflare-one"
	params={{ UIname: "User group IDs" }}
/>

#### User group names

Apply a device IP profile based on an [IdP group](/cloudflare-one/traffic-controls/identity-selectors/#idp-groups-in-gateway) name of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-names"
	product="cloudflare-one"
	params={{ UIname: "User group names" }}
/>

#### User name

<Render
	file="gateway/selectors/user-name"
	product="cloudflare-one"
	params={{ UIname: "User Name" }}
/>

#### SAML attributes

Apply a device IP profile based on an attribute name and value from a [SAML IdP](/cloudflare-one/traffic-controls/identity-selectors/#generic-saml-idp).

<Render file="gateway/selectors/saml-attributes" product="cloudflare-one" />

### Comparison operators

Comparison operators determine how device IP profiles match a selector.

| Operator | Meaning                                      |
| -------- | -------------------------------------------- |
| in       | matches at least one of the defined values   |
| not in   | does not match any of the defined values     |
| is       | equals the defined value                     |
| matches  | regular expression (regex) evaluates to true |

### Value

<Render
	file="gateway/value"
	product="cloudflare-one"
	params={{
		selector: "User email",
		selectordescription: "user's email domain",
		operator: "matches",
		valueOne: "@acme.com",
		valueTwo: "@widgets.com",
	}}
/>

### Logical operators

To evaluate multiple conditions in an expression, select a logical operator:

| Operator | Meaning                                       |
| -------- | --------------------------------------------- |
| And      | match all of the conditions in the expression |
| Or       | match any of the conditions in the expression |

### Order of precedence

The Cloudflare One Client checks the IP profiles from top to bottom as they appear in the Cloudflare One dashboard (lowest precedence number is checked first). The client follows the first match principle — once a device matches an IP profile, the client stops evaluating and no subsequent IP profiles can override the decision. You can rearrange the IP profiles in the Cloudflare One dashboard according to your desired order of precedence.

## Verify device IPs

### Via the dashboard

To check the virtual IP addresses assigned to a specific device registration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**.
2. Select your device > **View details**.
   :::tip[Device filters]
   The Cloudflare One dashboard defaults to showing devices that were last seen within the past year. You can select **Show filters** to change the date range or filter by the last active user.
   :::
3. Scroll down to **Users**. You will see the registrations associated with this device along with their assigned IPv4 and IPv6 addresses.

### Via the CLI

To check the device IP used by the device client's virtual network interface:

<Render file="warp/check-network-interface-ip" product="cloudflare-one" />

## View subnet usage

Monitor the consumption of your IPv4 subnets to ensure you have enough addresses for new device registrations. Devices will be unable to register if they match a subnet with no available IPs.

Use the Cloudflare One dashboard to view a high-level overview of assigned and available IPs:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles**.
2. Locate the **Device IP subnets** table.
3. The **IPs assigned** column displays the total number of IPs currently assigned to active device registrations versus the total capacity of the CIDR block.

If your subnet is approaching capacity, you can [expand your subnet](#edit-an-ip-subnet) to increase the number of available IPs. Alternatively, you can free up IPs by [deleting existing device registrations](/cloudflare-one/team-and-resources/devices/device-registration/#delete-a-device-registration), particularly revoked registrations that may be consuming IP space despite the device no longer being in use.

:::tip[Delete device registrations instead of revoking]
[Revoking a device registration](/cloudflare-one/team-and-resources/devices/device-registration/#revoke-a-device-registration) does not release the virtual IPs that are assigned to the registration. Because virtual IPs are a finite resource, Cloudflare strongly advises deleting a registration rather than revoking it.
:::

To get a list of all device registrations in a subnet (including revoked registrations), use the [Cloudflare API](/api/resources/zero_trust/subresources/devices/subresources/registrations/methods/list/). For example, the following script fetches all device registrations and their device IPs, and outputs all registrations within the specified CIDR block.

<Details header="Example script to filter registrations by IP">
1. Create a new file called `filter-device-ips.py` that contains the following code:
	```python
	import requests
	import ipaddress
	import json

    # --- Configuration ---
    AUTH_EMAIL = "<CLOUDFLARE_ACCOUNT_EMAIL>"
    AUTH_KEY = "<CLOUDFLARE_API_KEY>"  # Refer to https://developers.cloudflare.com/fundamentals/api/get-started/keys/. API token authentication is not currently supported for fetching device IPs.
    ACCOUNT_ID = "<CLOUDLFARE_ACCOUNT_ID"  # Refer to https://developers.cloudflare.com/fundamentals/account/find-account-and-zone-ids/.
    TARGET_CIDR = "100.64.0.0/10"

    # --- API request headers ---
    headers = {
    		"X-Auth-Email": AUTH_EMAIL,
    		"X-Auth-Key": AUTH_KEY,
    		"Content-Type": "application/json"
    }

    def get_all_registrations():
    		"""Fetches all device registrations including revoked registrations. """
    		devices = {}
    		url = f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/devices/registrations"
    		params = {"per_page": 50, "status": "all"}

    		while True:
    				response = requests.get(url, headers=headers, params=params).json()
    				if not response.get('success'):
    						print(f"Error fetching registrations: {response.get('errors')}")
    						break

    				for d in response.get('result', []):
    						# We use the ID as the key to link with IP data later
    						devices[d['id']] = d

    				cursor = response.get('result_info', {}).get('cursor')
    				if not cursor:
    						break
    				params['cursor'] = cursor

    		return devices

    def filter_by_cidr(device_map, network):
    		"""Fetch device IPs and return devices that fall within the target CIDR block."""
    		matches = []
    		device_ids = list(device_map.keys())

    		# API limits IP correlation to batches of 20
    		for i in range(0, len(device_ids), 20):
    				batch = device_ids[i:i+20]
    				# Construct parameters for the IP endpoint
    				params = {f"device_ids[{idx}]": d_id for idx, d_id in enumerate(batch)}
    				url = f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/teamnet/devices/ips"

    				res = requests.get(url, headers=headers, params=params).json()

    				if not res.get('success'):
    						print(f"Error fetching IPs: {res.get('errors')}")
    						continue

    				for item in res.get('result', []):
    						d_id = item.get('device_id')
    						ip_data = item.get('device_ips', {})
    						ipv4_str = ip_data.get('ipv4')

    						if ipv4_str:
    								try:
    										if ipaddress.IPv4Address(ipv4_str) in network:
    												if d_id in device_map:
    														full_data = device_map[d_id]
    														full_data['device_ips'] = ip_data
    														matches.append(full_data)
    								except ValueError:
    										continue

    		return matches

    if __name__ == "__main__":
    		try:
    				net = ipaddress.IPv4Network(TARGET_CIDR, strict=False)

    				print(f"[*] Fetching registrations (status=all)...")
    				all_devices = get_all_registrations()
    				print(f"[*] Found {len(all_devices)} total registrations.")

    				print(f"[*] Checking IP ranges for match...")
    				filtered_list = filter_by_cidr(all_devices, net)

    				if filtered_list:
    						print(f"\n--- Found {len(filtered_list)} Device(s) in {TARGET_CIDR} ---\n")
    						for dev in filtered_list:
    								print(json.dumps(dev, indent=2))
    								print("-" * 50)
    				else:
    						print(f"\nNo devices found within the {TARGET_CIDR} range.")

    		except Exception as e:
    				print(f"Script Error: {e}")
    ```

2. In the script configuration section, input your Cloudflare API credentials and your IP subnet range.

3. Open a terminal and navigate to the script directory. To run the script, type:
   ```sh
   python3 filter-device-ips.py
   ```

</Details>

## Edit an IP subnet

Cloudflare does not support editing an existing IPv4 subnet definition. To assign a different IPv4 subnet to your devices:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles**.
2. Under **Device IP profiles**, find the device group associated with the old subnet and select **Edit**.
3. Select **Create new subnet IP range** to define a new subnet.
4. Select **Save**.

The new subnet will appear in the **Device IP subnets** table. You can now delete the old subnet. Devices will only get an IP from the new subnet when they [re-register](/cloudflare-one/team-and-resources/devices/device-registration/#delete-a-device-registration); existing registrations will retain their [current IP](#verify-device-ips).

# Device profiles

import { Render, TabItem, Tabs, APIRequest } from "~/components";

<Render file="warp/device-profiles-intro" product="cloudflare-one" />

## Create a new profile

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.
2. Select **Create new profile**. This will make a copy of the **Default** profile.
3. Enter any name for the profile.
4. Create rules to define the devices that will use this profile. Learn more about the available [Selectors](#selectors), [Operators](/cloudflare-one/traffic-controls/network-policies/#comparison-operators), and [Values](/cloudflare-one/traffic-controls/network-policies/#value).
5. Configure [device client settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-settings) for these devices.

:::note

At this time, **Split Tunnels** and **Local Domain Fallback** can only be modified after you save the profile.
:::

6. Select **Create profile**.

Your profile will appear in the **Profile settings** list. You can rearrange the profiles in the list according to your desired [order of precedence](#order-of-precedence).

</TabItem>

<TabItem label="API">

Send a `POST` request to the [Devices API](/api/resources/zero_trust/subresources/devices/subresources/policies/subresources/custom/methods/create/):

<APIRequest
	path="/accounts/{account_id}/devices/policy"
	method="POST"
	json={{
		allow_mode_switch: false,
		allow_updates: false,
		allowed_to_leave: false,
		auto_connect: 600,
		captive_portal: 180,
		description:
			"Example device profile recommended in the implementation documentation. For details, refer to https://developers.cloudflare.com/learning-paths/replace-vpn/configure-device-agent/device-profiles/",
		disable_auto_fallback: true,
		enabled: true,
		exclude_office_ips: false,
		match:
			'identity.email in {"jdoe@example.com"} or any(identity.groups.name[*] in {"developers" "admin"}) and os.name == "windows"',
		name: "Example device profile",
		precedence: 101,
		service_mode_v2: {
			mode: "warp",
		},
		support_url: "https://support.example.com",
		switch_locked: true,
	}}
/>

</TabItem>
<TabItem label="Terraform (v5)">

1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
   - `Zero Trust Write`

2. Create a new profile using the [`cloudflare_zero_trust_device_custom_profile`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_custom_profile) resource:

   ```tf
   resource "cloudflare_zero_trust_device_custom_profile" "example" {
   	account_id            = var.cloudflare_account_id
   	name                  = "Example device profile"
   	description           = "Example device profile recommended in the implementation documentation. For details, refer to https://developers.cloudflare.com/learning-paths/replace-vpn/configure-device-agent/device-profiles/"
   	allow_mode_switch     = false
   	allow_updates         = false
   	allowed_to_leave      = false
   	auto_connect          = 600
   	captive_portal        = 180
   	disable_auto_fallback = true
   	enabled               = true
   	exclude_office_ips    = false
   	precedence            = 101
   	service_mode_v2       = {mode = "warp"}
   	support_url           = "https://support.example.com"
   	switch_locked         = true
   	tunnel_protocol       = "wireguard"

   	match = trimspace(replace(<<-EOT
   		identity.email in {"jdoe@example.com"}
   		or any(identity.groups.name[*] in {"developers" "admin"})
   		and os.name == "windows"
   	EOT
   	, "\n", " "))
   }
   ```

</TabItem>
</Tabs>

## Edit profile settings

<Render file="warp/edit-profile-settings" product="cloudflare-one" />

## Verify device profile

### Via the dashboard

To verify the last active device profile for a specific device:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**.
2. Under devices, find your device.
3. Review the device profile under **Last active device profile**.

To verify the last active device profile for a user's devices:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Users**.
2. Under **User name**, find the user you would like to investigate.
3. Select **Devices** to see all devices used by the user.
4. Find the device you want to investigate and verify the last active device profile for that device under the **Device profile** column.

Alternatively, you can use [DEX remote captures](/cloudflare-one/insights/dex/diagnostics/client-packet-capture/) to collect client diagnostic logs. The device profile UUID is shown in your [detection report](/cloudflare-one/insights/dex/diagnostics/client-packet-capture/#diagnostics-analyzer-beta) under `Profile ID`.

### Via the CLI

To check which device profile and profile settings are currently on a device, open a terminal and run:

```sh
warp-cli settings
```

The device profile UUID is shown in the `Profile ID` field.

## Selectors

You can configure device profiles to match against the following selectors, or criteria. Identity-based selectors are only available if the user [enrolled the device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) by logging in to an identity provider (IdP).

### User email

Apply a device profile based on the user's email.

<Render
	file="gateway/selectors/user-email"
	product="cloudflare-one"
	params={{ UIname: "User email" }}
/>

### User group emails

Apply a device profile based on an [IdP group](/cloudflare-one/traffic-controls/identity-selectors/#idp-groups-in-gateway) email address of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-email"
	product="cloudflare-one"
	params={{ UIname: "User group emails" }}
/>

### User group IDs

Apply a device profile based on an [IdP group](/cloudflare-one/traffic-controls/identity-selectors/#idp-groups-in-gateway) ID of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-ids"
	product="cloudflare-one"
	params={{ UIname: "User group IDs" }}
/>

### User group names

Apply a device profile based on an [IdP group](/cloudflare-one/traffic-controls/identity-selectors/#idp-groups-in-gateway) name of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-names"
	product="cloudflare-one"
	params={{ UIname: "User group names" }}
/>

### Operating system

Apply a device profile based on the operating system of the device.

| UI name          | API example                        |
| ---------------- | ---------------------------------- |
| Operating system | `os.name in {\"windows\" \"mac\"}` |

### Operating system version

Apply a device profile based on the [OS version](/cloudflare-one/reusable-components/posture-checks/client-checks/os-version/#determine-the-os-version) of the device.

| UI name                  | API example               |
| ------------------------ | ------------------------- |
| Operating system version | `os.version == \"1.2.0\"` |

<Render file="warp/os-version-semver" product="cloudflare-one" />

### Managed network

Apply a device profile based on the [managed network](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/) that the device is connected to.

| UI name         | API example                    |
| --------------- | ------------------------------ |
| Managed network | `network == \"Austin office\"` |

### SAML attributes

Apply a device profile based on an attribute name and value from a [SAML IdP](/cloudflare-one/traffic-controls/identity-selectors/#generic-saml-idp).

<Render file="gateway/selectors/saml-attributes" product="cloudflare-one" />

### Service token

Apply a device profile based on the [service token](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-service-token) used to enroll the device.

| UI name       | API example                                                               |
| ------------- | ------------------------------------------------------------------------- |
| Service Token | `identity.service_token_uuid == \"f174e90a-fafe-4643-bbbc-4a0ed4fc8415\"` |

## Comparison operators

Comparison operators determine how device profiles match a selector.

| Operator | Meaning                                    |
| -------- | ------------------------------------------ |
| is       | equals the defined value                   |
| in       | matches at least one of the defined values |

## Logical operators

To evaluate multiple conditions in an expression, select a logical operator:

| Operator | Meaning                                       |
| -------- | --------------------------------------------- |
| And      | match all of the conditions in the expression |
| Or       | match any of the conditions in the expression |

## Order of precedence

<Render
	file="warp/device-profile-order-of-precedence"
	product="cloudflare-one"
/>

# Configure the Cloudflare One Client

import { DirectoryListing } from "~/components";

You can configure Cloudflare One Client (formerly WARP) settings to work alongside existing infrastructure and provide users with differential access to resources.

:::note[Managed deployments]

If you are deploying the Cloudflare One Client with [device management software](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/), we recommend only supplying `organization` in your [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) and managing all other settings via the dashboard. Local policies deployed by your management software take precedence over dashboard settings. To ensure dashboard settings are applied as intended, remove the corresponding parameters from your managed deployment configuration.
:::

<DirectoryListing />

# Managed networks

import { Details, Render, TabItem, Tabs } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| All modes                                                                                  | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.1.861.0           |
| macOS    | ✅           | 2025.1.861.0           |
| Linux    | ✅           | 2025.1.861.0           |
| iOS      | ✅           | 1.0                    |
| Android  | ✅           | 1.0                    |
| ChromeOS | ✅           | 1.0                    |

</Details>

The Cloudflare One Client (formerly WARP) allows you to selectively apply specific [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) and device client settings when a device connects to a known network location, such as an office. To detect which network a device is on, the Cloudflare One Client connects to a TLS endpoint that you host on that network and validates its certificate. If the certificate matches, the device is on your managed network and receives the corresponding [device profile](#4-configure-device-profile) (if one has been configured for that network).

On this page, you will learn how to:

- Create a TLS endpoint on your trusted network.
- Configure the TLS endpoint in Zero Trust to set up a managed network.
- Apply the appropriate device profile to a device when the Cloudflare One Client detects it is on your managed network.

## Requirements

- The Cloudflare One Client scans for managed networks when the operating system's default route changes, the SSID of the active Wi-Fi connection changes, or the DNS servers of the default interface change. To minimize performance impact, reuse the same TLS endpoint across multiple locations unless you require distinct settings profiles for each location.
- Ensure that the device can only reach one managed network at any given time. If multiple managed networks are configured and reachable, there is no way to determine which settings profile the device will receive.

## Managed network detection logic

When you configure a managed network, the Cloudflare One Client uses the TLS endpoint to determine whether the device is on that network.

The time it takes to apply the correct device profile depends on how quickly the TLS endpoint responds.

If the TLS endpoint times out after 5 seconds, the Cloudflare One Client will determine that the device is not on a managed network and will apply the default device profile. The Cloudflare One Client only retries detection if a non-timeout error occurs. A timeout triggers fallback to the default device profile without further retries.

## 1. Choose a TLS endpoint

A TLS endpoint is a host on your network that serves a TLS certificate. The TLS endpoint acts like a network location beacon — when a device connects to a network, the Cloudflare One Client on the device detects the TLS endpoint and validates the TLS certificate against the SHA-256 fingerprint (if specified) or against the local certificate store to check that it is signed by a public certificate authority.

The TLS certificate can be hosted by any device on your network. However, the endpoint must be inaccessible to users outside of the network location. The Cloudflare One Client will automatically exclude the managed network endpoint from all device profiles to ensure that users cannot connect to this endpoint over Cloudflare Tunnel. We recommend choosing a host that is physically in the office which remote users do not need to access, such as a printer.

### Create a new TLS endpoint

If you do not already have a TLS endpoint on your network, you can set one up as follows:

1.  Generate a TLS certificate:

    ```sh
    openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout key.pem -out cert.pem -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com"
    ```

    The command will output a certificate in PEM format and its private key. Store these files in a secure place.

    :::note

    The Cloudflare One Client requires certificates to include `CN` and `subjectAltName` metadata. You can use `example.com` or any other domain.
    :::

2.  Configure an HTTPS server on your network to use this certificate and key. The example below demonstrates how to serve the TLS certificate from an nginx container in Docker:

        a. Create an nginx configuration file called `nginx.conf`:

        	```txt title="nginx.conf"
        	events {
        	worker_connections  1024;
        	}

        	http {
        			server {
        				listen              443 ssl;
        				ssl_certificate     /certs/cert.pem;
        				ssl_certificate_key /certs/key.pem;
        				location / {
        							return 200;
        				}
        			}
        	}
        	```

        If needed, replace `/certs/cert.pem` and `/certs/key.pem` with the locations of your certificate and key.

        b. Add the nginx image to your Docker compose file:

        	```yml title="docker-compose.yml"
        	services:
        		nginx:
        			image: nginx:latest
        			ports:
        				- 3333:443
        			volumes:
        				- ./nginx.conf:/etc/nginx/nginx.conf:ro
        				- ./certs:/certs:ro
        	```

        	If needed, replace `./nginx.conf` and `./certs` with the locations of your nginx configuration file and certificate.

        c. Start the server:

        	```sh
        	docker compose up -d
        	```

3.  To test that the TLS server is working, run a curl command from the end user's device:

    ```sh
    curl --verbose --insecure https://<private-server-IP>:3333/
    ```

    You need to pass the `--insecure` option because we are using a self-signed certificate. If the device is connected to the network, the request should return a `200` status code.

<Details header="Windows IIS">

To create a TLS endpoint using Windows Internet Information Services (IIS) Manager:

1.  Run Powershell as administrator.

2.  Generate a self-signed certificate:

    ```powershell
    New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -DnsName "office-name.example.internal" -FriendlyName "Cloudflare Managed Network Certificate" -NotAfter (Get-Date).AddYears(10)
    ```

    ```powershell output
    	PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

    Thumbprint                                Subject
    ----------                                -------
    0660C4FCD15F69C49BD080FEEA4136B3D302B41B  CN=office-name.example.internal
    ```

3.  Extract the certificate's SHA-256 fingerprint:

    ```powershell
    [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash((Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.FriendlyName -eq "Cloudflare Managed Network Certificate" }).RawData)) -replace "-", ""
    ```

    ```powershell output
    DD4F4806C57A5BBAF1AA5B080F0541DA75DB468D0A1FE731310149500CCD8662
    ```

    You will need the SHA-256 fingerprint to [configure the managed network in Zero Trust](#3-add-managed-network-to-zero-trust). Do not use the default SHA-1 thumbprint generated by the `New-SelfSignedCertificate` command.

4.  Open IIS Manager.

5.  In the **Connections** pane, right-click the **Sites** node and select **Add Website**.

6.  In **Site name**, enter any name for the TLS server (for example, `Managed Network Server`).

7.  In **Physical path**, enter any directory that contains a `.htm` or `html` file, such as `C:\inetpub\wwwroot`. Cloudflare does not validate the content within the directory.

8.  Under **Binding**, configure the following fields:
    - **Type**: _https_
    - **IP address**: _All Unassigned_
    - **Port**: `443`
    - **Host name**: Enter the certificate's Common Name (CN). The CN of our example certificate is `office-name.example.internal`.
    - **Require Server Name Indication**: Enabled
    - **SSL certificate**: Select the name of your TLS certificate. Our example certificate is called `Cloudflare Managed Network Certificate`.

9.  To test that the TLS server is working, run a curl command from the end user's device:

        	```sh
        	curl --verbose --insecure --resolve office-name.example.internal:443:<private-server-IP> https://office-name.example.internal
        	```

        	You need to pass the `--insecure` option because we are using a self-signed certificate. The `--resolve` option allows you to connect to the server's private IP but also pass the hostname to the server for SNI and certificate validation. If the device is connected to the network, the request should return your directory's default homepage (`C:\inetpub\wwwroot\iisstart.htm`).

</Details>

### Supported cipher suites

The Cloudflare One Client establishes a TLS connection using [Rustls](https://github.com/rustls/rustls). Make sure your TLS endpoint accepts one of the [cipher suites supported by Rustls](https://docs.rs/rustls/0.21.10/src/rustls/suites.rs.html#125-143).

## 2. Extract the SHA-256 fingerprint

The SHA-256 fingerprint is only required if your TLS endpoint uses a self-signed certificate.

<Render file="warp/sha-256-fingerprint" product="cloudflare-one" />

## 3. Add managed network to Cloudflare One

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles**.
2. Select **Managed networks** and select **Add new managed network**.
3. Name your network location.
4. In **Host and Port**, enter the private IP address and port number of your [TLS endpoint](#create-a-new-tls-endpoint) (for example, `192.168.185.198:3333`).

   :::note
   We recommend using the private IP of your managed network endpoint and not a hostname to prevent issues related to DNS lookups resolving the incorrect IP.
   :::

5. (Optional) In **TLS Cert SHA-256**, enter the [SHA-256 fingerprint](#2-extract-the-sha-256-fingerprint) of the TLS certificate. This field is only needed for self-signed certificates. If a TLS fingerprint is not supplied, the Cloudflare One Client validates the certificate against the local certificate store and checks that it is signed by a public certificate authority.
6. Select **Save**.

</TabItem>
<TabItem label="Terraform (v5)">

1.  Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
    - `Zero Trust Write`

2.  Add a managed network using the [`cloudflare_zero_trust_device_managed_network`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_managed_network) resource:

            ```tf
            resource "cloudflare_zero_trust_device_managed_networks" "office" {
            	account_id = var.cloudflare_account_id
            	name       = "Office managed network"
            	type       = "tls"
            	config = {
            		tls_sockaddr = "192.168.185.198:3333"
            		sha256       = "DD4F4806C57A5BBAF1AA5B080F0541DA75DB468D0A1FE731310149500CCD8662"
            	}
            }
            ```

</TabItem>
</Tabs>

The Cloudflare One Client will automatically exclude the TLS endpoint from all device profiles if it is specified as a private IP address. This exclusion prevents remote users from accessing the endpoint through the WARP tunnel on any port. If the TLS endpoint is specified as a hostname instead of a private IP, the Cloudflare One Client will not automatically exclude it.

:::note[Split Tunnels in Include mode]
If a device profile uses [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) in **Include** mode, ensure that your Split Tunnel entries do not contain the TLS endpoint IP address; otherwise the Cloudflare One Client will exclude the entire Split Tunnel entry from the tunnel. For example, if you are currently including `10.0.0.0/8` but your TLS endpoint is on `10.0.0.1`, use our [IP subtraction calculator](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#add-a-route) to remove `10.0.0.1` from `10.0.0.0/8`.
:::

## 4. Configure device profile

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.

2. Create a [new profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) or edit an existing profile.

3. To apply this profile whenever a device connects to your network, add the following rule:

   | Selector        | Operator | Value            |
   | --------------- | -------- | ---------------- |
   | Managed network | is       | `<NETWORK-NAME>` |

4. Save the profile.

</TabItem>
<TabItem label="Terraform (v5)">

In [`cloudflare_zero_trust_device_custom_profile`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_custom_profile), configure a `match` expression using the `network` selector. For example, the following device profile will match all devices connected a specific managed network:

```tf
resource "cloudflare_zero_trust_device_custom_profile" "office" {
	account_id            = var.cloudflare_account_id
	name                  = "Office"
	description           = "Devices connected to the office network"
	precedence            = 1
	service_mode_v2       = {mode = "warp"}

	match = trimspace(replace(<<-EOT
		network == "${cloudflare_zero_trust_device_managed_networks.office.name}"
	EOT
	, "\n", " "))
}
```

</TabItem>
</Tabs>

Managed networks are now enabled. Every time a device in your organization connects to a network (for example, when waking up the device or changing Wi-Fi networks), the Cloudflare One Client will determine its network location and apply the corresponding settings profile.

## 5. Verify managed network

To check if the Cloudflare One Client detects the network location:

1. Connect the Cloudflare One Client.
2. Disconnect and reconnect to the network.
3. Open a terminal and run `warp-cli debug alternate-network`.

## Related resources

- [Device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) - How to create and manage the device profiles you apply via managed networks.
- [Device client settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/) - Defines how the Cloudflare One Client behaves and what users can do.
- [Cloudflare One Client troubleshooting guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/troubleshooting-guide/) - Troubleshoot common Cloudflare One Client issues.

# Enable Posture only mode

import { TabItem, Tabs, Details, Width, APIRequest } from "~/components";

<Details header="Feature availability">

| System   | Availability |
| -------- | ------------ |
| Windows  | ✅           |
| macOS    | ✅           |
| Linux    | ✅           |
| iOS      | ✅           |
| Android  | ✅           |
| ChromeOS | ✅           |

</Details>

Posture only mode allows you to enforce device posture rules when a user connects to your [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/). This mode relies on a client certificate generated from your account to establish trust between the Access application and the device.

## 1. Turn on account settings

Using the API, enable client certificate provisioning for [your zone](/fundamentals/account/find-account-and-zone-ids/):

<APIRequest
	path="/zones/{zone_id}/devices/policy/certificates"
	method="PATCH"
	json={{
		enabled: true,
	}}
/>

## 2. Configure the Cloudflare One Client

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.

2. Choose a [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) and select **Edit**.

3. For **Service mode**, select **Posture only mode**.

4. Select **Save profile**.

5. [Enroll your device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) into your Zero Trust organization.

   When enrolled in Posture only mode, the Cloudflare One Client (formerly WARP) will automatically generate a client certificate and install the certificate on the device. This certificate is necessary to confirm the source of outgoing traffic.

## 3. (Optional) Verify the client certificate

1.  To view the client certificates installed on the device:

        <Tabs> <TabItem label="Windows">

        1. Open the **Start** menu and select **Run**.
        2. Enter `certlm.msc`.
        3. Go to **Personal** > **Certificates**.

        </TabItem> <TabItem label="macOS">

        1. Open **Keychain Access**.
        2. Go to **System** > **My Certificates**.

        </TabItem>

        <TabItem label="Linux">

        Open a terminal window and run the following command:

        ```sh
        $ certutil -L -d sql:/etc/pki/nssdb
        ```

        </TabItem>

        <TabItem label="iOS">

        Go to **Settings** > **General** > **About** > **Certificate Trust Settings**.

        </TabItem>

        <TabItem label="Android">

        The location of the client certificate may vary depending on the Android device.

        - **Samsung**: Go to **Settings** > **Security** > **Other security settings** > **View security certificates**.
        - **Google Pixel**: Go to **Security** > **Advanced settings** > **Encryption & credentials** > **Credential storage**.

        </TabItem>

        <TabItem label="ChromeOS">

        Go to **Settings** > **Apps** > **Google Play Store** > **Manage Android Preferences** > **Security** > **Credentials**.

        </TabItem>
        </Tabs>

        The client certificate name should match the **Device ID** in your Cloudflare One Client **Preferences**.

2.  To verify the client certificate in your Cloudflare account:
    1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), select the zone for which you enabled client certificates.
    2. Go to **SSL/TLS** > **Client Certificates**.

    The certificate name is the WARP enrollment **Device ID**.
    ![Example client certificate in the Cloudflare dashboard](~/assets/images/cloudflare-one/connections/device-information-only-cert.png)

## 4. Enforce the client certificate

To block traffic from devices that do not have a valid client certificate:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **SSL/TLS** > **Client Certificates**.
2. Under **Hosts**, select **Edit** and enter the hostname of your Access application (for example, `app.mycompany.com`). This enables mTLS authentication for the application.
3. Select **Create mTLS rule**.
4. Create a WAF custom rule that checks all requests to your application for a valid client certificate:
   | Field | Operator | Value | Logic | Action |
   | ----- | -------- | ----- | ----- | ------ |
   | Client Certificate | equals | Off | And | Block |
   | Hostname | equals | `app.mycompany.com` | | |
5. Select **Deploy**.

Posture only mode is now enabled on the device. To start enforcing device posture, set up a [WARP client check](/cloudflare-one/reusable-components/posture-checks/client-checks/) and add a _Require_ device posture rule to your [Access policy](/cloudflare-one/access-controls/policies/). When the device connects to the Access application for the first time, the browser will ask to use the client certificate installed by the Cloudflare One Client.

<Width size="large">
	![Browser prompts for client
	certificate](~/assets/images/cloudflare-one/connections/device-information-only-browser.png)
</Width>

## Limitations

Posture only mode is not compatible with the [Windows pre-login](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-prelogin/) feature. The user must be logged into Windows because the Cloudflare One Client needs to [install a certificate](#3-optional-verify-the-client-certificate) in the user store.

# Client modes

You can deploy the Cloudflare One Client (formerly WARP) in different modes to control the types of traffic sent to Cloudflare Gateway. The client mode determines which Zero Trust features are available on the device.

## Traffic and DNS mode (default)

The Cloudflare One Client routes device traffic for all ports and protocols, and forwards DNS resolution to the [client DNS resolver](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/).

Use when you want full security coverage, including DNS filtering, HTTP inspection, network firewall policies, and device posture checks.

| DNS filtering | Network filtering | HTTP filtering | Features enabled                                                                                                                                        |
| ------------- | ----------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Yes           | Yes               | Yes            | DNS policies, network policies, HTTP policies, Browser Isolation, identity-based policies, device posture checks, AV scanning, and Data Loss Prevention |

## DNS only mode

The Cloudflare One Client forwards DNS resolution to the Cloudflare account resolver, but does not route device traffic. Network and HTTP traffic is handled by the default mechanisms on your devices.

Use when you only want to apply DNS filtering to outbound traffic from your company devices.

| DNS filtering | Network filtering | HTTP filtering | Features enabled |
| ------------- | ----------------- | -------------- | ---------------- |
| Yes           | No                | No             | DNS policies     |

## Traffic only mode

The Cloudflare One Client routes device traffic for all ports and protocols. DNS resolution remains managed by the device operating system.

Use when you want to proxy network and HTTP traffic but keep your existing DNS filtering software.

| DNS filtering | Network filtering | HTTP filtering | Features enabled                                                                                                                          |
| ------------- | ----------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| No            | Yes               | Yes            | Network policies, HTTP policies, Browser Isolation, identity-based policies, device posture checks, AV scanning, and Data Loss Prevention |

:::note

- Traffic only mode disables all features that rely on the Cloudflare One Client for DNS resolution, including [domain-based split tunneling](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#domain-based-split-tunnels) and [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/).
- Only available on Windows, Linux, and macOS.
- Traffic only mode has a known limitation concerning [DNS servers with IPv6 addresses](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/known-limitations/#ipv6-dns-resolution-in-traffic-only-mode).

:::

## Local proxy mode

The Cloudflare One Client only forwards explicitly-directed local HTTP traffic.

Use when you want to filter traffic directed to specific applications.

| DNS filtering | Network filtering | HTTP filtering | Features enabled                                                                                                                          |
| ------------- | ----------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| No            | No                | Yes            | HTTP policies, Browser Isolation, identity-based policies, AV scanning, and Data Loss Prevention for traffic sent through localhost proxy |

### Set up Local proxy mode

When you create a Cloudflare One account, a default [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) is created in Traffic and DNS mode. To set up Local proxy mode, you will need to edit the default device profile or create a new device profile and set the client mode to Local proxy mode.

The default profile is used for all devices that are not assigned to a specific profile. If you want to apply Local proxy mode to a specific group of devices, you will need to create a new device profile and assign it to those devices.

To set up Local proxy mode:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources** > **Device profiles**.
2. Decide whether you would like to edit the default profile or create a new device profile.
3. Select the device profile you want to configure > **Edit** (If you only see **View**, you lack the permissions required to modify profiles).
4. Ensure the **Device tunnel protocol** is set to `MASQUE`.
5. Under **Service mode**, select **Local proxy mode**.
6. Select **Save profile**.

:::note[MDM deployment]

If you are deploying the Cloudflare One Client through MDM, the configuration file will override any device profile settings, including the client mode. Refer to the [`service_mode` parameter](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#service_mode) for more information.

:::

For devices using Local proxy mode, the Cloudflare One Client listens on the configured port at the address `127.0.0.1` (`localhost`). Cloudflare uses `40000` as the default port for the Cloudflare One Client in Local proxy mode, but you can modify this to any available port. You must explicitly configure individual applications or your system proxy settings to use this proxy.

Once configured, traffic to and from these applications will securely tunnel through the Cloudflare One Client.

To make more complex routing decisions (such as, routing traffic directly to the Internet or other proxies), you can use a [PAC file](/learning-paths/secure-internet-traffic/configure-device-agent/pac-files/).

### Limitations

- Local proxy mode can only be used by applications/operating systems that support SOCKS5/HTTP proxy communication.
- Requires the MASQUE [device tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol). Wireguard is not supported.
- Only available on Windows, Linux, and macOS.
- Local proxy mode has a timeout limit of 10 seconds for requests. If a request goes above the 10 second limit, Cloudflare will drop the connection.

## Posture only mode

The Cloudflare One Client collects device health and posture data, which you can reference in your security policies. The client does not route traffic or forward DNS queries in this mode.

Use when you only want to enforce [Cloudflare One Client device posture checks](/cloudflare-one/reusable-components/posture-checks/client-checks/) for zones in your account. To set up Posture only mode, refer to the [dedicated page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/device-information-only/).

| DNS filtering | Network filtering | HTTP filtering | Features enabled                                                                     |
| ------------- | ----------------- | -------------- | ------------------------------------------------------------------------------------ |
| No            | No                | No             | Device posture rules in [Access policies](/cloudflare-one/access-controls/policies/) |

## Modes comparison

Each client mode offers a different set of Zero Trust features.

| Client mode                                                                                                                                   | Best for                                         | DNS Filtering | Network Filtering | HTTP Filtering | Service mode (displayed in `warp-cli settings`) |
| --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ------------- | ----------------- | -------------- | ----------------------------------------------- |
| [**Traffic and DNS mode (default)**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-and-dns-mode-default) | Full security with all filtering capabilities    | ✅            | ✅                | ✅             | `WarpWithDnsOverHttps`                          |
| [**DNS only mode**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode)                                 | DNS filtering without routing device traffic     | ✅            | ❌                | ❌             | `DnsOverHttps`                                  |
| [**Traffic only mode**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode)                         | Traffic routing with existing DNS infrastructure | ❌            | ✅                | ✅             | `TunnelOnly`                                    |
| [**Local proxy mode**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#local-proxy-mode)                           | Filtering traffic to specific applications       | ❌            | ❌                | ✅             | `WarpProxy on port 40000`                       |
| [**Posture only mode**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#posture-only-mode)                         | Device posture checks without traffic routing    | ❌            | ❌                | ❌             | `PostureOnly`                                   |

## Related resources

- [Connectivity status](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/connectivity-status/) - Learn about the status messages displayed by the Cloudflare One Client during its connection process, and understand each stage as the client establishes a secure tunnel to Cloudflare.

# Client architecture

import { Render, TabItem, Tabs, GlossaryTooltip } from "~/components";

This guide explains how the Cloudflare One Client (formerly WARP) interacts with a device's operating system to route traffic in [Traffic and DNS mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-and-dns-mode-default) mode.

In [DNS only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode) mode, the IP traffic information does not apply. In [Traffic only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode) mode, the DNS traffic information does not apply.

## Client traffic flow

The Cloudflare One Client allows organizations to have granular control over the applications an end user device can access. The client forwards DNS and network traffic from the device to Cloudflare's global network, where Zero Trust policies are applied in the cloud. On all operating systems, the WARP daemon maintains three connections between the device and Cloudflare:

| Connection                                                                                                                                           | Protocol | Purpose                                                                                                              |
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| WARP tunnel ([via WireGuard or MASQUE](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol)) | UDP      | Send IP packets to Gateway for network policy enforcement, HTTP policy enforcement, and private network access.      |
| [DoH](https://www.cloudflare.com/learning/dns/dns-over-tls/)                                                                                         | HTTPS    | Send DNS requests to Gateway for DNS policy enforcement. The DoH connection is maintained inside of the WARP tunnel. |
| Device orchestration                                                                                                                                 | HTTPS    | Perform user registration, check device posture, apply device client profile settings.                               |

```mermaid
flowchart LR
subgraph Device
W[Cloudflare One Client] -.-> D
D[DNS proxy]
W -.-> V[Virtual interface]
end
subgraph Cloudflare
A[Zero Trust account]
subgraph Gateway
N[L3/L4 firewall]
G[DNS resolver]
end
end
W<--"Device
orchestration"-->A
subgraph tunnel["WARP tunnel"]
 ip@{ shape: text, label: "Network traffic" }
  dns@{ shape: text, label: "DNS traffic" }
end
V --- ip-->N
D --- dns-->G
N --> O[(Application)]
```

Your [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) configuration determines what IP traffic is sent down the WARP tunnel. Your [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) configuration determines which DNS requests are sent to Gateway via DoH. Traffic to the [device orchestration API](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#client-orchestration-api) endpoint does not obey Split Tunnel rules since the connection always operates outside of the WARP tunnel.

Next, you will learn how the Cloudflare One Client configures your operating system to apply your Local Domain Fallback and Split Tunnel routing rules. Implementation details differ between desktop and mobile clients.

## Windows, macOS, and Linux

The desktop client consists of two components: a service/daemon that handles all client functionality on your device, and a GUI wrapper that makes it easier for a user to interact with the daemon.

### DNS traffic

When you connect the Cloudflare One Client, the client creates a local DNS proxy on the device and binds it to these IP addresses on port 53 (the port designated for DNS traffic):

- **IPv4**: `127.0.2.2` and `127.0.2.3`
- **IPv6**:
  - macOS and Linux: `fd01:db8:1111::2` and `fd01:db8:1111::3`
  - Windows: `::ffff:127.0.2.2`

The Cloudflare One Client then configures the operating system to send all DNS requests to these IP addresses. All network interfaces on the device will now use this local DNS proxy for DNS resolution. In other words, all DNS traffic will now be handled by the Cloudflare One Client.

:::note

Browsers with DoH configured will bypass the local DNS proxy. You may need to disable DoH settings in the browser.
:::

Based on your Local Domain Fallback configuration, the Cloudflare One Client will either forward the request to Gateway for DNS policy enforcement or forward the request to your private DNS resolver.

- Requests to Gateway are sent over our [DoH connection](#overview) inside the WARP tunnel.
- Requests to your private DNS resolver are sent either inside or outside of the tunnel depending on your Split Tunnel configuration. For more information, refer to [How the Cloudflare One Client handles DNS requests](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/#how-the-cloudflare-one-client-handles-dns-requests).

```mermaid
flowchart LR
D{{DNS request}}-->L["Local DNS proxy <br> (127.0.2.2 and 127.0.2.3)"]-->R{In local domain fallback?}
R -- Yes --> F[Private DNS resolver]
R -- No --> G[Cloudflare Gateway]
```

You can verify that the operating system is using the Cloudflare One Client's local DNS proxy:

<Tabs> <TabItem label="macOS">

On macOS, open a terminal window and run `scutil --dns`. The DNS servers should be set to the Cloudflare One Client's local DNS proxy IPs.

```sh
scutil --dns
```

```sh {4-5} output
DNS configuration (for scoped queries)
resolver #1
  search domain[0] : <DNS-SEARCH-DOMAIN>
  nameserver[0] : 127.0.2.2
  nameserver[1] : 127.0.2.3
  if_index : 15 (en0)
  flags    : Scoped, Request A records
  reach    : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
resolver #2
  nameserver[0] : 127.0.2.2
  nameserver[1] : 127.0.2.3
  nameserver[2] : fd01:db8:1111::2
  nameserver[3] : fd01:db8:1111::3
  if_index : 23 (utun3)
  flags    : Scoped, Request A records, Request AAAA records
  reach    : 0x00030002 (Reachable,Local Address,Directly Reachable Address)
```

</TabItem> <TabItem label="Windows">

On Windows, open a PowerShell window and run `ipconfig`. The DNS servers should be set to the Cloudflare One Client's local DNS proxy IPs.

```powershell
ipconfig
```

```txt {15-16} output
Windows IP Configuration

Unknown adapter CloudflareWARP:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Cloudflare WARP Interface Tunnel
   Physical Address. . . . . . . . . :
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes
   IPv6 Address. . . . . . . . . . . : 2606:4700:110:8f79:145:f180:fc4:8106(Preferred)
   Link-local IPv6 Address . . . . . : fe80::83b:d647:4bed:d388%49(Preferred)
   IPv4 Address. . . . . . . . . . . : 172.16.0.2(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.255
   Default Gateway . . . . . . . . . :
   DNS Servers . . . . . . . . . . . : 127.0.2.2
                                       127.0.2.3
   NetBIOS over Tcpip. . . . . . . . : Enabled
```

</TabItem>

<TabItem label="Linux">

On Linux, check the `/etc/resolv.conf` file. The DNS servers should be set to the Cloudflare One Client's local DNS proxy IPs.

```sh
cat /etc/resolv.conf
```

```sh {2-3} output
# This file was generated by cloudflare-warp.
nameserver 127.0.2.2
nameserver 127.0.2.3
nameserver fd01:db8:1111::2
nameserver fd01:db8:1111::3
search <DNS-SEARCH-DOMAIN>
options edns0
options trust-ad
```

</TabItem> </Tabs>

### IP traffic

When you connect the Cloudflare One Client, it makes three changes on the device to control if traffic is sent inside or outside of the WARP tunnel:

- Creates a [virtual network interface](#virtual-interface).
- Modifies the operating system [routing table](#routing-table) according to your Split Tunnel rules.
- Modifies the operating system [firewall](#system-firewall) according to your Split Tunnel rules.

```mermaid
flowchart LR
P{{IP packet}}-->R["OS routing table"]-->F["OS firewall"] --> S{Excluded from Split Tunnels?}
S -- Yes --> A[(Application)]
S -- No --> U["Virtual interface<br> (172.16.0.2)"] --> G[Cloudflare Gateway]
```

#### Virtual interface

Virtual interfaces allow the operating system to logically subdivide a physical interface, such as a network interface controller (NIC), into separate interfaces for the purposes of routing IP traffic. The Cloudflare One Client's virtual interface is what maintains the WireGuard/MASQUE connection between the device and Cloudflare. By default, its IPv4 address is hardcoded as `172.16.0.2` for devices using WireGuard, whereas devices using MASQUE are [assigned a unique IP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#assign-a-unique-ip-address-to-each-device) from the CGNAT IP space (`100.96.0.0/12`). You can override the default virtual interface IP with a [custom device IP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/).

To view a list of all network interfaces on the operating system:

<Render file="warp/check-network-interface-ip" product="cloudflare-one" />

#### Routing table

The Cloudflare One Client edits the system routing table to control what IP traffic goes to Gateway. The routing table indicates which network interface should handle packets to a particular IP address. By default, all traffic routes through the Cloudflare One Client's virtual interface except for the IPs and domains on your Split Tunnel exclude list (which use the default interface on your device).

You can verify that the routing table matches your Split Tunnel rules:

<Tabs> <TabItem label="macOS">

To view the entire routing table on macOS, run `netstat -r`.

You can also search the routing table for a domain or IP address. In this example, we see that traffic to `google.com` is sent through `utun3`, which is the Cloudflare One Client's virtual interface on this device:

```sh
route get google.com
```

```sh {4} output
   route to: lga25s81-in-f14.1e100.net
destination: 136.0.0.0
       mask: 248.0.0.0
  interface: utun3
      flags: <UP,DONE,PRCLONING>
 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
       0         0         0         0         0         0      1280         0
```

In contrast, this DHCP address is excluded from the Cloudflare One Client and uses the default interface:

```sh
route get 169.254.0.0
```

```sh {4} output
   route to: 169.254.0.0
destination: 169.254.0.0
       mask: 255.255.0.0
  interface: en0
      flags: <UP,DONE,CLONING,STATIC>
 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
       0         0         0         0         0         0      1500   -210842
```

</TabItem> <TabItem label="Windows">

To view the entire routing table on Windows, run `netstat -r`.

You can also search the routing table for an IP address. In this example, we see that traffic to `1.1.1.1` is sent through the Cloudflare One Client's virtual interface:

```powershell
Find-NetRoute -RemoteIPAddress "1.1.1.1" | Select-Object InterfaceAlias -Last 1
```

```txt output
InterfaceAlias
--------------
CloudflareWARP
```

In contrast, this DHCP address is excluded from the Cloudflare One Client and uses the default interface:

```powershell
Find-NetRoute -RemoteIPAddress "169.254.0.0" | Select-Object InterfaceAlias -Last 1
```

```txt output
InterfaceAlias
--------------
Wi-Fi
```

</TabItem>

<TabItem label="Linux">

To view the entire routing table on Linux, run `ip -6 route show table all` or `ip -4 route show table all`.

You can also search the routing table for an IP address. In this example, we see that traffic to `1.1.1.1` is sent through the Cloudflare One Client's virtual interface:

```sh
ip route get 1.1.1.1
```

```sh output
1.1.1.1 dev CloudflareWARP table 65743 src 172.16.0.2 uid 1000
    cache
```

In contrast, this DHCP address is excluded from the Cloudflare One Client and uses the default interface:

```sh
ip route get 169.254.0.0
```

```sh output
169.254.0.0 dev ens18 src 172.24.8.6 uid 1000
    cache
```

</TabItem>

</Tabs>

#### System firewall

The Cloudflare One Client modifies the operating system firewall to enforce your Split Tunnel rules. This adds a layer of protection in case a service bypasses the routing table and tries to send traffic directly through another interface. For example, if traffic to `203.0.113.0` is supposed to be inspected by Gateway, we create a firewall rule that blocks `203.0.113.0` on all interfaces except for `utun`.

## iOS, Android, and ChromeOS

On iOS and Android/ChromeOS, the Cloudflare One Agent installs itself as a VPN client to capture and route all traffic. The app is built on the official VPN framework for iOS and Android. For more information, refer to Apple's [NetworkExtension documentation](https://developer.apple.com/documentation/networkextension) and Google's [Android developer documentation](https://developer.android.com/guide/topics/connectivity/vpn).

Note that ChromeOS runs the Android app in a virtual machine, rather than running a native Chrome app.

# Route traffic

import { GlossaryTooltip } from "~/components";

When the Cloudflare One Client (formerly WARP) is deployed on a device, Cloudflare will process all DNS queries and network traffic by default. However, under certain circumstances, you may need to exclude specific DNS queries or network traffic from the Cloudflare One Client. For example, you may need to resolve an internal hostname with a private DNS resolver instead of Cloudflare's [public DNS resolver](/1.1.1.1/).

Cloudflare recommends Enterprise users configure [Gateway resolver policies](/cloudflare-one/traffic-controls/resolver-policies/) to resolve traffic with custom resolvers. The Cloudflare One Client will send private DNS queries to Gateway, then Gateway will send the queries to custom resolvers based on matching policies.

Additionally, there are three options you can configure to exclude traffic from the Cloudflare One Client:

- [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/): Send DNS requests for specific domains to a resolver other than Cloudflare Gateway. Use this when you have private hostnames that do not resolve on the public Internet (for example, internal corporate domains).
  :::caution
  Gateway will not encrypt, monitor, or apply DNS policies to DNS queries to domain names entered in Local Domain Fallback.
  :::
- [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) Exclude mode: Exclude specific IP addresses or domains from the WARP tunnel. Excluded traffic bypasses the Cloudflare One Client and is handled by the local machine. Use this mode when you want most traffic to go through Gateway, but need to exclude certain routes for app compatibility or to run alongside a [third-party VPN](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/vpn/).
- [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) Include mode: Only route traffic to specific IP addresses or domains through the WARP tunnel. All other traffic bypasses the Cloudflare One Client. Use this mode when you only want specific traffic processed by Gateway, such as traffic to resources behind [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/).
  :::caution
  Gateway will not encrypt, manage, or monitor traffic excluded from the Cloudflare One Client by a Split Tunnel configuration.
  :::

## How the Cloudflare One Client handles DNS requests

When you use the Cloudflare One Client together with `cloudflared` Tunnels or third-party VPNs, Cloudflare evaluates each request and routes it according to the following traffic flow:

```mermaid
flowchart TD
    %% Accessibility
    accTitle: How the Cloudflare One Client handles DNS requests
    accDescr: Flowchart describing how the Cloudflare One Client routes DNS queries when using Local Domain Fallback, Split Tunnels, and Gateway resolver policies.

    A(["User requests resource"]) --> B["Cloudflare One Client proxies all DNS traffic"]
    B --> LDFCHK{"Cloudflare One Client checks if domain is listed in Local Domain Fallback policies"}

    %% Left branch (LDF exists)
    LDFCHK -- Domain exists in Local Domain Fallback policies --> C["Local Domain Fallback"]
    C --> ST["Split Tunnel processing"]

    ST --> STCHK{"Resolver IP included in WARP Tunnel per Split Tunnel configuration"}
    STCHK -- Resolver IP included in WARP Tunnel per Split Tunnel configuration --> QW["Query sent via WARP Tunnel to be resolved"]
    STCHK -- Resolver IP not included in WARP Tunnel per Split Tunnel configuration --> QO(["Query sent to resolver IP outside WARP Tunnel"])

    %% Gateway evaluation after query via WARP
    QW --> GWALLOW{"Allowed by Gateway"}
    GWALLOW -- Allowed by Gateway --> OR["Evaluated by Cloudflare on-ramp routes"]
    GWALLOW -- Blocked by Gateway Network or HTTP Policy --> BLK(["Traffic blocked by Cloudflare"])

    OR --> ORCHK{"Onramp routes include resolver IP"}
    ORCHK -- Onramp routes do not include resolver IP --> GP(["Gateway proxies query to resolver IP via normal Cloudflare One Client egress route"])
    ORCHK -- Onramp routes include resolver IP --> ADV["Cloudflare onramps advertise route that includes Resolver IP"]
    ADV --> PR(["Private resolver returns IP address to Cloudflare One Client"])

    %% Right branch (no LDF match)
    LDFCHK -- Domain does not exist in Local Domain Fallback policies --> GWR{"Gateway checks Resolver Policies (Enterprise only)"}

    GWR -- Resolver policy is not matched --> C1111a(["1.1.1.1"])

    GWR -- Resolver policy is matched --> MATCH(("Resolver policy directs query to one of the following"))
    MATCH --> IDNS(["Internal DNS"])
    MATCH --> C1111b(["1.1.1.1"])
    MATCH --> CUST(["Custom resolver"])
    CUST --> PNS(["Private network services<br>(Cloudflare Tunnel, Cloudflare WAN, Cloudflare Mesh)"])
```

#### Terms mentioned

##### On-ramps (how traffic gets onto Cloudflare)

- <GlossaryTooltip term="on-ramp">On-ramp</GlossaryTooltip>: Learn more about
  [On-ramps](/learning-paths/secure-internet-traffic/connect-devices-networks/choose-on-ramp/).
- [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/)
- [Cloudflare Mesh](/mesh/)
- [Cloudflare WAN](/cloudflare-wan/)

##### Routing features (how queries are handled)

- [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/)
- [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/)
- [Gateway Resolver Policies](/cloudflare-one/traffic-controls/resolver-policies/)

#### Resolvers (where queries are resolved)

- [Internal DNS](/dns/internal-dns/)
- [1.1.1.1](/1.1.1.1/)

## Add a DNS suffix

To deploy a DNS suffix search list to all devices on a [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/), configure [DNS search suffixes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#dns-search-suffixes) in your Cloudflare One Client settings.

For older client versions, you can manually configure DNS suffixes at the device level using the following instructions.

### macOS

To manually configure a DNS suffix on macOS:

1. Open **System Settings** (or **System Preferences** on older macOS versions).
2. Go to **Network** and select your active connection (**Wi-Fi** or **Ethernet**).
3. Select **Details** (or **Advanced**).
4. Go to the **DNS** tab.
5. Under **Search Domains**, select the `+` button and add your DNS suffix.
6. Select **OK**, then **Apply**.

### Windows

To manually configure a DNS suffix on Windows:

1. Open the **Search** bar in Windows, type **View network connections**, and select **Open**.
2. Right-click the network adapter (**Wi-Fi** or **Ethernet**) you want to modify and select **Properties**. (Admin privileges required.)
3. Double-click **Internet Protocol Version 4 (TCP/IPv4)**.
4. In the **Internet Protocol (TCP/IP) Properties** window, select **Advanced**.
5. Go to the **DNS** tab.
6. Select **Append these DNS suffixes (in order)**.
7. Select **Add**, enter your DNS suffix and select **Add**.
8. Select **OK** on all windows to apply changes.

# Local Domain Fallback

import { GlossaryTooltip, Render } from "~/components";

By default, Cloudflare Zero Trust excludes common top-level domains, used for local resolution, from being sent to Gateway for processing. These top-level domains are resolved by the local DNS resolver configured for the device on its primary interface.

You can add additional domains to the Local Domain Fallback list and specify a DNS server to use in place of the Gateway resolver. The Cloudflare One Client (formerly WARP) proxies these requests directly to the configured fallback servers.

## Limitations

Local Domain Fallback only applies to devices running the Cloudflare One Client.

Because DNS requests subject to Local Domain Fallback bypass the Gateway resolver, they are not subject to Gateway DNS policies or DNS logging. If you want to route DNS queries to custom resolvers and apply Gateway filtering, use [resolver policies](/cloudflare-one/traffic-controls/resolver-policies/). If both Local Domain Fallback and resolver policies are configured for the same device, Cloudflare will apply client-side Local Domain Fallback rules first.

<Render file="warp/ldf-best-practice" product="cloudflare-one" />

### AWS

<Render file="aws-resolver" product="cloudflare-one" />

## Manage local domains

### View domains

To view the domains subject to Local Domain Fallback:

<Render file="warp/view-local-domains" product="cloudflare-one" />

On this page, you will see a list of domains excluded from Gateway. You can [add](#add-a-domain) or [remove](#delete-a-domain) domains from the list at any time.

:::caution

Local Domain Fallback configuration only impacts where DNS requests get resolved, not the flow of traffic destined to those domains. If you want to prevent traffic from being sent to a specific domain or IP address, you must add those domains or IPs to your [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) configuration.

:::

To view the fallback domains applied to a device, you can:

- In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > find the target device and the **Last active device profile** > follow the [steps above](#view-domains).
- (Desktop only) Run `warp-cli settings` in the terminal of the target device and review the [fallback domains](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/troubleshooting-guide/#fallback-domains) section of the output.
- (Desktop only) Collect [client diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) for the device and review the [fallback domain](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/troubleshooting-guide/#fallback-domains) section in `warp_settings.txt`.

### Add a domain

<Render file="warp/add-local-domain" product="cloudflare-one" />

### Delete a domain

<Render file="warp/view-local-domains" product="cloudflare-one" />

4. Find the domain in the list and select **Delete**.

The domain will no longer be excluded from Gateway DNS policies, effective immediately.

## Reverse DNS lookups for internal IPs

By default, Warp sends [reverse DNS queries](https://www.cloudflare.com/learning/dns/glossary/reverse-dns/) to public DNS servers. To lookup the domain name associated with an internal IP address, [add a local domain fallback entry](#add-a-domain) for `in-addr.arpa` (IPv4) and/or `ip6.arpa` (IPv6) that points to your internal DNS server IP. `in-addr.arpa` and `ip6.arpa` are top-level domains [reserved](https://www.iana.org/domains/arpa) for reverse DNS queries. By adding a local domain fallback entry for these domains, all reverse DNS queries (such as `dig -x 1.1.1.1`) will now resolve through your local DNS server.

## Related resources

- [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) - Control which traffic goes through the Cloudflare One Client by including or excluding specific IPs or domains.
- [Cloudflare One Client with firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/) - Learn which IPs, domains, and ports to allow so users can deploy and connect the Cloudflare One Client successfully behind a firewall.

# Split Tunnels

import { Render } from "~/components";

<Render file="warp/split-tunnel-intro" product="cloudflare-one" />

## Change Split Tunnels mode

<Render file="warp/change-split-tunnels-mode" product="cloudflare-one" />

## Add a route

<Render file="warp/add-split-tunnels-route" product="cloudflare-one" />

### When to use Split Tunnels

Use Split Tunnels when you need to bypass Gateway entirely for a site or allow traffic through the [firewall that the Cloudflare One Client creates](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#system-firewall). Common scenarios include:

- Connect to a third-party application which requires the actual IP address of the end-user device (for example, [Microsoft 365](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#directly-route-microsoft-365-traffic)).
- Optimize voice and video.
- Connect to a [third-party VPN](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/vpn/) endpoint.

### When not to use Split Tunnels

Do not exclude a site from Split Tunnels if you want to see the traffic in your Gateway logs. In particular, we do not recommend using Split Tunnels to:

- Solve connectivity issues with a specific website. For configuration guidance, refer to our [troubleshooting guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#cannot-connect-to-a-specific-app-or-website).
- Solve performance issues with a specific website. Since Cloudflare operates within 50 milliseconds of 95% of the Internet-connected population, it is usually faster to send traffic through us. If you are encountering a performance-related issue, it is best to first explore your Gateway policies or reach out to Support.

## Routes for Split Tunnels Include mode

Many Cloudflare Zero Trust services rely on traffic going through the Cloudflare One Client, such as [device posture checks](/cloudflare-one/reusable-components/posture-checks/) and [device client session durations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/). If you are using Split Tunnels in Include mode, you will need to manually add Cloudflare Zero Trust domains and IPs in order for these features to function.

### Cloudflare Zero Trust domains

If you are using Split Tunnels in Include mode, you must include the following domains:

- The IdP used to authenticate to Cloudflare Zero Trust
- `<your-team-name>.cloudflareaccess.com`
- The application protected by the Access or Gateway policy
- `edge.browser.run` if using [Browser Isolation](/cloudflare-one/remote-browser-isolation/)

### Cloudflare Zero Trust IP addresses

#### Block page

If you are using Split Tunnels in Include mode and have [DNS policies](/cloudflare-one/traffic-controls/dns-policies/) with the [block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) enabled, you must include the IPs that blocked domains will resolve to. Unless you are using a [dedicated or BYOIP resolver IP](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#dns-resolver-ip) the block page will resolve to:

- `162.159.36.12`
- `162.159.46.12`

#### Team domain

In [Traffic only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode), you cannot [add domains](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#cloudflare-zero-trust-domains) to Split Tunnels. If you are using Split Tunnels in Include mode, you must include the IPs that resolve to `<your-team-name>.cloudflareaccess.com` instead:

- `104.19.194.29`
- `104.19.195.29`

## Device IP ranges

In Exclude mode, default device profiles exclude the CGNAT range (`100.64.0.0/10`), which contains the default [device IP range](/cloudflare-one/networks/routes/reserved-ips/#device-ips) (`100.96.0.0/12`). The Mesh setup wizard updates the default profile to route the device IP range through Cloudflare. If you did not use the wizard, or if another profile applies to the device, verify that the profile does not exclude the device IP range or a parent range that contains it.

## Automatically managed ranges

The Cloudflare One Client automatically includes the following ranges in Include mode, and automatically removes them from any exclusions configured in Exclude mode. This happens at runtime on the device: the ranges are not stored in your device profile, do not appear in the Split Tunnels list in the dashboard, and do not need to be added manually.

- **IPv4**: `172.64.128.0/20` — the default [Gateway initial resolved IP range](/cloudflare-one/networks/routes/reserved-ips/#gateway-initial-resolved-ips)
- **IPv6**: `2606:4700:0cf1::/48` — covers [device IPs](/cloudflare-one/networks/routes/reserved-ips/#device-ips), [Cloudflare source IPs](/cloudflare-one/networks/routes/reserved-ips/#cloudflare-source-ips), and [Gateway initial resolved IPs](/cloudflare-one/networks/routes/reserved-ips/#gateway-initial-resolved-ips)

You do not need to add these ranges to your Split Tunnels configuration. If you are troubleshooting a feature that depends on one of these ranges (for example, [hostname routing](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-private-hostname/) or [Cloudflare Mesh](/mesh/)), you can still add the range explicitly as a diagnostic step, but this should not be required for normal operation.

If your account uses a [custom initial resolved IP range](/cloudflare-one/networks/routes/configure-initial-resolved-ips/) instead of the default `172.64.128.0/20`, add that custom range to your Split Tunnels configuration.

## Domain-based Split Tunnels

Domain-based split tunneling has a few ramifications you should be aware of before deploying in your organization:.

- Routes excluded or included from Cloudflare One Client and Gateway visibility may change day to day, and may be different for each user depending on where they are.
- You may inadvertently exclude or include additional hostnames that happen to share an IP address. This commonly occurs if you add a domain hosted by a CDN or large Internet provider such as Cloudflare, AWS, or Azure. For example, if you wanted to exclude a VPN hosted on AWS, do not add `*.amazonaws.com` as that will open up your devices to all traffic on AWS. Instead, add the specific VPN endpoint (`*.cvpn-endpoint-<UUID>.prod.clientvpn.us-west-2.amazonaws.com`).
- Most services are a collection of hostnames. Until Split Tunnels mode supports [App Types](/cloudflare-one/traffic-controls/application-app-types/), you will need to manually add all domains used by a particular app or service.
- The Cloudflare One Client must handle the DNS lookup request for the domain. If a DNS result has been previously cached by the operating system or otherwise intercepted (for example, via your browser's secure DNS settings), the IP address will not be dynamically added to your Split Tunnel.

### Valid domains

| Split tunnel domain   | Matches                                                          | Does not match                                                |
| --------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- |
| `example.com`         | exact match of `example.com`                                     | subdomains such as `www.example.com`                          |
| `example.example.com` | exact match of `example.example.com`                             | `example.com` or subdomains such as `www.example.example.com` |
| `*.example.com`       | subdomains such as `www.example.com` and `sub2.sub1.example.com` | `example.com`                                                 |

### Platform differences

Domain-based Split Tunnels work differently on mobile clients than on desktop clients. If both mobile and desktop clients will connect to your organization, it is recommended to use Split Tunnels based on IP addresses or CIDR, which work the same across all platforms.

#### Windows, Linux and macOS

Clients on these platforms work by dynamically inserting the IP address of the domain immediately after it is resolved into the routing table for split tunneling. This allows the desktop clients to support wildcard domain prefixes (for example, `*.example.com`), not just a singular domain (like `example.com` or `www.example.com`).

Cloudflare One Client versions `2026.8.1755.1` and above attempt to maintain name-based Split Tunnel rule results across tunnel connections. Earlier versions did not maintain these results, so access to these domains was not maintained while the client was disconnected or reconnecting. Before the first tunnel connection, however, the client firewall blocks these domains. This is because the client has not yet queried which IP addresses are associated with each domain and therefore defaults to blocking this traffic to avoid unintended leaks.

#### iOS, Android and ChromeOS

Due to platform differences, mobile clients can only apply Split Tunnels rules when the tunnel is initially started. This means:

- Domain-based Split Tunnels rules are created when the tunnel is established based on the IP address for that domain at that time. The route is refreshed each time the tunnel is established.
- Wildcard domain prefixes (for example, `*.example.com`) are supported only if they have valid wildcard DNS records. Other wildcard domains are not supported because the client is unable to match wildcard domains to hostnames when starting up the tunnel. Unsupported wildcard domain prefixes can still exist in your configuration, but they will be ignored on mobile platforms.

## Remove a route

:::caution
Removing default Split Tunnel entries may cause users to lose Internet connectivity or block their access to local resources.
:::

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.
2. Locate the [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) you would like to modify and select **Edit**.
3. Under **Split Tunnels**, select **Manage**.
4. Find the IP address or hostname in the list and select the **Action** button. From the dropdown, select _Delete_.

<Render file="warp/client-notification-lag" product="cloudflare-one" />

If you need to revert to the default Split Tunnel entries recommended by Cloudflare, select **Restore default entries**.

## Related resources

- [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) - Resolve selected domains via local DNS instead of Cloudflare Gateway.
- [Cloudflare One Client with firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/) - Learn which IPs, domains, and ports to allow so users can deploy and connect the Cloudflare One Client successfully behind a firewall.

# Captive portal detection

import { Details, Badge, Render, Tabs, TabItem } from "~/components";

Captive portals are used by public Wi-Fi networks (such as airports, coffee shops, and hotels) to make a user agree to their Terms of Service or provide payment before allowing access to the Internet. When a user connects to the Wi-Fi, the captive portal blocks all HTTPS traffic until the user completes a captive portal login flow in their browser. This prevents the Cloudflare One Client (formerly WARP) from connecting to Cloudflare. At the same time, the Cloudflare One Client creates [firewall rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#ip-traffic) on the device to send all traffic to Cloudflare. The user is therefore unable to access the captive portal login screen unless they temporarily disconnect the Cloudflare One Client.

## Allow users to connect to captive portals

To allow users to connect through a captive portal, administrators can configure the following device client settings:

### No user interaction required

- Enable [Captive portal detection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#captive-portal-detection). This allows the Cloudflare One Client to temporarily disconnect when it detects a captive portal on the network. For more details, refer to [how captive portal detection works](#how-captive-portal-detection-works) and its [limitations](#limitations).
- Set [Device tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol) to **MASQUE**. When using MASQUE, client traffic will look like standard HTTPS traffic and is therefore less likely to be blocked by captive portals.

### User interaction required

- Enable [Lock device client switch](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#lock-device-client-switch) and enable [Allow admin override codes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-admin-override-codes). Users can contact the IT administrator for a one-time code that allows them to manually disconnect the Cloudflare One Client and connect to a portal.
- For employees who travel, disable [Lock device client switch](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#lock-device-client-switch) and set an [Auto connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect) duration. This allows the user to manually disconnect the Cloudflare One Client without contacting IT.

## How captive portal detection works

If the Cloudflare One Client cannot establish a connection to Cloudflare, it will:

1. Start the captive portal timer.

2. Send a series of requests to the [Cloudflare captive portal URLs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#captive-portal) and other OS and browser-specific captive portal URLs. These requests are sent outside of the WARP tunnel.

3. If a request is intercepted, the Cloudflare One Client assumes the network is behind a captive portal and fully opens the [system firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#ip-traffic). While the firewall is open, all device traffic will bypass the Cloudflare One Client.

4. Re-enable the firewall after the user successfully connects to the portal or after the timeout period expires.

## Limitations

- Due to [how captive portal detection works](#how-captive-portal-detection-works), it may be possible for an employee to spoof a captive portal in order to disconnect the Cloudflare One Client.
- Some captive portals, particularly those on airlines, may be slow to respond and exceed the captive portal detection timeout. Users will likely see a [CF_CAPTIVE_PORTAL_TIMED_OUT](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/#cf_captive_portal_timed_out) error when they try to connect. For context on the steps leading up to these errors, refer to [Connectivity status](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/connectivity-status/).
- The Cloudflare One Client may not be able to detect multi-stage captive portals, which redirect the user to different networks during the login process. Users will need to manually disconnect the Cloudflare One Client to get through the captive portal.
- Some public Wi-Fi networks are incompatible with running the Cloudflare One Client:
  - Captive portals that intercept all DNS traffic will block the Cloudflare One Client's [DoH connection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#overview). Users will likely see a [CF_NO_NETWORK](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/#cf_no_network) error after they login to the captive portal.
  - Captive portals that only allow HTTPS traffic will block the Cloudflare One Client's [Wireguard UDP connection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#overview). Users will likely see a [CF_HAPPY_EYEBALLS_MITM_FAILURE](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/#cf_happy_eyeballs_mitm_failure) error after they login to the captive portal.

:::note[Check system notifications]

Captive portal detection relies on system notifications to prompt the user. The login screen may not appear if a notification is dismissed or if the device is in Do Not Disturb mode, is screen recording, or if notifications for the Cloudflare One Client app are disabled in system settings.

:::

## Get captive portal logs <Badge text="Beta" variant="note" />

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| All modes                                                                                  | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.4.589.1           |
| macOS    | ✅           | 2025.4.589.1           |
| Linux    | ❌           |                        |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

Captive portal logs are used by Cloudflare Support to troubleshoot Cloudflare One Client captive portal issues. When an end user reports an issue with a captive portal, the IT administrator can ask the user to collect captive portal logs on their device. The administrator can then attach the logs to a Cloudflare Support ticket.

To get captive portal logs:

<Tabs>
<TabItem label="Version 2026.2+">

1. Open a terminal window.
2. Run the following command:

	```sh
	warp-diag captive-portal
	```
3. When prompted with `You're currently connected via interface '<INTERFACE>' (<SSID>). Is this interface connected to the network causing issues?`, select **Yes** to confirm.

</TabItem>
<TabItem label="Version 2026.1 and earlier">

1. Open the Cloudflare One Client.
2. Go to **Settings** (gear icon) > **Preferences** > **Advanced**.
3. Select **Collect Captive Portal Diag**.
4. The Cloudflare One Client will ask if the device is connected (or attempting to connect) to the Wi-Fi network that is causing issues. Select **Yes** to confirm.

</TabItem>
</Tabs>

:::note[macOS limitation]
On macOS, [**Automatically join this network**](https://support.apple.com/guide/mac-help/wi-fi-settings-on-mac-mh11935/mac) should be enabled on the Wi-Fi network that is causing issues. This setting is enabled by default. If manually disabled, the captive portal diagnostic will fail to capture meaningful data and the device will not automatically reconnect to this network.
:::

Once the diagnostic finishes running, the Cloudflare One Client will place a `warp-captive-portal-diag-<date>-<time>.zip` file on the user's desktop. The end user can now share this file with their IT administrator.

## Related resources

- [Connectivity status](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/connectivity-status/) - Learn about the status messages displayed by the Cloudflare One Client during its connection process, and understand each stage as the client establishes a secure tunnel to Cloudflare.

# Emergency Disconnect

import { Render, TabItem, Tabs, APIRequest } from "~/components";

Emergency disconnect allows organizations and administrators to disconnect and reconnect their fleet of Cloudflare One Clients (formerly WARP) independently from Cloudflare infrastructure. For example, in the event of a [Cloudflare network outage](#use-cases) you ensure that you can still manage your devices even if Cloudflare’s systems are down or unreachable.

Two mechanisms are available:

- **[External Emergency Disconnect](#set-up-external-emergency-disconnect)**: Cloudflare One Clients periodically poll a customer-hosted HTTPS endpoint for a disconnect signal. This requires network connectivity to your endpoint but works even when Cloudflare infrastructure is unreachable.
- **[Local Emergency Disconnect](#set-up-local-emergency-disconnect)**: The Cloudflare One Client monitors a local JSON file on the device for a disconnect signal. This does not require any network connectivity and is useful for disaster recovery scenarios where both Cloudflare and your own infrastructure may be unreachable.

Emergency disconnect can also be used in combination with the dashboard-initiated [Disconnect the Cloudflare One Client on all devices](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#disconnect-the-cloudflare-one-client-on-all-devices) setting. You can use any mechanism individually or together for multi-layer resilience. A disconnect signal from any source triggers disconnect; all sources must indicate normal operation for the client to reconnect. For details on how these settings interact, refer to [Device client settings precedence](#device-client-settings-precedence).


## Use cases

- **Security Incident Response**: Quickly terminate all WARP tunnels across the entire fleet.
- **Compliance and Auditing**: Fulfill requirements in sensitive or regulated environments that mandate an "emergency stop" capability that is fully isolated, auditable, and controlled by the organization's own infrastructure.
- **Disaster Recovery**: If devices cannot reach Cloudflare's API (due to a network outage, routing issue, or client-side misconfiguration), administrators retain the ability to force-disconnect the fleet via the customer-hosted endpoint or a local signal file.
- **Business Continuity Planning (BCP)**: Trigger emergency disconnect from local BCP scripts even when both Cloudflare and your own infrastructure are unreachable.
- **Local Automation**: Integrate with configuration management tools (Ansible, Puppet, Chef) or monitoring agents to manage the disconnect state without maintaining an HTTPS endpoint.

## Signal format

Both the external endpoint response payload and the local signal file content must be valid JSON with the following format:

```json
{
  "emergency_disconnect": false | true
}
```

- If `emergency_disconnect` is set to `true`, the device will initiate an emergency disconnect.
- If `emergency_disconnect` is set to `false`, the device will continue normal operation.

## Set up External Emergency Disconnect

<Render file="warp/external-disconnect-availability" product="cloudflare-one" />

When External Emergency Disconnect is enabled, Cloudflare One Clients will periodically poll a customer-hosted HTTPS endpoint. A client will only change its connection state if it receives a valid JSON payload with the new state. Any failure to successfully retrieve the state (such as endpoint unreachability, invalid certificate fingerprint, or an improperly structured payload) will not cause a state change on the client.

### External endpoint requirements

An external disconnect endpoint is an HTTPS server hosted outside of Cloudflare from which the Cloudflare One Client will fetch the emergency disconnect signal. The customer is fully responsible for managing this endpoint.

#### Endpoint URL

The external endpoint URL should:

- Use the HTTPS protocol.
- Use an IPv4 or IPv6 address as the host, not a domain.
- (Recommended) Use a public IP to ensure that devices can fetch the latest state regardless of their network location.

#### Cipher suites

The Cloudflare One Client establishes a TLS connection using [Rustls](https://github.com/rustls/rustls). Make sure your HTTPS endpoint accepts one of the [cipher suites supported by Rustls](https://docs.rs/rustls/0.21.10/src/rustls/suites.rs.html#125-143).

### 1. Create an external disconnect endpoint

To configure External Emergency Disconnect, you will need an HTTPS endpoint in your own infrastructure that serves the global disconnect signal. The Cloudflare One Client will poll the external endpoint and validate its TLS/SSL certificate against an SHA-256 fingerprint that you upload to Zero Trust. Refer to [External endpoint requirements](#external-endpoint-requirements) for more details.

The following example demonstrates how to deploy an external disconnect endpoint using an nginx container in Docker.

1.  Generate a TLS/SSL certificate:

    ```sh
    openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout key.pem -out cert.pem
    ```

    You will be prompted to fill in Distinguished Name (DN) fields. Fill in your organization's information or press `Enter` to use the default values.

    The command will output a certificate in PEM format and its private key. Store these files in a secure place.

2.  Configure an HTTPS server on your network to use this certificate and key:

        a. Create an nginx configuration file called `nginx.conf`:

        	```txt title="nginx.conf"
        	events {
        		worker_connections  1024;
        	}

        	http {
        			server {
        					listen              443 ssl;
        					ssl_certificate     /certs/cert.pem;
        					ssl_certificate_key /certs/key.pem;
        					location /status/disconnect {
        							default_type application/json;
        							return 200 '{"emergency_disconnect": false}';
        					}
        			}
        	}
        	```

        If needed, replace `/certs/cert.pem` and `/certs/key.pem` with the locations of your certificate and key.

        b. Add the nginx image to your Docker compose file:

        	```yml title="docker-compose.yml"
        	services:
        		nginx:
        			image: nginx:latest
        			ports:
        				- 3333:443
        			volumes:
        				- ./nginx.conf:/etc/nginx/nginx.conf:ro
        				- ./certs:/certs:ro
        	```

        	If needed, replace `./nginx.conf` and `./certs` with the locations of your nginx configuration file and certificate.

        c. Start the server:

        	```sh
        	docker compose up -d
        	```

3.  To test that the HTTPS endpoint is working, run a curl command from the end user's device. You need to pass the `--insecure` option because we are using a self-signed certificate.

    ```sh
    curl --insecure https://<server-ip>:3333/status/disconnect
    ```

    ```sh output
    {"emergency_disconnect": false}
    ```

### 2. Extract the SHA-256 fingerprint

<Render file="warp/sha-256-fingerprint" product="cloudflare-one" />

### 3. Turn on External Emergency Disconnect

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

To configure External Emergency Disconnect using the dashboard:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Management**.
2. Select **Global disconnection settings**.
3. Find **Manage device connection using an external signal** and select **Edit**.
4. Configure the following fields:
   - **Endpoint IP address and port**: Enter the HTTPS URL from which to fetch the external disconnect signal (for example, `https://192.0.2.1:3333/status/disconnect`). The endpoint must use HTTPS and have an IPv4 or IPv6 address as the host.
   - **Polling frequency**: Choose how often the Cloudflare One Client should fetch the external disconnect signal.
   - **Certificate fingerprint**: Enter the [SHA-256 fingerprint](#2-extract-the-sha-256-fingerprint) of the HTTPS server certificate (for example, `DD4F4806C57A5BBAF1AA5B080F0541DA75DB468D0A1FE731310149500CCD8662`).
5. Select **Save**.
6. Turn on **Manage device connection using an external signal**.

All Cloudflare One Clients in your organization will now start polling the external endpoint and connect or disconnect based on the response payload.

</TabItem>
<TabItem label="API">

To configure External Emergency Disconnect using the API, send a `PATCH` request to the `/devices/settings` endpoint:

<APIRequest
	path="/accounts/{account_id}/devices/settings"
	method="PATCH"
	json={{
		external_emergency_signal_enabled: true,
		external_emergency_signal_url: "https://192.0.2.1:3333/status/disconnect",
		external_emergency_signal_fingerprint:
			"DD4F4806C57A5BBAF1AA5B080F0541DA75DB468D0A1FE731310149500CCD8662",
		external_emergency_signal_interval: "1m",
	}}
/>

</TabItem>

<TabItem label="MDM">

To configure External Emergency Disconnect using an MDM, add the following parameters to your [MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/):

```xml
<key>external_emergency_signal_url</key>
<string>https://192.0.2.1:3333/status/disconnect</string>
<key>external_emergency_signal_fingerprint</key>
<string>DD4F4806C57A5BBAF1AA5B080F0541DA75DB468D0A1FE731310149500CCD8662</string>
<key>external_emergency_signal_interval</key>
<integer>60</integer>
```

</TabItem>
</Tabs>

:::note[Split Tunnels in Include mode]
The Cloudflare One Client will automatically exclude the external endpoint IP address from the WARP tunnel. If a device profile uses [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) in **Include** mode, ensure that your Split Tunnel entries do not contain the external endpoint IP; otherwise the Cloudflare One Client will exclude the entire Split Tunnel entry from the tunnel.
:::

### 4. Test External Emergency Disconnect

1. Ensure that the Cloudflare One Client is connected.
2. Ensure that the External Emergency Disconnect feature is [turned on](#3-turn-on-external-emergency-disconnect).
3. In your [external endpoint](#1-create-an-external-disconnect-endpoint) configuration, change `emergency_disconnect` to `true`:

   ```json
   { "emergency_disconnect": true }
   ```

4. You may need to reload the server to apply changes. To reload the [example `nginx` server](#1-create-an-external-disconnect-endpoint):
   ```sh
   docker exec <container-name-or-id> nginx -s reload
   ```

The Cloudflare One Client will automatically disconnect within the configured polling interval, and the Cloudflare One Client GUI will display [`Admin directed disconnect`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/#admin-directed-disconnect). To reconnect all devices, change `emergency_disconnect` back to `false`.

## Set up Local Emergency Disconnect

<Render file="warp/local-disconnect-availability" product="cloudflare-one" />

Local Emergency Disconnect allows organizations to trigger an emergency disconnect on the device itself, without requiring network access to any remote infrastructure. The Cloudflare One Client monitors a local JSON file at a fixed, admin-writable path. When the file contains a disconnect signal, the client enters the emergency disconnect state. Local scripts, configuration management tools (such as Ansible, Puppet, or Chef), or monitoring agents can create or modify the signal file directly on the device.

### Signal file path

The Cloudflare One Client monitors a fixed file path that requires administrative privilege to modify. The path is not configurable.

| Operating system | File path                                                           |
| ---------------- | ------------------------------------------------------------------- |
| Windows          | `%PROGRAMDATA%\Cloudflare\emergency_disconnect.json`                |
| macOS            | `/Library/Application Support/Cloudflare/emergency_disconnect.json` |
| Linux            | `/var/lib/cloudflare-warp/emergency_disconnect.json`                |

The signal file uses the same [JSON format](#signal-format) as the external HTTPS endpoint. If the file does not exist, the client treats it as normal operation (`false`). If the file contains invalid JSON, the client logs an error and does not change state. The client reacts to file changes within 30 seconds.

### 1. Turn on Local Emergency Disconnect

To enable the feature, deploy the `local_emergency_signal_enabled` parameter via your MDM. Add the following to your [MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/):

```xml
<key>local_emergency_signal_enabled</key>
<true />
```

The Cloudflare One Client will begin monitoring the [signal file path](#signal-file-path) once the MDM setting is applied. Configuration changes take effect without requiring a client restart.

### 2. Test Local Emergency Disconnect

1. Ensure that the Cloudflare One Client is connected.
2. Ensure that Local Emergency Disconnect is [turned on](#1-turn-on-local-emergency-disconnect).
3. Create the signal file at the [appropriate path](#signal-file-path) for your operating system with the following content:

<Tabs> <TabItem label="macOS">

```sh
sudo tee "/Library/Application Support/Cloudflare/emergency_disconnect.json" <<< '{"emergency_disconnect": true}'
```

</TabItem>
<TabItem label="Windows">

Open an elevated PowerShell prompt and run:

```txt
Set-Content -Path "$env:PROGRAMDATA\Cloudflare\emergency_disconnect.json" -Value '{"emergency_disconnect": true}'
```

</TabItem>
<TabItem label="Linux">

```sh
sudo tee /var/lib/cloudflare-warp/emergency_disconnect.json <<< '{"emergency_disconnect": true}'
```

</TabItem>
</Tabs>

The Cloudflare One Client will automatically disconnect within 30 seconds, and the Cloudflare One Client GUI will display [`Admin directed disconnect`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/#admin-directed-disconnect).

To reconnect, change `emergency_disconnect` to `false` or remove the file:

<Tabs> <TabItem label="macOS">

```sh
sudo rm "/Library/Application Support/Cloudflare/emergency_disconnect.json"
```

</TabItem>
<TabItem label="Windows">

```txt
Remove-Item "$env:PROGRAMDATA\Cloudflare\emergency_disconnect.json"
```

</TabItem>
<TabItem label="Linux">

```sh
sudo rm /var/lib/cloudflare-warp/emergency_disconnect.json
```

</TabItem>
</Tabs>

## Logs

Since emergency disconnect signals from external endpoints and local files are independent from Cloudflare's infrastructure, these disconnects are not logged by Cloudflare. Dashboard logs will only report changes to feature settings (such as turning on/off the feature or changing the endpoint URL), not disconnection events.

To get the current emergency disconnect status on a device, run:

```sh
warp-cli settings
```

```sh output
Merged configuration:
(override)	Emergency disconnect: true (issued @ 2025-12-09T13:57:42.597864Z)
```

The current status is also available in [client diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/#macoswindowslinux) in `warp-settings.txt`.

## Clear emergency disconnect state

### Clear External Emergency Disconnect

If the external endpoint becomes unavailable or serves an invalid configuration, Cloudflare One Clients can get stuck in the emergency disconnect state. You can recover clients by removing their External Emergency Disconnect configuration:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Management**.
2. Select **Global disconnection settings**.
3. Turn off **Manage device connection using an external signal**.

Cloudflare will propagate the new setting to clients, instructing them to stop polling and discard the cached emergency state.

</TabItem>
<TabItem label="API">

Send a `PATCH` request with the endpoint URL and fingerprint set to empty strings:

<APIRequest
	path="/accounts/{account_id}/devices/settings"
	method="PATCH"
	json={{
		external_emergency_signal_enabled: false,
		external_emergency_signal_url: "",
		external_emergency_signal_fingerprint: "",
		external_emergency_signal_interval: "1m",
	}}
/>

Cloudflare will propagate the new settings to clients, instructing them to stop polling and discard the cached emergency state.

</TabItem>

<TabItem label="MDM">

If you deployed External Emergency Disconnect using an MDM, remove the `external_emergency_signal_url` key (and other related keys) from the MDM profile. Then, use your MDM to push the changes to devices. The Cloudflare One Client will stop polling the external endpoint and discard its cached emergency state.

Alternatively, users can switch the Cloudflare One Client to a [different MDM configuration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/switch-organizations/) that does not have the feature configured.

</TabItem>
</Tabs>

### Clear Local Emergency Disconnect

To clear the local emergency disconnect state:

1. Remove or update the [signal file](#signal-file-path) so that `emergency_disconnect` is `false`.
2. Alternatively, remove the `local_emergency_signal_enabled` key from your MDM profile and push the change to devices to turn off the feature. The client will stop monitoring the local file and discard its cached local signal state.

### Local client reset

As a last resort, you can use the CLI to reset emergency disconnect on an individual device:

```sh
warp-cli registration delete
```

This command will clear the client registration, clear the local policy, and discard the cached emergency state. To reconnect, you will need to [turn off External Emergency Disconnect](#clear-external-emergency-disconnect) and then [re-enroll the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) with your Zero Trust organization.

## Device client settings precedence

Learn how global disconnect settings interact and how they impact other device client profile settings.

### Global disconnection settings

The client will honor disconnect signals from the Cloudflare dashboard (via [Disconnect the Cloudflare One Client on all devices](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#disconnect-the-cloudflare-one-client-on-all-devices)), the external endpoint, and the local signal file. A global disconnect is enforced if **any** source triggers it. All sources must indicate normal operation for the client to reconnect.

The following table shows how the three signal sources combine. If **any** source indicates disconnect, the client disconnects.

| Dashboard ([Disconnect all devices](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#disconnect-the-cloudflare-one-client-on-all-devices)) | External endpoint | Local file        | Result             |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ----------------- | ------------------ |
| On                                                                                                                                                                            | `true`            | `true`            | Force disconnected |
| On                                                                                                                                                                            | `true`            | `false`/absent    | Force disconnected |
| On                                                                                                                                                                            | `false`           | `true`            | Force disconnected |
| On                                                                                                                                                                            | `false`           | `false`/absent    | Force disconnected |
| Off                                                                                                                                                                           | `true`            | `true`            | Force disconnected |
| Off                                                                                                                                                                           | `true`            | `false`/absent    | Force disconnected |
| Off                                                                                                                                                                           | `false`           | `true`            | Force disconnected |
| Off                                                                                                                                                                           | `false`           | `false`/absent    | Normal operation   |

### Auto connect

[Auto connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect) does not apply while a global disconnect is in effect.

### Lock device client switch

[Lock device client switch](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#lock-device-client-switch) does not apply while a global disconnect is in effect. Users will be unable to connect the Cloudflare One Client unless they have an [admin override code](#admin-override).

### Admin override

A global disconnect will clear any existing [admin override codes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-admin-override-codes). The only way for users to reconnect during a global disconnect is by using a new [admin override code](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-admin-override-codes). For example, you may want to provide IT staff with a code so that they can test resolution of the incident that led to the global disconnect. The override code will exempt a specific user and device from the global disconnect until the override timeout expires.

# Device client settings

import {
	Details,
	GlossaryTooltip,
	Badge,
	Render,
	Tabs,
	TabItem,
} from "~/components";

Device client settings (formerly WARP) allow you to customize the Cloudflare One Client modes and permissions available to end users.

- [Global device client settings](#global-device-client-settings) are configurations which apply to all devices enrolled in your Zero Trust organization.
- [Global disconnection settings](#global-disconnection-settings) allow administrators to force-disconnect all Cloudflare One Clients during an incident or outage.
- [Device profile settings](#device-profile-settings) can vary across devices depending on which [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) is applied.

:::note

<Render file="warp/client-notification-lag" product="cloudflare-one" />
:::

## Global device client settings

### Allow admin override codes

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

:::note
To use **Allow admin override codes**, you must first have enabled [**Lock device client switch**](#lock-device-client-switch).
:::

When [**Lock device client switch**](#lock-device-client-switch) is enabled, users cannot toggle the Cloudflare One Client on and off on their device. Enabling **Allow admin override codes** gives users the ability to temporarily connect or disconnect the Cloudflare One Client using an override code provided by an admin. **Allow admin override codes** is only needed in a configuration where **Lock device client switch** is enabled.

Example use cases for **Allow admin override codes** include:

- Allowing users to momentarily disconnect the Cloudflare One Client to work around a temporary network issue such as an incompatible public Wi-Fi, or a firewall at a customer site blocking the connection.
- Allowing test users to connect the Cloudflare One Client while a global disconnect is in effect.

As admin, you can set a **Timeout** to define how long a user can toggle the client's connection toggle on or off after entering the override code. Cloudflare generates a new override code every hour that an admin can send to end users. The override code's validity adheres to fixed-hour time blocks and aims to be generous to the end user.

:::caution[Troubleshooting]

To learn more about override code timeouts and how Cloudflare calculates an override code's valid duration, refer to [Troubleshooting](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#admin-override-codes-expired).

If [Auto connect](#auto-connect) is enabled, the Cloudflare One Client will automatically reconnect, according to the value set for the auto connect timeout, even when using **Allow admin override codes**. Refer to [Troubleshooting](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#admin-override-codes-expired) for more information.

:::

#### Retrieve the override code

To retrieve the one-time code for a user:

1. Enable **Allow admin override codes**.
2. Go to **Team & Resources** > **Devices**.
3. Select **View details** for a connected device.
4. Scroll down to **User details** and select the user's name.
5. Copy the 7-digit **Override code** shown in the side panel.
6. Share this code with the user for them to enter on their device.

The user will have an unlimited amount of time to activate their code.

#### Enter the override code

To activate the override code on a user device:

<Tabs>
<TabItem label="Version 2026.2+">

1. Open the Cloudflare One Client and go to **Settings**.
2. In **Temporarily disconnect Cloudflare One Client**, select **Enter admin code**.
3. Enter the override code and select **Disconnect**.

</TabItem>
<TabItem label="Version 2026.1 and earlier">

1. In the Cloudflare One Client, go to **Settings** > **Preferences** > **Advanced**.
2. Select **Enter code**.
3. Enter the override code.

</TabItem>
</Tabs>

The user can now toggle the client's connection toggle or use the `warp-cli connect` command. The client will automatically reconnect after the [Auto connect period](#auto-connect), but the user can continue to connect or disconnect the Cloudflare One Client until the override expires.

### Install CA to system certificate store

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Traffic and DNS mode, Local proxy mode                                                            | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2024.12.554.0          |
| macOS    | ✅           | 2024.12.554.0          |
| Linux    | ✅           | 2024.12.554.0          |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

When `Enabled`, the Cloudflare One Client will [automatically install](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/) your organization's root certificate on the device.

### Assign a unique IP address to each device

<Details header="Feature availability">

| Operating Systems     | [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| --------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Windows, macOS, Linux | Traffic and DNS mode, Traffic only mode                                                           | All plans                                                     |

</Details>

Overrides the default IP address of the Cloudflare One Client's [virtual network interface](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#ip-traffic) such that each device has its own unique local interface IP.

This setting is primarily used as a prerequisite for [Cloudflare Mesh](/mesh/) and [MASQUE](#device-tunnel-protocol). You can also use it when the default IP conflicts with other local services on your network.

**Value:**

- `Disabled`: (default) Sets the local interface IP to `172.16.0.2` on all devices. This configuration is only respected by devices using [WireGuard](#device-tunnel-protocol) and does not affect devices using [MASQUE](#device-tunnel-protocol).

- `Enabled`: Sets the local interface IP on each device to its <GlossaryTooltip term="WARP CGNAT IP">CGNAT IP</GlossaryTooltip> or to a [custom device IP](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/).

The IP assigned to a device is permanent until the device unregisters from your Zero Trust organization or switches to a different registration. Disconnects and reconnects do not change the IP address assignment.

### Allow all Cloudflare One traffic to reach enrolled devices

<Details header="Feature availability">

| Operating Systems | [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ----------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All systems       | Traffic and DNS mode                                                                              | All plans                                                     |

</Details>

Allows traffic on-ramped using [Cloudflare Mesh](/mesh/) or [Cloudflare WAN](/cloudflare-one/networks/connectors/cloudflare-wan/) to route to devices enrolled in your Zero Trust organization.

Each device is assigned a virtual IP address in the <GlossaryTooltip term="WARP CGNAT IP">CGNAT IP</GlossaryTooltip> space (`100.96.0.0/12`) or a [custom device IP range](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-ips/). With this setting `Enabled`, users on your private network will be able to connect to these device IPs and access [TCP, UDP, and/or ICMP-based services](/cloudflare-one/traffic-controls/proxy/) on your devices. You can create [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) to control which users and devices can access the device IPs.

:::note
Ensure that traffic destined to your device IPs routes from your private network to Cloudflare Gateway. For example, if you are using [Cloudflare Mesh](/mesh/) connectivity, you must configure your [Split Tunnel settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) so that traffic to your Mesh IPs routes through the tunnel.
:::

## Global disconnection settings

### Disconnect the Cloudflare One Client on all devices

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.2.600.0           |
| macOS    | ✅           | 2025.2.600.0           |
| Linux    | ✅           | 2025.2.600.0           |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

:::note
Requires the [Super Administrator](/cloudflare-one/roles-permissions/) role.
:::

**Disconnect the Cloudflare One Client on all devices** allows administrators to fail open the Cloudflare One Client in case of an incident occurring in your environment, independent from incidents or outages affecting Cloudflare's services. When you turn on **Disconnect the Cloudflare One Client on all devices**, Cloudflare will disconnect all Windows, macOS, and Linux Cloudflare One Clients that are connected to your Zero Trust organization. This includes end user devices and [Cloudflare Mesh](/mesh/) nodes. End users will receive a notification on their device and the Cloudflare One Client will display [`Admin directed disconnect`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/#admin-directed-disconnect).

To resume normal operations, turn off **Disconnect the Cloudflare One Client on all devices**. The Cloudflare One Client will automatically reconnect.

For more information on how **Disconnect the Cloudflare One Client on all devices** works with other device client settings, refer to [Device client settings precedence](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#device-client-settings-precedence).

### Manage device connection using an external signal

<Render file="warp/external-disconnect-availability" product="cloudflare-one" />

Allows administrators to disconnect and reconnect the Cloudflare One Client independently from any Cloudflare infrastructure. When `Enabled`, Cloudflare One Clients will periodically poll the configured HTTPS endpoint and disconnect when they receive a valid disconnect signal.

To set up the external HTTPS endpoint, refer to [Emergency Disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect).

### Manage device connection using a local file

<Render file="warp/local-disconnect-availability" product="cloudflare-one" />

Allows administrators to disconnect and reconnect the Cloudflare One Client using a local JSON file on the device, without requiring any network connectivity. When `Enabled`, the Cloudflare One Client monitors a fixed file path for a disconnect signal. This is useful for disaster recovery scenarios where both Cloudflare and your own infrastructure may be unreachable.

To set up the local signal file, refer to [Emergency Disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-local-emergency-disconnect).

## Device profile settings

### Captive portal detection

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

When `Enabled`, the Cloudflare One Client will automatically disconnect when it detects a <GlossaryTooltip term="captive portal">captive portal</GlossaryTooltip>, and it will automatically reconnect after the **Timeout** duration.

Since captive portal implementations vary, the Cloudflare One Client may not detect all captive portals. For more information, refer to [Captive portal detection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/captive-portals/).

### Mode switch

<Details header="Feature availability">

| Operating Systems | [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ----------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All systems       | Any mode                                                                                          | All plans                                                     |

</Details>

When `Enabled`, users have the option to switch between [Traffic and DNS mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-and-dns-mode-default) and [DNS only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode). This feature does not support switching between any other modes.

### Device tunnel protocol

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                               | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2024.11.309.0          |
| macOS    | ✅           | 2024.11.309.0          |
| Linux    | ✅           | 2024.11.309.0          |
| iOS      | ✅           | 1.7                    |
| Android  | ✅           | 2.0                    |
| ChromeOS | ✅           | 2.0                    |

</Details>

Configures the protocol used to route IP traffic from the device to Cloudflare Gateway. To check the active protocol on a device, open a terminal and run `warp-cli settings | grep protocol`.

**Value**:

- **WireGuard**: Establishes a [WireGuard](https://www.wireguard.com/) connection to Cloudflare. The Cloudflare One Client will encrypt traffic using a non-FIPs compliant cipher suite, `TLS_CHACHA20_POLY1305_SHA256`. When switching from MASQUE to WireGuard, users may lose Internet connectivity if their Wi-Fi network blocks the [ports and IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip) required for WireGuard to function.
- **MASQUE**: (default) Establishes an HTTP/3 connection to Cloudflare. The Cloudflare One Client will encrypt traffic using TLS 1.3 and a [FIPS 140-3](https://csrc.nist.gov/pubs/fips/140-3/final) compliant cipher suite, `TLS_AES_256_GCM_SHA384`. [Assign a unique IP address to each device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#assign-a-unique-ip-address-to-each-device) is enabled by default for devices with MASQUE enabled.

For more details on WireGuard versus MASQUE, refer to our [blog post](https://blog.cloudflare.com/zero-trust-warp-with-a-masque).

### Lock device client switch

<Details header="Feature availability">

| Operating Systems | [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ----------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All systems       | Any mode                                                                                          | All plans                                                     |

</Details>

Allows the user to disconnect the Cloudflare One Client.

**Value:**

- `Disabled`: (default) The user is able to connect or disconnect the Cloudflare One Client at their discretion. When the client is disconnected, the user will not have the ability to reach sites protected by Access that leverage certain device posture checks.
- `Enabled`: The user is prevented from disconnecting the Cloudflare One Client. The client will always start in the connected state.

On MDM deployments, you must also include the `auto_connect` parameter with at least a value of `0`. This will prevent clients from being deployed in the off state without a way for users to manually enable them.

### Allow device to leave organization

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

When `Enabled`, users can log out from your Zero Trust organization by selecting **Logout from Zero Trust** in the Cloudflare One Client UI. The **Logout from Zero Trust** button is only available for devices that were [enrolled manually](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/). Devices that enrolled using an <GlossaryTooltip term="MDM file">MDM file</GlossaryTooltip> are always prevented from leaving your Zero Trust organization.

On Windows, [Device Management Protection](#device-management-protection) prevents users from logging out, even when this setting is `Enabled`.

### Device Management Protection <Badge text="Beta" variant="caution" size="small" />

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.9.0.0             |
| macOS    | ❌           |                        |
| Linux    | ❌           |                        |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

When `Enabled`, protects the Cloudflare One Client against unauthorized modification or uninstallation. Users must provide an administrator-generated code to uninstall, upgrade, or downgrade the client. Users also cannot log out of your Zero Trust organization. For more information, refer to [Uninstall protection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/uninstall-protection/).

### Allow updates

<Details header="Feature availability">

| Operating Systems     | [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| --------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| macOS, Windows, Linux | Any mode                                                                                          | All plans                                                     |

</Details>

When `Enabled`, users will receive update notifications when a new version of the client is available. Only turn this on if your users are local administrators with the ability to add or remove software from their device.

:::note

When a [client version assignment](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/client-version-assignments/) targets a device, the client suppresses local update notifications on that device. This prevents end users from seeing a dashboard-managed install and a local update prompt at the same time. To turn off client version assignments on a device, set [`allow_managed_deployments`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#allow_managed_deployments) to `false` in your MDM file.

:::

### Auto connect

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

When `Enabled`, the client will automatically reconnect if it has been disabled for the specified **Timeout** value. This setting is best used in conjunction with [Lock device client switch](#lock-device-client-switch) above.

We recommend keeping this set to a very low value — usually just enough time for a user to log in to hotel or airport Wi-Fi. If any value is specified, the client defaults to the Connected state (for example, after a reboot or the initial install).

**Value:**

- `0`: Allow the switch to stay in the off position indefinitely until the user turns it back on.
- `1` to `1440`: Turn switch back on automatically after the specified number of minutes.

### Support URL

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

When `Enabled`, the **Send Feedback** button in the Cloudflare One Client appears and will launch the URL specified. Example **Support URL** values are:

- `https://support.example.com`: Use an https\:// link to open your companies internal help site.
- `mailto:yoursupport@example.com`: Use a `mailto:` link to open your default mail client.

### Service mode

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

Allows you to choose the operational mode of the client. Refer to [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes) for a detailed description of each mode.

### Local Domain Fallback

<Details header="Feature availability">

| Operating Systems | [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ----------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All systems       | Traffic and DNS mode, DNS only mode                                                               | All plans                                                     |

</Details>

Configures the Cloudflare One Client to redirect DNS requests to a private DNS resolver. For more information, refer to our [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) documentation.

### Split Tunnels

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

Configures the Cloudflare One Client to exclude or include traffic to specific IP addresses or domains. For more information, refer to our [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) documentation.

### Directly route Microsoft 365 traffic

<Render file="warp/all-systems-modes-plans" product="cloudflare-one" />

Creates [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) Exclude entries for all [Microsoft 365 IP addresses specified by Microsoft](https://docs.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-ip-web-service). To use this setting, **Split Tunnels** must be set to **Exclude IPs and domains**. Once enabled, all Microsoft 365 network traffic will bypass the Cloudflare One Client and Gateway.

:::note
Along with the IP addresses provided by Microsoft's [IP Address and URL web service](https://learn.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-ip-web-service?view=o365-worldwide), enabling this option automatically adds the following Split Tunnel Exclude entries for Microsoft Teams:

- `24.24.24.24/32`
- `52.120.0.0/14`
  :::

### Allow users to enable local network exclusion

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                               | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2024.1.159.0           |
| macOS    | ✅           | 2024.1.160.0           |
| Linux    | ✅           | 2024.2.62.0            |
| iOS      | ❌           | N/A[^1]                |
| Android  | ✅           | 1.4                    |
| ChromeOS | ✅           | 1.4                    |

[^1]: Current versions of iOS do not allow LAN traffic to route through the WARP tunnel. Therefore, this feature is not needed on iOS.

</Details>

This setting is intended as a workaround for users whose home network uses the same set of IP addresses as your corporate private network. To use this setting, **Split Tunnels** must be set to **Exclude IPs and domains**.

When `Enabled`, users have the option to access local network resources (such as printers and storage devices) while connected to the Cloudflare One Client. When the user turns on [**Access Local Network**](#access-local-network-as-a-user), the Cloudflare One Client will detect the local IP range advertised by the user's home network (for example, `10.0.0.0/24`) and temporarily exclude this range from the WARP tunnel. The user will need to re-request access after the **Timeout** expires. Setting **Timeout** to `0 minutes` will allow LAN access until the next client reconnection, such as a reboot or a laptop waking from sleep.

:::caution[Warning]

Enabling this setting comes with two major consequences:

- **Device is exposed to security threats.** The user may be unaware that traffic to what used to be their company's private network is now actually being routed to their local network. This leaves the device vulnerable to [on-path attackers](https://www.cloudflare.com/learning/security/threats/on-path-attack/) and other security vulnerabilities. For example, imagine that a user's typical workflow involves logging into a remote desktop on the corporate network at `10.0.0.30`. A bad actor could set up a fake server on the local network at `10.0.0.30`. If the user goes to `10.0.0.30` while **Access local network** is enabled, the attacker can now steal their credentials.
- **User loses access to corporate resources.** — While accessing their local network, the user will be unable to connect to corporate resources that fall within the same IP/CIDR range.

:::

#### Access local network as a user

To turn on local network access in the Cloudflare One Client:

<Tabs>

<TabItem label="Windows and macOS">

1. Open the Cloudflare One Client and go to **Settings**.
2. In **Temporarily access local network resources**, select **Access resources**.

<Details header="Version 2026.1 and earlier">

1. Open the Cloudflare One Client.
2. Select the gear icon.
3. Select **Access Local Network**.

</Details>

</TabItem>

<TabItem label="Linux">

1. Open a terminal window.
2. Run `warp-cli override local-network start`.

</TabItem>

<TabItem label="Android and ChromeOS">

1. Open the Cloudflare One Agent app.
2. Go to **Settings** > **Advanced** > **Connection Options**.
3. Select **Access Local Network**.

</TabItem>

</Tabs>

#### Limitations

- The Cloudflare One Client will only exclude local networks in the [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) address space. Other IP addresses such as CGNAT are not supported.
- The maximum excluded subnet size is `/24`.
- If a device has multiple network interfaces with distinct local IP ranges, the Cloudflare One Client will only exclude one of those networks. To access a specific local network, disable the other interfaces and disconnect/reconnect the Cloudflare One Client.

### Client interface IP DNS registration

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                               | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.2.600.0           |
| macOS    | ❌           |                        |
| Linux    | ❌           |                        |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

When `Enabled`, the operating system will register the Cloudflare One Client's [local interface IP](#assign-a-unique-ip-address-to-each-device) (CGNAT IP or `172.16.0.2`) with your on-premise DNS server when the DNS server is reachable.

If you use on-premise DNS infrastructure (such as Active Directory), we recommend turning this setting on for remote [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) and turning it off for [managed network](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/) device profiles. In this configuration, remote devices will register their client interface IP, while on-premise devices will only register their local DHCP address. This allows the on-premise DNS server to resolve device hostnames no matter where the device is located.

### SCCM VPN boundary support

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                               | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.5.735.1           |
| macOS    | ❌           |                        |
| Linux    | ❌           |                        |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

Microsoft's [System Center Configuration Manager](https://learn.microsoft.com/en-us/intune/configmgr/) (SCCM) is used to manage software on Windows devices based on the [boundary group](https://learn.microsoft.com/en-us/intune/configmgr/core/servers/deploy/configure/define-site-boundaries-and-boundary-groups), or network location, to which they belong. You can assign Cloudflare One Clients to a SCCM boundary group based on their [managed network](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/) and other device profile attributes. When **SCCM VPN Boundary Support** is turned on, the Cloudflare One Client will modify the description field on its [virtual network interface](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#ip-traffic). This allows you to define a VPN boundary group that matches on the network interface description.

**Value:**

- `Disabled`: (default) The client network interface description is `Cloudflare WARP Interface Tunnel`.

- `Enabled`: The client network interface description is `(SCCM) Cloudflare WARP Interface Tunnel` for devices which have the [SCCM client](https://learn.microsoft.com/en-us/intune/configmgr/core/clients/deploy/deploy-clients-to-windows-computers) installed. Devices without the SCCM client will still use the default `Cloudflare WARP Interface Tunnel` description. The Cloudflare One Client checks if the SCCM client is installed by looking for the SMS Agent Host (`ccmexec.exe`) Windows service.

#### Example SCCM configuration

Assume you want to push software updates from a cloud based [distribution point](https://learn.microsoft.com/en-us/intune/configmgr/core/servers/deploy/configure/boundary-groups-distribution-points) if the device is remote, but use on-prem servers if the device is on the office network. To set up these boundary groups:

1.  In Zero Trust:

        a. Turn on **SCCM VPN Boundary Support** for remote [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/).

        b. Turn off **SCCM VPN Boundary Support** for [on-prem device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/#4-configure-device-profile).

        c. (Optional) Verify device settings:

    <Details header="Verify SCCM VPN Boundary Support">

    To check if **SCCM VPN Boundary Support** is active on a device, run the following command:

    ```cmd
    warp-cli settings | findstr "SCCM VPN Boundary"
    ```

    ```cmd output
    (network policy) SCCM VPN Boundary Support: true
    ```

    You can also verify network interface details for the `CloudflareWARP` adapter:

    ```cmd
    ipconfig /all
    ```

    ```cmd output {8}
    Windows IP Configuration

    ...

    Unknown adapter CloudflareWARP:

    	Connection-specific DNS Suffix  . :
    	Description . . . . . . . . . . . : (SCCM) Cloudflare WARP Interface Tunnel
    	Physical Address. . . . . . . . . :
    	DHCP Enabled. . . . . . . . . . . : No
    	Autoconfiguration Enabled . . . . : Yes
    	IPv6 Address. . . . . . . . . . . : 2001:db8:110:8f79:145:f180:fc4:8106(Preferred)
    	Link-local IPv6 Address . . . . . : fe80::83b:d647:4bed:d388%49(Preferred)
    	IPv4 Address. . . . . . . . . . . : 172.16.0.2(Preferred)
    	Subnet Mask . . . . . . . . . . . : 255.255.255.255
    	Default Gateway . . . . . . . . . :
    	DNS Servers . . . . . . . . . . . : 127.0.2.2
    																			127.0.2.3
    	NetBIOS over Tcpip. . . . . . . . : Disabled
    ```

    </Details>

2.  In Microsoft SCCM:

        a. [Create a boundary](https://learn.microsoft.com/en-us/intune/configmgr/core/servers/deploy/configure/boundaries#create-a-boundary) with the following settings:
        	- **Description**: `Remote Cloudflare One Clients`
        	- **Type**: _VPN_
        	- **Connection description**: `(SCCM) Cloudflare WARP Interface Tunnel`

    b. Assign this boundary to one or more boundary groups.

When the device is remote, the client interface description changes to `(SCCM) Cloudflare WARP Interface Tunnel` and the SCCM server will determine that the device belongs to the VPN boundary group. The device can now download updates from the distribution point assigned to this boundary group. When a network change occurs and the Cloudflare One Client detects a managed network, it will revert the interface description to `Cloudflare WARP Interface Tunnel` and the boundary condition will no longer be satisfied. The device will match your local IP range and be considered as on-prem.

### NetBIOS over TCPIP

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                               | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.1.89.1            |
| macOS    | ❌           |                        |
| Linux    | ❌           |                        |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

NetBIOS over TCP/IP (NetBT) is a legacy protocol used for name resolution and other features on Windows. NetBT has been deprecated for years, but Windows has not removed it. The Cloudflare One Client disables NetBT on the tunnel interface by default for security reasons and to align with modern best practices. This setting allows you to override the default behavior and enable NetBT over the WARP tunnel.

#### When to enable NetBT

You should turn on **NetBIOS over TCPIP** only if devices need to access internal resources over NetBT. Example scenarios include:

- **Legacy name resolution**: You rely on NetBIOS to resolve single-label names (such as `\\SERVER01`), instead of modern alternatives like mDNS for single-label names or standard DNS for Fully Qualified Domain Names (such as `\\server01.corp.internal`).
- **SMBv1**: You are accessing very old file shares or printers that do not support modern SMB (v2/v3) and require NetBT for discovery.
- **Legacy applications**: You use specialized internal software that hard-codes NetBIOS for node-to-node communication.

Otherwise, the recommendation is to always disable **NetBIOS over TCPIP**. You can choose a different setting for [remote devices](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) versus [on-prem devices](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/#4-configure-device-profile).

#### Verify NetBT settings

To check if **NetBIOS over TCPIP** is enabled on the client tunnel interface, run the following command:

```txt
warp-cli settings | findstr "NetBT"
```

```txt output
(network policy) NetBT: true
```

You can also verify network interface details for the `CloudflareWARP` adapter:

```txt
ipconfig /all
```

```txt output {16}
Windows IP Configuration
...
Unknown adapter CloudflareWARP:
    Connection-specific DNS Suffix  . :
    Description . . . . . . . . . . . : Cloudflare WARP Interface Tunnel
    Physical Address. . . . . . . . . :
    DHCP Enabled. . . . . . . . . . . : No
    Autoconfiguration Enabled . . . . : Yes
    IPv6 Address. . . . . . . . . . . : 2001:db8:110:8f79:145:f180:fc4:8106(Preferred)
    Link-local IPv6 Address . . . . . : fe80::83b:d647:4bed:d388%49(Preferred)
    IPv4 Address. . . . . . . . . . . : 172.16.0.2(Preferred)
    Subnet Mask . . . . . . . . . . . : 255.255.255.255
    Default Gateway . . . . . . . . . :
    DNS Servers . . . . . . . . . . . : 127.0.2.2
    																		127.0.2.3
    NetBIOS over Tcpip. . . . . . . . : Enabled
```

### VNET availability <Badge text="Beta" variant="caution" size="small" />

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                               | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.5.0               |
| macOS    | ✅           | 2026.5.0               |
| Linux    | ✅           | 2026.5.0               |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

By default, the Cloudflare One Client shows every [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) (VNET) in your account in the [VNET dropdown](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/#connect-to-a-virtual-network). **VNET availability** restricts the dropdown to a subset of VNETs and chooses a default VNET on a per-[device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) basis. The active profile controls which VNETs devices can see and switch between.

For example, if your QA team uses a `staging-vnet` and your sales team should never reach it, you can assign the sales device profile to only include `production-vnet`. Sales users will not see `staging-vnet` in the dropdown.

### DNS search suffixes <Badge text="Beta" variant="caution" size="small" />

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> DNS only mode </li></ul>                                   | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.5.0               |
| macOS    | ✅           | 2026.5.0               |
| Linux    | ✅           | 2026.5.0               |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

A DNS search suffix (also known as a search domain) automatically appends a domain to single-label names so that users can type `jira` into a browser and the operating system resolves `jira.internal.example`. Search suffixes are typically advertised by the local network over DHCP, but devices connected to the Cloudflare One Client use the WARP virtual interface instead, which does not inherit network-advertised suffixes.

Use **DNS search suffixes** to deploy an ordered list of up to 25 suffixes to the Cloudflare One Client on a per-[device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) basis. The client appends each suffix in order to single-label name queries until one resolves successfully. This setting replaces the per-device manual configuration described in [Add a DNS suffix](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/#add-a-dns-suffix).

# Uninstall protection

import { APIRequest, Details, Render, TabItem, Tabs } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.9.0.0             |
| macOS    | ❌           |                        |
| Linux    | ❌           |                        |
| iOS      | ❌           |                        |
| Android  | ❌           |                        |
| ChromeOS | ❌           |                        |

</Details>

Uninstall protection protects the Cloudflare One Client (formerly WARP) on Windows against unauthorized modification or uninstallation. When it is on, users must provide an administrator-generated code to uninstall the client. Without a valid code, the uninstall is blocked.

## Protected actions

When uninstall protection is on, the Cloudflare One Client blocks the following actions until a valid code is entered:

- Uninstalling the client from **Settings** > **Apps**, Control Panel, or `msiexec`.
- Upgrading or downgrading the client with the `.msi` installer.
- Logging out of your Zero Trust organization from the client UI.
- Deleting the device registration with `warp-cli registration delete`.

On Windows 10 version 21H2 (build 19044) and later, the client also installs a file system driver. The driver blocks changes to client files in `C:\Program Files\Cloudflare` and `C:\ProgramData\Cloudflare`. It also blocks changes to the client's registry keys and services. The driver keeps enforcing these protections when the Cloudflare One Client service is stopped.

The driver also blocks changes to [MDM files](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/), such as `C:\ProgramData\Cloudflare\mdm.xml`. To change an MDM file on a protected device, [unlock the client](#unlock-the-client-with-warp-cli) first.

:::note
Uninstall protection blocks the standard ways to remove the client. It is not a complete defense against a local administrator who tampers with the device directly.
:::

## Uninstall codes

Cloudflare provides two types of codes. Both types expire after a duration you choose, from 1 to 24 hours.

Device-specific uninstall codes follow fixed one-hour blocks aligned to UTC hours. The duration starts at the beginning of the UTC hour in which you generate the code. For example, a one-hour code generated at 10:45 UTC expires at 11:00 UTC.

| Code                           | Format               | Works on                   | Use when                                                  |
| ------------------------------ | -------------------- | -------------------------- | --------------------------------------------------------- |
| Device-specific uninstall code | 7 digits             | One device                 | A user needs to uninstall, upgrade, or downgrade manually |
| Account-wide uninstall token   | 104-character string | Every device in an account | You uninstall, upgrade, or downgrade with MDM or scripts  |

The Cloudflare One Client verifies both types of codes locally. The device does not need an Internet connection to accept a code.

When the client accepts a code, it turns off uninstall protection until the code expires, plus one hour. The extra hour lets a slow upgrade or uninstall finish. Uninstall protection turns back on automatically after this period.

:::caution
An account-wide uninstall token works on every device in your account. You cannot revoke a token before it expires. Share tokens only with trusted administrators, and choose the shortest duration that fits your task.
:::

## Turn on uninstall protection

Uninstall protection is a [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) setting. It is off by default. Changes to this setting are recorded in your [audit logs](/fundamentals/account/account-security/audit-logs/).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.
2. Locate the device profile you would like to update and select **Configure**.
3. Turn on **Device Management Protection**.
4. Select **Save profile**.

</TabItem> <TabItem label="API">

To change device profiles, you need an [API token](/fundamentals/api/get-started/create-token/) with `Zero Trust Write` permissions.

To turn on uninstall protection for a custom device profile, send a `PATCH` request to the [Devices API](/api/resources/zero_trust/subresources/devices/subresources/policies/subresources/custom/methods/edit/):

<APIRequest
	path="/accounts/{account_id}/devices/policy/{policy_id}"
	method="PATCH"
	json={{
		uninstall_protection: true,
	}}
/>

To turn on uninstall protection for the default device profile, send a `PATCH` request to the [Devices API](/api/resources/zero_trust/subresources/devices/subresources/policies/subresources/default/methods/edit/):

<APIRequest
	path="/accounts/{account_id}/devices/policy"
	method="PATCH"
	json={{
		uninstall_protection: true,
	}}
/>

</TabItem> </Tabs>

<Render file="warp/client-notification-lag" product="cloudflare-one" />

The device must receive the updated profile before uninstall protection takes effect. After that, uninstall protection stays on when the device is offline.

## Generate an uninstall code

### Device-specific uninstall code

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**.
2. In **Your devices**, select the device.
3. Select the three dots (**...**) next to **Delete**, then select **Generate uninstall code**.
4. In **Duration (hours)**, enter how long the code is valid, from `1` to `24` hours.
5. Select **Generate code**.
6. Copy the 7-digit code and share it with the user.

</TabItem> <TabItem label="API">

Send a `POST` request to the [Devices API](/api/resources/zero_trust/subresources/devices/subresources/override_codes/methods/create/). Set `scope` to `device` and set `device_id` to the device ID. Set `duration_hours` to a value from `1` to `24`. The [API token](/fundamentals/api/get-started/create-token/) needs at least `Zero Trust Read` permissions:

<APIRequest
	path="/accounts/{account_id}/devices/override_codes"
	method="POST"
	json={{
		type: "uninstall_protection",
		scope: "device",
		device_id: "$DEVICE_ID",
		duration_hours: 4,
	}}
/>

The `code` field in the response contains the device-specific uninstall code:

```json output
{
	"result": {
		"code": "1234567"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

To find the device ID, open the device in **Team & Resources** > **Devices** and copy the **ID** at the top of the page. You can also run `warp-cli registration show` on the device and copy the `Device ID` value.

</TabItem> </Tabs>

A device-specific uninstall code only works on the device it was generated for.

### Account-wide uninstall token

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Management**.
2. Under **Client control**, find **Account-wide uninstall token** and select **Generate token**.
3. In **Duration (hours)**, enter how long the token is valid, from `1` to `24` hours.
4. Select **Generate token**.
5. Copy the token.

</TabItem> <TabItem label="API">

Send a `POST` request to the [Devices API](/api/resources/zero_trust/subresources/devices/subresources/override_codes/methods/create/). Set `scope` to `account` and do not include `device_id`. Set `duration_hours` to a value from `1` to `24`. The [API token](/fundamentals/api/get-started/create-token/) needs at least `Zero Trust Read` permissions:

<APIRequest
	path="/accounts/{account_id}/devices/override_codes"
	method="POST"
	json={{
		type: "uninstall_protection",
		scope: "account",
		duration_hours: 4,
	}}
/>

The `code` field in the response contains the account-wide uninstall token.

</TabItem> </Tabs>

## Uninstall a protected client

### Uninstall manually

To uninstall the Cloudflare One Client on a protected device:

1. Get a device-specific uninstall code from your administrator.
2. Start the uninstall. For example, go to **Settings** > **Apps** > **Installed apps** and uninstall the Cloudflare One Client.
3. When the installer prompts you, enter the device-specific uninstall code and select **Continue**.
4. If Windows asks for administrator approval, approve the request.

The uninstall continues after the client accepts the code. If you select **Cancel**, the uninstall stops and the client stays installed.

If the code is wrong or expired, the installer shows an error and you can enter the code again. After repeated wrong codes, the client makes you wait before you try again. The wait increases with each wrong code, up to one minute.

### Uninstall with MDM or a script

Silent uninstalls require an account-wide uninstall token. Pass the token to the installer in the `UNINSTALL_TOKEN` property:

```powershell
msiexec /x "<LOCAL_PACKAGE_PATH>" /qn UNINSTALL_TOKEN="<ACCOUNT_WIDE_UNINSTALL_TOKEN>"
```

To find the `.msi` package path, refer to [Uninstall the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#uninstall-the-cloudflare-one-client).

If the token is missing or invalid, `msiexec` exits with error code `1603` and the client stays installed. You cannot use a device-specific uninstall code in a silent uninstall.

The installer treats any uninstall that runs as the `SYSTEM` account as silent. This includes Microsoft Intune and other MDM tools, even when you use the `/qb` option.

## Upgrade or downgrade a protected client

Upgrades and downgrades with the `.msi` installer also require a code:

- **Manual upgrade or downgrade**: The installer prompts for a device-specific uninstall code.
- **Silent upgrade or downgrade**: Pass an account-wide uninstall token in the `UNINSTALL_TOKEN` property.

```powershell
msiexec /i "Cloudflare_WARP_<VERSION>.msi" /qn UNINSTALL_TOKEN="<ACCOUNT_WIDE_UNINSTALL_TOKEN>"
```

The following updates do not require a code:

- Updates delivered by [client version assignments](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/client-version-assignments/).
- Upgrades from a client version that does not support uninstall protection. Uninstall protection turns on after the upgrade completes.

### Silent downgrade to an earlier version

A silent downgrade to a client version earlier than 2026.9.0.0 ignores the `UNINSTALL_TOKEN` property. The earlier installer does not pass the token to the installed client, so the downgrade fails.

To downgrade silently, unlock the client with the account-wide uninstall token first, then run the earlier installer:

```powershell
warp-cli uninstall-protection unlock --account "<ACCOUNT_WIDE_UNINSTALL_TOKEN>"
msiexec /i "Cloudflare_WARP_<VERSION>.msi" /qn
```

Run both commands within the token's validity period. For more information, refer to [Unlock the client with warp-cli](#unlock-the-client-with-warp-cli).

Manual downgrades are not affected. The installer prompts for a device-specific uninstall code as usual.

## Unlock the client with warp-cli

You can turn off uninstall protection on a device with `warp-cli`. Use this before an action that does not go through the installer prompt, such as logging out of your Zero Trust organization.

To unlock with a device-specific uninstall code:

```sh
warp-cli uninstall-protection unlock --device <DEVICE_SPECIFIC_UNINSTALL_CODE>
```

To unlock with an account-wide uninstall token:

```sh
warp-cli uninstall-protection unlock --account <ACCOUNT_WIDE_UNINSTALL_TOKEN>
```

To check whether uninstall protection is on, and how long it stays off after an unlock:

```sh
warp-cli uninstall-protection show
```

```txt output
Uninstall protection: disabled
Grace period remaining: 7140 seconds
```

If the Cloudflare One Client service is not running, `warp-cli` sends the code directly to the file system driver. Run `warp-cli` from an elevated command prompt in this case.

## Log out of a protected device

When uninstall protection is on, the client disables the **Logout** button.

This applies even when [Allow device to leave organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-device-to-leave-organization) is turned on. To log out, first [unlock the client](#unlock-the-client-with-warp-cli).

## Turn off uninstall protection

To turn off uninstall protection, turn off **Device Management Protection** in the device profile. If you use the API, set `uninstall_protection` to `false`.

Devices turn off uninstall protection after they receive the updated profile. Devices that are offline stay protected until they reconnect. To remove the client from an offline device, use an uninstall code.

## Troubleshooting

### Code is not accepted

Check the following:

- The code has not expired.
- A device-specific uninstall code was generated for this device. Codes do not work on other devices.
- The code was generated for the device's current ID. Run `warp-cli registration show` on the device and check that the `Device ID` value matches the **ID** of the device in **Team & Resources** > **Devices**.
- You entered a device-specific uninstall code in the installer prompt. The prompt does not accept account-wide uninstall tokens.

### Review logs

The installer writes lines that start with `Uninstall authorization:` to the [Windows Installer log](https://learn.microsoft.com/en-us/troubleshoot/windows-client/application-management/enable-windows-installer-logging). To create a log, add `/l*v <PATH_TO_LOG_FILE>` to your `msiexec` command.

[Diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) include rejected codes and the actions that the file system driver blocked.

# Device enrollment permissions

import { Render } from "~/components";

Device enrollment permissions determine which users can connect new devices to your organization's Cloudflare Zero Trust instance.

## Set device enrollment permissions

<Render file="warp/device-enrollment" product="cloudflare-one" />

Users can now [enroll their device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) by logging in to your identity provider. To prevent users from logging out of your organization after they enroll, disable [Allow devices to leave organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-device-to-leave-organization) in your device client settings.

## Example policies

### Check for service token

Instead of requiring users to authenticate with their credentials, you can use a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) to enroll devices without any user interaction. Because users are not required to log in to an identity provider, identity-based policies cannot be enforced on these devices.

To enroll devices using a service token:

<Render file="warp/service-token-enrollment" product="cloudflare-one" />

You can verify which devices have enrolled by going to **Team & Resources** > **Devices**. Devices that enrolled using a service token (or any other Service Auth policy) will have the **Email** field show as `non_identity@<team-name>.cloudflareaccess.com`.

### Check for mTLS certificate

Enterprise customers can enforce [mutual TLS authentication](/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/) during device enrollment.

<Render file="warp/device-enrollment-mtls" product="cloudflare-one" />

When users [log in to your Zero Trust organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) from the Cloudflare One Client, their device must present a valid client certificate in order to connect.

# Cloudflare One Client with firewall

import { Render, Details } from "~/components";

If your organization uses a firewall or other policies to restrict or intercept Internet traffic, you may need to exempt the following IP addresses and domains to allow the Cloudflare One Client (formerly WARP) to connect.

## Client orchestration API

The Cloudflare One Client connects to Cloudflare via a standard HTTPS connection outside the tunnel for operations like registration or settings changes. To perform these operations, you must allow the following IPs and domains:

<Render file="warp/client-orchestration-ips" product="cloudflare-one" />

Cloudflare One Client version 2026.6.0 and later uses `api.devices.cloudflare.com`. Versions earlier than 2026.6.0 use `zero-trust-client.cloudflareclient.com` and `notifications.cloudflareclient.com`.

These domains may resolve to different IP addresses. The Cloudflare One Client overrides the resolved IPs with the IPs listed above. To avoid connectivity issues, allow those IPs through your firewall.

<Details header="FedRAMP High requirements">

To deploy the Cloudflare One Client in FedRAMP High environments, you will need to allow a different set of IPs and domains through your firewall:

- IPv4 API endpoints: `162.159.213.1` and `172.64.98.1`
- IPv6 API endpoints: `2606:54c1:11::` and `2a06:98c1:4b::`
- SNIs: `api.devices.fed.cloudflare.com` and `notifications.devices.fed.cloudflare.com`

</Details>

## DoH IP

:::note
Only required for [DNS only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode) mode.
:::

In [DNS only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode) mode, the Cloudflare One Client sends DNS requests to Gateway over an HTTPS connection. For DNS to work correctly, you must allow the following IPs and domains:

- IPv4 DoH addresses: `162.159.36.1` and `162.159.46.1`
- IPv6 DoH addresses: `2606:4700:4700::1111` and `2606:4700:4700::1001`
- SNIs: `<ACCOUNT_ID>.cloudflare-gateway.com`

Even though `<ACCOUNT_ID>.cloudflare-gateway.com` may resolve to different IP addresses, the Cloudflare One Client overrides the resolved IPs with the IPs listed above. To avoid connectivity issues, ensure that the above IPs are permitted through your firewall.

<Details header="FedRAMP High requirements">
To deploy the Cloudflare One Client in FedRAMP High environments, you will need to allow a different set of IPs and domains through your firewall:

- IPv4 DoH addresses: `172.64.100.3` and `172.64.101.3`
- IPv6 DoH addresses: `2606:54c1:13::2`
- SNIs: `<ACCOUNT_ID>.fed.cloudflare-gateway.com`

</Details>

### Android devices

If you are deploying the Cloudflare One Agent on Android/ChromeOS, you must also add `cloudflare-dns.com` to your firewall exception list. On Android/ChromeOS devices, the Cloudflare One Client uses `cloudflare-dns.com` to resolve domains on your [Split Tunnel list](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/#domain-based-split-tunnels).

## Client authentication endpoint

When you [log in to your Cloudflare One organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/), you will have to complete the authentication steps required by your organization in the browser window that opens. To perform these operations, you must allow the following domains:

- The IdP used to authenticate to Cloudflare One
- `<your-team-name>.cloudflareaccess.com`

<Details header="FedRAMP High requirements">
To deploy WARP in FedRAMP High environments, you will need to allow different domains through your firewall:

- FedRAMP High IdP used to authenticate to Cloudflare One
- `<your-team-name>.fed.cloudflareaccess.com`.

</Details>

## Cloudflare One Client ingress IP {/* warp-ingress-ip */}

The Cloudflare One Client connects to the following IP addresses, depending on which [tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol) is configured for your device (WireGuard or MASQUE). All network traffic from your device to Cloudflare goes through these IPs and ports over UDP.

### WireGuard

|                |                                             |
| -------------- | ------------------------------------------- |
| IPv4 address   | `162.159.193.0/24`                          |
| IPv6 address   | `2606:4700:100::/48`                        |
| Default port   | `UDP 2408`                                  |
| Fallback ports | `UDP 500` <br/> `UDP 1701` <br/> `UDP 4500` |

### MASQUE

|                |                                                                                                                     |
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
| IPv4 address   | `162.159.197.0/24`                                                                                                  |
| IPv6 address   | `2606:4700:102::/48`                                                                                                |
| Default port   | `UDP 443`                                                                                                           |
| Fallback ports | `UDP 500` <br/> `UDP 1701` <br/> `UDP 4500` <br/> `UDP 4443` <br/> `UDP 8443` <br/> `UDP 8095` <br/> `TCP 443` [^1] |

[^1]: Required for HTTP/2 fallback

:::note

Before you [log in to your Cloudflare One organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/), you may see the IPv4 range `162.159.192.0/24`. This IP is used for the consumer WARP client ([1.1.1.1 with WARP](/warp-client/)) and is not required for Zero Trust services.
:::

<Details header="FedRAMP High requirements">

Devices will use the MASQUE protocol in FedRAMP High environments. To deploy the Cloudflare One Client for FedRAMP High, you will need to allow the following IPs and ports:

|                |                                                                                                                     |
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
| IPv4 address   | `162.159.239.0/24`                                                                                                  |
| IPv6 address   | `2606:4700:105::/48`                                                                                                |
| Default port   | `UDP 443`                                                                                                           |
| Fallback ports | `UDP 500` <br/> `UDP 1701` <br/> `UDP 4500` <br/> `UDP 4443` <br/> `UDP 8443` <br/> `UDP 8095` <br/> `TCP 443` [^1] |

[^1]: Required for HTTP/2 fallback

</Details>

## Captive portal

The following domains are used as part of our captive portal check:

- `cloudflareportal.com`
- `cloudflareok.com`
- `cloudflarecp.com`
- `www.msftconnecttest.com`
- `captive.apple.com`
- `connectivitycheck.gstatic.com`

## Connectivity checks

As part of establishing the WARP tunnel, the client runs connectivity checks inside and outside of the tunnel.

### Outside tunnel

The client connects to the following destinations to verify general Internet connectivity outside of the WARP tunnel. Make sure that these IPs and domains are on your firewall allowlist.

- `162.159.197.3`
- `2606:4700:102::3`
- `engage.cloudflareclient.com`: The client will always send requests directly to an IP in the [ingress IPv4 or IPv6 range](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip) (or to your [`override_warp_endpoint`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#override_warp_endpoint) if set). Requests will not use a proxy server, even if one is configured for the system.

Even though `engage.cloudflareclient.com` may resolve to different IP addresses, the Cloudflare One Client overrides the resolved IPs with the IPs listed above. To avoid connectivity issues, ensure that the above IPs are permitted through your firewall.

### Inside tunnel

The Cloudflare One Client connects to the following destinations to verify connectivity inside of the WARP tunnel:

- `162.159.197.4`
- `2606:4700:102::4`
- `connectivity.cloudflareclient.com`

Because this check happens inside of the tunnel, you do not need to add these IPs and domains to your firewall allowlist. However, since the requests go through Gateway, ensure that they are not blocked by a Gateway HTTP or Network policy.

## NEL reporting (optional)

The Cloudflare One Client reports connectivity issues to the Network Error Logging (NEL) endpoint via `a.nel.cloudflare.com`. This is not technically required to operate but will result in errors in our logs if not excluded properly.

## Latency statistics (optional)

The Cloudflare One Client generates ICMP traffic to the [ingress IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip) when running tunnel latency tests. This is not technically required to operate but will result in errors in our logs if not excluded properly.

## Time synchronization (optional)

The Cloudflare One Client attempts to synchronize the exact time by NTP (`UDP 123`) to [Cloudflare's Time Service](/time-services/ntp/usage/) via `time.cloudflare.com`. This is not technically required to operate but will result in errors in our logs if not excluded properly.

## Scope of firewall rules

### Required scopes

If your organization does not currently allow inbound/outbound communication over the IP addresses, ports, and domains described above, you must manually add an exception. The rule at a minimum needs to be scoped to the following process based on your platform:

- Windows: `C:\Program Files\Cloudflare\Cloudflare WARP\warp-svc.exe`, `C:\Program Files\Cloudflare\Cloudflare WARP\warp-updater.exe`, and `C:\Program Files\Cloudflare\Cloudflare WARP\warp-updater-armed.exe`
- macOS: You must explicitly allow the core networking daemon, updater, and GUI component as shown in the following instructions.
  1.  Core networking daemon: `/Applications/Cloudflare WARP.app/Contents/Resources/CloudflareWARP`

      This binary does not have a Bundle ID and must be allowed via full path.

  2.  Updater: `/Applications/Cloudflare WARP.app/Contents/Resources/warp-updater`

  3.  GUI component, choose one of the following three identifiers depending on your MDM or firewall vendor's preferred format:

      `/Applications/Cloudflare WARP.app` (Path)

      `/Applications/Cloudflare WARP.app/Contents/MacOS/Cloudflare WARP` (Path)

      `com.cloudflare.1dot1dot1dot1.macos` (Bundle ID)

  :::caution[macOS 15.0 through 15.4]

  Due to changes in macOS Sequoia versions 15.0 through 15.4, you must update your [macOS firewall settings](https://support.apple.com/guide/mac-help/change-firewall-settings-on-mac-mh11783/mac) to allow the Cloudflare One Client to manage your device's firewall. Later versions of macOS are not affected because of changes Apple introduced to fix the unexpected breaking changes in their firewall.

  To allow the Cloudflare One Client to function on macOS Sequoia versions 15.0 through 15.4 while still blocking unwanted incoming traffic, follow these steps:

      1. Turn off the following [macOS firewall settings](https://support.apple.com/guide/mac-help/change-firewall-settings-on-mac-mh11783/mac):
      	- **Block all incoming connections**
      	- **Automatically allow built-in software to receive incoming connections**
      	- **Automatically allow downloaded signed software to receive incoming connections**
      2. Add the [WARP daemon, updater, and GUI processes](#required-scopes) to the firewall exceptions list and set them to _Allow incoming connections_.
      3. Restrict the other allow exceptions to only the processes you want receiving traffic.
      4. (Optional) Do not grant users administrative privileges, otherwise they will be able to modify firewall settings and exceptions.

  :::

### Optional scopes

#### DEX tests

To run [Digital Experience Monitoring tests](/cloudflare-one/insights/dex/tests/), you will need to allow the `warp-dex` process to generate network traffic to your target destinations:

- Windows: `C:\Program Files\Cloudflare\Cloudflare WARP\warp-dex.exe`
- macOS: `/Applications/Cloudflare WARP.app/Contents/Resources/warp-dex`

#### Network statistics

To use the network connectivity tests built into the Cloudflare One Client GUI, you will need to allow the GUI application to generate network traffic:

- Windows: `C:\Program Files\Cloudflare\Cloudflare WARP\Cloudflare WARP.exe`
- macOS: `/Applications/Cloudflare WARP.app`

## Related resources

- [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) - Resolve selected domains via local DNS instead of Cloudflare Gateway.
- [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) - Control which traffic goes through the Cloudflare One Client by including or excluding specific IPs or domains.

# Deploy the Cloudflare One Client

Depending on how your organization is structured, you can deploy the Cloudflare One Client (formerly WARP) in one of two ways:

- [Manual deployment](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) — Users download and enroll the client themselves. This is the simplest way to get started, best suited for small organizations.

- [Managed deployment](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) — Bigger organizations with MDM tools like Intune or JAMF can deploy the Cloudflare One Client to their entire fleet of devices from a single operation.

# Manual deployment

import {
	Details,
	GlossaryTooltip,
	Render,
	Steps,
	TabItem,
	Tabs,
} from "~/components";

If you plan to direct your users to manually download and configure the Cloudflare One Client (formerly WARP), users will need to connect the client to your organization's Cloudflare Zero Trust instance.

## Prerequisites

- Complete [Zero Trust organization setup](/cloudflare-one/setup/#2-create-a-zero-trust-organization), including subscription activation.
- [Set device enrollment permissions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) to specify which users can connect.

## Windows, macOS, and Linux

### Enroll using the GUI

<Render file="warp/enroll-desktop" product="cloudflare-one" />

The device is now protected by your organization's Zero Trust policies.

### Enroll using the CLI

To enroll your device using the terminal:

<Steps>

1. [Download](https://pkg.cloudflareclient.com/) and install the Cloudflare One Client package.

2. Open a terminal window. Ensure that you are logged into the terminal as the current user and not as root.

3. (Optional) Validate your organization's <GlossaryTooltip term="team name">team name</GlossaryTooltip>:

   <Tabs syncKey="os">
   <TabItem label="macOS / Linux">

   ```sh
   curl --fail --silent --show-error \
     "https://<YOUR_TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/certs" \
     > /dev/null
   ```

   </TabItem>
   <TabItem label="Windows (PowerShell)">

   ```powershell
   Invoke-WebRequest -UseBasicParsing "https://<YOUR_TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/certs" | Out-Null
   ```

   </TabItem>
   </Tabs>

   A successful request confirms that the team domain exists and is reachable. It does not confirm enrollment eligibility. Compare the value with the team name in your Zero Trust settings.

4. Enroll into Cloudflare Zero Trust using your team name:

   ```sh
   warp-cli registration new <your-team-name>
   ```

5. In the browser window that opens, complete the authentication steps required by your organization.

   Once authenticated, you will see a success page and a dialog prompting you to open a link.

6. Select **Open Link**.

7. Verify the registration in the terminal:

   ```sh
   warp-cli registration show
   ```

   <Details header="Troubleshoot missing registration">

   The `warp-cli registration new` success message does not prove that organization enrollment completed. If the browser reports an invalid enrollment request while the CLI reports success, treat the enrollment as failed. Inspect the enrollment endpoint HTTP response in browser developer tools. You can also collect [diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) with `warp-diag`.

   Do not open the bare `https://<YOUR_TEAM_NAME>.cloudflareaccess.com/warp` URL as a fallback. It does not contain the active enrollment session. If the registration continues to be missing, manually copy the authentication token from the browser to the Cloudflare One Client:

   1. On the success page, right-click and select **View Page Source**.
   2. Find the HTML metadata tag that contains the token. For example, `<meta http-equiv="refresh" content"=0;url=com.cloudflare.warp://<your-team-name>.cloudflareaccess.com/auth?token=yeooilknmasdlfnlnsadfojDSFJndf_kjnasdf..." />`
   3. Copy the URL field: `com.cloudflare.warp://<your-team-name>.cloudflareaccess.com/auth?token=<your-token>`
   4. In the terminal, run the following command using the URL obtained in the previous step.

      ```sh
      warp-cli registration token "com.cloudflare.warp://<your-team-name>.cloudflareaccess.com/auth?token=<your-token>"
      ```

   If you get a `401` error, then the token has expired. Generate a new one by refreshing the web page and quickly grab the new token from the page source.

   </Details>

8. If you did not configure the client to [auto-connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect), manually connect:

   ```sh
   warp-cli connect
   ```

9. If the device profile routes `www.cloudflare.com` through WARP, verify the data path:

   <Tabs syncKey="os">
   <TabItem label="macOS / Linux">

   ```sh
   curl --silent https://www.cloudflare.com/cdn-cgi/trace | grep '^warp=on$'
   ```

   </TabItem>
   <TabItem label="Windows (PowerShell)">

   ```powershell
   if (-not (curl.exe --silent https://www.cloudflare.com/cdn-cgi/trace | Select-String '^warp=on$')) { exit 1 }
   ```

   </TabItem>
   </Tabs>

   In Include mode, expect `warp=off` for destinations that are not in the include list. Test an included destination instead. In DNS-only mode, use the registration output from step 7 to verify enrollment. DNS-only mode cannot carry network traffic.

</Steps>

The device is now protected by your organization's Zero Trust policies. For more information on all available commands, run `warp-cli --help`.

## iOS, Android, and ChromeOS

### Enroll manually

<Render file="warp/enroll-ios-android" product="cloudflare-one" />

The device is now protected by your organization's Zero Trust policies.

### Enroll using a URL

<Details header="Feature availability">

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ❌           |                        |
| macOS    | ❌           |                        |
| Linux    | ❌           |                        |
| iOS      | ✅           | 1.10                   |
| Android  | ✅           | 2.4                    |
| ChromeOS | ✅           | 2.4                    |

</Details>

Administrators can provide users with a custom login URL that automatically fills in your organization's <GlossaryTooltip term="team name">team name</GlossaryTooltip> during device enrollment. Using a URL reduces the potential for error that comes with manual entry of the team name.

The Cloudflare One Client supports URLs accessed through a direct link or with a URL handler such as a QR code. Direct links are currently only supported in Safari and Firefox. If your default browser is Chrome (or another unsupported browser), we recommend embedding the link in a QR code.

#### Generate a login URL

To generate a URL for device enrollment:

1. Copy the following link, replacing `<your-team-name>` with your Zero Trust <GlossaryTooltip term="team name">team name</GlossaryTooltip>:

   ```txt
   cf1app://oneapp.cloudflare.com/team?name=<your-team-name>
   ```

2. (Optional) Use any QR code generator to embed the link in a QR code.
3. Distribute the link or QR code to users.

#### Use the login URL

To enroll a device using a login URL:

1.  [Download](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) and install the Cloudflare One Agent app.
2.  Go to the [login URL](#generate-a-login-url) provided by your account administrator. To use a QR code, open the QR scanner app on your device and scan the QR code.

        The Cloudflare One Agent app will open and start the onboarding flow.

        :::note
        If the device is already enrolled in the account associated with this URL, Cloudflare One agent will bypass onboarding and show the **Connected** switch.
        :::

3.  To complete the onboarding flow:

        a. Review the privacy policy and select **Accept**.

    b. On the **Enter team name** screen, confirm that the pre-populated <GlossaryTooltip term="team name">team name</GlossaryTooltip> matches your organization.

        :::note[`Already Authenticated` error]
        If Cloudflare One Agent is logged in using another team name, you must first log out of that account. Go to **Settings** > **Account** to log out, and then retry the QR code or login link.
        :::

    c. Complete the authentication steps required by your organization.

        d. After authenticating, select **Install VPN Profile**.

        e. In the **Connection request** popup window, select **OK**.

4.  If you did not enable [auto-connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect), manually turn on the switch to **Connected**.

The device is now protected by your organization's Zero Trust policies.

## Virtual machines

By default, virtual machines (VMs) are subject to the device client settings of the host. If you want to deploy a separate instance of the Cloudflare One Client in a VM, you must configure the VM to operate in bridged networking mode.

# Cloudflare One Client with legacy VPNs

import { Render } from "~/components";

The Cloudflare One Client (formerly WARP) can run alongside most legacy third-party VPNs. However, both the Cloudflare One Client and your VPN try to control the same things on the device: which traffic goes where (routing), which DNS server answers queries, and which firewall rules apply. To prevent conflicts, you must split these responsibilities between the two products:

- IP traffic is split tunneled between the Cloudflare One Client and the VPN. All VPN traffic must bypass the Cloudflare One Client and vice versa.
- The VPN bypasses/allows/excludes all domains, IPs, and ports listed in [Cloudflare One Client with firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/).
- DNS resolution is handled by either the Cloudflare One Client or the VPN. You must disable DNS filtering in one of the two products.

For the most stable and consistent connection, we recommend connecting your [private network or individual applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/) to Cloudflare instead of using a legacy VPN. However, until you can migrate, the following guidelines will help get your Zero Trust deployment up and running.

## Traffic and DNS mode

In [Traffic and DNS mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-and-dns-mode-default), the Cloudflare One Client must be allowed to capture and route all DNS traffic on the device. You can use [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) to send DNS requests to a server behind your third-party VPN or firewall, but the request must first go through the client's local DNS proxy. Refer to [client architecture](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/) for more information about this requirement.

If you cannot disable DNS on your VPN, switch to [Traffic only mode](#secure-web-gateway-without-dns-filtering) mode to disable DNS in the Cloudflare One Client.

### 1. Configure the VPN

Perform these steps in your third-party VPN software. Refer to your VPN's documentation for specific instructions on how to configure these settings.

1. Enable split tunneling in the third-party VPN.

2. Disable DNS configuration in the third-party VPN.

### 2. Configure WARP

<Render file="warp/vpn-ip-traffic" product="cloudflare-one" />

3. (Optional) In [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/), add the domains that you want to resolve using your VPN's private DNS servers. For example,
   | Domain | DNS Servers |
   | ------------------------ | -------------------------------- |
   | `internal.wiki.intranet` | `172.31.26.130`, `172.31.23.120` |

You can now [test](#test-the-configuration) if WARP runs alongside the VPN.

## Traffic only mode

In [Traffic only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode), the Cloudflare One Client only controls IP routing — it does not manage DNS. This is the simpler option when your VPN must retain DNS control, because you only need to split tunnel IP traffic.

### 1. Configure the VPN

Enable split tunneling in your third-party VPN software. Refer to your VPN's documentation for specific instructions on how to configure this setting.

### 2. Configure WARP

<Render file="warp/vpn-ip-traffic" product="cloudflare-one" />

3. In your device profile, verify that **Service mode** is set to **Traffic only mode**.

## Test the configuration

We recommend enabling the Cloudflare One Client before enabling your third-party VPN. Some third-party VPNs must be the last to edit a network's configuration or they will fail.

1. Connect the Cloudflare One Client.
2. Connect the third-party VPN client.
3. To test your Split Tunnel configuration, connect to a private IP address that is behind the VPN. For example, you can open a terminal and run `ping <SERVER-IP>`.
4. To test your DNS configuration, connect to an internal domain that is behind the VPN. For example, you can open a browser and go to `internal.wiki.intranet`.

:::caution[Test before updates]

Once you have a configuration in place and working, make sure to thoroughly test compatibility before updating your VPN software. Compatibility testing with what are essentially competing software will need to be done with each new version.

:::

# Client version assignments

import { Details, Tabs, TabItem, APIRequest, GlossaryTooltip } from "~/components";

Client version assignments let you target a specific Cloudflare One Client (formerly WARP) version at a group of devices from the Cloudflare dashboard, without touching your <GlossaryTooltip term="MDM file">MDM file</GlossaryTooltip> or asking users to update the client themselves.

Once an assignment is in place, matching devices silently upgrade or downgrade to the target version. End users see an **Update in progress** banner in the client GUI while the install runs. The client returns to normal operation once the update completes.

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.6.0               |
| macOS    | ✅           | 2026.6.0               |
| Linux    | ❌           | —                      |
| iOS      | ❌           | —                      |
| Android  | ❌           | —                      |
| ChromeOS | ❌           | —                      |

</Details>

:::note

In the first general availability (GA) release of the Cloudflare One Client that supports this feature (`2026.6.0`), client version assignments will be effectively a no-op: there is only one version available, so any assignment targeting `2026.6.0` resolves to the version devices are already running. Client version assignments become meaningful starting with the second GA release, when there is a previous version to upgrade from or roll back to.

:::

## How it works

A **deployment group** links one or more target client versions to one or more policy IDs from your [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/). Each entry in the group specifies a `target_environment` (`windows` or `macos`) and the `version` to deploy on that platform. A single deployment group can target both platforms at once.

When you save a deployment group, the Cloudflare API pushes the assignment to every device whose device profile policy ID is in the group. Each device then:

1. Compares its running version against the assigned version on its next registration refresh.
2. If the versions differ, downloads the target installer and verifies its cryptographic signature.
3. Runs the installer silently and resumes normal operation once the new version is in place.

The client coordinates the install through a separate persistent OS service that ships alongside the existing service in client version `2026.6.0` and later. The signed-in user does not need administrator privileges to upgrade or downgrade the client because the client and updater services already run with the privileges required to install the assigned version. If any step fails — download, signature verification, or install — the device stays on its previous version and remains functional.

:::note

When a device is targeted by a deployment group, the client suppresses the local **[Allow updates](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-updates)** notification flow on that device, so end users do not see a dashboard-managed install and a local update prompt at the same time.

:::

## Set up a deployment group

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**.
2. Select the **Management** tab.
3. Under **Client version assignments**, select **Manage**.
4. Select **Create new deployment group**.
5. Under **Name your deployment group**, enter a name.
6. Under **Select device profiles**, select one or more device profiles whose devices should receive this assignment.
7. Under **Assign client version**, select an operating system, a release track, and a client version. To target additional platforms with the same group, select **Add OS** and repeat.
8. Select **Save**.

</TabItem>
<TabItem label="API">

Send a `POST` request to the [Deployment Groups API](/api/resources/zero_trust/subresources/devices/subresources/deployment_groups/methods/create/):

<APIRequest
	path="/accounts/{account_id}/devices/deployment-groups"
	method="POST"
	json={{
		name: "Engineering Ring 0",
		version_config: [
			{ target_environment: "windows", version: "2026.6.0" },
			{ target_environment: "macos", version: "2026.6.0" },
		],
		policy_ids: ["<POLICY_UUID_1>", "<POLICY_UUID_2>"],
	}}
/>

</TabItem>
<TabItem label="Terraform (v6)">

1. Add the following permission to your [`cloudflare_api_token`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/api_token):
   - `Zero Trust Write`

2. Create a deployment group using the [`cloudflare_zero_trust_device_deployment_groups`](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_device_deployment_groups) resource:

   ```tf
   resource "cloudflare_zero_trust_device_deployment_groups" "example" {
   	account_id = var.cloudflare_account_id
   	name       = "Engineering Ring 0"
   	version_config = [
   		{
   			target_environment = "windows"
   			version            = "2026.6.0"
   		},
   		{
   			target_environment = "macos"
   			version            = "2026.6.0"
   		},
   	]
   	policy_ids = [
   		"<POLICY_UUID_1>",
   		"<POLICY_UUID_2>",
   	]
   }
   ```

</TabItem>
</Tabs>

## When a device evaluates an assignment

After you create, update, or delete a deployment group, the API notifies affected devices of the change. Delivery can take up to 15 minutes. Once a device receives the notification, it evaluates the assignment shortly after and installs the target version if it differs from the running version.

Devices also re-evaluate the most recent assignment they have received in these additional situations:

- **When the client service starts or restarts.**
- **When the device wakes from sleep.**

## Verify a device received the assignment

To confirm a device has received its assigned version, open a terminal on the device and run:

```sh
warp-cli settings
```

The output includes the resolved target version under `version_config`. If the device is not yet running that version, the next evaluation triggers an install.

## Override an assignment with MDM

You can suppress client version assignments on individual devices by setting [`allow_managed_deployments`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#allow_managed_deployments) to `false` in your MDM file. When this parameter is `false`, the device ignores any assignment from the dashboard and stays on its current version. Use this for devices that must be pinned through MDM rather than the dashboard.

## Limitations

- Devices must be running client version `2026.6.0` or later to apply an assignment. Older versions ignore the assignment entirely.
- Once an install has started on a device, it cannot be cancelled. Changing the target version while a device is still downloading the previous target cancels the download cleanly.
- Each device profile policy ID can belong to only one deployment group at a time.

## Antivirus and endpoint security configuration

Starting in version `2026.6.0`, the client install adds a second persistent OS service that handles updates triggered by client version assignments. If your endpoint protection or antivirus tools maintain process or service allowlists, add the following alongside your existing entries for the Cloudflare One Client:

| Platform | Service identifier         | Process name             |
| -------- | -------------------------- | ------------------------ |
| macOS    | `com.cloudflare.warp.updater` | `warp-updater`            |
| Windows  | `CloudflareWARPUpdater`    | `warp-updater-armed.exe` |

## Troubleshoot a failed update

If a device does not receive its assigned version, collect diagnostic logs using [`warp-diag`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/). The diagnostic archive includes an `updater/` directory with per-attempt installer logs and a human-readable update history summary that you can share with Cloudflare Support.

# Hardware-backed registration

import { Details } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability            | Minimum WARP version |
| -------- | ----------------------- | -------------------- |
| Windows  | ✅                      | 2026.6.0             |
| macOS    | ✅ (Apple silicon only) | 2026.6.0             |
| Linux    | ✅                      | 2026.6.0             |
| iOS      | ❌                      | N/A                  |
| Android  | ❌                      | N/A                  |
| ChromeOS | ❌                      | N/A                  |

</Details>

Hardware-backed registration binds a device registration to a non-exportable private key stored in device hardware. The Cloudflare One Client uses this key to prove that API requests originate from the device that created the registration.

By default, the Cloudflare One Client stores its API token in the device keystore. An attacker who extracts that token can replay it from another device. Hardware-backed registration protects against this token extraction by requiring every API request to be authenticated with a key that never leaves the device hardware.

Before you turn on hardware-backed registration, note the following:

- **Re-registration is required.** Turning the setting on or off invalidates the existing registration and forces affected devices to register again. The Cloudflare One Client does not migrate a registration between hardware-backed and standard registration.
- **Configure it at the organization layer.** Set `hardware_backed_registration` in [`organization_configs`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#organization_configs) so the setting applies consistently to every configuration for an organization.
- **Certificates expire.** The hardware-backed certificate is valid for 90 days. A device that stays offline until the certificate expires — for example, during an extended vacation — must register again.

## How it works

When hardware-backed registration is turned on, the Cloudflare One Client performs the following steps during [device registration](/cloudflare-one/team-and-resources/devices/device-registration/):

1. The client generates a non-exportable key pair in a hardware security module on the device.
2. The client creates a certificate signing request (CSR) for the key and sends it with the registration request.
3. Cloudflare issues a client certificate for the key and returns it to the client.
4. The client authenticates all subsequent API requests with [mutual TLS (mTLS)](/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/), signing the TLS handshake with the hardware-backed key.

Cloudflare validates each request against the certificate stored for the registration. Requests that do not present the matching certificate are rejected. Because the private key cannot leave the device, an extracted API token alone is not enough to make valid API requests from another device.

The client renews the certificate automatically before it expires, reusing the existing hardware-backed key so the registration is preserved.

## Hardware requirements

Hardware-backed registration uses the security hardware available on each desktop platform:

| Operating system | Hardware                       |
| ---------------- | ------------------------------ |
| Windows          | TPM 2.0                        |
| macOS            | Secure Enclave (Apple silicon) |
| Linux            | TPM 2.0                        |

Devices without a supported security module cannot complete a hardware-backed registration.

## Turn on hardware-backed registration

Hardware-backed registration is turned off by default. To turn it on, set the [`hardware_backed_registration`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#hardware_backed_registration) parameter to `true` in the [`organization_configs`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#organization_configs) layer of your [MDM configuration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/).

The following example turns on hardware-backed registration for the `example-team` organization:

```xml
<dict>
  <key>organization_configs</key>
  <dict>
    <key>example-team</key>
    <dict>
      <key>hardware_backed_registration</key>
      <true/>
    </dict>
  </dict>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>example-team</string>
      <key>display_name</key>
      <string>Example team</string>
    </dict>
  </array>
</dict>
```

## Limitations

Hardware-backed registration is available on Windows, macOS on Apple silicon, and Linux only. Intel-based Macs are not supported. Mobile platforms (iOS, Android, and ChromeOS) are not supported. The feature applies only to Zero Trust registrations that use an [identity provider](/cloudflare-one/integrations/identity-providers/) or a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/). Consumer registrations are not supported.

# Managed deployment

import { Render, GlossaryTooltip } from "~/components";

<Render file="warp/mdm-intro" product="cloudflare-one" />

This page provides generic instructions for an automated deployment. If you want to deploy the Cloudflare One Client manually, refer to the [instructions for manual deployment](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/).

:::caution

<Render file="warp/mdm-dash-conflict" product="cloudflare-one" />

:::

## Prerequisites

- Refer to the [Download page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#windows) to review system requirements and download the installer for your operating system.
- <Render file="warp/connectivity-status-check" product="cloudflare-one" />

## Windows

The Cloudflare One Client for Windows allows for an automated install via tools like Intune, AD, or any script or management tool that can execute a `.msi` file.

### Install the Cloudflare One Client

To install the Cloudflare One Client, run the following command:

```bash
msiexec /i "Cloudflare_WARP_<VERSION>.msi" /qn ORGANIZATION="your-team-name" SUPPORT_URL="http://support.example.com"
```

#### Supported properties

The Cloudflare One Client MSI installer supports the following [public properties](https://learn.microsoft.com/en-us/windows/win32/msi/public-properties):

- `ORGANIZATION`
- `GATEWAY_UNIQUE_ID`
- `AUTH_CLIENT_ID`
- `AUTH_CLIENT_SECRET`
- `ONBOARDING`
- `OVERRIDE_API_ENDPOINT`
- `OVERRIDE_DOH_ENDPOINT`
- `OVERRIDE_WARP_ENDPOINT`
- `SERVICE_MODE`
- `SUPPORT_URL`
- `SWITCH_LOCKED`

Refer to [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for a description of each property.

### Uninstall the Cloudflare One Client

To uninstall the Cloudflare One Client:

1. First, locate the `.msi` package with the following PowerShell command:

```powershell
Get-WmiObject Win32_Product | Where-Object { $_.Name -match "Cloudflare One Client" } | Sort-Object -Property Name | Format-Table IdentifyingNumber, Name, LocalPackage -AutoSize
```

```txt output
IdentifyingNumber                      Name                  LocalPackage
-----------------                      ----                  ------------
{9949770E-B807-4610-9A96-8CD4997A736A} Cloudflare One Client C:\WINDOWS\Installer\cd8edd0.msi
```

2. You can then use the LocalPackage output in the uninstall command. For example,

```powershell
msiexec /x "<LOCAL_PACKAGE_PATH>" /quiet
```

:::note
If [uninstall protection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/uninstall-protection/) is turned on, add `UNINSTALL_TOKEN="<ACCOUNT_WIDE_UNINSTALL_TOKEN>"` to the command. Silent uninstalls, upgrades, and downgrades fail without a valid token.
:::

### Update MDM parameters

The on-disk configuration of the Windows client can be changed at any time by modifying or replacing the contents of `C:\ProgramData\Cloudflare\mdm.xml`. The format of this file is as follows:

```xml
<dict>
  <key>organization</key>
  <string>your-team-name</string>
	<key>onboarding</key>
	<false/>
</dict>
```

:::note
If [uninstall protection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/uninstall-protection/) is turned on, the client blocks changes to `mdm.xml`. [Unlock the client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/uninstall-protection/#unlock-the-client-with-warp-cli) with `warp-cli uninstall-protection unlock` first.
:::

Changes to this file are processed immediately by the Cloudflare One Client.

### Authenticate in embedded browser

By default the Cloudflare One Client will use the user's default browser to perform registration. You can override the default setting to instead authenticate users in an embedded browser. The embedded browser will work around any protocol handler issues that may prevent the default browser from launching.

To use an embedded browser:

1. Download and install WebView2 by following the [Microsoft instructions](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
2. Add a registry key with the following command:
   ```txt
   REG ADD HKLM\SOFTWARE\Cloudflare\CloudflareWARP /f /v UseWebView2 /t REG_SZ /d y
   ```

The Cloudflare One Client will now launch WebView2 when the user is registering their device with Zero Trust.

## macOS

The Cloudflare One Client for macOS allows for an automated install via tools like Jamf, Intune, Kandji, or JumpCloud or any script or management tool that can place a `com.cloudflare.warp.plist` file in `/Library/Managed Preferences`. The plist can also be wrapped in a `.mobileconfig`.

<Render file="warp/intune-lob-caution" product="cloudflare-one" />

If you do not wish to use a management tool, you can manually place an `mdm.xml` file in `/Library/Application Support/Cloudflare`.

### Prepare file for MDM deployment

#### `plist` file

1. [Download](/cloudflare-one/static/mdm/com.cloudflare.warp.plist) an example `com.cloudflare.warp.plist` file.
2. Replace `your-team-name` with your Cloudflare One <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
3. Modify the file with your desired [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

#### `mobileconfig` file

1. [Download](/cloudflare-one/static/mdm/CloudflareWARP.mobileconfig) an example `.mobileconfig` file.
2. Replace `your-team-name` with your Cloudflare One <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
3. Run `uuidgen` from your macOS Terminal. This will generate a value for `PayloadUUID`, which you can use to replace the default value used for `PayloadUUID`.
4. Modify the file with your desired [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

### Place an unmanaged `mdm.xml` file

You can configure [Cloudflare One Client deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) on macOS by manually placing an `mdm.xml` file in `/Library/Application Support/Cloudflare`. This deployment method is an alternative to pushing a `plist` or `mobileconfig` using an MDM tool.

The format of `/Library/Application Support/Cloudflare/mdm.xml` is as follows:

```xml
<dict>
  <key>organization</key>
  <string>your-team-name</string>
</dict>
```

## Linux

The Cloudflare One Client for Linux allows for an automated install via the presence of an `mdm.xml` file in `/var/lib/cloudflare-warp`. The format of `/var/lib/cloudflare-warp/mdm.xml` is as follows:

```xml
<dict>
  <key>organization</key>
  <string>your-team-name</string>
</dict>
```

Refer to [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for a list of accepted arguments.

To learn how to automate Cloudflare One Client deployment on headless servers, refer to our [tutorial](/cloudflare-one/tutorials/deploy-client-headless-linux/).

## iOS

:::note[Migrate from 1.1.1.1]

The legacy iOS client, [1.1.1.1: Faster Internet](https://apps.apple.com/us/app/1-1-1-1-faster-internet/id1423538627), has been replaced by the Cloudflare One Agent. Learn more in our [migration guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/cloudflare-one-agent-migration/).
:::

The Cloudflare One Client for iOS, known in the App Store as [Cloudflare One Agent](https://apps.apple.com/us/app/cloudflare-one-agent/id6443476492), allows for an automated install via tools like Jamf, Intune, or SimpleMDM.

To proceed with the installation, here is an example of the XML code you will need:

```xml
<dict>
    <key>organization</key>
    <string>your-team-name</string>
    <key>auto_connect</key>
    <integer>1</integer>
    <key>switch_locked</key>
    <false />
    <key>service_mode</key>
    <string>warp</string>
    <key>support_url</key>
    <string>https://support.example.com</string>
</dict>
```

Refer to [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for a description of each argument.

## Android / ChromeOS

:::note[Migrate from 1.1.1.1]

The legacy Android client, [1.1.1.1 + WARP: Safer Internet](https://play.google.com/store/apps/details?id=com.cloudflare.onedotonedotonedotone), has been replaced by the Cloudflare One Agent. Learn more in our [migration guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/cloudflare-one-agent-migration/).
:::

The Cloudflare One Client for Android, known in the Google Play store as [Cloudflare One Agent](https://play.google.com/store/apps/details?id=com.cloudflare.cloudflareoneagent), allows for an automated install via tools like Intune, Google Endpoint Manager, and others.

To proceed with the installation, here is an example of the XML code you will need:

```xml
<key>organization</key>
<string>your-team-name</string>
<key>switch_locked</key>
<true />
<key>auto_connect</key>
<integer>0</integer>
<key>gateway_unique_id</key>
<string>your_gateway_doh_subdomain</string>
<key>service_mode</key>
<string>warp</string>
<key>support_url</key>
<string>https://support.example.com</string>
```

If your MDM tool does not support XML, you may need to convert the XML to JSON. Here is an example below:

```json
{
	"organization": "your-team-name",
	"gateway_unique_id": "your_gateway_doh_subdomain",
	"onboarding": true,
	"switch_locked": true,
	"auto_connect": 0,
	"service_mode": "warp",
	"support_url": "https://support.example.com"
}
```

Refer to [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for a description of each value.

# Parameters

import { GlossaryTooltip, Details, InlineBadge } from "~/components";

Each Cloudflare One Client (formerly WARP) supports the following set of parameters as part of their deployment, regardless of the deployment mechanism.

:::note

Most of the parameters listed below are also configurable in Cloudflare One under **Team & Resources** > **Devices**. In the event of conflicting settings, the Cloudflare One Client will always give precedence to settings on the local device (for example, in your `mdm.xml` or `com.cloudflare.warp.plist` files).

:::

## Required for full Cloudflare Zero Trust features

For the majority of Cloudflare Zero Trust features to work, you need to specify a team name. Examples of Cloudflare Zero Trust features which depend on the team name are [HTTP policies](/cloudflare-one/traffic-controls/http-policies/), [Browser Isolation](/cloudflare-one/remote-browser-isolation/), and [device posture](/cloudflare-one/reusable-components/posture-checks/).

### `organization`

Instructs the client to register the device with your organization. Registration requires authentication via an [IdP](/cloudflare-one/integrations/identity-providers/) or [Service Auth](/cloudflare-one/access-controls/service-credentials/service-tokens/).

**Value Type:** `string`

**Value:** Your <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

## Required for DNS-only policy enforcement

This field is used to enforce DNS policies when deploying the client in DoH-only mode.

### `gateway_unique_id`

Instructs the client to direct all DNS queries to a specific [Gateway DNS location](/cloudflare-one/traffic-controls/resolver-endpoints/). This value is only necessary if deploying without a [team name](#organization) or in an organization with multiple DNS locations. If you do not supply a DoH subdomain, we will automatically use the default Gateway DNS location for your organization.

**Value Type:** `string`

**Value:** Your <GlossaryTooltip term="DoH subdomain">DoH subdomain</GlossaryTooltip>.

## Organization parameters

You can use the following parameters to configure a specific Zero Trust organization.

### `allow_managed_deployments`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.6.0               |
| macOS    | ✅           | 2026.6.0               |
| Linux    | ❌           | —                      |
| iOS      | ❌           | —                      |
| Android  | ❌           | —                      |
| ChromeOS | ❌           | —                      |

</Details>

Local kill switch for [client version assignments](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/client-version-assignments/) pushed from the Cloudflare dashboard. Use this parameter to opt a device out of dashboard-managed version changes regardless of what is configured in the dashboard.

By default, the device applies any assignment the dashboard sends. If no assignment targets the device, the parameter has no effect.

**Value Type:** `boolean`

**Value:**

- `true` — (default) The device applies client version assignments configured for it in the Cloudflare dashboard. If no assignment targets the device, this setting has no effect.
- `false` — The device ignores client version assignments and stays on its current installed version, regardless of what is configured in the dashboard.

### `allow_updates`

Specifies whether the client should check for updates prior to initial registration.

**Value Type:** `boolean`

**Value:**

- `true` — (default) The client automatically checks for updates, prompting the user to install them.
- `false` — The client does not automatically check for updates.

### `auth_client_id`

Enrolls the device in your Zero Trust organization using a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token).
Requires the `auth_client_secret` parameter.

**Value Type:** `string`

**Value:** Client ID of the service token.

Example configuration:

```xml
<key>auth_client_id</key>
<string>88bf3b6d86161464f6509f7219099e57.access</string>
<key>auth_client_secret</key>
<string>bdd31cbc4dec990953e39163fbbb194c93313ca9f0a6e420346af9d326b1d2a5</string>
```

:::note
The service token must have _Service Auth_ [device enrollment permissions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-service-token). Allow permissions will not work for service tokens.
:::

### `auth_client_secret`

Enrolls the device in your Zero Trust organization using a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token).
Requires the `auth_client_id` parameter.

**Value Type:** `string`

**Value:** Client Secret of the service token.

### `auto_connect`

If switch has been turned off by user, the client will automatically turn itself back on after the specified number of minutes. We recommend keeping this set to a very low value — usually just enough time for a user to log in to hotel or airport Wi-Fi. If any value is specified for `auto_connect` the default state of the Cloudflare One Client will always be Connected (for example, after the initial install or a reboot).

**Value Type:** `integer`

**Value:**

- `0` — Allow the switch to stay in the off position indefinitely until the user turns it back on.
- `1` to `1440` — Turn switch back on automatically after the specified number of minutes.

:::note

This parameter replaces the old `enabled` property, which can no longer be used in conjunction with the new `switch_locked` and `auto_connect`. If you want to use these parameters, you must remove `enabled`.
:::

### `display_name`

Identifies a Zero Trust organization in the Cloudflare One Client GUI when the client is deployed with [multiple organizations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/switch-organizations/). Required if the `organization` parameter is specified within a [`configs` array](#configs).

**Value Type:** `string`

**Value:** Organization nickname shown to users in the Cloudflare One Client GUI (for example, `Test environment`).

### `enable_netbt`

NetBIOS over TCP/IP (NetBT) is a legacy feature in Windows primarily used for name resolution in some [rare scenarios](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#when-to-enable-netbt). The Cloudflare One Client disables NetBT on the tunnel interface by default for security reasons. If your organization still relies on legacy applications that require NetBT, you can override the default behavior and enable NetBT.

**Value Type:** `boolean`

**Value:**

- `false` — (default) Disables NetBT on the Cloudflare One Client tunnel interface.
- `true` — Enables NetBT on the Cloudflare One Client tunnel interface.

### `enable_pmtud`

[Path MTU Discovery (PMTUD)](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/path-mtu-discovery/) allows the Cloudflare One Client to discover the largest packet size that can be sent over the current network and optimize connection performance.

**Value Type:** `boolean`

**Value:**

- `false` — Disables PMTUD.
- `true` — Enables PMTUD on the Cloudflare One Client tunnel interface. This is the default in Cloudflare One Client version 2026.6.822.0 and later.

In earlier versions, the default is `false`.

### `enable_post_quantum`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                      | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2025.5.735.1         |
| macOS    | ✅           | 2025.5.735.1         |
| Linux    | ✅           | 2025.5.735.1         |
| iOS      | ✅           | 1.10                 |
| Android  | ✅           | 2.4                  |
| ChromeOS | ✅           | 2.4                  |

</Details>

The Cloudflare One Client uses [post-quantum cryptography](/ssl/post-quantum-cryptography/) to secure connections from the device to Cloudflare's network. Post-quantum cryptography requires the [MASQUE protocol](#warp_tunnel_protocol) and is enabled by default on all devices using MASQUE.

**Value Type:** `boolean`

**Value:**

- `false` — Disables post-quantum key agreement.
- `true` — Enables post-quantum key agreement for all traffic through the WARP tunnel.

### `environment`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2025.9.558.0         |
| macOS    | ✅           | 2025.9.558.0         |
| Linux    | ✅           | 2025.9.558.0         |
| iOS      | ✅           | 1.12.0               |
| Android  | ✅           | 2.5.1                |
| ChromeOS | ✅           | 2.5.1                |

</Details>

Configures the Cloudflare One Client to connect to Cloudflare's FedRAMP High authorized environment.

**Value Type:** `string`

**Value:**

- `normal` — (default) The Cloudflare One Client connects to the standard API endpoints, IPs, and domains (like `<ACCOUNT_ID>.cloudflare-gateway.com`) and forwards traffic to Cloudflare data centers worldwide.
- `fedramp_high` — The Cloudflare One Client connects to FedRAMP-specific API endpoints, IPs, and domains (like `<ACCOUNT_ID>.fed.cloudflare-gateway.com`). Traffic is forwarded to FedRAMP High compliant data centers for processing. To configure the FedRAMP High environment, you must allow the [FedRAMP-specific endpoints, IPs, and domains](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/) through your firewall.

When using [multiple configurations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/switch-organizations/) for the same organization, all configurations must specify the same `environment` value. A single organization cannot operate in both the normal and FedRAMP High environments.

In version 2026.5.0 and above, use [`organization_configs`](#organization_configs) to set `environment` once per organization rather than repeating it in every config entry:

```xml
<dict>
  <key>organization_configs</key>
  <dict>
    <key>mycompany-gov</key>
    <dict>
      <key>environment</key>
      <string>fedramp_high</string>
    </dict>
  </dict>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>mycompany-gov</string>
      <key>display_name</key>
      <string>Production</string>
    </dict>
    <dict>
      <key>organization</key>
      <string>mycompany-gov</string>
      <key>display_name</key>
      <string>Staging</string>
    </dict>
    <dict>
      <key>organization</key>
      <string>test-org</string>
      <key>display_name</key>
      <string>Test</string>
      <key>environment</key>
      <string>normal</string>
    </dict>
  </array>
</dict>
```

In earlier versions, each configuration for that organization must include `environment` explicitly:

```xml
<dict>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>mycompany-gov</string>
      <key>display_name</key>
      <string>Production</string>
      <key>environment</key>
      <string>fedramp_high</string>
    </dict>
    <dict>
      <key>organization</key>
      <string>mycompany-gov</string>
      <key>display_name</key>
      <string>Staging</string>
      <key>environment</key>
      <string>fedramp_high</string>
    </dict>
    <dict>
      <key>organization</key>
      <string>test-org</string>
      <key>display_name</key>
      <string>Test</string>
      <key>environment</key>
      <string>normal</string>
    </dict>
  </array>
</dict>
```

### `external_emergency_signal_fingerprint`

The SHA-256 fingerprint that the Cloudflare One Client will use to validate the [`external_emergency_signal_url`](#external_emergency_signal_url) HTTPS endpoint. Refer to [External Emergency Disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect) for details on how to extract this fingerprint.

**Value Type:** `string`

**Value:** SHA-256 fingerprint of the HTTPS server certificate (for example, `DD4F4806C57A5BBAF1AA5B080F0541DA75DB468D0A1FE731310149500CCD8662`)

### `external_emergency_signal_interval`

How often the Cloudflare One Client will poll [`external_emergency_signal_url`](#external_emergency_signal_url) for an [External Emergency Disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect) signal.

**Value Type:** `integer`

**Value:** Polling frequency in seconds (minimum `30`, default `300`)

### `external_emergency_signal_url`

The HTTPS endpoint that the Cloudflare One Client will poll for an [External Emergency Disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-external-emergency-disconnect) signal.

**Value Type:** `string`

**Value:** `https://192.0.2.1:3333/status/disconnect`

The URL must use `https://` and use an IPv4 or IPv6 address as host (not a domain).

### `hardware_backed_registration`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability            | Minimum WARP version |
| -------- | ----------------------- | -------------------- |
| Windows  | ✅                      | 2026.6.0             |
| macOS    | ✅ (Apple silicon only) | 2026.6.0             |
| Linux    | ✅                      | 2026.6.0             |
| iOS      | ❌                      | N/A                  |
| Android  | ❌                      | N/A                  |
| ChromeOS | ❌                      | N/A                  |

</Details>

Binds the registration to a non-exportable key stored in device hardware (Secure Enclave or TPM 2.0) and authenticates API requests with mTLS. For more information, refer to [Hardware-backed registration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/hardware-backed-registration/).

**Value Type:** `boolean`

**Value:**

- `false` — (default) The Cloudflare One Client stores its API token in the device keystore.
- `true` — The Cloudflare One Client generates a hardware-backed key during registration and uses mTLS for subsequent API calls.

:::caution
Changing this value after a device has registered forces the device to register again. Existing registrations are not migrated.
:::

### `local_emergency_signal_enabled`

Enables [Local Emergency Disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/emergency-disconnect/#set-up-local-emergency-disconnect). When enabled, the Cloudflare One Client monitors a fixed-path JSON file on the device for an emergency disconnect signal.

**Value Type:** `boolean`

**Value:**

- `false` — (default) Local signal file monitoring is disabled.
- `true` — The client monitors the local signal file for emergency disconnect.

### `managed_network_probe_count`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.9.1555.1          |
| macOS    | ✅           | 2026.9.1555.1          |
| Linux    | ✅           | 2026.9.1555.1          |
| iOS      | ❌           | —                      |
| Android  | ❌           | —                      |
| ChromeOS | ❌           | —                      |

</Details>

Sets how many extra times the Cloudflare One Client checks for [managed networks](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/) after a check finds none. The client checks for managed networks when the device registers and when the network changes. A temporary connection failure during that check can apply the wrong [device profile](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) until the next network change. Extra probes let the client correct the device profile without a network change. Each probe checks every managed network.

To set the wait between probes, use [`managed_network_probe_interval_secs`](#managed_network_probe_interval_secs).

**Value Type:** `integer`

**Value:**

- `0` — Turns off extra probes. The client only checks for managed networks on registration and network changes, as in versions before 2026.9.1555.1.
- `1` — (default) Runs one extra probe.
- `2` to `10` — Runs up to this number of extra probes. The client treats values above `10` as `10`.
- `-1` — Probes until the client finds a managed network.

### `managed_network_probe_interval_secs`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.9.1555.1          |
| macOS    | ✅           | 2026.9.1555.1          |
| Linux    | ✅           | 2026.9.1555.1          |
| iOS      | ❌           | —                      |
| Android  | ❌           | —                      |
| ChromeOS | ❌           | —                      |

</Details>

How long the Cloudflare One Client waits after a [managed network](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/) check finds no managed network before it probes again. A network change that does not start a new check restarts a pending wait.

To set the number of probes, use [`managed_network_probe_count`](#managed_network_probe_count).

**Value Type:** `integer`

**Value:** Wait time in seconds (minimum `30`, maximum `300`, default `30`)

The client replaces values outside this range with the nearest limit.

### `mdm_file_debounce_secs`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.9.1555.1          |
| macOS    | ✅           | 2026.9.1555.1          |
| Linux    | ✅           | 2026.9.1555.1          |
| iOS      | ❌           | —                      |
| Android  | ❌           | —                      |
| ChromeOS | ❌           | —                      |

</Details>

How long the Cloudflare One Client waits after the MDM file (`mdm.xml` or `com.cloudflare.warp.plist`) changes before it applies the new configuration. Each new change restarts the wait, so the client applies several quick changes together.

**Value Type:** `integer`

**Value:** Wait time in seconds (minimum `1`, maximum `60`, default `10`)

The client replaces values outside this range with the nearest limit.

### `mdm_file_unavailable_debounce_secs`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2026.9.1555.1          |
| macOS    | ✅           | 2026.9.1555.1          |
| Linux    | ✅           | 2026.9.1555.1          |
| iOS      | ❌           | —                      |
| Android  | ❌           | —                      |
| ChromeOS | ❌           | —                      |

</Details>

How long the Cloudflare One Client keeps the last applied MDM configuration after the MDM file goes missing or becomes unreadable. The wait starts at the first file change that leaves the file unavailable. If the file is still unavailable when the wait ends, the client removes the MDM configuration, including any managed registration.

Some MDM tools replace the file by deleting it and creating it again. A wait longer than that gap keeps the client from removing the registration.

**Value Type:** `integer`

**Value:** Wait time in seconds (minimum `0`, maximum `300`)

The client replaces values outside this range with the nearest limit. If you do not set this parameter, the client waits 20 seconds on macOS. On Windows and Linux, the client removes the MDM configuration as soon as it fails to read the file.

### `onboarding`

Controls the visibility of the onboarding screens that ask the user to review the privacy policy during an application's first launch.

**Value Type:** `boolean`

**Value:**

- `false` — Screens hidden.
- `true` — (default) Screens visible.

### `override_api_endpoint`

Overrides the [IP address](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#client-orchestration-api) used by the Cloudflare One Client to communicate with the client orchestration API. If you set this parameter, be sure to update your organization's firewall to ensure the new IP is allowed through.

This functionality is intended for use with a Cloudflare China local network partner or any other third-party network partner that can maintain the integrity of network traffic. Most IT admins should not set this setting as it will redirect all API traffic to a new IP.

**Value Type:** `string`

**Value:** `1.2.3.4` — Redirect all client orchestration API calls to `1.2.3.4`.

The string must be a valid IPv4 or IPv6 address, otherwise the Cloudflare One Client will fail to parse the entire MDM file.

### `override_doh_endpoint`

:::note
Only supported in DNS only mode.[^1]
:::

[^1]: Traffic and DNS mode is supported in client version 2025.2.664.0 and below. In version 2025.4.589.1 and above, this parameter does not apply to Traffic and DNS mode because all DoH traffic goes inside of the WARP tunnel.

Overrides the [IP address](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#doh-ip) used by the Cloudflare One Client to resolve DNS queries via DNS over HTTPS (DoH). If you set this parameter, be sure to update your organization's firewall to ensure the new IP is allowed through.

This functionality is intended for use with a Cloudflare China local network partner or any other third-party network partner that can maintain the integrity of network traffic. Most IT admins should not set this setting as it will redirect all DoH traffic to a new IP.

**Value Type:** `string`

**Value:** `1.2.3.4` — Redirect all DNS over HTTPS lookups to `1.2.3.4`.

The string must be a valid IPv4 or IPv6 address, otherwise the Cloudflare One Client will fail to parse the entire MDM file.

### `override_warp_endpoint`

Overrides the [IP address and UDP port](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip) used by the Cloudflare One Client to send traffic to Cloudflare's edge. If you set this parameter, be sure to update your organization's firewall to ensure the new IP is allowed through.

This functionality is intended for use with a Cloudflare China local network partner or any other third-party network partner that can maintain the integrity of network traffic. Most IT admins should not set this setting as it will redirect all Cloudflare One Client traffic to a new IP.

**Value Type:** `string`

**Value:** `203.0.113.0:500` — Redirect all Cloudflare One Client traffic to `203.0.113.0` on port `500`.

The string must be a valid IPv4 or IPv6 socket address (containing the IP address and port number), otherwise the Cloudflare One Client will fail to parse the entire MDM file.

### `service_mode`

Allows you to choose the operational mode of the client.

**Value Type:** `string`

**Value:**

- `warp` — (default) [Traffic and DNS mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-and-dns-mode-default).
- `1dot1` — [DNS only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode).
- `proxy` — [Local proxy mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#local-proxy-mode). Use the `proxy_port` parameter to specify the localhost SOCKS proxy port (between `0`-`66535`). For example,
  ```xml
  <key>service_mode</key>
  <string>proxy</string>
  <key>proxy_port</key>
  <integer>44444</integer>
  ```
- `postureonly` — [Posture only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#posture-only-mode).
- `tunnelonly` - [Traffic only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode).

### `support_url`

When the Cloudflare One Client is deployed via MDM, the in-app **Send Feedback** button is disabled by default. This parameter allows you to re-enable the button and direct feedback towards your organization.

**Value Type:** `string`

**Value:**

- `https://<support.example.com>` — Use an `https://` link to open your company's internal help site.
- `mailto:<yoursupport@example.com>` — Use a `mailto:` link to open your default mail client.

### `switch_locked`

Allows the user to turn off the client switch and disconnect the Cloudflare One Client.

**Value Type:** `boolean`

**Value:**

- `false` — (default) The user is able to turn the switch on/off at their discretion. When the switch is off, the user will not have the ability to reach sites protected by Access that leverage certain device posture checks.
- `true` — The user is prevented from turning off the switch. The Cloudflare One Client will automatically start in the connected state.

On new deployments, you must also include the `auto_connect` parameter with at least a value of `0`. This will prevent clients from being deployed in the off state without a way for users to manually enable them.

:::note
This parameter replaces the old `enabled` property, which can no longer be used in conjunction with the new `switch_locked` and `auto_connect`. If you want to use these parameters, you must remove `enabled`.
:::

### `unique_client_id`

:::note
Only valid for iOS and Android/ChromeOS.
:::

Assigns a unique identifier to the device for the [device UUID posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/device-uuid).

**Value Type:** `string`

**Value:** UUID for the device (for example, `496c6124-db89-4735-bc4e-7f759109a6f1`).

### `use_in_app_webview`

:::note
Only valid for Android and ChromeOS.
:::

Uses an in-app WebView for Zero Trust authentication instead of the default system browser. This setting is required when Always-on VPN with Lockdown mode is enabled.

**Value Type:** `boolean`

**Value:**

- `false` — (default) Uses the default system browser for Zero Trust authentication.
- `true` — Uses an in-app WebView for Zero Trust authentication.

### `warp_tunnel_protocol`

Configures the protocol used to route IP traffic from the device to Cloudflare Gateway. For more information, refer to [Device tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol).

**Value Type:** `string`

**Value:**

- `masque` — (default) [MASQUE](https://datatracker.ietf.org/wg/masque/about/) protocol
- `wireguard` — [WireGuard](https://www.wireguard.com/) protocol

## Top-level parameters

Top-level parameters determine how the Cloudflare One Client manages device registrations.

### `configs`

Allows a user to [switch between Zero Trust organizations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/switch-organizations/) in the Cloudflare One Client GUI. The `configs` array is also required when using another [top-level parameter](#top-level-parameters) such as `multi_user` or `pre_login`, even if only one organization is specified.

**Value Type:** `array`

**Value:** An array containing one or more Zero Trust organizations.

### `multi_user`

Enables multiple user registrations on a Windows device.

**Value Type:** `boolean`

**Value:**

- `false` — (default) Only one Cloudflare One Client registration is stored per device. After a user logs in to the Cloudflare One Client, their settings and identity will apply to all traffic from the device.
- `true` — Each Windows user has their own Cloudflare One Client registration. For more information, refer to [Multiple users on a Windows device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-multiuser/).

### `organization_configs`

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2026.5.0             |
| macOS    | ✅           | 2026.5.0             |
| Linux    | ✅           | 2026.5.0             |
| iOS      | ❌           | N/A                  |
| Android  | ❌           | N/A                  |
| ChromeOS | ❌           | N/A                  |

</Details>

Defines organization-wide settings that apply to all [`configs`](#configs) entries for a given organization, overriding any conflicting values set within individual config entries.

**Value Type:** `dict`

**Value:** A dict where each key is an organization name (team name) and each value is a dict of organization-wide settings.

Supported settings:

| Key | Description |
| --- | ----------- |
| `environment` | Overrides the [`environment`](#environment) value for all configs belonging to this organization. |
| `hardware_backed_registration` | Overrides the [`hardware_backed_registration`](#hardware_backed_registration) value for all configs belonging to this organization. |

Use `organization_configs` when you have multiple configurations for the same organization and want to enforce a single `environment` value across all of them. Any `environment` value set inside an individual `configs` entry for that organization will be overridden by the value specified in `organization_configs`.

Example configuration where `test-org` has two configs (Production and Staging), both enforced to use `fedramp_high` via `organization_configs`:

```xml
<dict>
  <key>organization_configs</key>
  <dict>
    <key>test-org</key>
    <dict>
      <key>environment</key>
      <string>fedramp_high</string>
    </dict>
  </dict>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>test-org</string>
      <key>display_name</key>
      <string>Production</string>
    </dict>
    <dict>
      <key>organization</key>
      <string>test-org</string>
      <key>display_name</key>
      <string>Staging</string>
    </dict>
  </array>
</dict>
```

### `pre_login`

Allows the Cloudflare One Client to connect with a service token before a user completes the initial Windows login. For more information, refer to [Connect the Cloudflare One Client before Windows login](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-prelogin/).

## Per-app VPN parameters (Android)

[Per-app VPN](https://support.google.com/work/android/answer/9213914?hl=en) parameters allow you to choose the Android apps that can send traffic through the WARP tunnel. Admins can configure these parameters via any MDM tool that supports deploying an Android app to managed devices or work profiles.

### `app_identifier`

An application package name/bundle identifier which uniquely identifies the app on the Google Play Store. This application will be tunneled through the Cloudflare One Client service.

**Value Type**: `string`

**Value**: The app identifier can be found in the ID query parameter of the specific app's Play Store URL. For example: in the case of `https://play.google.com/store/apps/details?id=com.cloudflare.cloudflareoneagent`, the app identifier for the Cloudflare One Agent app is `com.cloudflare.cloudflareoneagent`.

### `is_browser`

An optional property. `is_browser` will help the Cloudflare One Agent application decide which browser to open instead of the default browser for specific features such as re-authentication and Gateway block notifications. If needed, admins should explicitly indicate that a given `tunneled_app` is a browser, rather than relying on automatic browser detection.

**Value Type**: `boolean`

**Value**: If the value is `true`, identifies the application defined in `app_identifier` as a browser. The default value is `false` and `is_browser` is an optional property.

# Path MTU Discovery (PMTUD)

import { Details, Render } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> Traffic only mode </li></ul>                      | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2025.9.173.1         |
| macOS    | ✅           | 2025.9.173.1         |
| Linux    | ✅           | 2025.9.173.1         |
| iOS      | ❌           |                      |
| Android  | ❌           |                      |
| ChromeOS | ❌           |                      |

</Details>

The [Maximum Transmission Unit (MTU)](https://www.cloudflare.com/learning/network-layer/what-is-mtu/) is the largest data packet size that a device can send over a network without fragmentation. When you connect to services through the Cloudflare One Client (formerly WARP), your data is encapsulated, which adds extra headers and increases the overall packet size. On some networks, especially cellular or guest Wi-Fi networks, the network's MTU may be smaller than the Cloudflare One Client's [default packet size](#recommended-mtu). This mismatch forces packets to be fragmented or dropped entirely, leading to connection instability or complete connection failures.

The Cloudflare One Client's Path MTU Discovery (PMTUD) feature solves this problem by actively probing for the minimum MTU along the entire network path between the device and Cloudflare. The Cloudflare One Client will then dynamically adjust its [tunnel interface](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#virtual-interface) MTU based on the probe results. This allows the Cloudflare One Client to maintain a stable connection on low MTU networks and take advantage of higher MTUs when available.

:::note
Certain features may be disabled or degraded at low MTU thresholds. For details, refer to [Minimum MTUs](#minimum-mtus).
:::

## Prerequisites

- The Cloudflare One Client must be configured to use the [MASQUE tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol).

## Enable or disable Path MTU Discovery

PMTUD is now enabled by default (Cloudflare One Client version 2026.6.822.0 and above). To disable PMTUD on your devices, [deploy an MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#windows) with the `enable_pmtud` key set to `false`. For example:

```xml
<dict>
	<key>organization</key>
	<string>your-team-name</string>
	<key>warp_tunnel_protocol</key>
  <string>masque</string>
	<key>enable_pmtud</key>
  <false/>
</dict>
```

This configuration disables the PMTUD feature and explicitly configures the MASQUE tunnel protocol.

With Path MTU Discovery disabled, the Cloudflare One Client will not send active probes to detect the network path MTU and will not update its tunnel interface MTU accordingly. If enabled, you can expect PMTUD probes to generate an extra 25 Mb/day of traffic coming from the device.

## Verify PMTUD is enabled

To check if PMTUD is active on a device, open a terminal and run the following command:

```sh
warp-cli settings | grep -i pmtu
```

```sh output
(local policy)	PMTUD enabled: true
```

If PMTUD is enabled, the output will show `PMTUD enabled: true`.

## Minimum MTUs

### Recommended MTU

The Cloudflare One Client requires the following MTUs for full functionality and performance:

| Device tunnel protocol | IPv4       | IPv6       |
| ---------------------- | ---------- | ---------- |
| WireGuard              | 1340 bytes | 1360 bytes |
| MASQUE                 | 1361 bytes | 1381 bytes |

### Path MTU Discovery

For the PMTUD feature to work, the network path must support an MTU of at least 1281 bytes. The 1281 bytes consists of:

- 1200 bytes: Minimum QUIC datagram
- 53 bytes: WARP MASQUE encapsulation
- 28 bytes: IP/UDP headers

### IPv6

To send IPv6 traffic through the Cloudflare One Client, the network path must support an MTU of at least 1361 bytes. The 1361 bytes consists of:

- 1280 bytes: Minimum IPv6 packet size
- 53 bytes: WARP MASQUE encapsulation
- 28 bytes: IP/UDP headers

If PMTUD is enabled and the MTU is less than 1361 bytes, then the Cloudflare One Client will automatically disable IPv6 on the tunnel interface.

### WebRTC

To send WebRTC traffic through the Cloudflare One Client, the network path must support an MTU of at least 1361 bytes. Below 1361 bytes, WebRTC connections will experience progressively degraded performance. This minimum MTU impacts [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/) and any other website that uses WebRTC (such as video conferencing and media streaming services).

## Check your MTU

You can check your current network path MTU by collecting [Cloudflare One Client diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/).

1. Run the `warp-diag` command on the device or [collect logs via the dashboard](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/#collect-logs-via-the-dashboard).
2. Open the resulting `warp-debugging-info-<date>-<time>.zip` file.
3. Open `connectivity.txt` and search for `PMTU`.

   ```txt title="connectivity.txt" {16-17}
   ====================================================================
   H3 Quic Connect
   ====================================================================

   Testing H3 QUIC connectivity to 'https://cloudflare-quic.com/cdn-cgi/l4-stats' result: Successful
   IPv4:
   "
   Headers:
   	server address=104.18.26.14:443
   	...

   Body:
   	transport=TCP
   	...

   PMTU:
   	1500 bytes
   "
   ```

The example above shows an MTU of 1500 bytes, which meets the [recommended MTU requirements](#recommended-mtu) for the Cloudflare One Client. If your MTU falls below the recommended threshold, consider [enabling Path MTU Discovery](#enable-or-disable-path-mtu-discovery) to optimize connection performance.

# Register the Cloudflare One Client with minimal user interaction

import { Render, GlossaryTooltip, Tabs, TabItem } from "~/components";

Administrators can automate Cloudflare One Client (formerly WARP) registration on managed devices and minimize the number of clicks required from an end user.

During the default Cloudflare One Client enrollment process, end users typically need to complete several steps in order to login:

1. Review Terms and Conditions in the Cloudflare One Client GUI and acknowledge your company's use of the Cloudflare One Client.
2. Select their identity provider from the Cloudflare Access login screen.
3. Complete the authentication steps required by the identity provider.
4. Interact with a browser popup requesting permission to launch the Cloudflare One Client.

This guide covers how to eliminate steps 1, 2 and 4 from your Cloudflare One Client deployment.

:::note[Service token authentication]
If you are looking to eliminate all user interaction, you can [enroll devices using service tokens](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-service-token). Because users are not required to log in to an identity provider, identity-based policies and logging will not be available on these devices.
:::

On iOS and Android / ChromeOS, end users will still be asked questions required by their platform such as accepting notifications or installing the VPN Profile.

## Turn off onboarding screens

To skip the Terms and Conditions screens that are usually presented to users, set the [`onboarding` parameter](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) to `false` in your [MDM deployment file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/). Here is an example `mdm.xml` file:

```xml
<dict>
  <key>organization</key>
  <string>your-team-name</string>
	<key>onboarding</key>
	<false/>
</dict>
```

## Turn on instant authentication

If you are only using one identity provider for device enrollment, turn on **Apply instant authentication** in your [device enrollment permissions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#set-device-enrollment-permissions). This allow users to skip the Cloudflare Access login page and go directly to your SSO login event.

## Allow browser to launch the Cloudflare One Client

You can configure your browser to automatically launch the Cloudflare One Client application after a successful login and skip the **Open Cloudflare WARP.app** popup.

![Browser popup requesting permission to open the Cloudflare One Client](~/assets/images/cloudflare-one/connections/warp-protocol-handler.png)

_Note: Labels in this image may reflect a previous product name._

### Chromium-based browsers

Chromium-based browsers such as Google Chrome and Microsoft Edge have a policy setting called [AutoLaunchProtocolsFromOrigins](https://learn.microsoft.com/en-us/DeployEdge/microsoft-edge-policies#autolaunchprotocolsfromorigins). This setting takes in two parameters: a protocol for the browser to launch and the origins that are allowed to launch it. For the browser to launch the Cloudflare One Client, you need to set the protocol to `com.cloudflare.warp` and the origin to your <GlossaryTooltip term="team domain">Cloudflare Zero Trust team domain</GlossaryTooltip> (`https://<your-team-name>.cloudflareaccess.com`).

<Tabs>
<TabItem label="Windows">
On Windows, you can configure `AutoLaunchProtocolsFromOrigins` by adding a new registry key.

To add the registry key manually:

1. Open Registry Editor as Administrator.
2. Navigate to the policies folder for your browser:
   - Google Chrome: `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome`
   - Microsoft Edge: `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge`
     :::note
     You may need to create the `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome` folder if it does not already exist.
     :::
3. Create a new string value:
   - **Value Name**: `AutoLaunchProtocolsFromOrigins`
   - **Value Data**: `[{"allowed_origins": ["https://<your-team-name>.cloudflareaccess.com/"], "protocol": "com.cloudflare.warp"}]`

   Be sure to replace `<your-team-name>` with your actual <GlossaryTooltip term="team name">Zero Trust team name</GlossaryTooltip>.

Instead of using the Registry Editor, the registry key can also be created using a Group Policy Object (GPO), PowerShell script, or with an MDM such as [Intune](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/partners/intune/#update-mdm-parameters).

</TabItem>
<TabItem label="macOS">

On macOS, you can configure `AutoLaunchProtocolsFromOrigins` by deploying a property list (plist) file for the browser. The exact instructions will vary depending on your [MDM](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/partners/). The general procedure is as follows:

1.  Create a new plist file with the following name (case sensitive):
    - Google Chrome: `com.google.Chrome.plist`
    - Microsoft Edge: `com.microsoft.Edge.plist`

2.  Using a text editor, add the following content to your plist:

    ```xml
    <key>AutoLaunchProtocolsFromOrigins</key>
    <array>
    	<dict>
    		<key>allowed_origins</key>
    		<array>
    			<string>https://<your-team-name>.cloudflareaccess.com</string>
    		</array>
    		<key>protocol</key>
    		<string>com.cloudflare.warp</string>
    	</dict>
    </array>
    ```

    Be sure to replace `<your-team-name>` with your actual <GlossaryTooltip term="team name">Zero Trust team name</GlossaryTooltip>.

3.  Some MDMs require converting the `.plist` to a `.mobileconfig` before pushing it to a device. You can use a [file converter](https://github.com/timsutton/mcxToProfile) or modify the following example `com.google.Chrome.mobileconfig`:

        ```xml
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
        <dict>
        		<key>PayloadIdentifier</key>
        		<string>com.google.chrome</string>
        		<key>PayloadRemovalDisallowed</key>
        		<true/>
        		<key>PayloadScope</key>
        		<string>System</string>
        		<key>PayloadType</key>
        		<string>Configuration</string>
        		<key>PayloadUUID</key>
        		<string>8FCBDCA7-87B3-4610-A01A-B0FE4C5B57C8</string>
        		<key>PayloadOrganization</key>
        		<string></string>
        		<key>PayloadVersion</key>
        		<integer>1</integer>
        		<key>PayloadDisplayName</key>
        		<string>Google Chrome Policy</string>
        		<key>PayloadContent</key>
        		<array>
        				<dict>
        						<key>PayloadType</key>
        						<string>com.apple.ManagedClient.preferences</string>
        						<key>PayloadVersion</key>
        						<integer>1</integer>
        						<key>PayloadIdentifier</key>
        						<string>com.normandale</string>
        						<key>PayloadUUID</key>
        						<string>8FCBDCA7-87B3-4610-A01A-B0FE4C5B57C8</string>
        						<key>PayloadEnabled</key>
        						<true/>
        						<key>PayloadDisplayName</key>
        						<string>Custom: (com.google.Chrome)</string>
        						<key>PayloadContent</key>
        						<dict>
        								<key>com.google.Chrome</key>
        								<dict>
        										<key>Forced</key>
        										<array>
        												<dict>
        														<key>mcx_preference_settings</key>
        														<dict>
        																<key>AutoLaunchProtocolsFromOrigins</key>
        																<array>
        																<dict>
        																<key>allowed_origins</key>
        																<array>
        																<string>https://<your-team-name>.cloudflareaccess.com</string>
        																</array>
        																<key>protocol</key>
        																<string>com.cloudflare.warp</string>
        																</dict>
        																</array>
        														</dict>
        												</dict>
        										</array>
        								</dict>
        						</dict>
        				</dict>
        		</array>
        </dict>
        </plist>
        ```

4.  Upload the `.plist` or `.mobileconfig` file to your preferred MDM.
5.  Deploy the configuration profile to your devices.

For more information on configuring browser policies on macOS, refer to the [Google Chrome](https://support.google.com/chrome/a/answer/9020077?hl=en&ref_topic=7650028&sjid=15337530832025656704-NA) or [Microsoft Edge](https://learn.microsoft.com/en-us/deployedge/configure-microsoft-edge-on-mac) documentation.

</TabItem>
</Tabs>

# Switch between Zero Trust organizations

import { Details, Render, TabItem, Tabs } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2024.1.159.0         |
| macOS    | ✅           | 2024.1.160.0         |
| Linux    | ✅           | 2024.2.62.0          |
| iOS      | ✅           | 1.7                  |
| Android  | ✅           | 1.4                  |
| ChromeOS | ✅           | 1.4                  |

</Details>

In the Cloudflare One Client (formerly WARP), users can switch between multiple Zero Trust organizations (or other [MDM parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/)) that administrators specify in an MDM file. Common use cases include:

- Allow IT security staff to switch between test and production environments.
- Allow Managed Service Providers to support multiple customer accounts.
- Allow users to switch between the default Cloudflare One Client ingress IPs and the [Cloudflare China ingress IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#override_warp_endpoint).

## MDM file format

To enable multiple organizations, administrators need to modify their [MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) to take an array of configurations. Each configuration must include a `display_name` parameter that will be visible to users in the Cloudflare One Client GUI. Because display names are listed in the same order as they appear in the MDM file, we recommend putting the most used configurations at the top of the file. When a user opens the Cloudflare One Client for the first time, they will be prompted to log into the first configuration in the list.

An MDM file supports a maximum of 25 configurations. The following example includes three configurations.

### XML

```xml title="mdm.xml"
<dict>
	<key>configs</key>
	<array>
		<dict>
			<key>organization</key>
			<string>mycompany</string>
			<key>display_name</key>
			<string>Production environment</string>
		</dict>
		<dict>
			<key>organization</key>
			<string>mycompany</string>
			<key>override_api_endpoint</key>
			<string>203.0.113.0</string>
			<key>override_doh_endpoint</key>
			<string>203.0.113.0</string>
			<key>override_warp_endpoint</key>
			<string>203.0.113.0:0</string>
			<key>display_name</key>
			<string>China employees</string>
		</dict>
		<dict>
			<key>organization</key>
			<string>test-org</string>
			<key>display_name</key>
			<string>Test environment</string>
		</dict>
	</array>
</dict>
```

### plist

[Download](/cloudflare-one/static/mdm/multiple-orgs/com.cloudflare.warp.plist) an example `.plist` file.

### mobileconfig

[Download](/cloudflare-one/static/mdm/multiple-orgs/CloudflareWARP.mobileconfig) an example `.mobileconfig` file.

## Switch organizations in the Cloudflare One Client

To switch to a different organization as a user:

<Tabs> <TabItem label="Windows, macOS, and Linux">

1. Open the Cloudflare One Client on your device.
2. Go to **Home**. The **Configuration** dropdown will show the organizations that the admin has configured for your device.

<Details header="Version 2026.1 and earlier">
	In the Cloudflare One Client, select the gear icon > **Switch
	configurations**.
</Details>

</TabItem> <TabItem label="iOS and Android">

1. Open the Cloudflare One Agent app on your device.
2. Go to **Settings** > **Advanced** > **Switch configurations**. The menu will show the organizations that the admin has configured for your device.

</TabItem> </Tabs>

3. Select the configuration that you want to connect to.
4. If prompted, complete the authentication steps required for the new organization. Your authentication information will be saved and you will be able to switch back and forth between configurations.

:::note
Only admins can [add additional organizations](#mdm-file-format) to the client GUI. To connect to an organization that is not displayed in the GUI, manually log out[^1] of the old organization and [enroll](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) in the new organization.
:::

[^1]: Logging out is only possible if [Allow device to leave organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-device-to-leave-organization) is enabled for your device.

### Troubleshooting

When switching organizations or connecting for the first time, keep the following in mind:

- If this is the first time connecting to an organization, web browsers like Chrome may require a full restart to correctly recognize and trust the organization's root certificate. Cloudflare recommends closing all browser windows after the initial connection. All subsequent switches should not require a restart.
- On macOS, ensure the specific CA certificate for the new organization is properly trusted by verifying its status in Keychain Access.
- Switching configurations may sometimes momentarily disconnect the Cloudflare One Client. If this occurs, simply re-enable the Cloudflare One Client to restore the connection.

# Multiple users on a Windows device

import { Details, Render } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                       | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2025.6.1400.0        |
| macOS    | ❌           |                      |
| Linux    | ❌           |                      |
| iOS      | ❌           |                      |
| Android  | ❌           |                      |
| ChromeOS | ❌           |                      |

</Details>

The Cloudflare One Client (formerly WARP) supports multiple users on a single Windows device. In multi-user mode, each user has their own [device registration](/cloudflare-one/team-and-resources/devices/device-registration/), and the Cloudflare One Client will automatically switch device registrations when a user logs in to their Windows account. All traffic to Cloudflare will be attributed to the currently active Windows user. This allows administrators to apply identity-based policies and device settings, audit user activity, and remove individual users from a shared workstation.

:::caution[DNS logging]
If a user enables **Log DNS queries** in the Cloudflare One Client GUI (or runs `warp-cli dns log enable`), the Cloudflare One Client will store all DNS queries on the device onto disk. Any user on the device will be able to examine the DNS queries of another user.
:::

## Enable multi-user mode

To enable multi-user support on Windows, [deploy an MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#windows) onto the device with the `multi_user` key set to `true`. For example:

```xml
<dict>
  <key>multi_user</key>
  <true/>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>your-team-name</string>
      <key>display_name</key>
      <string>Default</string>
    </dict>
  </array>
</dict>
```

To use multi-user mode alongside the [Windows pre-login](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-prelogin/) and [Switch between Zero Trust organizations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/switch-organizations/) options:

```xml
<dict>
  <key>multi_user</key>
	<true/>
  <key>pre_login</key>
  <dict>
    <key>organization</key>
    <string>mycompany</string>
    <key>auth_client_id</key>
    <string>88bf3b6d86161464f6509f7219099e57.access</string>
    <key>auth_client_secret</key>
    <string>bdd31cbc4dec990953e39163fbbb194c93313ca9f0a6e420346af9d326b1d2a5</string>
  </dict>
  <key>configs</key>
  <array>
		<dict>
			<key>organization</key>
			<string>mycompany</string>
			<key>display_name</key>
			<string>Production environment</string>
  	</dict>
		<dict>
			<key>organization</key>
			<string>test-org</string>
			<key>display_name</key>
			<string>Test environment</string>
		</dict>
  </array>
</dict>
```

When enabling multi-user mode for the first time, users will need to re-register even if they had a previous registration.

## Cloudflare One Client registration logic

The following flowchart shows how Cloudflare One Client registration settings take effect as users log in and out:

```mermaid
flowchart TB
    start(["Enable multi-user mode"])-->reg["Active Windows user is prompted to register Cloudflare One Client"]
		reg--"Log out of Windows"-->prelogin
		reg--"Switch user"-->regexists

    subgraph preloginbehavior["Windows login screen"]
		prelogin{{"Is there a pre-login <br />registration?"}}
    preloginyes["Use pre-login settings"]
		prelogin--"Yes"-->preloginyes
		prelogin-. "No" .->preloginno
		preloginno["Stay registered as <br />previous Windows user"]
		end

		preloginbehavior--"Log in to Windows"---->regexists{{"Has the user already registered with Cloudflare One Client?"}}
		regexists--"Yes"-->user["Switch to that user's registration"]
		regexists-. "No" .->reg
```

### Fast user switching

:::note
Requires [multi-user mode](#enable-multi-user-mode).
:::

[Fast user switching](https://learn.microsoft.com/windows/win32/shell/fast-user-switching) is a Windows feature that allows users to switch accounts without logging out. With fast user switching, multiple users may be logged in to the device and generating network traffic. The Cloudflare One Client will attribute all traffic to the user who has the [interactive windows station](http://techcommunity.microsoft.com/blog/askperf/sessions-desktops-and-windows-stations/372473). For example, if user A is logged in and fast user switches to user B, traffic from both accounts will appear to come from user B. This is because user B is now actively using the Windows desktop GUI. Now assume that user B logs out and there is no [pre-login registration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-prelogin/); the Cloudflare One Client will continue to attribute traffic to user B until user A logs back in to the Windows desktop.

To accurately attribute network traffic to specific users, Cloudflare recommends disabling fast user switching or at the very least configuring a [pre-login registration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-prelogin/).

# Block internet access until user authenticates

import { Badge, Details } from "~/components";

## Availability <Badge text="Beta" variant="caution" size="small" />

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                         | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2026.5.0             |
| macOS    | ❌           |                      |
| Linux    | ❌           |                      |
| iOS      | ❌           |                      |
| Android  | ❌           |                      |
| ChromeOS | ❌           |                      |

</Details>

When **no-auth-no-internet** is enabled, the Cloudflare One Client locks down general internet traffic on the device whenever the device is in an unauthenticated state (i.e., without a valid device registration). During this lockdown, the client allows only the traffic required for the device to remain on the network and for the user to complete IdP authentication. Once the user signs in, normal connectivity resumes and your configured Gateway, Access, RBI, and DLP policies take effect.

When this feature is enabled, the authentication is done via in-app WebView2 browser instead of the default browser on the system.

The lockdown re-engages automatically any time the device transitions back to an unauthenticated state — for example, if the registration is deleted/revoked, another OS user that has never authenticated with the Cloudflare One Client logs in while in [multi-user mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-multiuser/), or the user switches into a new organization.

## Prerequisites

- The Cloudflare One Client must be [deployed via MDM](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#windows).
- The device must have [WebView2](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#authenticate-in-embedded-browser) available. By default, the WebView2 runtime should be present on all Windows versions that Cloudflare One Client supports.

## Enable no-auth-no-internet

To enable the feature, [deploy an MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#windows) with the `no_auth_no_internet` top-level key set to `true`:

```xml
<dict>
  <key>no_auth_no_internet</key>
  <true/>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>your-team-name</string>
      <key>display_name</key>
      <string>Default</string>
    </dict>
  </array>
</dict>
```

When the Cloudflare One Client reads this configuration and detects that no user is authenticated, it applies the firewall lockdown and prompts the user to authenticate. After successful authentication, the lockdown is completely lifted.

## Block access to RFC 1918 ranges in lockdown state

By default, the Cloudflare One Client permits traffic to [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) private address ranges (`10.0.0.0/8`, `172.16.0.0/12`, and `192.168.0.0/16`) while the device is locked down. This allows the device to reach on-premise resources such as a domain controller, an MDM server, or a local printer before the user authenticates.

To block RFC 1918 traffic during lockdown, set `no_auth_no_internet_block_rfc_1918` to `true`:

```xml
<dict>
  <key>no_auth_no_internet</key>
  <true/>
  <key>no_auth_no_internet_block_rfc_1918</key>
  <true/>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>your-team-name</string>
      <key>display_name</key>
      <string>Default</string>
    </dict>
  </array>
</dict>
```

## Limitations

- The lockdown is enforced only when there is an active interactive user session (for example, a user is logged in and the device is not locked).
- A [pre-login](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-prelogin/) registration is also considered a valid registration, and the lockdown will not be enforced when a pre-login registration is in use. 
- Since authentication now occurs in an embedded WebView2 window, IdP flows that depend on the user's default browser (for example, browser-specific extensions or password managers) may not work.
- Some Windows applications, including Copilot and Teams (personal), may retain internet access while the Cloudflare One Client authentication window is open.

# Connect the Cloudflare One Client before Windows login

import { Details, Render } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/)                               | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| <ul><li> Traffic and DNS mode</li><li> DNS only mode </li><li> Traffic only mode </li><li> Local proxy mode </li></ul> | All plans                                                     |

| System   | Availability | Minimum WARP version |
| -------- | ------------ | -------------------- |
| Windows  | ✅           | 2025.6.1400.0        |
| macOS    | ❌           |                      |
| Linux    | ❌           |                      |
| iOS      | ❌           |                      |
| Android  | ❌           |                      |
| ChromeOS | ❌           |                      |

</Details>

With Cloudflare Zero Trust, you can use an on-premise Active Directory (or similar) server to validate a remote user's Windows login credentials. Before the user enters their Windows login information for the first time, the Cloudflare One Client (formerly WARP) establishes a connection using a service token. This initial connection is not associated with a user identity. Once the user completes the Windows login, the Cloudflare One Client switches to an identity-based session and applies the user registration to all future logins.

## Prerequisites

- Active Directory resources are [connected to Cloudflare](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/).

## 1. Create a service token

<Render file="access/create-service-token" product="cloudflare-one" />

## 2. Create a device enrollment policy

In your [device enrollment permissions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#set-device-enrollment-permissions), create the following policy:

| Rule Action  | Rule type | Selector      | Value          |
| ------------ | --------- | ------------- | -------------- |
| Service Auth | Include   | Service Token | `<TOKEN-NAME>` |

## 2. (Optional) Restrict access during pre-login

Devices enrolled via a service token are identified by the email address `non_identity@<team-name>.cloudflareaccess.com`. Using this email address, you can apply specific [device profile settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/) and [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) during the pre-login state. For example, you could provide access to only those resources necessary to complete the Windows login and/or device management activities.

<Details header="Example device profile rule">

| Selector         | Operator | Value                                           | Logic |
| ---------------- | -------- | ----------------------------------------------- | ----- |
| User email       | in       | `non_identity@<team-name>.cloudflareaccess.com` | And   |
| Operating system | is       | Windows                                         |       |

</Details>

<Details header="Example Gateway network policy">

| Selector                     | Operator | Value                                           | Logic |
| ---------------------------- | -------- | ----------------------------------------------- | ----- |
| Destination IP               | in list  | `Active Directory servers`                      | And   |
| User email                   | in       | `non_identity@<team-name>.cloudflareaccess.com` | And   |
| Passed Device Posture Checks | in       | `Windows 10 or higher (OS version)`             |       |

| Action |
| ------ |
| Allow  |

</Details>

## 3. Configure the MDM file

To enable the Windows pre-login feature, an MDM file in the following format must be [deployed](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#windows) on the device. In the following example, the `pre_login` key allows the device to connect using the service token, while `configs` contains your default Zero Trust configuration.

```xml
<dict>
  <key>pre_login</key>
  <dict>
    <key>organization</key>
    <string>mycompany</string>
    <key>auth_client_id</key>
    <string>TOKEN-ID</string>
    <key>auth_client_secret</key>
    <string>TOKEN-SECRET</string>
  </dict>
  <key>configs</key>
  <array>
    <dict>
      <key>organization</key>
      <string>mycompany</string>
      <key>display_name</key>
      <string>Default</string>
    </dict>
  </array>
</dict>
```

The Cloudflare One Client will apply the pre-login configuration when no other Cloudflare One Client registration exists and the user has not yet logged into Windows. When the pre-login configuration is in effect, the device will appear on **Team & Resources** > **Devices** with the email `non_identity@<team-name>.cloudflareaccess.com`.

After the user logs into Windows, the Cloudflare One Client will automatically switch to the default MDM configuration and prompt the user to authenticate with the IdP. Once authenticated, the Cloudflare One Client registers and connects with the user identity. The **Team & Resources** > **Devices** page will now show a new device associated with the user's email.

If [multi-user mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-multiuser/) is turned off, this user registration will be used for any subsequent connections, including before the next Windows user login. Deleting the user registration would cause the Cloudflare One Client to switch back to the pre-login configuration as soon as the user logs out of Windows.

To learn how the pre-login configuration works with [multi-user mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-multiuser/), refer to the [Cloudflare One Client registration flowchart](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/windows-multiuser/#cloudflare-one-client-registration-logic).

# Fleet

import { Render } from "~/components";

This guide covers how to deploy the Cloudflare One Client (formerly WARP) using [Fleet](https://fleetdm.com/) device management software.

## macOS

### 1. Create a custom MDM file

1. [Download](/cloudflare-one/static/mdm/CloudflareWARP.mobileconfig) an example `.mobileconfig` file.
2. Modify the file with your desired [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

### 2. Upload MDM file to Fleet

1. In the Fleet admin console, go to **Controls**.
2. From the **Teams** dropdown, select the team (group of hosts) that requires the Cloudflare One Client.
3. Select **OS settings** > **Custom settings**.
4. Select **Add profile** and upload the custom `.mobileconfig`.
5. Select the hosts which require the Cloudflare One Client:
   - **All hosts**: Deploys the Cloudflare One Client to all hosts in the team.
   - **Custom**: Deploys the Cloudflare One Client to a subset of the hosts in the team. Use [labels](https://fleetdm.com/guides/managing-labels-in-fleet#basic-article) to define the hosts that should be included or excluded.
6. Select **Add profile**.

The defined hosts will immediately receive the deployment profile, but the Cloudflare One Client is not yet installed.

### 3. Download Cloudflare One Client package for macOS

Visit the [Download page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos) to review system requirements and download the installer for your operating system.

### 4. Upload Cloudflare One Client package to Fleet

To add the Cloudflare One Client installer package for distribution to your hosts enrolled in Fleet:

1. In the Fleet admin console, go to **Software**.
2. From the **Teams** dropdown, select the team (group of hosts) that requires the Cloudflare One Client.
3. Select **Add Software** and upload the `.pkg` file that was previously downloaded.

### 5. Install the Cloudflare One Client with Fleet

To deploy the uploaded `.pkg` file to your hosts:

1. In the Fleet admin console, go to **Hosts**.
2. Select the host that requires the Cloudflare One Client.
3. Go to **Software** and search for `Cloudflare`.
4. Select **Actions** > **Install**.

Installation will happen automatically when the host comes online. To deploy with REST API or GitOps, refer to the [Fleet documentation](https://fleetdm.com/guides/deploy-software-packages). <Render file="warp/connectivity-status-check" product="cloudflare-one" />

### 6. Uninstall the Cloudflare One Client with Fleet

To uninstall the Fleet-deployed Cloudflare One Client:

1. In the Fleet admin console, go to **Hosts**.
2. Select the host that requires the Cloudflare One Client to be uninstalled.
3. Go to **Software** and search for `Cloudflare`.
4. Select **Actions** > **Uninstall**.

## Windows

### 1. Download Cloudflare One Client package for Windows

Visit the [Download page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#windows) to review system requirements and download the installer for your operating system.

### 2. Upload Cloudflare One Client package to Fleet

To add the Cloudflare One Client installer package for distribution to your hosts enrolled in Fleet:

1. In the Fleet admin console, go to **Software**.
2. From the **Teams** dropdown, select the team (group of hosts) that requires the Cloudflare One Client.
3. Select **Add Software** and upload the `.msi` file that was previously downloaded.
4. (Optional) To allow users to install the Cloudflare One Client from Fleet Desktop, select **Self-service**.
5. Select **Advanced options**.
6. In **Install script**, replace the default script with the following:

```bash
$logFile = "${env:TEMP}/fleet-install-software.log"

try {

$installProcess = Start-Process msiexec.exe `
  -ArgumentList "/quiet /norestart ORGANIZATION=your-team-name SUPPORT_URL=https://example.com /lv ${logFile} /i `"${env:INSTALLER_PATH}`"" `
  -PassThru -Verb RunAs -Wait

Get-Content $logFile -Tail 500

Exit $installProcess.ExitCode

} catch {
  Write-Host "Error: $_"
  Exit 1
}
```

Refer to [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for a description of each argument.

### 3. Install the Cloudflare One Client with Fleet

To deploy the uploaded `.pkg` file to your hosts:

1. In the Fleet admin console, go to **Hosts**.
2. Select the host that requires the Cloudflare One Client.
3. Go to **Software** and search for `Cloudflare`.
4. Select **Actions** > **Install**.

Installation will happen automatically when the host comes online. To deploy with REST API or GitOps, refer to the [Fleet documentation](https://fleetdm.com/guides/deploy-software-packages). <Render file="warp/connectivity-status-check" product="cloudflare-one" />

### 4. Uninstall the Cloudflare One Client with Fleet

To uninstall the Fleet-deployed Cloudflare One Client:

1. In the Fleet admin console, go to **Hosts**.
2. Select the host that requires the Cloudflare One Client to be uninstalled.
3. Go to **Software** and search for `Cloudflare`.
4. Select **Actions** > **Uninstall**.

## Linux

Fleet allows you to [execute custom scripts](https://fleetdm.com/guides/scripts) on Linux hosts. The following example script creates an [MDM file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#linux) and installs the Cloudflare One Client on an Ubuntu 22.04 host:

```sh
#!/bin/sh

# Write the mdm.xml file
touch /var/lib/cloudflare-warp/mdm.xml
echo -e "<dict>\n   <key>organization</key>\n   <string>your-team-name</string>\n</dict>
" > /var/lib/cloudflare-warp/mdm.xml

# Add cloudflare gpg key
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg

# Add this repo to your apt repositories
echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ any main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list

# Install
sudo apt-get -y update && sudo apt-get -y install cloudflare-warp
```

To install the Cloudflare One Client on other Linux distributions, refer to the [package repository](https://pkg.cloudflareclient.com/).

# Hexnode

import { Render } from "~/components";

## Windows

1. Create a script file with `.bat`, `.cmd`, and `.ps1` file formats to download, install and configure the Cloudflare One Client (formerly WARP) Windows application on the device. Listed below is a sample script with all of the configurable parameters:

   ```python
   <# Choose file name for downloading application #>
   $filename = filename.msi'

   <# Download URL of the installer. #>
   $url = 'https://downloads.cloudflareclient.com/v1/download/windows/ga'
   Write-Host 'Downloading App from' $url
   Invoke-WebRequest -Uri $url -OutFile $filename

   <# Run the installer and wait for the installation to finish #>
   $arguments = "ORGANIZATION="exampleorg" SERVICE_MODE="warp" GATEWAY_UNIQUE_ID="fmxk762nrj" SUPPORT_URL="http://support.example.com""

   $installProcess = (Start-Process $filename -ArgumentList $arguments -PassThru -Wait)

   <# Check if installation was successful #>
   if ($installProcess.ExitCode -ne 0) {
       Write-Host "Installation failed!"
       exit $installProcess.ExitCode
   }
   else {
       Write-Host "Installation completed successfully!"
   }
   ```

2. Push the script file to the devices using Hexnode.

3. On your Hexnode console, go to **Manage** > **Devices**.

4. Select your device name. This will take you to the **Device Summary**.

5. Select **Actions** > **Execute Custom Script**.

6. Choose the script file source as _Upload file_, then upload the script file.

7. Select **Execute**.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

## macOS

1. [Download](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos) the Cloudflare One Client for macOS.

2. On your Hexnode console, go to **Apps** > **Add Apps** > **Enterprise App**.

3. Select _macOS_ as the app platform.

4. Add an app name, category and description.

5. Upload the `Cloudflare_WARP_<VERSION>.pkg` file and select **Add**.

6. Set up an XML file with the supported app configurations for the app.
   Here is a sample XML file with the accepted parameters.

   ```xml
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   <plist version="1.0">
   <dict>
   <key>organization</key>
   <string>organizationname</string>
   <key>auto_connect</key>
   <integer>1</integer>
   <key>switch_locked</key>
   <false />
   <key>service_mode</key>
   <string>warp</string>
   <key>support_url</key>
   <string>https://support.example.com</string>
   </dict>
   </plist>
   ```

7. On your Hexnode console, go to **Policies**.

8. Create a new policy and provide a policy name.

9. Go to **macOS** > **App Management** > **Mandatory Apps** and start setting up the policy.

10. Select **Add** and select the previously uploaded Cloudflare One Client app.

11. Go to **App Configurations** > **Add new configuration**.

12. Select the _Cloudflare One Client_ app and upload the XML file from Step 6.

13. Now go to **Policy Targets** and associate the policy with the target entities.

This will push the app along with the configurations to the selected devices.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

## iOS

1. On your Hexnode console, go to **Apps** > **Add Apps** > **Store App**.

2. Select _iOS_ as the app platform.

3. Search for [**Cloudflare One Agent**](https://apps.apple.com/us/app/cloudflare-one-agent/id6443476492) and **Add** the app.

4. Set up an XML file with the supported app configurations for the app. Refer this sample XML code to identify the supported arguments:

   ```xml
   <dict>
   <key>organization</key>
   <string>yourorganization</string>
   <key>auto_connect</key>
   <integer>1</integer>
   <key>switch_locked</key>
   <false />
   <key>service_mode</key>
   <string>warp</string>
   <key>support_url</key
   <string>https://support.example.com</string>
   </dict>
   ```

5. Upload the app configurations in Hexnode:
   1. On your Hexnode console, go to the **Apps** tab.
   2. Find the Cloudflare One Agent app and select its name.
   3. Select the settings icon and choose **App Configuration**.
   4. Upload the XML file in the corresponding field.
   5. Select **Save**.

6. Push the app to the target devices using Hexnode.
   1. On your Hexnode console, go to **Policies** and create a new policy.
   2. Provide a name for the policy and go to **iOS**.
   3. Go to **Mandatory Apps** > **Configure**.
   4. Select **Add** > **Add app**, check the required app, and select **Done**.
   5. Go to **Policy Targets** and associate the policy with the required target devices.

This will push the app along with the configurations to the selected devices.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

## Android

1. On your Hexnode console, go to **Apps** > **Add Apps** > **Managed Google Apps**.
2. Search for the app [**Cloudflare One Agent**](https://play.google.com/store/apps/details?id=com.cloudflare.cloudflareoneagent).
3. Approve the app as a Managed Google Play app.
4. Go to **Policies** and create a new policy.
5. Go to **Android** > **App Configurations** > **Add new configuration**.
6. Find the **Cloudflare One Agent** app and set up your custom configurations.
7. Go to **Policy Targets** and associate the policy with the required target devices.
8. Save the policy.

This will push the app along with the configurations to the selected devices.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

# Partners

import { DirectoryListing } from "~/components";

Cloudflare Zero Trust integrates with [Cloudflare Technology Partner](https://www.cloudflare.com/partners/technology-partners/) tools to help you deploy the Cloudflare One Client (formerly WARP) to bigger fleets of devices. Thanks to these collaborations, you can distribute the Cloudflare One Client application to end-user devices and remotely set up advanced configurations in real time.

This is a list of Technology Partners Cloudflare Zero Trust works with:

<DirectoryListing />

If you do not see your management software listed above, we can almost certainly still work with it. Refer to our [instructions for managed deployments](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) to understand what configuration files are required.

# Intune

import { Render } from "~/components";

This guide covers how to deploy the Cloudflare One Client (formerly WARP) using Microsoft Intune.

## Windows

### Deploy the Cloudflare One Client

To deploy the Cloudflare One Client on Windows using Intune:

1. [Download the `Cloudflare_WARP_<VERSION>.msi` installer](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#windows).
2. Log in to your Microsoft Intune account.
3. Go to **Apps** > **All Apps** > **Add**.
4. In **App type**, select _Line-of-business app_ from the drop-down menu. Select **Select**.
5. Select **Select app package file** and upload the `Cloudflare_WARP_<VERSION>.msi` installer you downloaded previously.
6. Select **OK**.
7. For **Run this script using the logged on credentials**, choose _No_.
8. For **Enforce script signature check**, choose _No_.
9. In the **Name** field, we recommend entering the version number of the package being uploaded.
10. In the **Publisher** field, we recommend entering `Cloudflare, Inc`.
11. In the **Command-line arguments** field, enter a valid installation command. For example:

    ```txt
    /qn ORGANIZATION="your-team-name" SUPPORT_URL="http://support.example.com"
    ```

    Refer to [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for a description of each argument. You can change these parameters at any time by pushing a new [MDM file](#update-mdm-parameters).

12. Select **Next**.
13. Add the users or groups who require the Cloudflare One Client and select **Next**.
14. Review your configuration and select **Create**.

Intune is now configured to deploy the Cloudflare One Client.

### Update MDM parameters

You can use Intune to update [MDM parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) for the Cloudflare One Client. On Windows, these parameters are stored on the local device in `C:\ProgramData\Cloudflare\mdm.xml`.

To push a new `mdm.xml` file using Intune:

1.  Log in to your Microsoft Intune account.
2.  Go to **Devices** > **Scripts and remediations**.
3.  Select the **Platform scripts** tab and select **Add**.
4.  Select **Windows 10 and later**.
5.  Enter a name for the script (for example, `Deploy Cloudflare mdm.xml`).
6.  In **PowerShell script**, upload the following `.ps1` file. Be sure to modify the XML content with your desired [parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

        ```powershell title="mdm-template.ps1"
        # Define the path to the file
        $filePath = "C:\ProgramData\Cloudflare\mdm.xml"

        # Create the XML content as a string
        $xmlContent = @"
        <dict>
        	<key>multi_user</key>
        	<true/>
        	<key>pre_login</key>
        	<dict>
        		<key>organization</key>
        		<string>mycompany</string>
        		<key>auth_client_id</key>
        		<string>88bf3b6d86161464f6509f7219099e57.access</string>
        		<key>auth_client_secret</key>
        		<string>bdd31cbc4dec990953e39163fbbb194c93313ca9f0a6e420346af9d326b1d2a5</string>
        	</dict>
        	<key>configs</key>
        	<array>
        		<dict>
        			<key>organization</key>
        			<string>mycompany</string>
        			<key>display_name</key>
        			<string>Production environment</string>
        		</dict>
        		<dict>
        			<key>organization</key>
        			<string>test-org</string>
        			<key>display_name</key>
        			<string>Test environment</string>
        		</dict>
        	</array>
        </dict>
        "@

        # Ensure the directory exists
        $directory = Split-Path $filePath -parent
        if (-not (Test-Path $directory)) {
        	New-Item -ItemType Directory -Path $directory | Out-Null
        }

        # Write the XML content to the file
        try {
        	$xmlContent | Out-File -Encoding UTF8 -FilePath $filePath
        	Write-Host "mdm.xml file created successfully at: $filePath"
        }
        catch {
        	Write-Error "Failed to create mdm.xml file: $_"
        }
        ```

7.  In **Assignments**, select the Windows devices that should receive the new `mdm.xml` file.
8.  To deploy the script, select **Add**.

Intune will now execute the Powershell script on the target devices and overwrite the previous `mdm.xml` file. Once the new `mdm.xml` file is created, the Cloudflare One Client will immediately start using the new configuration. <Render file="warp/connectivity-status-check" product="cloudflare-one" />

If you prefer to use Intune's Win32 App tool to run the Powershell script, refer to the [Intune documentation](https://learn.microsoft.com/en-us/mem/intune/apps/apps-win32-app-management).

## macOS

The following steps outline deploying the Cloudflare One Client on macOS using Intune.

<Render file="warp/intune-lob-caution" product="cloudflare-one" />

### Prerequisites

- A [Microsoft Intune account](https://login.microsoftonline.com/).
- A Cloudflare account that has a [Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization).
- macOS devices enrolled in Intune.

### Deployment order

:::tip[Best practice]
Deploy configuration profiles (steps 1, 2, and 3) before the Cloudflare One Client application (step 4) itself. This order ensures that when the Cloudflare One Client installs, it already has the required permissions and certificates, avoiding failed installations.
:::

- Upload user-side certificate.
- Allow system extensions (bundle ID and team identifier policy).
- Upload MobileConfig (custom configuration policy).
- Upload and assign the Cloudflare One Client `.pkg` (application policy).

### 1. Upload user-side certificate

#### 1.1 Download user-side certificate

<Render
	file="intune-download-certificate"
	product="cloudflare-one"
	params={{ os: "macOS" }}
/>

#### 1.2 Upload user-side certificate to Intune

1. In the [Microsoft Intune admin center](https://intune.microsoft.com), go to **Devices** > select **macOS**.

   ![Intune admin console where you select macOS before creating a policy](~/assets/images/cloudflare-one/connections/intune/devices-macos.png)

2. Under **Manage devices**, select **Configuration**.

   ![Intune admin console where you will create a new policy](~/assets/images/cloudflare-one/connections/intune/manage-devices-configuration.png)

3. Select **Create** > **New Policy**.

4. For **Profile Type**, select _Templates_ > select **Trusted certificate** as the Template name > select **Create**.

5. In **Basics**, input the necessary field(s) and give your policy a name like `Cloudflare certificate` > select **Next**.

6. For **Deployment Channel**, select **Device Channel**.

7. Upload your file (Intune may request `.cer` format, though `.crt` files are also accepted) > select **Next**.

8. In **Assignments**, select an option (for example, **Add all devices** or **Add all users**) that is valid for your scope. This will be the same scope for all steps. Select **Next**.

9. Review your configuration in **Review + create** and select **Create**.

Sharing this certificate with Intune automates the installation of this certificate on your user devices, creating trust between browsers on a user's device and Cloudflare.

### 2. Upload `MobileConfig` configuration

1. Open a text editor and paste in the following `.mobileconfig` template:

   ```xml
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   <plist version="1.0">
   		<dict>
   				<key>PayloadDisplayName</key>
   				<string>Cloudflare WARP</string>
   				<key>PayloadIdentifier</key>
   				<string>cloudflare_warp</string>
   				<key>PayloadOrganization</key>
   				<string>Cloudflare, Ltd.</string>
   				<key>PayloadRemovalDisallowed</key>
   				<false/>
   				<key>PayloadType</key>
   				<string>Configuration</string>
   				<key>PayloadScope</key>
   				<string>System</string>
   				<key>PayloadUUID</key>
   				<string>YOUR_PAYLOAD_UUID_HERE</string>
   				<key>PayloadVersion</key>
   				<integer>1</integer>
   				<key>PayloadContent</key>
   				<array>
   						<dict>
   								<key>organization</key>
   								<string>YOUR_TEAM_NAME_HERE</string>
   								<key>auto_connect</key>
   								<integer>120</integer>
   								<key>onboarding</key>
   								<false/>
   								<key>PayloadDisplayName</key>
   								<string>Warp Configuration</string>
   								<key>PayloadIdentifier</key>
   								<string>com.cloudflare.warp.YOUR_PAYLOAD_UUID_HERE</string>
   								<key>PayloadOrganization</key>
   								<string>Cloudflare Ltd.</string>
   								<key>PayloadType</key>
   								<string>com.cloudflare.warp</string>
   								<key>PayloadUUID</key>
   								<string>YOUR_PAYLOAD_UUID_HERE</string>
   								<key>PayloadVersion</key>
   								<integer>1</integer>
   						</dict>
   				</array>
   		</dict>
   </plist>
   ```

2. Open your macOS Terminal and run `uuidgen`. This will generate a value for `PayloadUUID`. Use this value to replace the default value (`YOUR_PAYLOAD_UUID_HERE`) used in the template (three locations total).

3. Update your organization's string (`YOUR_TEAM_NAME_HERE`) with your [team name](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name).

4. Modify the file with your desired [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

   ```xml
   <array>
   	<dict>
   			<key>organization</key>
   			<string>YOUR_TEAM_NAME_HERE</string>
   			// add desired deployment parameters here
   ```

   :::tip[Best practice]
   Start by deploying the template in its default, minimal form. This helps you verify a successful deployment before adding custom parameters.
   :::

5. In the [Microsoft Intune admin center](https://intune.microsoft.com), go to **Devices** > **macOS**.

6. Under **Manage devices**, select **Configuration**.

7. Select **Create** > **New Policy**.

8. For **Profile Type**, select _Templates_ > select **Custom** as the **Template name** > select **Create**.

9. In **Basics**, input the necessary field(s) > select **Next**.

10. In **Custom configuration profile name**, input a name.

11. For **Deployment Channel**, select **Device Channel**.

12. Under **Configuration profile file**, upload the `.mobileconfig` file that you created in your text editor in step 1 > select **Next**.

13. In **Assignments**, select an option (for example, **Add all devices** or **Add all users**) that is valid for your scope. This will be the same scope for all steps.

14. Review your configuration and create your policy.

By completing this step, you preconfigure the Cloudflare One Client with your team settings so it connects automatically upon installation.

### 3. Upload Cloudflare One Client `.pkg`

:::tip[Best practice]
Complete Step 4 at least one hour after steps 1, 2, and 3 so clients have enough time to check in and update their device configurations.
:::

1. [Download the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos) in `.pkg` format.

   :::note[Repeat this step to update the Cloudflare One Client when a new release is available]
   Every time a new Cloudflare One Client version is released, you must repeat this process and get a new `.pkg` file for the new version.
   :::

2. Log in to the [Microsoft Intune admin center](https://intune.microsoft.com), and go to **Apps** > **macOS**.
3. Select **Create**.
4. For **App type**, select _macOS app (PKG)_.
5. In **App information**, select the `.pkg` file you downloaded and input required details. Enter `Cloudflare` as the Publisher.
6. In **Requirements**, refer to the OS versions listed in [stable releases for macOS](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos) and find what matches for you.
7. In **Detection rules**, note that the Cloudflare One Client package will have filled in the App bundle ID and App version.
8. In **Assignments**, select an option (for example, **Add all devices** or **Add all users**) that is valid for your scope. Select **Next**.
9. Review your configuration in **Review + create** and select **Create**.

By completing this step, you deliver the Cloudflare One Client to targeted macOS devices, either automatically (assignment scope set as **Required**) or on-demand (assignment scope as **Available**) through your company portal.

:::caution
On versions 2026.3.566.1 and later, Intune may repeatedly reinstall the Cloudflare One Client after you upload the `.pkg`, restarting the client and dropping the WARP connection. This happens because Intune adds the client's embedded framework bundles to the app's **Included apps** detection list, but Intune cannot detect those frameworks as installed applications on their own. To fix this, remove every entry except `com.cloudflare.1dot1dot1dot1.macos` from the **Included apps** list, set **Ignore app version** to **Yes**, and reapply the change each time you upload a new package. Refer to [Repeated reinstalls on macOS with Microsoft Intune](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/known-limitations/#repeated-reinstalls-on-macos-with-microsoft-intune).
:::

## iOS

The following steps outline how to deploy the Cloudflare One Agent (Cloudflare One Client) on iOS using Microsoft Intune and preconfigure it with MDM parameters.

### Prerequisites

- A [Microsoft Intune account](https://intune.microsoft.com)
- A Cloudflare account that has a [Zero Trust organization](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name)
- iOS/iPadOS devices enrolled in Intune
- [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) enabled in Cloudflare Gateway (if you plan to inspect HTTPS traffic)

### 1. Upload user-side certificate

#### 1.1 Download user-side certificate

<Render
	file="intune-download-certificate"
	product="cloudflare-one"
	params={{ os: "iOS" }}
/>

#### 1.2 Upload user-side certificate to Intune

1. In the [Microsoft Intune admin center](https://intune.microsoft.com), go to **Devices** > select **iOS/iPadOS**.

   ![Intune admin console where you select iOS/iPadOS before creating a policy](~/assets/images/cloudflare-one/connections/intune/devices-iOS.png)

2. Under **Manage devices**, select **Configuration**.

   ![Intune admin console where you will create a new policy](~/assets/images/cloudflare-one/connections/intune/manage-devices-configuration-iOS.png)

3. Select **Create** > **New Policy**.

4. For **Profile Type**, select _Templates_ > select **Trusted certificate** as the Template name > select **Create**.

5. In **Basics**, input the necessary field(s) and give your policy a name like `Cloudflare certificate` > select **Next**.

6. For **Deployment Channel**, select **Device Channel**.

7. Upload your file (Intune may request `.cer` format, though `.crt` files are also accepted) > select **Next**.

8. In **Assignments**, select an option (for example, **Add all devices** or **Add all users**) that is valid for your scope. This will be the same scope for all steps. Select **Next**.

9. Review your configuration in **Review + create** and select **Create**.

Sharing this certificate with Intune automates the installation of this certificate on your user devices, creating trust between browsers on a user's device and Cloudflare.

### 2. Add Cloudflare One Agent app to Intune configuration

1. In the [Microsoft Intune admin center](https://intune.microsoft.com), select **Apps** > **iOS/iPadOS**.

2. Select **Create**.

3. For App type, select _iOS store app_ > select **Select** to continue.

4. Select **Search the App Store** and search for the [Cloudflare One Agent](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#ios). After you have found the Cloudflare One Agent, select it and select **Select** to continue.

   :::caution[Add the right app]

   Make sure to add the [Cloudflare One Agent](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#ios) application. Do not add the 1.1.1.1 app.

   :::

5. The fields in **App information** will be filled in automatically. Select **Next** to continue.

6. In **Assignments**, select an option (for example, **Add all devices** or **Add all users**) that is valid for your scope. Select **Next**.

7. Review your configuration in **Review + create** and select **Create**.

By completing this step, you deliver the Cloudflare One Client to targeted iOS devices, either automatically (assignment scope set as **Required**) or on-demand (assignment scope as **Available**) through your company portal.

### 3. Configure Cloudflare One Agent app

1. In the [Microsoft Intune admin center](https://intune.microsoft.com), select **Apps** > **Manage apps** > **Configuration**.

2. Select **Create** > _Managed devices_.

3. In **Basics**, input the necessary field(s) and give your policy an easily identifiable name like `Cloudflare One Agent`. Select _iOS/iPadOS_ for Platform and target the Cloudflare One Agent app. Select **Next**.

4. In **Settings**, select _Enter XML data_ and copy and paste the following:

   ```xml
   <dict>
   	<key>organization</key>
   	<string>YOUR_TEAM_NAME_HERE</string>
   	<key>auto_connect</key>
   	<integer>1</integer>
   </dict>
   ```

   Replace `YOUR_TEAM_NAME_HERE` with your [team name](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name). Review the definitions of the above parameters in the [Parameters documentation](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

   :::tip[Successfully complete your registration]

   You should set the [`auto_connect`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#auto_connect) parameter to `1` to ensure the Cloudflare One Client auto-connects to Cloudflare. If you set this parameter to `0` or exclude it, the client will not auto-connect, and registration will not complete successfully.

   If you do not include this parameter, registration will not be complete without manual intervention by the user. Manual intervention requires opening the Cloudflare One Client application and attempting to connect.

   :::

5. In **Assignments**, select an option (for example, **Add all devices** or **Add all users**) that is valid for your scope. Select **Next**.

6. Review your configuration in **Review + create** and select **Create**.

By completing this step, you preconfigure the Cloudflare One Agent with your [Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization) and connection settings so that enrolled iOS devices automatically apply a consistent Cloudflare One Client configuration when the app installs.

### Intune configuration

Intune allows you to insert [predefined variables](https://learn.microsoft.com/en-us/mem/intune/apps/app-configuration-policies-use-ios#tokens-used-in-the-property-list) into the XML configuration file. For example, you can set the [`unique_client_id`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#unique_client_id) key to `{{deviceid}}` for a [device UUID posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/device-uuid/) deployment.

### Per-app VPN for iOS

:::note
Per-app VPN is supported on Cloudflare One Agent version `1.8` or greater for iOS.
:::

Before proceeding with per-app VPN configuration, you must make sure [Auto connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect) is disabled in Zero Trust. To disable Auto connect:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.
2. Select your device profile and select **Edit**.
3. Turn off **Auto Connect**.

To configure per-app VPN:

1. Log in to Microsoft Intune admin center for your organization.
2. Go to **Devices** > **iOS/iPadOS Devices** > **Manage Devices** > **Configuration** > select **+ Create** > **New Policy.**
3. Select _Templates_ in the **Profile Type** dropdown menu, then select **VPN** as the **Template Name** and select **Create**.
4. Give the configuration a name, and an optional description, if you desire, then select **Next**.
5. Select _Custom VPN_ from the **Connection Type** dropdown menu.
6. Expand the **Base VPN** section.
   - Give the VPN connection a name.
   - Enter "1.1.1.1" as the VPN server address (this value is not actually used.)
   - Set _Username and password_ as the **Authentication method**.
   - Enter "com.cloudflare.cloudflareoneagent" as the VPN identifier.
   - Enter any Key and Value into the custom VPN attributes (Cloudflare One does not use these but Intunes requires at least one entry.)
7. Expand the **Automatic VPN** section.
   - Select _Per-app VPN_ as the **Type of automatic VPN**.
   - Select _packet-tunnel_ as the **Provider Type**. Select **Next**.
8. Add any Groups, Users, or Devices to which you want to distribute this configuration and select **Next**.
9. Review the settings and select **Create**.
10. Go to **Apps** > **iOS/iPadOS Apps** and select **+ Add**.
11. Select _iOS store app_ from the **App Type** dropdown > **Select**.
12. Select **Search the App Store**, then search for the app whose traffic you want to go through the VPN > select the desired app > **Select**.
13. Review the selected app settings and select **Next**.
14. Select **+ Add Group** to add the group of users to which to distribute this app. Then select **None** underneath VPN.
15. Select the configuration you just created from the VPN dropdown menu and select **OK**.
16. Select **Next**, review the settings, then select **Create**.
17. Repeat steps 10-16 for each app you want to use the VPN with.

:::note

To support re-authentication, you must include a third-party browser that Cloudflare One can use to re-authenticate the user. The following third-party browsers are supported:

- Google Chrome
- Firefox
- Firefox Focus
- Microsoft Edge
- Brave
- Opera

Cloudflare One will continue to use a Safari window for initial authentication per-security best practices.
:::

:::note
Cloudflare One cannot apply split tunnel setting for a per app VPN. Included or excluded domains can be added to the Configuration Profile under Associated Domains and Excluded Domains sections respectively.
:::

## Android

To deploy the Cloudflare One Client on Android devices:

1. Log in to your Microsoft Intune account.
2. Go to **Apps** > **Android** >**Add**.
3. In **App type**, select _Managed Google Play app_.
4. Add the **Cloudflare One Agent** app from the Google Play store. Its application ID is `com.cloudflare.cloudflareoneagent`.
5. Go to **Apps** > **App Configuration policies** > **Add**.
6. Select _Managed devices_.
7. In **Name**, enter `Cloudflare One Agent`.
8. For **Platform**, select _Android Enterprise_.
9. Choose your desired **Profile Type**.
10. For **Targeted app**, select **Cloudflare One Agent**. Select **Next**.
11. For **Configuration settings format**, select _Enter JSON data_. Enter your desired [deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) in the `managedProperty` field. For example:

    ```json
    {
    	"kind": "androidenterprise#managedConfiguration",
    	"productId": "app:com.cloudflare.cloudflareoneagent",
    	"managedProperty": [
    		{
    			"key": "app_config_bundle_list",
    			"valueBundleArray": [
    				{
    					"managedProperty": [
    						{
    							"key": "organization",
    							"valueString": "your-team-name"
    						},
    						{
    							"key": "display_name",
    							"valueString": "Production environment"
    						},
    						{
    							"key": "service_mode",
    							"valueString": "warp"
    						},
    						{
    							"key": "onboarding",
    							"valueBool": false
    						},
    						{
    							"key": "support_url",
    							"valueString": "https://support.example.com/"
    						}
    					]
    				},
    				{
    					"managedProperty": [
    						{
    							"key": "organization",
    							"valueString": "test-org"
    						},
    						{
    							"key": "display_name",
    							"valueString": "Test environment"
    						}
    					]
    				}
    			]
    		}
    	]
    }
    ```

    Alternatively, if you do not want to copy and paste the JSON data, you can change **Configuration settings format** to _Use configuration designer_ and manually configure each deployment parameter.

    Once you have configured the deployment parameters, select **Next**.

12. Assign users or groups to this policy and select **Next**.
13. Save the app configuration policy.
14. Assign users or groups to the application:
    1. Go to **Apps** > **Android** > **Cloudflare One Agent** > **Manage Properties**.
    2. Select **Edit** and add users or groups.
    3. Select **Review + save** > **Save**.

Intune will now deploy the Cloudflare One Agent to user devices.

:::caution
If Cloudflare One Agent fails to register on Android with Always-On VPN enabled, review the limitation for [Always-On VPN with Lockdown Mode in Microsoft Intune](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/known-limitations/#always-on-vpn-with-lockdown-mode-in-microsoft-intune).
:::

### Per-app VPN for Android

:::note
Per-app VPN is supported on the Cloudflare One Agent app version `2.3` or greater for Android.
:::

Review the following steps to approve and deploy the Cloudflare One Agent application in Microsoft Intune and use a configuration policy to set up the per-app VPN. To use the per-app VPN, the admin must have linked the Microsoft Intune account with the Google-managed Play account. For more information, refer to [Connect your Intune account to your managed Google Play account in the Microsoft documentation](https://learn.microsoft.com/en-us/mem/intune/enrollment/connect-intune-android-enterprise).

#### Approve the Cloudflare One Agent app within Microsoft Intune

1. Log into the Microsoft Intune admin center.
2. Go to **Apps** > **All apps** > select **Add**.
3. In App type, select _Managed Google Play_.
4. Search for _Cloudflare One Agent_ > select the app > select **Sync**.
5. Once the sync is successful, admin will see the Cloudflare One Agent app within the **All apps** view in the Microsoft Intune admin center.

#### Configure your Cloudflare One Agent app policy

To configure your Cloudflare One Agent app policy:

1. In the Microsoft Intune admin center, go to **Apps** > **App configuration policies** > select **Add** > **Managed Devices**.
2. Fill out the basic details of your configuration policy:
   1. Enter the **Name** of the profile. (For example: Cloudflare One Agent - configuration policy)
   2. Select the Platform as **Android Enterprise**.
   3. Select the desired **Profile Type**. (For example: Personally-Owned Work Profile Only)
   4. Select **Cloudflare One Agent** as the **Targeted app**.
   5. Select **Next**.

3. Fill out the settings for the configuration policy.
   1. Select **Configuration setting format** as **Enter JSON data**.
   2. Enter your desired deployment parameters in the `managedProperty` field. For example:

      ```sh
      	{
      	"kind": "androidenterprise#managedConfiguration",
      	"productId": "app:com.cloudflare.cloudflareoneagent",
      	"managedProperty": [
      		{
      			"key": "app_config_bundle_list",
      			"valueBundleArray": [
      				{
      					"managedProperty": [
      						{
      							"key": "organization",
      							"valueString": "${ORGANIZATION_NAME-1}"
      						},
      						{
      							"key": "service_mode",
      							"valueString": "warp"
      						},
      						{
      							"key": "onboarding",
      							"valueBool": true
      						},
      						{
      							"key": "display_name",
      							"valueString": "${UNIQUE_DISPLAY_NAME-1}"
      						},
      						{
      							"key": "warp_tunnel_protocol",
      							"valueString": "MASQUE"
      						},
      						{
      							"key": "tunneled_apps",
      							"valueBundleArray" :[
      								{
      									"managedProperty": [
      										{
      											"key": "app_identifier",
      											"valueString": "com.android.chrome" # Application package name/unique bundle identifier for the Chrome app browser
      										},
      										{
      											"key": "is_browser",
      											"valueBool": true
      										}
      									]
      								},
      								{
      									"managedProperty": [
      										{
      											"key": "app_identifier",
      											"valueString": "com.google.android.gm" # Application package name/unique bundle identifier for the Gmail app
      										},
      										{
      											"key": "is_browser",
      											"valueBool": false # Default value is false, if a user does not define `is_browser` property our app would not treat `app_identifier` package name as a browser.
      										}
      									]
      								}
      							]
      						}
      					]
      				},
      				{
      					"managedProperty": [
      						{
      							"key": "organization",
      							"valueString": "${ORGANIZATION_NAME-1}"
      						},
      						{
      							"key": "service_mode",
      							"valueString": "warp"
      						},
      						{
      							"key": "display_name",
      							"valueString": "${UNIQUE_DISPLAY_NAME-2}"
      						},
      						{
      							"key": "warp_tunnel_protocol",
      							"valueString": "wireguard"
      						}
      					]
      				}
      			]
      		}
      	]
      }
      ```

      Refer to [Per-app VPN parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#per-app-vpn-parameters-android) to learn more about the MDM parameters introduced to support the per-app VPN for Android devices.

   3. After you have configured the deployment parameters, click **Next**.

4. Fill out the assignments for the configuration policy. The admin can `Include` or `Exclude` specific groups of users to this policy. After you finish, select **Next**.
5. Review the policy and select **Create**.

#### Assign users to the Cloudflare One Agent application

1. Go to **Apps** > **All Apps** > select **Cloudflare One Agent**.
2. Under **Manage**, select **Properties** and near **Assignments**, select **Edit**.
3. Add the groups of users in the assignments > select **Review + Save** > select **Save**.

Intune will now deploy the Cloudflare One Agent application on a user's device with the managed parameters.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

# Jamf

import { Render } from "~/components";

This guide covers how to deploy the Cloudflare One Client (formerly WARP) using Jamf.

## macOS

### Prerequisites

- A [Jamf Pro account](https://www.jamf.com/products/jamf-pro/)
- A Cloudflare account that has a [Zero Trust organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization)
- macOS devices enrolled in Jamf

### 1. Upload the Cloudflare One Client package

1. [Download](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos) the `Cloudflare_WARP.pkg` file.
2. Log in to your [Jamf](https://www.jamf.com/) account.
3. Go to **\*Settings** (gear icon).
4. Select **Computer Management** > **Packages** > **New**.
5. Upload the `Cloudflare_WARP_<VERSION>.pkg` file.
6. For **Display Name**, we recommend entering the version number of the package being uploaded.
7. Select **Save** to complete the upload.

:::note[Repeat this step to update the Cloudflare One Client when a new release is available]
Every time a new Cloudflare One Client version is released, you must repeat this process and upload a new `.pkg` file for the new version.
:::

### 2. Create a Jamf policy

1. Go to **Computers** > **Policies** > **+ New**.
2. Enter a **Display Name** such as `Cloudflare One Client`.
3. For **Triggers**, choose the events that will trigger a Cloudflare One Client deployment. We recommend selecting **Startup**, **Login**, **Enrollment Complete**, and **Recurring Check-in**.
4. Select **Packages** > **Configure**.
5. Select **Add** next to the `Cloudflare_WARP_<VERSION>.pkg` file you previously uploaded.
6. Select **Save**.

### 3. Add a Configuration Profile

1. Go to **Computers** > **Configuration Profiles** > **New**.
2. Enter a name for your new profile, such as `Cloudflare Zero Trust`.
3. Scroll through the **Options** list and select **Application & Custom Settings** > **Upload**.
4. In **Preference Domain**, enter `com.cloudflare.warp`.
5. To configure the **Property List**:
   1. [Create a `plist` file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#plist-file) with your desired deployment parameters.
   2. Upload your `plist` file to Jamf and select **Save**.
6. (Recommended) Advanced security features require deploying a [user-side certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) so that devices can establish trust with Cloudflare when their traffic is inspected. To deploy a user-side certificate using Jamf:
   1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), [generate and activate](/cloudflare-one/team-and-resources/devices/user-side-certificates/#generate-a-cloudflare-root-certificate) a Cloudflare root certificate.
   2. [Download the Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/#download-a-cloudflare-root-certificate) in `.pem` format.
   3. [Convert](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/#convert-the-certificate) the certificate to `.cer` format.
   4. In your Jamf configuration profile, scroll down the **Options** list and select **Certificate** > **Configure**.
   5. Enter a **Display name** for the certificate such as `Cloudflare root certificate`.
   6. In the **Select Certificate Option** dropdown, select _Upload_.
   7. Upload your `.cer` file and select **Save**.
7. Go to **Scope** to configure which devices in your organization will receive this profile.
8. Select **Save**.

Jamf will now deploy the Cloudflare One Client to targeted macOS devices. <Render file="warp/connectivity-status-check" product="cloudflare-one" />

## iOS

The Cloudflare One Agent allows for an automated install via Jamf.

### Prerequisites

Create an [XML file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#ios) with your custom deployment preferences.

### Configure Jamf for iOS

1. Log in to your [Jamf](https://www.jamf.com/) account.
2. Go to **Devices** > **Mobile Device Apps** > **+ New**.
3. Select _App store app or apps purchased in volume_ and select **Next**.
4. In the search box, enter `Cloudflare One Agent`. Select **Next**.
5. In the row for _Cloudflare One Agent by Cloudflare Inc._, select **Add**. To verify that it is the correct application, view it in the [App Store](https://apps.apple.com/us/app/cloudflare-one-agent/id6443476492).
6. Go to **Scope** and specify the devices in your organization that will receive the application.
7. Go to **App Configuration** and copy/paste your XML file.
8. Select **Save**.

Jamf is now configured to deploy the Cloudflare One Agent.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

### Per-app VPN

:::note
Per-app VPN is supported on Cloudflare One Agent version `1.8` or greater for iOS.
:::

Before proceeding with per-app VPN configuration, you must make sure [Auto connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect) is disabled in Zero Trust. To disable Auto connect:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Device profiles** > **General profiles**.
2. Select your device profile and select **Edit**.
3. Turn off **Auto Connect**.

To configure per-app VPN:

1. Log in to the Jamf dashboard for your organization.
2. Go to **Devices** > **Configuration Policies** > select **+ New**.
3. Under **Options**, select **VPN**. Then:
   - Give the VPN a **Connection Name**.
   - Select _Per-App VPN_ from the **VPN Type** dropdown menu.
   - Check the box for **Automatically start Per-App VPN connection**.
4. Under Per-App VPN Connection Type, set the **Connection Type** to _Custom SSL_ via the dropdown menu. Then, enter `com.cloudflare.cloudflareoneagent` as the **Identifier**, `1.1.1.1` as the **Server**, and `com.cloudflare.cloudflareoneagent.worker` as the **Provider Bundle Identifier**.
5. Set the **Provider Type** to _Packet-Tunnel_ and select the checkboxes for **Include All Networks** and **Enable VPN on Demand**.
6. Go to the **Scope** tab and add the devices that will use the Per-App VPN.
7. Save the Configuration Profile.
8. Go to **Devices** > **Mobile Device Apps** > select **+ New**.
9. As the **App Type**, select **App Store app or apps purchased in volume** and select **Next**.
10. In the search bar, enter the name of the app that you want to use the VPN for and select **Next**.

:::note
Alternatively, if you already know the **Bundle Identifier** of the app you want to go through the VPN, select **Enter Manually**.
:::

11. Find the app you are looking for in the search results and select **Add**.
12. Select your preferred **Distribution Method** and under **Per-App Networking**, select the VPN connection you just configured.
13. Repeat steps 8-12 for each app you want to use the VPN.

:::note

To support re-authentication, you must include a third-party browser that Cloudflare One can use to re-authenticate the user. The following third-party browsers are supported:

- Google Chrome
- Firefox
- Firefox Focus
- Microsoft Edge
- Brave
- Opera

Cloudflare One will continue to use a Safari window for initial authentication per-security best practices.
:::

:::note
Cloudflare One cannot apply split tunnel setting for a per app VPN. Included or excluded domains can be added to the Configuration Profile under Safelisted Domains and Blocklisted Domains sections respectively.
:::

# JumpCloud

import { Render } from "~/components";

## Windows

1. Log in to the [JumpCloud Admin Portal](https://console.jumpcloud.com).

2. Go to **Device Management** > **Software Management**.

3. Select the **Windows** tab, then select **(+)**.

   ![Configuring the Cloudflare One Client in the JumpCloud Windows tab](~/assets/images/cloudflare-one/connections/jumpcloud.png)

   _Note: Labels in this image may reflect a previous product name._

4. In the **Software Name** field, enter a unique display name.

5. In the **Package ID** field, enter `warp`.

6. Select **Install this software**.

7. (Optional) Select **Keep software package up to date** to automatically update this app as updates become available.

8. (Optional) Select **Allow end users to delay updates for up to one week** to avoid updates during a busy time.

9. Select **save**.

10. Select the device(s) you want to deploy the app to:
    - **Single device**: Go to the **Devices** tab and select the target device.
    - **Device group**: Go to the **Device Groups** tab and select the target device group.

11. Select **save**.

12. Select **save** again.

Verify that the Cloudflare One Client was installed by selecting the app and viewing the **Status** tab.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

## macOS

1. Log in to the [JumpCloud Admin Portal](https://console.jumpcloud.com).

2. Go to **Device Management** > **Software Management**.

3. Select the **Apple** tab, then select **(+)**.

   ![Configuring the Cloudflare One Client in the JumpCloud Apple tab](~/assets/images/cloudflare-one/connections/jumpcloud-mac.png)

   _Note: Labels in this image may reflect a previous product name._

4. In the **Software Description** field, enter a unique display name.

5. In the **Software Package URL**, enter the URL location of the `Cloudflare_WARP_<VERSION>.pkg` file. If you do not already have the installer package, [download it here](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos).

6. Select the device(s) you want to deploy the app to:
   - **Single device**: Go to the **Devices** tab and select the target device. To select all devices, select the checkbox next to **Type**.
   - **Device group**: Go to the **Device Groups** tab and select the target device group. To select all device groups, select the checkbox next to **Type**.

7. Select **save** to install the client.

Verify that the Cloudflare One Client was installed by selecting the app and viewing the **Status** tab.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

# Kandji

import { Render } from "~/components";

Kandji deploys the Cloudflare One Client (formerly WARP) as a custom app. For an overview of how Kandji deploys custom apps, refer to their [knowledge base article](https://support.kandji.io/custom-apps-overview).

## macOS

For the simplest deployment, Kandji has created a downloadable configuration profile that enables the Cloudflare One Client's user notifications and configures its Privacy Preference Policy Control ([PPPC](https://support.kandji.io/create-a-privacy-preferences-policy-control-profile)) to have Full Disk Access.

1. Download the [custom profile](https://github.com/kandji-inc/support/blob/64e0d8c8fa393d0967d2519aea60b5c834754563/Configuration%20Profiles/cloudflare_warp.mobileconfig).

2. Add the custom profile:
   1. Go to **Library** > **Add New** > **Add Library Item** > **Custom Profile**.
   2. Select **Add & Configure**.

3. Configure the custom profile:
   1. Enter a **Name** for the custom configuration profile.
   2. Assign your custom profile to a test Blueprint.
   3. Set **Device Families** to _Mac_.
   4. Upload the `cloudflare_warp.mobileconfig` file you previously downloaded.
   5. Save the custom profile.

   ![Configuring custom profile for the Cloudflare One Client in Kandji](~/assets/images/cloudflare-one/connections/custom-profile.png)

   _Note: Labels in this image may reflect a previous product name._

4. Add a custom app:
   1. Go to **Library** > **Add New** > **Add Library Item** > **Custom App**.
   2. Select **Add & Configure**.

5. Configure the custom app:
   1. Name the custom app.

   2. Assign the custom app to the same test Blueprint used for the profile.

   3. Select **Audit and Enforce** as the installation type.

   4. Copy the **Audit and Enforce Script** [below](#audit-and-enforce-script) and paste it into the **Audit Script** text field.

   5. To enforce a minimum app version, update the **ENFORCED_VERSION** variable in the audit script with the version number the audit script should enforce (for example, `1.5.207.0`).

      If **ENFORCED_VERSION** is left blank (`""`), the audit script will not check for a version and will only check for the presence of the Cloudflare WARP.app in the Applications folder or a subfolder within **Applications**. Refer to the script comments for more details.

   6. In the **Install Details** section, select **Installer Package**.

   7. Under **Installer Package**, upload the `Cloudflare_WARP_<VERSION>.pkg` file. If you do not already have the installer package, [download it here](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#macos).

   8. Select **Save**.

To verify that the Cloudflare One Client was installed, select the app in the **Custom App** library and view its **Status** tab.

<Render file="warp/connectivity-status-check" product="cloudflare-one" />

## Audit and Enforce Script

The following audit script checks if the Cloudflare One Client is installed and optionally enforces a minimum version number.

```python
#!/bin/zsh

###################################################################################################
# Created by Matt Wilson | se@kandji.io | Kandji, Inc. | Solutions Engineering
###################################################################################################
# Created on 07/30/2021
###################################################################################################
# Software Information
###################################################################################################
# This script is designed to check if an application is present. If the app is present, the
# script will check to see if a minimum version is being enforced. If a minimum app version is not
# being enforced, the script will only check to see if the app is installed or not.
###################################################################################################
# License Information
###################################################################################################
# Copyright 2021 Kandji, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this
# software and associated documentation files (the "Software"), to deal in the Software
# without restriction, including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
# to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or
# substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###################################################################################################

# Script version
_VERSION="1.0.0"

###################################################################################################
###################################### VARIABLES ##################################################
###################################################################################################
# If you would like to enforce a minimum version, be sure to update the ENFORCED_VERSION variable
# with the version number that the audit script should enforce. (Example version number
# 1.5.207.0). If ENFORCED_VERSION is left blank, the audit script will not check for a version and
# will only check for the presence of the Cloudflare WARP app at the defined APP_PATH.
ENFORCED_VERSION="1.5.207.0"

###################################################################################################

# Make sure that the application matches the name of the app that will be installed.
# This script will dynamically search for the application in the Applications folder. So
# there is no need to define an application path. The app must either install in the
# Applications folder or up to 3 sub-directories deep.
#   For example Applications/<app_folder_name>/<app_name.app>
APP_NAME="Cloudflare WARP.app"

# Change the PROFILE_PAYLOAD_ID_PREFIX variable to the profile prefix you want to wait on before
# running the installer. If the profile is not found, this audit and enforce script will exit 00
# and do nothing until the next kandji agent check-in.
PROFILE_PAYLOAD_ID_PREFIX="io.kandji.cloudflare.C59FD67"

###################################################################################################
###################################### FUNCTIONS ##################################################
###################################################################################################

return_installed_app_version() {
    # Return the currently installed application version
    #
    # $1 - Is the name of the application.
    local app_name="$1"
    local installed_version="" # Initialize local variable

    # Uses the find binary to look for the app inside of the Applications directory and
    # any subdirectories up to 3 levels deep.
    local find_app="$(/usr/bin/find /Applications -maxdepth 3 -name $app_name)"
    local ret="$?"

    # Check to see if the app is installed.
    if [[ "$ret" -eq 0 ]] && [[ -d "$find_app" ]] &&
        [[ "$app_name" == "$(/usr/bin/basename $find_app)" ]]; then
        # If the previous command returns true and the returned object is a directory
        # and the app name that we are looking for is exactly equal to the app name
        # found by the find command.

        # Gets the installed app version and replaces any "-" with "."
        installed_version=$(/usr/bin/defaults read \
            "$find_app/Contents/Info.plist" CFBundleShortVersionString |
            /usr/bin/sed "s/-/./g")

    else
        installed_version="None"
    fi

    echo "$installed_version"
}

###################################################################################################
###################################### MAIN LOGIC #################################################
###################################################################################################

# All of the main logic be here ... modify at your own risk.

# The profiles variable will be set to an array of profiles that match the prefix in
# the PROFILE_PAYLOAD_ID_PREFIX variable
profiles=$(/usr/bin/profiles show | grep "$PROFILE_PAYLOAD_ID_PREFIX" | sed 's/.*\ //')

# If the PROFILE_PAYLOAD_ID_PREFIX is not found, exit 0 to wait for the next agent run.
if [[ ${#profiles[@]} -eq 0 ]]; then
    echo "no profiles with ID $PROFILE_PAYLOAD_ID_PREFIX were found ..."
    echo "Waiting until the profile is installed before proceeding ..."
    echo "Will check again at the next Kandji agent check-in ..."
    exit 0

else
    echo "Profile prefix $PROFILE_PAYLOAD_ID_PREFIX present ..."

    # Uses the find binary to look for the app inside of the Applications directory and
    # any subdirectories up to 3 levels deep.
    find_app="$(/usr/bin/find /Applications -maxdepth 3 -name $APP_NAME)"
    ret="$?"

    # Check to see if the app is installed.
    if [[ "$ret" -eq 0 ]] && [[ -d "$find_app" ]] &&
        [[ "$APP_NAME" == "$(/usr/bin/basename $find_app)" ]]; then
        # If the previous command returns true and the returned object is a directory
        # and the app name that we are looking for is exactly equal to the app name
        # found by the find command.
        echo "$find_app was found ..."

        # Check to see if an ENFORCED_VERSION is set. If not, exit 0.
        if [[ "$ENFORCED_VERSION" == "" ]]; then
            echo "A minimum enforced version is not set ..."
            exit 0
        fi

        # Get the currently install version
        # Pass the APP_NAME variable from above to the return_installed_app_version function
        # Removing the periods from the version number so that we can make a comparison.
        installed_version="$(return_installed_app_version $APP_NAME | /usr/bin/sed 's/\.//g')"

        # Removing the periods from the version number so that we can make a comparison.
        enforced_version="$(echo $ENFORCED_VERSION | /usr/bin/sed 's/\.//g')"

        # Check to see if the installed_version is less than the enforced_version. If it is then
        # exit 1 to initiate the installation process.
        if [[ "$installed_version" -lt "$enforced_version" ]]; then
            echo "Installed app version $installed_version less than enforced version $ENFORCED_VERSION"
            echo "Starting the app install process ..."
            exit 1

        else
            echo "Enforced vers: $enforced_version"
            echo "Installed app version: $installed_version"
            echo "Minimum app version enforcement met ..."
            echo "No need to run the installer ..."
            exit 0
        fi

    else
        echo "$APP_NAME was not found in the Applications folder ..."
        echo "Need to install $APP_NAME ..."
        exit 1

    fi

fi

exit 0
```

## TLS decryption

The Kandji macOS agent uses certificate pinning, which is incompatible with [Gateway TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/). If Gateway TLS decryption is [turned on](/cloudflare-one/traffic-controls/http-policies/tls-decryption/#turn-on-tls-decryption), you must create a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/common-policies/#skip-inspection-for-groups-of-applications) to exempt Kandji from SSL/TLS inspection. For more information, refer to the [Kandji documentation](https://support.kandji.io/kb/using-kandji-on-enterprise-networks#SSL/TLS-Inspection).

# Download Cloudflare One Client beta releases

import { Render, Details, LinkButton, WARPReleases } from "~/components";

Cloudflare tests new Cloudflare One Client features and improvements in an unstable beta release before adding them to the [stable release](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/). Beta releases are not recommended for production environments. To get early access to new features, download the latest beta client from the links below.

## Windows

<LinkButton href="https://downloads.cloudflareclient.com/v1/download/windows/beta">
	Download latest beta release
</LinkButton>

<Render file="warp/system-requirements/windows" product="cloudflare-one" />

<WARPReleases track="windows/beta" />

## macOS

<LinkButton href="https://downloads.cloudflareclient.com/v1/download/macos/beta">
	Download latest beta release
</LinkButton>

<Render file="warp/system-requirements/macos" product="cloudflare-one" />

<WARPReleases track="macos/beta" />

# Migrate 1.1.1.1 app

import { TabItem, Tabs } from "~/components";

Users can connect to Cloudflare Zero Trust services through an agent that runs on their device. Cloudflare previously bundled that functionality into the [WARP Client](/warp-client/), an application that also provides privacy-focused DNS and VPN services for consumers (known as 1.1.1.1 w/ WARP). Supporting both enterprise and consumer functionality in the same application allowed us to build Zero Trust upon the same foundation used by millions of consumers across the globe, but has limited the pace at which changes could be released. As a result, we are launching a dedicated Cloudflare One Agent that replaces the Cloudflare One Client for Zero Trust deployments.

The Cloudflare One Agent supports all existing Zero Trust functionality. The underlying connection technology remains the same, and improvements made to performance and reliability based on feedback from 1.1.1.1 w/ WARP users will continue to be built into the Cloudflare One Agent.

## macOS, Windows, and Linux

No action is required for desktop clients at this time. The existing Cloudflare One Client will continue to support both Zero Trust and 1.1.1.1 functionality.

## iOS and Android

Zero Trust users must migrate from the 1.1.1.1 app to the Cloudflare One Agent app. Cloudflare is no longer supporting customers using the 1.1.1.1 app for Zero Trust features.

Organizations can migrate their teams with minimal disruption in one of two modes: [manually](#migrate-manual-deployments) or via a [managed endpoint solution](#migrate-managed-deployments).

### Migrate manual deployments

If you downloaded and installed the 1.1.1.1 app manually, here are the recommended migration steps:

<Tabs> <TabItem label="android">

1. Update the **1.1.1.1** app to version 6.29 or above. The update ensures that 1.1.1.1 can [co-exist](#what-to-do-with-the-old-app) with the new Cloudflare One Agent app.

2. If you have enabled [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), ensure that you have a [Do Not Inspect policy](/cloudflare-one/traffic-controls/get-started/http/) in place for the following applications:
   - _Google Services (Do Not Inspect)_
   - _Google Play Store (Do Not Inspect)_
   - _Google (Do Not Inspect)_
   - _Google Drive (Do Not Inspect)_
   - _Google Chat (Do Not Inspect)_
   - _Google Meet (Do Not Inspect)_

   This prevents certificate pinning issues when performing the Android migration.

3. Follow [these instructions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/#ios-android-and-chromeos) to install and enroll the Cloudflare One Agent.

</TabItem> <TabItem label="ios">

1. Update the **1.1.1.1** app to version 6.22 or above. The update ensures that 1.1.1.1 can [co-exist](#what-to-do-with-the-old-app) with the new Cloudflare One Agent app.
2. [Download](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#ios) the **Cloudflare One Agent** app.
3. Launch the Cloudflare One Agent app. All settings from 1.1.1.1 will automatically migrate over to the Cloudflare One Agent. The user does not need to reauthenticate.

</TabItem> </Tabs>

The 1.1.1.1 app will automatically log you out of Cloudflare Zero Trust and revert to consumer mode. Selecting **Login to Cloudflare Zero Trust** in 1.1.1.1 opens an onboarding screen where you can choose the Cloudflare One Agent app and log in to your Zero Trust organization.

#### What to do with the old app

While both 1.1.1.1 and Cloudflare One Agent can exist on the device, iOS and Android will only allow one of these applications to connect at a time.

To access your company's resources, you must use the Cloudflare One Agent app.

You can use the 1.1.1.1 app for personal browsing. When connected to 1.1.1.1 w/ WARP, your traffic will be encrypted and privately routed via Cloudflare's network, and your employer will not be able to see any of your browsing activity. To learn more about consumer WARP services, refer to [WARP client](/warp-client/).

If you do not wish to use the old 1.1.1.1 app for personal browsing, you may [uninstall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/uninstall/#ios-and-android) it.

### Migrate managed deployments

If you deployed the 1.1.1.1 app with an [MDM provider](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/), perform the migration as follows:

<Tabs> <TabItem label="android">

1. Using your MDM tool, update the **1.1.1.1** app to version 6.29 or above. The update ensures that 1.1.1.1 can co-exist with the new Cloudflare One Agent app during the migration.

2. Add the **Cloudflare One Agent** app from the Google Play store. Its application ID is `com.cloudflare.cloudflareoneagent`.

3. Copy your [MDM deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#android--chromeos) for the **1.1.1.1** app.

4. Paste the MDM deployment parameters into the **Cloudflare One Agent** app configuration. Make sure that you do not accidentally overwrite the application ID (`com.cloudflare.cloudflareoneagent`).

5. Using your MDM tool, install the **Cloudflare One Agent** on your devices.

6. On Android, the user will need to [re-authenticate](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/#ios-android-and-chromeos) to the new application, following the same onboarding steps they went through initially.

</TabItem> <TabItem label="ios">

1. Using your MDM tool, update the **1.1.1.1** app to version 6.22 or above. The update ensures that 1.1.1.1 can co-exist with the new Cloudflare One Agent app during the migration.

2. Add the **Cloudflare One Agent** app from the App store. Its application ID is `com.cloudflare.cloudflareoneagent`.

3. Copy your [MDM deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/#ios) for the **1.1.1.1** app.

4. Paste the MDM deployment parameters into the **Cloudflare One Agent** app configuration. Make sure that you do not accidentally overwrite the application ID (`com.cloudflare.cloudflareoneagent`).

5. Using your MDM tool, install the **Cloudflare One Agent** on your devices.

On iOS, the user does not need to re-authenticate — registration data from the 1.1.1.1 app is automatically migrated to the new Cloudflare One Agent app.

</TabItem> </Tabs>

Once users have enrolled, the migration process is complete. The 1.1.1.1 app will revert to [consumer mode](#what-to-do-with-the-old-app) and ignore the existing MDM configuration profile. If you do not wish to keep the 1.1.1.1 app, you may uninstall it and delete its MDM configuration.

### Verify migration

To check whether a user has migrated, go to **Team & Resources** > **Devices**. A device enrolled through the Cloudflare One Agent will appear as a new device with a new device ID. Their old 1.1.1.1 registration will remain as an inactive device.

# Download Cloudflare One Client stable releases

import { Render, Details, LinkButton, WARPReleases } from "~/components";

This page contains the stable Cloudflare One Client (formerly WARP) releases from the past year. We recommend using stable releases for production environments. You can download stable releases from the links below after checking requirements.

Cloudflare also offers an unstable [beta release track](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/beta-releases/) with the latest features and improvements, and an [LTS release track](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/lts-releases/) with extended support durations.

<Render file="warp/support-lifecycle" product="cloudflare-one" />

## Windows

<LinkButton href="https://downloads.cloudflareclient.com/v1/download/windows/ga">
	Download latest stable release
</LinkButton>

<Render file="warp/system-requirements/windows" product="cloudflare-one" />

<WARPReleases track="windows/ga" />

## macOS

<LinkButton href="https://downloads.cloudflareclient.com/v1/download/macos/ga">
	Download latest stable release
</LinkButton>

<Render file="warp/system-requirements/macos" product="cloudflare-one" />

<WARPReleases track="macos/ga" />

## Linux

<LinkButton href="https://pkg.cloudflareclient.com/">
	Package repository
</LinkButton>

<Render file="warp/system-requirements/linux" product="cloudflare-one" />

<WARPReleases track="linux/ga" />

## iOS

|                |         |
| -------------- | ------- |
| **OS version** | iOS 15+ |

[Download from the iOS App Store](https://apps.apple.com/us/app/cloudflare-one-agent/id6443476492) or search for "Cloudflare One Agent".

:::note[Migrate from 1.1.1.1]

The legacy iOS client, [1.1.1.1: Faster Internet](https://apps.apple.com/us/app/1-1-1-1-faster-internet/id1423538627), has been replaced by the Cloudflare One Agent. Learn more in our [migration guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/cloudflare-one-agent-migration/).

:::

## Android

|                |      |
| -------------- | ---- |
| **OS version** | 5.0+ |

[Download from the Google Play store](https://play.google.com/store/apps/details?id=com.cloudflare.cloudflareoneagent) or search for "Cloudflare One Agent".

:::note[Migrate from 1.1.1.1]

The legacy Android client, [1.1.1.1 + WARP: Safer Internet](https://play.google.com/store/apps/details?id=com.cloudflare.onedotonedotonedotone), has been replaced by the Cloudflare One Agent. Learn more in our [migration guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/cloudflare-one-agent-migration/).

:::

## ChromeOS

|                |                                     |
| -------------- | ----------------------------------- |
| **OS version** | Chromebooks manufactured after 2019 |

Chromebooks are supported by our [Android app](#android). All Chromebooks made after 2019 should fully support our Android app. If you have a Chromebook made before 2019, [refer to this list](https://www.chromium.org/chromium-os/chrome-os-systems-supporting-android-apps/) to verify that your device is supported.

# Download Cloudflare One Client LTS releases

import { Render, Details, LinkButton, WARPReleases } from "~/components";

Long-Term Support (LTS) releases are stable releases that are guaranteed to continue receiving security bug fixes for at least 12 months or 90 days after the next LTS release, whichever is greater.

<Render file="warp/support-lifecycle" product="cloudflare-one" />

:::note
No LTS releases are currently available, as Cloudflare is still rolling out our new LTS release process. When a stable release is declared an LTS release, it will be listed on this page and announced in the [Cloudflare One Client changelog](/cloudflare-one/changelog/cloudflare-one-client/).
:::

## Windows

<Render file="warp/system-requirements/windows" product="cloudflare-one" />

## macOS

<Render file="warp/system-requirements/macos" product="cloudflare-one" />

## Linux

<Render file="warp/system-requirements/linux" product="cloudflare-one" />

# Cloudflare One Client lifecycle and support policy

This page details the technical support policies for the Cloudflare One Client (formerly WARP), which operating systems and their versions are supported and for how long, and the process by which Cloudflare One Client features will be deprecated.

:::note
Both the Cloudflare One Client and the OS it is installed on must be in support as defined in this document for their combined use to be supported.
:::

## Cloudflare One Client lifecycle

### Desktop platforms

Cloudflare One Client releases for Windows, macOS, and Linux come in two forms: beta and stable. Occasionally, a stable release will be declared a Long-Term Support release (LTS).

- [Beta releases](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/beta-releases/) allow for early testing of new features before the features ship in the next stable release. Beta releases are not guaranteed to get security fixes and are not recommended for production environments.
- [Stable releases](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/), including those labeled as LTS releases, are production-ready and will include the latest features as well as functional and security bug fixes. Functional and security bugs found in non-LTS stable releases will be fixed in later stable releases; they are not backported to previous versions. Therefore, Cloudflare recommends regularly deploying the latest stable release.
- [LTS releases](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/lts-releases/) receive security bug fixes for a guaranteed minimum of 12 months. When Cloudflare publishes a new LTS release, the previous LTS release continues to receive security fixes for an additional 90 days — giving you a migration window. If the gap between two LTS releases is longer than 12 months, the migration window extends the total support period beyond 12 months. For example, if 15 months pass between two LTS releases, the earlier release receives security fixes for 18 months total (15 months until the next LTS release, plus the 90-day migration window). Cloudflare will announce an upcoming LTS release in advance so you can plan the migration.

  To ensure timely security fixes with less frequent [version testing](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/update/#test-before-updates), customers may choose to deploy only LTS releases and skip the stable releases in between. This approach is recommended for large or risk-averse organizations where stability is more important than rapid adoption of the latest features.

### Mobile platforms

Cloudflare One Client releases for iOS, iPadOS, Android, ChromeOS, and ChromeOS Flex are limited to stable releases released via the iOS App Store or Google Play Store. Therefore, security fixes will be shipped via the latest release.

### Feature deprecation policy

Major features included in a Cloudflare One Client release will not be removed while that release is still receiving security fixes.

Cloudflare will provide a minimum 90-day notice prior to removing major features in future releases. This allows customers on the stable release track to prepare for feature removal without delaying the adoption of the latest release. Customers on the LTS release track already have a 90-day overlap period between LTS releases.

### Release schedule

Cloudflare does not operate on a fixed release schedule; all releases for the Cloudflare One Client are incremental. When a new Cloudflare One Client version is released, Cloudflare will publish release notes on the [Downloads page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) and in the [changelog](/changelog/product/cloudflare-one-client/).

## Supported operating systems

The Cloudflare One Client is guaranteed to support operating systems for the primary maintenance timespan provided by the vendors or maintainers. This is to ensure that security fixes in the Cloudflare One Client are always supported by security fixes in the underlying operating system.

### Windows

The Cloudflare One Client support policy for Windows follows [Microsoft's Lifecycle Policy](https://learn.microsoft.com/en-us/lifecycle/).

- **Windows 10 and 11**: The Cloudflare One Client supports [Windows client versions](https://learn.microsoft.com/en-us/windows/release-health/supported-versions-windows-client) as long as they remain in active servicing under Microsoft's Modern Lifecycle Policy. Enterprise LTSC editions must remain under Mainstream Support.
- **Windows Server**: Cloudflare One Client support for Windows Server is pending. Once testing is complete, our policy will be to support [Windows Server LTSC releases](https://learn.microsoft.com/en-us/windows/release-health/windows-server-release-info) within their Mainstream Support window. Annual Channel releases of Windows Server will not be supported.

:::note
There is only one version of Windows 10 still supported by the Cloudflare One Client: the latest LTSC release. Standard Windows 10 releases are no longer supported. We strongly encourage customers to deploy Windows 11 for desktop devices.
:::

As of December 2025, the following versions of Windows are supported:

| Windows version                         | Supported until                                                                                                                            |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Windows&nbsp;10&nbsp;21H2&nbsp;LTSC     | January 2027                                                                                                                               |
| Windows&nbsp;11&nbsp;24H2&nbsp;LTSC     | October 2029                                                                                                                               |
| Windows&nbsp;11&nbsp;25H2               | October 2027                                                                                                                               |
| Windows&nbsp;11&nbsp;24H2               | October 2026                                                                                                                               |
| Windows&nbsp;Server&nbsp;2025&nbsp;LTSC | Pending full testing. Once complete, will be supported until November 2029.                                                                |
| Windows&nbsp;Server&nbsp;2022&nbsp;LTSC | Pending full testing. Once complete, will be supported until October 2026.                                                                 |
| Windows&nbsp;Server&nbsp;2019&nbsp;LTSC | [To be determined](#older-versions-of-windows-server) as it is currently out of mainstream Microsoft support. Usage is highly discouraged. |

#### Older versions of Windows Server

The Cloudflare One Client will support the most recent Windows Server version that has left Microsoft's Mainstream Support window, as migration of Windows Server has been observed to take significantly longer than most operating systems. Once Windows Server releases have left Mainstream Support, the Cloudflare One Client does not guarantee an amount of time it will continue to be supported, though end of support will be announced 90 days in advance. We strongly recommend migrating as quickly as possible to supported versions of Windows Server to avoid incidents caused by unfixed bugs in the operating system. In all cases, the Cloudflare One Client will not attempt to fix security issues in the underlying operating system.

#### Windows Subsystem for Linux (WSL)

Windows Subsystem for Linux v2 (WSLv2) is supported by the Cloudflare One Client that is installed on the Windows host (not a Cloudflare One Client running inside WSLv2), so long as the host version of Windows is supported.

### macOS

The Cloudflare One Client supports the current major version of macOS and the two previous major versions. Devices on a previous major version must have the latest minor and patch updates installed (for example, `26.6.1`) to receive support. This policy aligns with Apple's standard security update cycle, as well as the comparatively rapid release of new macOS versions compared to other desktop operating systems.

As of September 2026, the following major versions of macOS are supported:

| macOS version                          | Supported until               |
| -------------------------------------- | ----------------------------- |
| macOS&nbsp;27&nbsp;(Golden&nbsp;Gate) | Release of 2029 major version |
| macOS&nbsp;26&nbsp;(Tahoe)             | Release of 2028 major version |
| macOS&nbsp;15&nbsp;(Sequoia)           | Release of 2027 major version |

### Debian

The Cloudflare One Client supports all Debian releases within their [standard EOL window](https://www.debian.org/releases/). Devices must be updated to the latest point release (for example, `12.12`) to receive support.

As of December 2025, the following versions of Debian are supported:

| Debian version                 | Supported until |
| ------------------------------ | --------------- |
| Debian&nbsp;13&nbsp;(Trixie)   | June 2030       |
| Debian&nbsp;12&nbsp;(Bookworm) | June 2028       |

### Ubuntu

The Cloudflare One Client supports all Ubuntu releases within their [Standard Security Maintenance window](https://ubuntu.com/about/release-cycle). Devices must be updated to the latest point release (for example, `22.04.5`) to receive support.

As of December 2025, the following versions of Ubuntu are supported:

| Ubuntu version                                          | Supported until |
| ------------------------------------------------------- | --------------- |
| Ubuntu&nbsp;26.04&nbsp;(Resolute&nbsp;Raccoon)          | April 2031      |
| Ubuntu&nbsp;25.10&nbsp;(Questing&nbsp;Quokka)           | July 2026       |
| Ubuntu&nbsp;25.04&nbsp;(Plucky&nbsp;Puffin)             | January 2026    |
| Ubuntu&nbsp;24.04&nbsp;LTSC&nbsp;(Noble&nbsp;Numbat)    | April 2029      |
| Ubuntu&nbsp;22.04&nbsp;LTSC&nbsp;(Jammy&nbsp;Jellyfish) | April 2027      |

### Red Hat Enterprise Linux (RHEL)

Cloudflare One Client support for RHEL is pending. Once testing is complete, our policy will be to support all major versions of RHEL within their [Full Support window](https://access.redhat.com/product-life-cycles). Devices must be updated to the latest minor release (for example, `9.4`) to receive support.

As of April 2026, only RHEL 8 has completed full compatibility testing, which is now out of the Red Hat Full Support window. Starting with Cloudflare One Client version 2026.6.822.0, RHEL 9 and RHEL 10 are supported for [Cloudflare Mesh](/mesh/) functionality only.

This section will be updated as we add RHEL support to match Red Hat's support lifecycle.

| RHEL version | Supported until                                                     |
| ------------ | ------------------------------------------------------------------- |
| RHEL&nbsp;10 | Pending full testing. Supported for Cloudflare Mesh until May 2030. |
| RHEL&nbsp;9  | Pending full testing. Supported for Cloudflare Mesh until May 2027. |

### iOS and iPadOS

The Cloudflare One Client supports the current major version of iOS and iPadOS as well as the two previous major versions. Devices must have the latest available update installed (for example, `17.7.2`) to receive support. This policy aligns with Apple's standard security update cycle, as well as the comparatively rapid release of new iOS and iPadOS versions compared to other mobile operating systems.

:::note
Starting with iOS and iPadOS 27, Apple introduced [Connectivity Assist](https://support.apple.com/127686) on by default, which falls back to cellular network connectivity when Wi-Fi connectivity is failing. This means unless Connectivity Assist is actively turned off, then protections provided by Cloudflare One Client (and any other Zero Trust networking or VPN app) may be unintentionally bypassed when Wi-Fi connectivity exists but is slow. This is because Wi-Fi remains the active network connection, whereas turning Wi-Fi off entirely and using cellular network connectivity would result in expected functionality. To avoid this edge case, turn off Connectivity Assist.
:::

As of October 2026, the following versions of iOS and iPadOS are supported:

| iOS or iPadOS version            | Supported until               |
| -------------------------------- | ----------------------------- |
| iOS&nbsp;and&nbsp;iPadOS&nbsp;27 | Release of 2029 major version |
| iOS&nbsp;and&nbsp;iPadOS&nbsp;26 | Release of 2028 major version |
| iOS&nbsp;and&nbsp;iPadOS&nbsp;18 | Release of 2027 major version |
| iOS&nbsp;and&nbsp;iPadOS&nbsp;17 | November 2026 |

### Android

The Cloudflare One Client supports the current major Android release and the three previous major releases. Devices must have the latest available [Android Security Patch Level](https://source.android.com/docs/security/bulletin/asb-overview) installed to receive support.

:::note
The Cloudflare One Client is regularly tested on much older versions of Android to maximize compatibility with common devices, which tend to lag behind newer versions of Android. While security fixes are not guaranteed from a support contract perspective, the Cloudflare One Client is currently expected to generally work on Android 9 or later.
:::

As of December 2025, the following versions of Android are supported:

| Android version   | Supported until                                           |
| ----------------- | --------------------------------------------------------- |
| Android&nbsp;16   | Release of 2029 major version                             |
| Android&nbsp;15   | Release of 2028 major version                             |
| Android&nbsp;14   | Release of 2027 major version                             |
| Android&nbsp;13   | Release of 2026 major version                             |
| Android&nbsp;9-12 | Not officially supported, but expected to generally work. |

### ChromeOS

The Cloudflare One Client supports only the current ChromeOS release on the Stable, LTS, and LTSC channels.

Unlike other operating systems listed in this document, specific ChromeOS version numbers are not tracked here due to the rapid release cadence of the platform (approximately every four weeks to six months). Refer to the official [ChromeOS Release Schedule](https://chromiumdash.appspot.com/schedule) to verify the current version for your channel.

# Update the Cloudflare One Client

import { Render } from "~/components";

This guide covers best practices for updating the Cloudflare One Client (formerly WARP).

## When to update the Cloudflare One Client

There are two update strategies:

- **Always deploy the latest stable release** (recommended) — You get the newest bug fixes, performance improvements, and features.
- **Deploy only LTS releases** — If your organization has limited update cycles due to change management, QA testing, or other constraints, you can skip intermediate stable releases and deploy only the latest [LTS (Long-Term Support) release](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/lts-releases/). This strategy reduces deployment churn while still addressing security bug fixes in a timely manner.

If you run into issues that require troubleshooting or support tickets, one of the first requested actions by our support team will be to update your clients to the latest version.

<Render file="warp/support-lifecycle" product="cloudflare-one" />

:::tip
To get notified of new releases, subscribe to the [Cloudflare One Client changelog](/changelog/product/cloudflare-one-client/).
:::

## How to update the Cloudflare One Client

### Windows, macOS, and Linux

#### Managed devices

JAMF, Intune, and other MDM tools perform software updates by installing a new binary file. If you deployed the Cloudflare One Client using a [device management tool](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/), the update procedure will look exactly the same as your initial installation. To update the Cloudflare One Client, push the [latest binary file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) with the same deployment parameters. End users will not be signed out of their client, and they will not have to manually engage with the update.

#### Devices managed from the Cloudflare dashboard

On Windows and macOS devices running Cloudflare One Client version `2026.6.0` or later, you can assign a target client version to groups of devices directly from the Zero Trust dashboard. Matching devices silently install the target version on their next registration refresh, without requiring an MDM push or end-user action. For more information, refer to [Client version assignments](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/client-version-assignments/).

#### Unmanaged devices

If your users have local administration rights on their device, you can allow them to update the Cloudflare One Client on their own via the client GUI. [**Allow updates**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-updates) is usually disabled on managed devices, as it can introduce version consistency control issues if client versions are centrally managed by IT.

### iOS, Android, and ChromeOS

The iOS App Store and Google Play store can automatically push automatic updates to devices which have auto update enabled. We recommend using this method to keep the Cloudflare One Agent up-to-date on your mobile devices (managed or unmanaged).

## Test before updates

Most issues that occur after an update are due to compatibility issues between the Cloudflare One Client and third party security software. Before rolling out an update to your organization, be sure to test the new Cloudflare One Client release alongside your other software.

To deploy an update incrementally:

1. Install the latest version of the Cloudflare One Client on a single device.
2. Verify connectivity in your Gateway logs, and verify that your third party software still works as expected.
3. Deploy the update to a few more devices that represent a broad set of configurations within your organization. For example, you could include devices from a variety of departments such as Engineering, Human Resources, and IT.
4. Verify connectivity for these devices.
5. Once everything is working, deploy the update to the rest of your organization.

# Client errors

import { Details, Render, Tabs, TabItem } from "~/components";

This page lists the error codes that can appear in the Cloudflare One Client (formerly WARP) GUI. If you do not see your error below, refer to [common issues](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/) or [contact Cloudflare Support](/support/contacting-cloudflare-support/).

<Render file="warp/troubleshooting-guide-reference" product="cloudflare-one" />

<div class="medium-img">

![Example of error message in Cloudflare One Client GUI](~/assets/images/cloudflare-one/connections/warp-gui-error.png)

</div>

## CF_CAPTIVE_PORTAL_TIMED_OUT

### Symptoms

- Unable to login to a captive portal network
- No Internet connectivity

### Cause

[Captive portal detection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#captive-portal-detection) is turned on and one of the following issues occurred:

- The user did not complete the captive portal login process within the time limit set by the Cloudflare One Client.
- The captive portal redirected the user to a flow that is not yet supported by the captive portal detection feature.

### Resolution

1. Increase the [captive portal timeout](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#captive-portal-detection) to allow users more time to login.
2. If this does not resolve the issue, allow users to manually [disconnect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#lock-device-client-switch). We recommend setting an [auto connect](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#auto-connect) value so that the client turns itself back on after a few minutes.

## CF_CONNECTIVITY_FAILURE_UNKNOWN

### Symptoms

- Unable to connect the Cloudflare One Client
- No Internet connectivity
- User may be behind a captive portal

### Cause

The initial [connectivity check](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#connectivity-checks) failed for an unknown reason. Refer to [Unable to connect the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#unable-to-connect-warp) for the most common reasons why this error occurs.

### Resolution

1. Retrieve [client diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) for the device.
2. Follow the troubleshooting steps in [Unable to connect the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#unable-to-connect-warp).

## CF_DNS_LOOKUP_FAILURE

### Symptoms

- Unable to connect the Cloudflare One Client
- Unable to browse the Internet
- `nslookup` and `dig` commands fail on the device

### Cause

The Cloudflare One Client was unable to resolve hostnames via its [local DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic).

### Resolution

1. Verify that the network the user is on has DNS connectivity.
2. Verify that DNS resolution works when the Cloudflare One Client is disabled.
3. Ensure that no third-party tools are interfering with the Cloudflare One Client for control of DNS.
4. Ensure that no third-party tools are [performing TLS decryption](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#a-third-party-security-product-is-interfering-with-gateway) on traffic to the [WARP IP addresses](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/).

## CF_DNS_PROXY_FAILURE

### Symptoms

- Unable to connect the Cloudflare One Client in a [mode that enables DNS filtering](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/).

### Cause

A third-party process (usually a third-party DNS software) is bound to port `53`, which is used by the Cloudflare One Client's [local DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) to perform DNS resolution. The name of third-party process will appear in the GUI error message.

On macOS, you may see `mDNSResponder` instead of the specific application name -- `mDNSResponder` is a macOS system process that handles DNS requests on behalf of other processes. There is no known way to determine which process caused `mDNSResponder` to bind to port `53`, but the most common culprits are virtual machine software (for example, Docker and VMware Workstation) and the macOS Internet Sharing feature.

### Resolution

1. Remove or disable DNS interception in the third-party process.

<Details header="mDNSResponder">

Below is a non-exhaustive list of third-party software that are known to cause `mDNSResponder` to bind to port `53`. Rather than try to stop `mDNSResponder`, you should either configure the third-party software so that they no longer use port `53`, or temporarily disable them before connecting the Cloudflare One Client.

- **Docker**: [Turn off kernel networking for UDP](https://github.com/docker/for-mac/issues/7008#issuecomment-1746653802) in Docker. Alternatively, uncheck **Start Docker Desktop when you sign in to your computer** under [**Settings** > **General**](https://docs.docker.com/desktop/settings-and-maintenance/settings/#general). Disabling the automatic startup process will prevent Docker from binding to port `53` before the Cloudflare One Client.
- **Internet Sharing feature**: To disable Internet Sharing:
  1. On macOS, go to **System Settings** > **General** > **Sharing**.
  2. Turn off **Internet Sharing**.
- **Certain VM software (such as VMware Workstation or Parallels)**: The presence of VM software does not guarantee that it is the offending program, since compatibility with the Cloudflare One Client is highly dependent on the VM's configuration. To work around the issue, connect the Cloudflare One Client before running any VMs:
  1. Stop/quit all VMs.
  2. Connect the Cloudflare One Client.
  3. Start the VMs again.
- **Apple Container**: [Apple Container](https://github.com/apple/container) will also bind `mDNSResponder` to port `53` if started before the Cloudflare One Client (or while the Client is stopped).
  1. On macOS, run this in the terminal to temporarily stop the container system: `container system stop`
  2. Cloudflare One Client DNS mode should now start with no errors.
  3. You can then restart the container system (`container system start`) and pass explicit DNS flags when running containers.

</Details>

2. Alternatively, switch the Cloudflare One Client to [Traffic only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode) mode.

## CF_FAILED_READ_SYSTEM_DNS_CONFIG

### Symptoms

- Unable to connect the Cloudflare One Client
- Unable to browse the Internet

### Cause

The Cloudflare One Client could not read the system DNS configuration, most likely because it contains an invalid nameserver or search domain.

### Resolution

On macOS and Linux, validate that `/etc/resolv.conf` is [formatted correctly](https://man7.org/linux/man-pages/man5/resolv.conf.5.html) and check for [invalid characters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#maclinux-the-devices-etcresolvconf-file-has-an-invalid-character).

On Windows, validate that the registry entry `HKLM\System\CurrentControlSet\Services\TCPIP\Parameters\SearchList` contains only valid search domains. Examples of invalid entries include IP addresses and domains that start with a period (such as `.local`).

## CF_FAILED_TO_SET_MTLS

### Symptoms

- Unable to connect the Cloudflare One Client

### Cause

The device failed to present a [valid mTLS certificate](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-mtls-certificate) during device enrollment.

### Resolution

1. Ensure that there are no admin restrictions on certificate installation.
2. Re-install the client certificate on the device.

## CF_HAPPY_EYEBALLS_MITM_FAILURE

### Symptoms

- Unable to connect the Cloudflare One Client

### Cause

A router, firewall, antivirus software, or other third-party security product is blocking UDP on the WARP ports.

### Resolution

1. Configure the third-party security product to allow the [Cloudflare One Client ingress IPs and ports](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip).
2. Ensure that your Internet router is working properly and try rebooting the router.
3. Check that the device is not revoked by going to **Team & Resources** > **Devices**.

## CF_HOST_UNREACHABLE_CHECK

### Symptoms

- Unable to connect the Cloudflare One Client
- No Internet connectivity
- User may be behind a captive portal

### Cause

The [connectivity check](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#connectivity-checks) inside of the WARP tunnel has failed.

### Resolution

1. Check for the presence of third-party HTTP filtering software (AV, DLP, or firewall) that could be intercepting traffic to the [WARP IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall).
2. In the third-party software, bypass inspection for all IP traffic going through the Cloudflare One Client. To find out what traffic routes through the WARP tunnel, refer to [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/).

## CF_INSUFFICIENT_DISK

### Symptoms

- Unable to connect the Cloudflare One Client
- OS warns that the disk is full

### Cause

The hard drive is full or has incorrect permissions for the Cloudflare One Client to write data.

### Resolution

1. Ensure that your device meets the [HD space requirements](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) for the Cloudflare One Client.
2. Check for disk permissions that may prevent the Cloudflare One Client from using disk space.
3. Empty trash or remove large files.

## CF_INSUFFICIENT_FILE_DESCRIPTORS

### Symptoms

- Unable to connect the Cloudflare One Client
- Unable to open files on the device

### Cause

The device does not have sufficient file descriptors to create network sockets or open files.

### Resolution

Increase the file descriptor limit in your system settings.

## CF_INSUFFICIENT_MEMORY

### Symptoms

- Unable to connect the Cloudflare One Client
- Device is very slow

### Cause

The device does not have enough memory to run the Cloudflare One Client.

### Resolution

1. Ensure that your device meets the [minimum memory requirements](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) for the Cloudflare One Client.
2. List all running processes to check memory usage.

## CF_LOCAL_POLICY_FILE_FAILED_TO_PARSE

### Symptoms

- Unable to connect the Cloudflare One Client

### Cause

The Cloudflare One Client was deployed on the device using an invalid MDM configuration file.

### Resolution

1. Review the [managed deployment guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) for your operating system.
2. Locate the MDM configuration file on your device.
3. Ensure that the file is formatted correctly and only contains [accepted arguments](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/).

## CF_NO_NETWORK

### Symptoms

- Unable to connect the Cloudflare One Client
- No Internet connectivity

### Cause

The device is not connected to a Wi-Fi network or LAN that has connectivity to the Internet.

### Resolution

1. Launch the network settings panel on your device.
2. Ensure that you are connected to a valid network.
3. Check that your device is retrieving a valid IP address.
4. If this does not resolve the error, try rebooting your device or running your system's network diagnostics tool.

## CF_REGISTRATION_MISSING

### Symptoms

- Unable to connect the Cloudflare One Client

### Cause

The device is not authenticated to an [organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization) because:

- The device was revoked in Zero Trust.
- The registration was corrupted or deleted for an unknown reason.

### Resolution

<Tabs>
<TabItem label="Version 2026.2+">

1.  Launch the Cloudflare One Client.
2.  Go to **Profile** > **Account information**.
3.  Select **Re-Authenticate**.
4.  Complete the authentication steps required by your organization.
5.  If this does not resolve the error, select **Logout** and then [re-enroll your device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/). Logging out is only possible if [Allow device to leave organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-device-to-leave-organization) is enabled for your device.
6.  If the issue persists, contact your administrator for assistance.

</TabItem>

<TabItem label="Version 2026.1 and earlier">

7.  Launch the Cloudflare One Client.
8.  Select the gear icon and go to **Preferences** > **Account**.
9.  Select **Re-Authenticate Session**.
10. Complete the authentication steps required by your organization.
11. If this does not resolve the error, select **Logout from Cloudflare Zero Trust** and then log back in. Logging out is only possible if [Allow device to leave organization](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-device-to-leave-organization) is enabled for your device.
12. If the issue persists, contact your administrator for assistance.

</TabItem>
</Tabs>

### CF_REGISTRATION_MISSING (Revoked)

#### Cause

Your device was unenrolled from your company's [organization](/cloudflare-one/setup/#2-create-a-zero-trust-organization) by an administrator on your account.

#### Resolution

Contact your company or team administrator for assistance.

## CF_TLS_INTERCEPTION_BLOCKING_DOH

### Symptoms

- DNS requests fail to resolve when the Cloudflare One Client is connected.

### Cause

A third-party application or service is intercepting DNS over HTTPS traffic from the Cloudflare One Client.

### Resolution

Configure the third-party application to exempt the [WARP DoH IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#doh-ip).

## CF_TLS_INTERCEPTION_CHECK

### Symptoms

- Unable to connect the Cloudflare One Client

### Cause

A third-party security product on the device or network is performing TLS decryption on HTTPS traffic. For more information, refer to the [Troubleshooting guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#a-third-party-security-product-is-interfering-with-gateway).

### Resolution

In the third-party security product, disable HTTPS inspection and TLS decryption for the [WARP IP addresses](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/).

## Admin directed disconnect

### Symptoms

- Unable to connect the Cloudflare One Client

### Cause

The account administrator has disconnected the Cloudflare One Client for all devices registered to the account.

### Resolution

The account administrator must turn off both of the following features: - [Disconnect the Cloudflare One Client on all devices](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#disconnect-the-cloudflare-one-client-on-all-devices) - [Manage device connection using an external signal](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#manage-device-connection-using-an-external-signal)

# Common issues

import { Render, Tabs, TabItem } from "~/components";

This section covers the most common issues you might encounter as you deploy the Cloudflare One Client (formerly WARP) in your organization, or turn on new features that interact with the client.

<Render file="warp/troubleshooting-guide-reference" product="cloudflare-one" />

## Connectivity and registration

### Stuck on "Disconnected" or frequent flapping
If the Cloudflare One Client is stuck in the `Disconnected` state or frequently changes between `Connected` and `Disconnected`, this indicates that the client cannot establish a connection to Cloudflare's global network.

In your [client diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/), `daemon.log` will typically show one or more of the following errors:

- Happy Eyeball checks failing: `All Happy Eyeballs checks failed`.
- Connectivity checks timing out for `connectivity.cloudflareclient.com`.

**Common causes**:
- **Firewall blocks**: A local or network firewall is blocking the [required IP addresses](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/).
- **VPN interference**: A third-party VPN is fighting for control over the routing table or DNS. Refer to the [VPN compatibility guide](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/vpn/).
- **ISP blocks**: Your country or ISP may be explicitly blocking client traffic.

### Registration error (Authentication Expired)
When registering the client, you may see `Authentication Expired` or `Registration error. Please try again later`.

**Common causes**:
- **System clock out of sync**: Your computer system clock must be properly synced via NTP. If your clock is off by more than 20 seconds, the authentication token (JWT) will be invalid.
- **Prompt timeout**: You must complete the registration in your browser and return to the client within one minute of the prompt.

### (Linux) DNS connectivity check failed
This error often means that `systemd-resolved` is not allowing the client to resolve DNS requests. In `daemon.log`, you will see `DNS connectivity check failed to resolve host="warp-svc."`.

**Solution**:
1. Add `ResolveUnicastSingleLabel=yes` to `/etc/systemd/resolved.conf`.
2. Ensure no other DNS servers are explicitly configured in that file.
3. Restart the service: `sudo systemctl restart systemd-resolved.service`.

### (Mac/Linux) Invalid character in resolv.conf
The client cannot parse `resolv.conf` files containing invalid characters like `!@#$%^&*()<>?` in `search` directives. Remove these characters to restore service.

## Browser and certificate issues

### "Your connection is not private" or untrusted warnings
Advanced security features require the [Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) to be trusted on the device.

- **Chrome/Edge**: These browsers cache certificates. If you installed the certificate while the browser was running, you must restart the browser.
- **Root certificate expiry**: The default Cloudflare root certificate expired on February 2, 2025. If you are seeing errors, [generate and activate a new certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/#generate-a-cloudflare-root-certificate) in the dashboard.

### 2025 Certificate migration
Starting with version 2024.12.554.0, the client can automatically install new certificates as soon as they are **Available** in the dashboard. For older versions, certificates had to be marked **In-Use** first. Ensure **Install CA to system certificate store** is enabled in your [Device settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/).

## Windows-specific issues

### Windows shows "No Internet access"
This is often a cosmetic error with Windows Network Connectivity Status Indicator (NCSI). Apps like Outlook or JumpCloud may refuse to connect because of this status.

**Solution**:
Configure NCSI to detect the client's local DNS proxy and use active probing by setting these registry keys to `1`:
- `HKEY_LOCAL_MACHINE\SOFTWARE\POLICIES\MICROSOFT\Windows\NetworkConnectivityStatusIndicator\UseGlobalDNS`
- `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet\EnableActiveProbing`

### Setup Wizard ends prematurely
This usually indicates a missing dependency, such as .NET Framework `4.7.2` or later. Legacy systems (like Windows 10 Enterprise 1607) may require a manual update of the [.NET Framework Runtime](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472).

## Other environment issues

### WSL2 connectivity
If WSL2 loses connectivity, check your [split tunnel configuration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/). The IP range used by WSL to communicate with the host may be accidentally included in the tunnel. Exclude the WSL network range to restore connectivity.

### SMTP port 25 blocked
The client blocks outgoing traffic on port `25` to prevent spam and reputational harm to IP addresses used by customers. Use port `587` or `465` for encrypted email.

### Admin override codes expired
[Admin override codes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#allow-admin-override-codes) are time-sensitive and adhere to fixed-hour blocks. A code generated at 9:30 AM with a 1-hour timeout will expire at 10:00 AM because its validity is counted within the 9:00 AM-10:00 AM window.

---

## Next steps

- [Diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/) - Learn how to collect logs for support.
- [Known limitations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/known-limitations/) - Review unsupported features and environments.
- [Troubleshooting Cloudflare One](/cloudflare-one/troubleshooting/) - View troubleshooting guides for other products.

# Connectivity status

import {} from "~/components";

The Cloudflare One Client (formerly WARP) GUI displays the following status messages when transitioning from a **Disconnected** to **Connected** state. These messages indicate the connectivity stage of the Cloudflare One Client daemon as it establishes a connection from the device to Cloudflare. The [client mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) determines which messages are displayed during the connection process. If the Cloudflare One Client encounters an error while connecting, the status message will change to an [error code](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/client-errors/).

To print status messages to the console, run the `warp-cli -l status` command before connecting the client.

| Status message                       | Description                                                                                                                                                                                                                                                                        |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Verifying connection settings        | Initializes connection components based on your [device client settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/).                                                                                                                           |
| Validating network                   | Validates Ethernet and/or Wi-Fi network connectivity.                                                                                                                                                                                                                              |
| Initializing IP connection           | Checks for IPv4 and IPv6 connectivity to Cloudflare using the [Happy Eyeballs algorithm](https://datatracker.ietf.org/doc/html/rfc6555).                                                                                                                                           |
| Establishing a connection            | Connects to the endpoint discovered by Happy Eyeballs.                                                                                                                                                                                                                             |
| Building a Tunnel                    | Creates a [virtual network interface](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#virtual-interface) on the operating system for the WARP tunnel.                                                                              |
| Configuring the firewall             | Configures the system firewall to allow WARP tunnel traffic.                                                                                                                                                                                                                       |
| Setting up your routing table        | Updates the [system routing table](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#routing-table) based on your [Split Tunnel rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/). |
| Configuring your firewall rules      | Configures the [system firewall](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#system-firewall) based on your Split Tunnel rules.                                                                                                |
| Checking connectivity to DNS         | Checks connectivity to the DNS endpoint (`<account-id>.cloudflare-gateway.com`).                                                                                                                                                                                                   |
| Setting local endpoint communication | Configures local DNS proxy sockets.                                                                                                                                                                                                                                                |
| Configuring local DNS proxy          | Creates a [local DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) for DNS resolution.                                                                                                                       |
| Applying DNS settings                | Sets the local DNS proxy as the default DNS server on the device.                                                                                                                                                                                                                  |
| Configuring forward proxy            | (Only in [Local proxy mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#local-proxy-mode)) Configures the SOCKS proxy.                                                                                                                              |
| Confirming Tunnel connection         | Checks connectivity to [destinations](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#connectivity-checks) inside and outside of the WARP tunnel.                                                                                                             |
| Validating DNS configuration         | Verifies that DNS requests are answered by WARP's local DNS proxy.                                                                                                                                                                                                                 |
| Verifying SOCKS proxy configuration  | (Only in [Local proxy mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#local-proxy-mode)) Verifies the SOCKS proxy configuration.                                                                                                                  |
| Ensuring MTLS identity               | (Only in [Posture only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/device-information-only/)) Installs a client certificate for mTLS authentication.                                                                                           |

# Diagnostic logs

import { Render, Stream, TabItem, Tabs } from "~/components";

The Cloudflare One Client (formerly WARP) provides diagnostic logs that you can use to troubleshoot connectivity issues on a device.

<Stream
	id="c29964ab3dcf7c3432ebb2b4e93c3aca"
	title="Understand Cloudflare WARP diagnostic logs"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/5c4895ec-83b0-4bab-1a8e-e1adb18e8f00/public"
	showMoreVideos={false}
	chapters={{
		Introduction: "0s",
		"What are warp-diag files?": "44s",
		"How to download and navigate warp-diag files": "1m16s",
		"warp-status.txt": "2m06s",
		"warp-settings.txt": "2m29s",
		"daemon.log": "3m37s",
		"Addition tips": "8m07s",
		Conclusion: "8m43s",
	}}
/>

## macOS/Windows/Linux

### Collect logs via the CLI

<Render file="warp/warpdiag-run" product="cloudflare-one" />

### Collect logs via the dashboard

<Render file="dex/pcaps-run-availability" product="cloudflare-one" />

You can collect client diagnostic logs remotely from the Zero Trust dashboard by using Digital Experience Monitoring (DEX) [remote captures](/cloudflare-one/insights/dex/diagnostics/client-packet-capture/).

<Render file="dex/pcaps-run" product="cloudflare-one" />

#### Download remote captures

<Render file="dex/pcaps-download" product="cloudflare-one" />

#### Diagnostics analyzer (beta)

<Render file="dex/pcaps-view-warp-diag" product="cloudflare-one" />

### `warp-diag` logs

The `warp-debugging-info-<date>-<time>.zip` archive contains the following files:

| File name                                                                                           | Description                                                                                                                                                                                                                                                                                                                                            |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `.qlog`                                                                                             | QLog files used to debug MASQUE connection issues.                                                                                                                                                                                                                                                                                                     |
| `.pcap`                                                                                             | Packet capture (PCAP) files manually generated using `warp-cli debug pcap` commands, or automatically generated via [extra debug logging](#extra-debug-logging).                                                                                                                                                                                                                                                         |
| `.etl`                                                                                              | Windows-only trace files generated by [extra debug logging](#extra-debug-logging), containing packet captures, `netsh` TCPIP traces, and Windows Filtering Platform (WFP) captures.                                                                                                                                                                                                 |
| `.xml`                                                                                              | Windows-only WFP trace files generated by [extra debug logging](#extra-debug-logging).                                                                                                                                                                                                                                                                     |
| `alternate-networks-check.txt`                                                                      | Connectivity status for each [managed network](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/).                                                                                                                                                                                                                      |
| `boringtun.log`                                                                                     | Log for the WARP tunnel that serves traffic from the device to Cloudflare's global network.                                                                                                                                                                                                                                                            |
| `bound-dns-ports.txt`                                                                               | Active processes on port `53`.                                                                                                                                                                                                                                                                                                                         |
| `captive-portal-hotspot-detect.txt`                                                                 | HTTP response of `captive.apple.com`                                                                                                                                                                                                                                                                                                                   |
| `connectivity.txt`                                                                                  | DNS resolution and HTTP trace requests to [validate a successful connection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#connectivity-checks). Can be used to determine whether traffic is routing through the WARP tunnel.                                                                                                   |
| `*-checks-*.txt`                                                                                    | One-shot connectivity checks that run when [extra debug logging](#extra-debug-logging) is first enabled. These contain the same information as `connectivity.txt`.                                                                                                                                                                                                                 |
| `daemon_dns.log`                                                                                    | Contains detailed DNS logs if **Log DNS queries** is enabled in the Cloudflare One Client.                                                                                                                                                                                                                                                             |
| `daemon.log`                                                                                        | Detailed log of all actions performed by the Cloudflare One Client, including all communication between the device and Cloudflare's global network. **Note:** This is the most useful debug log.                                                                                                                                                       |
| `date.txt`                                                                                          | Date and time (UTC) when you ran the `warp-diag` command.                                                                                                                                                                                                                                                                                              |
| `dex.log`                                                                                           | Logs related to [DEX test](/cloudflare-one/insights/dex/tests/) execution.                                                                                                                                                                                                                                                                             |
| `dhcp-lease-plists.txt`                                                                             | DHCP lease information from `/var/db/dhcpclient/leases/` for each interface that has a DHCP lease.                                                                                                                                                                                                                                                     |
| `dhcp-lease.txt`                                                                                    | DHCP lease information from `ipconfig` (macOS) or `nmcli` (Linux).                                                                                                                                                                                                                                                                                     |
| `dig.txt`                                                                                           | DNS lookup query output for `cloudflare.com` and `apple.com`.                                                                                                                                                                                                                                                                                          |
| `dns_stats.log`                                                                                     | Statistics on the DNS queries received and resolved by the Cloudflare One Client, generated every two minutes.                                                                                                                                                                                                                                         |
| `dns-check.txt`                                                                                     | Verifies that the Cloudflare One Client DNS servers are set as system default. For [operating modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) where DNS filtering is enabled, this file contains the IPs of the local DNS proxy (`127.0.2.2:0`, `127.0.2.3:0`, `[fd01:db8:1111::2]:0`, and `[fd01:db8:1111::3]:0`). |
| `dynamic.log`                                                                                       | Reserved for use by Cloudflare Support.                                                                                                                                                                                                                                                                                                                |
| `etc-hosts.txt`                                                                                     | Static DNS config of device.                                                                                                                                                                                                                                                                                                                           |
| `firewall-pfctl-all.txt`                                                                            | Packet filter (pf) firewall configuration (macOS only).                                                                                                                                                                                                                                                                                                |
| `firewall-rules.txt`                                                                                | The [system firewall rules](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#system-firewall) configured by the Cloudflare One Client.                                                                                                                                                                  |
| `gui-launcher.log`                                                                                  | macOS console log showing application launch.                                                                                                                                                                                                                                                                                                          |
| `gui-log.log`                                                                                       | Log file for the GUI app that users interact with.                                                                                                                                                                                                                                                                                                     |
| `hostname.txt`                                                                                      | Name of the device.                                                                                                                                                                                                                                                                                                                                    |
| `ifconfig.txt` <br/> `ipconfig.txt`                                                                 | IP configuration of each network interface.                                                                                                                                                                                                                                                                                                            |
| `installed_applications.txt`                                                                        | List of installed applications.                                                                                                                                                                                                                                                                                                                        |
| `installed_cert.pem`                                                                                | [Root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) installed on the system.                                                                                                                                                                                                                                        |
| `installer.log` <br/> `msi-installer.log`                                                           | MSI or PKG installation log.                                                                                                                                                                                                                                                                                                                           |
| `InstallHistory.plist` <br/> `macos_installer.log`                                                  | macOS software installation logs.                                                                                                                                                                                                                                                                                                                      |
| `ipc.log`                                                                                           | Logs IPC communication between the GUI and daemon. Useful for situations where the GUI crashes or is unable to communicate with the daemon.                                                                                                                                                                                                            |
| `kernel-modules.txt`                                                                                | List of loaded kernel modules (macOS and Linux) or drivers (Windows).                                                                                                                                                                                                                                                                                  |
| `launchd-dumpstate.txt`                                                                             | Current state of the macOS `launchd system` including the loaded jobs, their status, and dependencies.                                                                                                                                                                                                                                                 |
| `local_policy.redacted.txt`<br/> `mdm.plist` <br/> `mdm.xml`                                        | [Managed deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) on the device.                                                                                                                                                                                                                  |
| `lsb-release.txt`                                                                                   | Output from the `lsb_release` command (Linux only).                                                                                                                                                                                                                                                                                                    |
| `netstat.txt` <br/> `routetable.txt`                                                                | Routing table used by the device.                                                                                                                                                                                                                                                                                                                      |
| `netstat-v6.txt`                                                                                    | IPv6 routing table (Linux only).                                                                                                                                                                                                                                                                                                                       |
| `platform.txt`                                                                                      | Operating system of the device.                                                                                                                                                                                                                                                                                                                        |
| `ps.txt` <br/> `processes.txt`                                                                      | List of all active processes on the device when `warp-diag` was run.                                                                                                                                                                                                                                                                                   |
| `resolv.conf`                                                                                       | The contents of the `/etc/resolv.conf` file on Mac/Linux, where system DNS servers are configured.                                                                                                                                                                                                                                                     |
| `route.txt`                                                                                         | Output from the `ip route get` command, used to verify that network traffic is going over the correct interface. You can optionally use the `warp-diag --enable-all-routes` flag to include tests for all IPs and domains in your Split Tunnel configuration.                                                                                          |
| `route-change.log`                                                                                  | Changes to the IP routing table.                                                                                                                                                                                                                                                                                                            |
| `scutil-dns.txt`                                                                                    | DNS configuration on macOS/Linux (available in `ipconfig.txt` on Windows).                                                                                                                                                                                                                                                                             |
| `scutil-networkinfo.txt`                                                                            | IPv4 and IPv6 network interface configuration on macOS (available in `interfaces-config.txt` on Windows).                                                                                                                                                                                                                                              |
| `scutil-proxy.txt`                                                                                  | Proxy configuration on macOS/Linux (available in `ipconfig.txt` on Windows).                                                                                                                                                                                                                                                                           |
| `snapshots-collection.log`                                                                          | Logs generated when collecting `snapshots/*.log`. Used to debug why the Cloudflare One Client failed to collect a snapshot.                                                                                                                                                                                                                            |
| `snapshots/*.log`                                                                                   | Diagnostics generated when an error occurs.                                                                                                                                                                                                                                                                                                            |
| `stats.log`                                                                                         | Uptime and throughput stats for the WARP tunnel, generated every two minutes.                                                                                                                                                                                                                                                                          |
| `sw-vers.txt`                                                                                       | Operating system of the device.                                                                                                                                                                                                                                                                                                                        |
| `sysinfo.json`                                                                                      | CPU and memory usage when `warp-diag` was run. This information is useful for determining whether slow speeds are due to heavy system load.                                                                                                                                                                                                            |
| `system-extension-diagnostics.txt`                                                                  | Status and health of loaded system extensions (macOS only).                                                                                                                                                                                                                                                                                            |
| `systeminfo.txt` <br/> `system-profile.txt`                                                         | System software overview.                                                                                                                                                                                                                                                                                                                              |
| `System.evtx`                                                                                       | Windows system event log.                                                                                                                                                                                                                                                                                                                              |
| `taskdump.log`                                                                                      | If the daemon hangs, this file will contain a dump of the currently running processes. This is helpful in debugging hangs, deadlocks, and tasks.                                                                                                                                                                                                       |
| `timezone.txt`                                                                                      | Local timezone of the device specified as a UTC offset.                                                                                                                                                                                                                                                                                                |
| `traceroute.txt`                                                                                    | Traceroute to the [Cloudflare One Client ingress IPs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/firewall/#warp-ingress-ip) showing the path from the device to Cloudflare's global network.                                                                                                                                                            |
| `*-traceroute-*.txt`                                                                                | One-shot traceroute to Tunnel edge connection IPs, Device API endpoint hostname and IPs, and DNS resolver hostname and IPs; started when [extra debug logging](#extra-debug-logging) is first enabled.                                                                                                                                                             |
| `uname.txt`                                                                                         | Linux-only system information including kernel version.                                                                                                                                                                                                                                                                                                |
| `updater/`                                                                                          | Directory containing per-attempt installer logs and downloaded package metadata for [client version assignments](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/client-version-assignments/). Useful for diagnosing failed updates triggered from the Cloudflare dashboard.                                       |
| `v4interfaces.txt` <br/> `v4subinterfaces.txt` <br/> `v6interfaces.txt` <br/> `v6subinterfaces.txt` | IPv4 and IPv6 network configuration on Windows.                                                                                                                                                                                                                                                                                                        |
| `version.txt`                                                                                       | [Cloudflare One Client version](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) installed on the device.                                                                                                                                                                                                                               |
| `warp-account.txt`                                                                                  | Cloudflare One Client device enrollment information.                                                                                                                                                                                                                                                                                                   |
| `warp-bus-metrics.txt`                                                                              | Metrics for the internal message bus framework used by the Cloudflare One Client.                                                                                                                                                                                                                                                                      |
| `warp-device-posture.txt`                                                                           | Current [device posture](/cloudflare-one/reusable-components/posture-checks/client-checks/) status.                                                                                                                                                                                                                                               |
| `warp-dex-data.txt`                                                                                 | Currently configured [DEX tests](/cloudflare-one/insights/dex/tests/) and their most recent statuses.                                                                                                                                                                                                                                                  |
| `warp-dns-fallbacks.txt`                                                                            | List of default DNS fallbacks used by the WARP DNS proxy.                                                                                                                                                                                                                                                                                              |
| `warp-dns-lock.json`                                                                                | Default DNS providers and network interface information.                                                                                                                                                                                                                                                                                               |
| `warp-dns-stats.txt`                                                                                | Summary of recent DNS queries on the device since `dns-stats.log` was generated.                                                                                                                                                                                                                                                                       |
| `warp-network.txt`                                                                                  | Network settings on the device detected by the Cloudflare One Client.                                                                                                                                                                                                                                                                                  |
| `warp-settings.txt`                                                                                 | [Cloudflare One Client settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/) applied to the device.                                                                                                                                                                                                                 |
| `warp-stats.txt`                                                                                    | Uptime and throughput of the WARP tunnel since `stats.log` was generated.                                                                                                                                                                                                                                                                              |
| `warp-status.txt`                                                                                   | Status of the Cloudflare One Client connection (`Connected` or `Disconnected`).                                                                                                                                                                                                                                                                        |
| `wdutil-info.txt`                                                                                   | Wi-Fi diagnostics (macOS only).                                                                                                                                                                                                                                                                                                                        |
| `xpc-launchd.log`                                                                                   | Most recent log file for the `launchd` process on macOS.                                                                                                                                                                                                                                                                                               |

#### Multiple versions of the same log

The `warp-debugging-info` folder may contain multiple versions of the same log, such as `daemon.log`, `daemon.1.log`, and `daemon.2.log`. Since logs can get very long, they are rotated either daily or when they exceed a certain size.

- `<logfile>.log` is the most current log. This is almost always the log you should be looking at, as it shows events that occurred on the day you ran the `warp-diag` command.
- `<logfile>.1.log` shows events from the previous day.
- `<logfile>.2.log` shows events from two days before.

:::note

In timestamped logs such as `daemon.log`, the most recent events will appear at the end of the file.
:::

### Extra debug logging

Starting with version 2026.5.0, you can enable extra debug logging with the command `warp-cli debug extra start`.
Extra debug logging times out after 10 minutes unless you manually stop it with `warp-cli debug extra stop`.

This enables a few more logging features:

- Packet captures (PCAPs) on all interfaces (on Windows, the original `.etl` file is preserved; other PCAP commands convert it to `.pcap` before removing it)
- QLog files for MASQUE tunnel connections
- DNS logs for [service modes with DNS filtering](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/)
- One-shot connectivity checks (same information as `connectivity.txt`, collected when extra debug logging starts rather than when `warp-diag` runs)
- One-shot traceroutes to:
	- DNS resolver IPs
	- DNS resolver hostname
	- Device API endpoint IPs
	- Device API endpoint hostname
	- Tunnel edge connection IPs
- On Windows, if you are an administrator:
	- [`netsh` trace](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netsh-trace) with the `Microsoft-Windows-TCPIP` provider
	- [WFP capture](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netsh-wfp)

If extra debug logging is enabled, the command `warp-cli debug extra reconnect` forces the tunnel connection to restart.
This can be useful for collecting log information if the tunnel connection fails.

:::note[Extra debug logging space requirements]

On Windows, log data created by extra debug logging may consume 600 MB of space or more over 10 minutes of collection.
The `netsh` TCPIP trace file contains the majority of this data (500 MB), and compresses well into the diagnostic archive.

Extra debug logging on other platforms generates significantly less data.

:::

### Log retention window

Each client log file (such as `connection_stats.log`, `dns_stats.log`, `daemon.log`, `boringtun.log`, `daemon_dns.log`, `dex.log` and `captive-portal.log`) is limited by size and age:

- **Maximum file size:** 10 MB
- **Maximum file age:** 24 hours
- **Maximum number of retained versions per log:** 4 (for example, `daemon.log`, `daemon.1.log`, `daemon.2.log`, and `daemon.3.log`)

Log files may include data from up to 96 hours (four days) prior to when `warp-diag` was run, but only if log activity is minimal. In environments with high logging volume (for example, repeated errors), logs may rotate more frequently, and the captured window could be much shorter (only a few hours).

Always check the timestamps at the end of each log file to verify the time range covered by the diagnostic archive.

:::note[Linux log behavior]

On Linux, the WARP daemon logs are written to the system logs by the `warp-svc` service.

If you need to adjust the maximum log level or filter what gets logged, you can configure the WARP `systemd` unit file using the [`LogLevelMax=` option](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#LogLevelMax=).

The Cloudflare One Client does not provide an official method to configure logging levels during the installation process.

:::

## iOS/Android/ChromeOS

### Collect logs

To view client logs on mobile devices:

<Tabs>
<TabItem label="iOS">

1. Open the Cloudflare One Agent app.
2. Go to **Settings** > **Advanced** > **Diagnostics**.
3. Collect extension logs:
	1. From the **Diagnostics** page, select **Console logs** > **Extension logs**.
	2. Select **Share** and choose a file sharing method.
	3. Enter a descriptive file name (such as `extension.log`) if available for your sharing method. Then share or save the file.
4. Collect application logs:
	1. From the **Diagnostics** page, select **Console logs** > **Application logs**.
	2. Select **Share** and choose a file sharing method.
	3. Enter a descriptive file name (such as `application.log`) if available for your sharing method. Then share or save the file.
5. (Optional) Collect qlogs for devices using the [MASQUE tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol):
	1. From the **Diagnostics** page, turn on **Enable qlogs**.
	2. Select **Export QLogs**.
	3. Enter a descriptive file name (such as `qlogs.zip`) if available for your sharing method. Then share or save the file.
</TabItem>
<TabItem label="Android/ChromeOS">

1. Open the Cloudflare One Agent app.
2. Go to **Settings** > **Advanced** > **Diagnostics**.
3. Scroll down to the **Debug logs** section.
4. (Optional) Turn on **Enable qlogs** for devices using the [MASQUE tunnel protocol](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#device-tunnel-protocol).
5. Select **Download logs**.
6. Share the `warp-debugging-info-<date>-<time>.zip` file via email, Google Drive, or another installed app.

</TabItem>
</Tabs>

### Mobile app logs

The following log files are available for iOS, Android, and ChromeOS devices.

#### iOS

| Name                                    | Description           |
| --------------------------------------- | ------------------------------------ |
| **Extension logs**                          | Logs from the core VPN tunnel service, including connection state changes and packet flow operations between the device and Cloudflare's network. Similar to the desktop `daemon.log` file.                   |
| **Application logs**                        | Logs from the iOS app process, including GUI events, setting changes, and API calls.                         |
| **Qlogs**                                  | QUIC protocol event logs used to debug MASQUE/HTTP3 tunnel connections. Archive contains up to 10 `.sqlog` files named by connection ID. |

#### Android/ChromeOS

The `warp-debugging-info-<date>-<time>.zip` archive contains the following files:

| Name             | Description                           |
| ---------------- | ---------------------------------------------------- |
| `unhandled_exception.log`   | Captures errors in the Android application layer that were not caught and could have crashed the app.                                |
| `console.log` | Logs from the Android application layer, including VPN service events, API calls, failed DNS queries, GUI interactions, and setting changes. |
| `native_tunnel.log`  | Logs from the core VPN tunnel service, including connection state changes and packet flow operations between the device and Cloudflare's network. Similar to the desktop `daemon.log` file.                        |
| `<connection_id>.sqlog`    | QUIC protocol event logs used to debug MASQUE/HTTP3 tunnel connections. |

# Troubleshoot the Cloudflare One Client

import { DirectoryListing } from "~/components";

Explore guides to resolve common connectivity and configuration issues with the Cloudflare One Client (formerly WARP).

<DirectoryListing />

# Known limitations

import { Render } from "~/components";

Below, you will find information on devices, software, and configurations that are incompatible with the Cloudflare One Client (formerly WARP).

<Render file="warp/troubleshooting-guide-reference" product="cloudflare-one" />

## Windows Server

The Cloudflare One Client does not run on Windows Server. Refer to the [downloads page](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) for a list of supported operating systems.

## Cloudflare One Client disconnected on Windows ARM

On Windows devices with ARM-based processors, the Cloudflare One Client can sometimes get [stuck in a disconnected state](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#unable-to-connect-warp) when you connect (such as when installing the Cloudflare One Client for the first time).

To work around this issue, you can temporarily remove the WARP network adapter:

1. Open the Cloudflare One Client GUI and disconnect.
2. In Windows, open Device Manager.
3. Select **View** > **Show hidden devices**.
4. Under **Network adapters**, find **Cloudflare WARP Interface Tunnel** and select **Uninstall device**.
5. Select **Attempt to remove the drive for this device**, then select **Uninstall**.
6. Reconnect the Cloudflare One Client.

The Cloudflare One Client will now reinstall its network adapter, and the Cloudflare One Client GUI should now show **Connected**.

## Managed network on legacy Windows Server

[Managed network detection](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/managed-networks/) will not work when the TLS certificate is served from IIS 8.5 on Windows Server 2012 R2. To work around the limitation, move the certificate to a different host.

## nslookup on Windows in DoH mode

On Windows devices in [DNS only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#dns-only-mode), `nslookup` by default sends DNS requests to the [WARP local DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) over IPv6. However, because the Cloudflare One Client uses an IPv4-mapped IPv6 address (instead of a real IPv6 address), `nslookup` will not recognize this address type and the query will fail:

```txt
C:\Users\JohnDoe>nslookup google.com
Server:  UnKnown
Address:  ::ffff:127.0.2.2

*** UnKnown can't find google.com: No response from server
```

To work around the issue, specify the IPv4 address of the [WARP local DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) in your query:

```bash
C:\Users\JohnDoe>nslookup google.com 127.0.2.2
```

Alternatively, use PowerShell:

```powershell
Resolve-DnsName -Name google.com
```

## Comcast DNS servers

Comcast DNS traffic (to the IPs below) cannot be proxied through the Cloudflare One Client. This is because Comcast rejects DNS traffic that is not sent directly from the user's device.

- IPv4 Addresses: `75.75.75.75` and `75.75.76.76`
- IPv6 Addresses: `2001:558:feed::1` and `2001:558:feed::2`

To work around the issue, you can either:

- Create a [Split Tunnel rule](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) that excludes the above IPs from the Cloudflare One Client.
- Configure your device or router to use a public DNS server such as [`1.1.1.1`](https://1.1.1.1/dns/).

## Cox DNS servers

Similar to the [Comcast DNS servers](#comcast-dns-servers) limitation listed above, Cox DNS servers will not respond to traffic from the WARP egress IPs (or any IP that is not a Cox IP). The workaround is nearly identical, except that Cox DNS servers may be specific to the individual end user. You can either:

- Create a [Split Tunnel rule](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) that excludes all Cox DNS servers. For business customers, refer to the [COX documentation](https://www.cox.com/business/support/cox-business-dns-and-mail-exchange-hosting-services.html) for the DNS server IPs. For residential customers, check your local DNS servers. The residential DNS servers typically fall under `68.105.28.0/24` and `68.105.29.0/24`.
- Configure your device or router to use a public DNS server such as [`1.1.1.1`](https://1.1.1.1/dns/).

## HP Velocity

The HP Velocity driver has a bug which will cause a blue screen error on devices running the Cloudflare One Client. HP recommends [uninstalling this driver](https://support.hp.com/gb-en/document/c06266198).

## Dell firmware version 1.35.0

For Dell devices running firmware version `1.35.0` (released 2025-07-07), regardless of operating system, Cloudflare has confirmed a bug that prevents the WARP service from starting. Cloudflare recommends users experiencing these issues upgrade their Dell device firmware to version `1.36.0` or later.

## Cisco Meraki

Cisco Meraki devices have a bug where client traffic can sometimes be identified as [`Statistical-P2P`](https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/qos_nbar/prot_lib/config_library/pp4600/nbar-prot-pack4600/s.html#wp1488575851) and de-prioritised or dropped entirely. To resolve the issue, disable `Statistical-P2P` on the Cisco Meraki device.

## Windows Teredo

The [Windows Teredo](https://learn.microsoft.com/en-us/windows/win32/teredo/about-teredo) interface conflicts with the Cloudflare One Client. Since Teredo and the Cloudflare One Client will fight for control over IPv6 traffic routing, you must disable Teredo on your Windows device. This allows the Cloudflare One Client to provide IPv6 connectivity on the device.

## Docker on Linux with bridged networking

[Docker](https://www.docker.com/products/container-runtime/) on Linux does not perform the underlying network tunnel MTU changes required by the Cloudflare One Client. This can cause connectivity issues inside of a Docker container when the Cloudflare One Client is enabled on the host machine. For example, `curl -v https://cloudflare.com > /dev/null` will fail if run from a Docker container that is using the default bridge network driver.

To work around this issue, users of the Cloudflare One Client with Docker on Linux can manually reconfigure the MTU on Docker's network interface. You can either modify `/etc/docker/daemon.json` to include:

```json
{
	"mtu": 1420
}
```

or create a Docker network with a working MTU value:

```sh
docker network create -o "com.docker.network.driver.mtu=1420" my-docker-network
```

The MTU value should be set to the MTU of your host's default interface minus 80 bytes for the WARP protocol overhead. Most MTUs are 1500, so 1420 should work for most users.

## Access Cloudflare One Client DNS from Docker

The Cloudflare One Client runs a local DNS proxy on `127.0.2.2` and `127.0.2.3`. You may need access to these addresses from within Docker containers to resolve internal-only or fallback domains. The default Docker [bridge network](https://docs.docker.com/engine/network/drivers/bridge/) copies the DNS settings from the host, but filters out loopback DNS addresses like `127.0.2.2` and `127.0.2.3`, so containers cannot use them.

To enable Cloudflare One Client DNS resolution with containers:

- Use a [custom Docker network](https://docs.docker.com/engine/network/#user-defined-networks) (recommended): Allows the Docker container to still use the bridge network driver that maintains network isolation from the host. If you are creating your own bridge network, you should also [adjust the MTU accordingly](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/known-limitations/#docker-on-linux-with-bridged-networking).
- Use [host networking](https://docs.docker.com/engine/network/drivers/host/) (not recommended): Removes the security benefits of network isolation and may lead to port conflicts.

The following example uses a special host (`connectivity-check.warp-svc`) that is only resolvable by the local DNS proxy to show the supported Docker networking modes.

```
# This host is not resolvable by default
❯ docker run --rm alpine nslookup connectivity-check.warp-svc.
Server:         8.8.8.8
Address:        8.8.8.8:53

** server can't find connectivity-check.warp-svc.: NXDOMAIN
** server can't find connectivity-check.warp-svc.: NXDOMAIN

# Create a bridge network called demo
❯ docker network create demo
e1e1943a6995a7e8c115a1c60357fe64f87a3ae90074ce6e4c3f0d2bba3fa892

# The host is resolvable by running a container under this custom network
❯ docker run --rm --net demo alpine nslookup connectivity-check.warp-svc.
Server:         127.0.0.11
Address:        127.0.0.11:53Non-authoritative answer:
Name:   connectivity-check.warp-svc
Address: ::ffff:127.0.2.2
Name:   connectivity-check.warp-svc
Address: ::ffff:127.0.2.3Non-authoritative answer:
Name:   connectivity-check.warp-svc
Address: 127.0.2.2
Name:   connectivity-check.warp-svc
Address: 127.0.2.3

# The host is also resolvable by running a container using a host network
❯ docker run --rm --net host alpine nslookup connectivity-check.warp-svc.
Server:         127.0.0.11
Address:        127.0.0.11:53Non-authoritative answer:
Name:   connectivity-check.warp-svc
Address: ::ffff:127.0.2.2
Name:   connectivity-check.warp-svc
Address: ::ffff:127.0.2.3Non-authoritative answer:
Name:   connectivity-check.warp-svc
Address: 127.0.2.2
Name:   connectivity-check.warp-svc
Address: 127.0.2.3
```

## Linux DNS domains with systemd-resolved versions earlier than 252.3

On some Linux systems with `systemd-resolved` versions earlier than `252.3`, DNS domains configured on non-WARP interfaces may not resolve as expected while the Cloudflare One Client is connected. This can affect hostnames that rely on DHCP-provided search domains or interface-specific DNS routing domains.

For example, commands that resolve the device hostname may take several seconds to start, or may print an error similar to:

```txt
sudo: unable to resolve host <HOSTNAME>: Temporary failure in name resolution
```

This happens because `systemd-resolved` versions earlier than `252.3` do not support the link-specific DNS configuration that the Cloudflare One Client uses on newer Linux distributions. On these systems, the Cloudflare One Client falls back to a global DNS configuration. As a result, DNS domains configured on other interfaces, such as cloud-provider or corporate network search domains, may not be routed or resolved consistently while WARP is connected.

To work around this issue, upgrade to a Linux distribution with `systemd-resolved` version `252.3` or later when possible. If the issue affects local hostname resolution, add the local hostname to `/etc/hosts` so that Linux resolves it before using DNS:

```sh
echo "127.0.1.1 $(hostname)" | sudo tee -a /etc/hosts
```

Before adding the entry, check whether `/etc/hosts` already contains a mapping for the hostname. If your organization manages `/etc/hosts`, contact your administrator before changing it.

## Windows App connection issue

When the Cloudflare One Client is active on a local machine, users may be unable to connect to a Windows 365 PC using the [Windows App](https://aka.ms/WindowsApp). This issue does not affect browser-based connections to Windows 365.

To resolve this, exclude the networks specified below from any relevant Cloudflare One Client [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/). The required networks are listed under the `WindowsVirtualDesktop` service tag in the [Azure IP Ranges and Service Tags - Public Cloud](https://www.microsoft.com/en-us/download/details.aspx?id=56519) resource (search for `"name": "WindowsVirtualDesktop"`).

Microsoft previously provided a [PowerShell script](https://github.com/microsoft/Windows365-PSScripts/tree/main/Windows%20365%20Gateway%20IP%20Lookup) to retrieve these networks, but it has since been deprecated. The relevant networks are now consolidated to the following subnets and should be excluded from any relevant Cloudflare One Client device profiles:

```
40.64.144.0/20
51.5.0.0/16
57.156.5.248/29
57.156.73.192/28
172.183.252.22/32
2603:1061:2010::/48
2603:1061:2011::/48
```

## Windows 10 in Microsoft 365 Cloud PC is not supported

Use of the Cloudflare One Client in a Microsoft 365 Windows 10 Cloud PC is not supported. To work around this limitation, use Windows 11.

## IPv6 DNS resolution in Traffic only mode

In [Traffic only mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-only-mode), devices using IPv6 DNS servers may experience connectivity issues if these servers are not manually excluded from the WARP tunnel.

Unlike common IPv4 DHCP configurations where DNS servers often fall within automatically excluded private address ranges, IPv6 environments typically require manual exclusion of DNS server addresses via split tunnel settings for proper operation.

If your DNS server uses an IPv6 address, you must manually exclude it using [split tunnel settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) for Traffic only mode to work properly.

## Ivanti Secure Access (formerly Pulse Secure)

The Ivanti Secure Access VPN client can conflict with the Cloudflare One Client by installing Windows Filtering Platform (WFP) rules that block outgoing traffic to the [WARP local DNS proxy](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/client-architecture/#dns-traffic) on port `53`. This results in `Host not found` errors or a total loss of Internet connectivity even when Ivanti is disabled or disconnected.

To resolve this, contact Ivanti support or your administrator to modify or remove the specific firewall rules blocking traffic to `127.0.2.2`.

## Always-On VPN with Lockdown Mode in Microsoft Intune

If you are using Microsoft Intune to deploy the Cloudflare One Client on Android with [Always-On VPN and Lockdown mode enabled](https://learn.microsoft.com/en-us/intune/intune-service/configuration/device-restrictions-android-for-work?tabs=aecorporate#fully-managed-dedicated-and-corporate-owned-work-profile-devices-5), the Cloudflare One agent may fail to register. This is because Lockdown mode prevents the Cloudflare One agent from accessing the underlying network to complete the registration process.

This is a known limitation of the Android OS, which has been reported to Google. You can track the status of the feature request on the [Google Issue Tracker](https://issuetracker.google.com/issues/238109298?pli=1).

To work around this issue, you can disable Lockdown mode while keeping Always-On VPN enabled:

1.  In your Intune profile, disable **Lockdown mode** while keeping **Always-On VPN** enabled.
2.  Use the [`auto_connect`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#auto_connect) and [`switch_locked`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#switch_locked) parameters in the managed configuration for seamless connectivity.
3.  Instruct users to launch the Cloudflare One agent app and complete the one-time registration manually.

## Repeated reinstalls on macOS with Microsoft Intune

When you deploy the Cloudflare One Client `.pkg` to macOS with Microsoft Intune, Intune may repeatedly reinstall the package. Each reinstall restarts the client and briefly drops the WARP connection, and you may see the client reinstalled several times within a single Intune evaluation cycle.

This affects Cloudflare One Client versions from 2026.3.566.1 — the first release with the redesigned client interface (the new client UI). The redesigned client bundles several embedded frameworks inside the application, which is what triggers the reinstall loop. This is an Intune-side detection configuration issue rather than a client defect, so apply the following workaround on any affected version.

This happens because of how Intune detects whether a macOS `.pkg` app is already installed. When you upload the package, Intune automatically populates the app's **Included apps** detection list with every bundle it finds inside the package — including the roughly two dozen embedded framework bundles that ship inside the client (for example `io.flutter.flutter-macos`, `org.sparkle-project.Sparkle`, and several `org.cocoapods.*` bundles). Intune only considers the app installed when _every_ entry in the **Included apps** list is detected on the device.

These embedded frameworks are not independently installable apps. They live inside the client's application bundle and have no installer receipt or standalone presence, so Intune cannot detect them as installed applications on their own. As a result, Intune never detects them, always concludes the app is not fully installed, and reinstalls the package — cycling through each undetectable framework. Intune detects the client's own bundle identifier (`com.cloudflare.1dot1dot1dot1.macos`) correctly the entire time. The extra framework entries are what fail detection and drive the reinstall loop. Cloudflare has reported this behavior to Microsoft.

To work around this issue, remove the embedded framework bundles from the **Included apps** list so that only the client's own bundle identifier remains, and turn off version-based detection:

1. In the [Microsoft Intune admin center](https://intune.microsoft.com), go to **Apps** > **macOS** and select the Cloudflare One Client app.
2. Go to **Properties** and, next to **Detection rules**, select **Edit**.
3. In the **Included apps** list, remove every entry except `com.cloudflare.1dot1dot1dot1.macos`.
4. Set **Ignore app version** to **Yes** so that detection succeeds regardless of the deployed client version.
5. Select **Review + save** to apply the change.

![The Included apps detection list in Intune with only the Cloudflare One Client bundle identifier and Ignore app version set to Yes](~/assets/images/cloudflare-one/connections/intune/intune-included-apps-detection.png)

:::note
Intune repopulates the **Included apps** list with the full set of embedded bundles every time you upload the `.pkg`, so reapply this workaround on each upload.
:::

## Windows 11 24H2 performance issues

For Windows 11 24H2 users, Microsoft has confirmed a regression that may lead to performance issues like mouse lag, audio cracking, or other slowdowns. Cloudflare recommends users experiencing these issues upgrade to a minimum [Windows 11 24H2 version KB5062553](https://support.microsoft.com/en-us/topic/july-8-2025-kb5062553-os-build-26100-4652-523e69cb-051b-43c6-8376-6a76d6caeefd) or higher for resolution.

## False positive malware warning on Windows with KB5055523

Windows devices with KB5055523 installed may receive a warning about `Win32/ClickFix.ABA` being present in the installer. To resolve this false positive, update Microsoft Security Intelligence to version [1.429.19.0](https://www.microsoft.com/en-us/wdsi/definitions/antimalware-definition-release-notes?requestVersion=1.429.19.0) or later.

# Cloudflare One Client troubleshooting guide

import { Render } from "~/components";

<Render file="troubleshooting/warp-client" product="cloudflare-one" />

# Install certificate using the Cloudflare One Client

import { Details, Render } from "~/components";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| All modes                                                                                | All plans                                                     |

| System              | Availability | Minimum client version |
| ------------------- | ------------ | -------------------- |
| Windows             | ✅           | 2024.12.554.0        |
| macOS               | ✅           | 2024.12.554.0        |
| Linux <sup>\*</sup> | ✅           | 2024.12.554.0        |
| iOS                 | ❌           |                      |
| Android             | ❌           |                      |
| ChromeOS            | ❌           |                      |

<sup>*</sup> Only supported on Debian-based systems.
</Details>

The [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) can automatically install a Cloudflare certificate or [custom root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/) on Windows, macOS, and Debian/Ubuntu Linux devices. On mobile devices and Red Hat-based systems, you will need to [install the certificate manually](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/).

The certificate is required if you want to [apply HTTP policies to encrypted websites](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), display custom [block pages](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/), and more.

## Install a certificate using the Cloudflare One Client

To configure the Cloudflare One Client to install a root certificate on your organization's devices:

1. (Optional) [Upload](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/) a custom root certificate to Cloudflare.
2. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices** > **Management**.
3. Under **Global Cloudflare One Client settings**, turn on [**Install CA to system certificate store**](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/settings/#install-ca-to-system-certificate-store).
4. [Install](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) the Cloudflare One Client on the device.
5. [Enroll the device](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) in your Zero Trust organization.
6. (Optional) If the device is running macOS Big Sur or newer, [manually trust the certificate](#manually-trust-the-certificate).

The Cloudflare One Client will now download any [certificates set to **Available**](/cloudflare-one/team-and-resources/devices/user-side-certificates/#activate-a-root-certificate). After download, the Cloudflare One Client will add the certificates to the device's system certificate store in `installed_certs/<certificate_id>.pem` and append the contents to the `installed_cert.pem` file. If you have any scripts using `installed_cert.pem`, Cloudflare recommends you set them to use the individual files in the `installed_certs/` directory instead. `installed_certs.pem` will be deprecated by 2025-06-31.

:::note

<Render file="warp/client-notification-lag" product="cloudflare-one" />
:::

The Cloudflare One Client does not install certificates to individual applications. You will need to [manually add certificates](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/#add-the-certificate-to-applications) to applications that rely on their own certificate store instead of the system certificate store.

## Access the installed certificate

After installing the certificate using the Cloudflare One Client, you can verify successful installation by accessing the device's system certificate store.

### macOS

To access the installed certificate in macOS:

1. Open Keychain Access.
2. In **System Keychains**, go to **System** > **Certificates**.
3. Open your certificate. The default Cloudflare certificate name is **Gateway CA - Cloudflare Managed G1**.
4. If the certificate is trusted by all users, Keychain Access will display **This certificate is marked as trusted for all users**.

The Cloudflare One Client will also place the certificate in `/Library/Application Support/Cloudflare/installed_cert.pem` for reference by scripts or tools.

#### Manually trust the certificate

macOS Big Sur and newer do not allow the Cloudflare One Client to automatically trust the certificate. To manually trust the certificate:

1. In Keychain Access, [find and open the certificate](#macos).
2. Open **Trust**.
3. Set **When using this certificate** to _Always Trust_.
4. (Optional) Restart the device to reset connections to Zero Trust.

Alternatively, you can configure your mobile device management (MDM) to automatically trust the certificate on all of your organization's devices.

### Windows

To access the installed certificate in Windows:

1. Open the Start menu and select **Run**.
2. Enter `certlm.msc`.
3. Go to **Trusted Root Certification Authority** > **Certificates**. The default Cloudflare certificate name is **Gateway CA - Cloudflare Managed G1**.

The Cloudflare One Client will also place the certificate in `%PROGRAMDATA%\Cloudflare\installed_cert.pem` for reference by scripts or tools.

### Debian-based Linux distributions

On Debian-based Linux distributions, the certificate is stored in `/usr/local/share/ca-certificates`. The default installed Cloudflare certificate name is `managed-warp.pem`. The Cloudflare One Client will create a symbolic link named `managed-warp.crt` to use as its root certificate. If your system is not using `managed-warp.crt`, run the following commands to update the system store:

1. Update your list of custom CA certificates.

   ```sh
   sudo update-ca-certificates
   ```

2. Go to the system certificate store.

   ```sh
   cd /usr/local/share/ca-certificates
   ```

3. Verify your system has both the `managed-warp.pem` file and the `managed-warp.crt` symbolic link. For example:

   ```sh
   ls -l
   ```

   ```sh output
   lrwxrwxrwx 1 root root   49 Jan  3 21:46 managed-warp.crt -> /usr/local/share/ca-certificates/managed-warp.pem
   -rw-r--r-- 1 root root 1139 Jan  3 21:46 managed-warp.pem
   ```

The Cloudflare One Client will also place the certificate in `/var/lib/cloudflare-warp/installed_cert.pem` for reference by scripts or tools.

## Uninstall the certificate

If the certificate was installed by the Cloudflare One Client, it is automatically removed when you turn on another certificate for inspection in Cloudflare One, turn off **Install CA to system certificate store**, or [uninstall the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/uninstall/). The Cloudflare One Client does not remove certificates that were installed manually (for example, certificates added to third-party applications).

To manually remove the certificate, refer to the instructions supplied by your operating system or the third-party application.

# Deploy custom certificate

import { Render, Tabs, TabItem, APIRequest, Details } from "~/components";

:::note
Only available on Enterprise plans.
:::

Enterprise customers who do not wish to install a [Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/) have the option to upload their own root certificate to Cloudflare. This feature is sometimes referred to as Bring Your Own Public Key Infrastructure (BYOPKI). Gateway will use your uploaded certificate to encrypt all sessions between the end user and Gateway, enabling all HTTPS inspection features that previously required a Cloudflare certificate. You can upload multiple certificates to your account, but only one can be active at any given time. You also need to upload a private key to intercept domains with JIT certificates and to enable the [block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/).

You can upload either a root certificate or a full certificate chain (root certificate plus intermediate certificates). Uploading a certificate chain allows end-user devices to only install the root certificate, which can simplify certificate management for larger enterprises.

You can upload up to five custom root certificates. If your organization requires more than five certificates, contact your account team.

:::caution
Custom certificates are limited to use between your users and the Gateway proxy. Gateway connects to origin servers using publicly trusted certificates, similar to how a browser validates secure websites.

If your users need to connect to self-signed origin servers, create an HTTP Allow policy for the origin server with the [untrusted certificate action](/cloudflare-one/traffic-controls/http-policies/#untrusted-certificates) set to _Pass through_.
:::

## Generate a custom root CA

1. Open a terminal.

2. (Optional) Create a directory for the root CA and change into it.

   ```sh
   mkdir -p /root/customca
   cd /root/customca
   ```

   You can generate the certificate files in any directory. This step keeps things organized. If you skip it, files will be created in your current working directory.

3. Generate a private key for the root CA.

   ```sh
   openssl genrsa -out <CUSTOM-ROOT-PRIVATE-KEY>.pem 2048
   ```

   The `2048` value specifies the RSA key size in bits. You can use `4096` for stronger security at the cost of slightly slower TLS handshakes.

	 :::caution
	 Keep the private key secure — if it is compromised, an attacker could issue trusted certificates on your behalf.
	 :::

4. Generate a self-signed root certificate.

   ```sh
   openssl req -x509 -sha256 -new -nodes \
     -key <CUSTOM-ROOT-PRIVATE-KEY>.pem \
     -days 365 \
     -out <CUSTOM-ROOT-CERT>.pem \
     -addext "basicConstraints=critical,CA:TRUE" \
     -addext "keyUsage=critical,keyCertSign,cRLSign"
   ```

   The `-addext` flags add the `basicConstraints` and `keyUsage` extensions required by [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) for CA certificates. Without them, some TLS clients may reject certificates signed by your custom CA. In particular, Python 3.13 and later enforce strict RFC 5280 compliance by default (`ssl.VERIFY_X509_STRICT`), causing HTTPS requests to fail for devices using the Cloudflare One Client when the uploaded CA does not include these extensions.

   The `-days 365` value controls certificate expiry. A shorter duration reduces risk if the key is compromised, but requires more frequent rotation. Rotating a deployed BYOPKI certificate is a disruptive operation, so choose an expiry that balances security with operational overhead.

   <Details header="Error: `Unknown cipher or option -addext`">
   If your system runs OpenSSL versions older than 1.1.1, the `-addext` flag is not available. Use a config file instead:

   ```sh
   openssl req -x509 -sha256 -new -nodes \
     -key <CUSTOM-ROOT-PRIVATE-KEY>.pem \
     -days 365 \
     -out <CUSTOM-ROOT-CERT>.pem \
     -config <(printf '[req]\ndistinguished_name=dn\n[dn]\n[v3_ca]\nbasicConstraints=critical,CA:TRUE\nkeyUsage=critical,keyCertSign,cRLSign') \
     -extensions v3_ca
   ```
   </Details>

5. Verify the required RFC 5280 extensions are present:

	```sh
	openssl x509 -in <CUSTOM-ROOT-CERT>.pem -noout -ext keyUsage,basicConstraints
	```

	The output should include:

	```txt
	X509v3 Basic Constraints: critical
			CA:TRUE
	X509v3 Key Usage: critical
			Certificate Sign, CRL Sign
	```

	If these fields are missing, regenerate the certificate using the command in step 4.

6. To review the private key, run the following command:

	```sh
	openssl rsa -in <CUSTOM-ROOT-PRIVATE-KEY>.pem -text
	```

	To review the certificate, run the following command:

	```sh
	openssl x509 -in <CUSTOM-ROOT-CERT>.pem -text
	```

When preparing your certificate and private key for upload, be sure to remove any unwanted characters, such as mismatching subdomains in the certificate's common name.

## Deploy a custom root certificate

You can upload a single root certificate or a full certificate chain. When uploading a certificate chain via the dashboard, API, or Terraform, concatenate the root certificate and any intermediate certificates in PEM format, with the root certificate first.

<Tabs syncKey="dashPlusAPI">

<TabItem label="Dashboard">
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings** > **Certificates**.
2. Select **Upload certificate**.
3. Enter the private key and SSL certificate you generated or select **Paste certificate from file** to upload them from a file. If uploading a certificate chain, paste all certificates (root and intermediates) in PEM format with the root certificate first.
4. Select **Upload custom certificate**.

    You can now [use the generated custom root certificate](#use-a-custom-root-certificate) for inspection.

</TabItem>

<TabItem label="API">

1. <Render
   	file="upload-mtls-cert"
   	params={{ key: " ", cert: "root CA or certificate chain" }}
   	product="cloudflare-one"
   />

   When uploading a certificate chain, the `certificates` field should contain all certificates in PEM format. To format this field, order the root certificate first, then concatenate any intermediate certificates.

2. Set the certificate as available for use in inspection with the [Activate a Zero Trust certificate endpoint](/api/resources/zero_trust/subresources/gateway/subresources/certificates/methods/activate/). This will deploy the certificate across the Cloudflare global network.

   <APIRequest
   	path="/accounts/{account_id}/gateway/certificates/{certificate_id}/activate"
   	method="POST"
   />

   The response will return the certificate and a `pending_deployment` binding status. For example:

   ```json {12}
   {
   	"errors": [],
   	"messages": [],
   	"success": true,
   	"result": {
   		"in_use": false,
   		"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
   		"certificate": "-----BEGIN CERTIFICATE-----\\n ... \\n-----END CERTIFICATE-----\\n",
   		"issuer_org": "Example Inc.",
   		"issuer_raw": "O=Example Inc.,L=California,ST=San Francisco,C=US",
   		"fingerprint": "E9:19:49:AA:DD:D8:1E:C1:20:2A:D8:22:BF:A5:F8:FC:1A:F7:10:9F:C7:5B:69:AB:0:31:91:8B:61:B4:BF:1C",
   		"binding_status": "pending_deployment",
   		"type": "custom",
   		"updated_at": "2014-01-01T05:20:00.12345Z",
   		"uploaded_on": "2014-01-01T05:20:00.12345Z",
   		"created_at": "2014-01-01T05:20:00.12345Z",
   		"expires_on": "2014-01-01T05:20:00.12345Z"
   	}
   }
   ```

3. Use the [Get Zero Trust certificate details endpoint](/api/resources/zero_trust/subresources/gateway/subresources/certificates/methods/get/) to verify the certificate's binding status is set to `available`.

   <APIRequest
   	path="/accounts/{account_id}/gateway/certificates/{certificate_id}"
   	method="GET"
   />

   ```json {12}
   {
   	"errors": [],
   	"messages": [],
   	"success": true,
   	"result": {
   		"in_use": false,
   		"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
   		"certificate": "-----BEGIN CERTIFICATE-----\\n ... \\n-----END CERTIFICATE-----\\n",
   		"issuer_org": "Example Inc.",
   		"issuer_raw": "O=Example Inc.,L=California,ST=San Francisco,C=US",
   		"fingerprint": "E9:19:49:AA:DD:D8:1E:C1:20:2A:D8:22:BF:A5:F8:FC:1A:F7:10:9F:C7:5B:69:AB:0:31:91:8B:61:B4:BF:1C",
   		"binding_status": "available",
   		"type": "custom",
   		"updated_at": "2014-01-01T05:20:00.12345Z",
   		"uploaded_on": "2014-01-01T05:20:00.12345Z",
   		"created_at": "2014-01-01T05:20:00.12345Z",
   		"expires_on": "2014-01-01T05:20:00.12345Z"
   	}
   }
   ```

4. (Optional) Verify the certificate is installed on your user's devices either [with the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/) or [manually](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/).

5. Use the [Patch Zero Trust account configuration endpoint](/api/resources/zero_trust/subresources/gateway/subresources/configurations/methods/edit/) to turn on the certificate for use in inspection. For example:

<APIRequest
	path="/accounts/{account_id}/gateway/configuration"
	method="PATCH"
	json={{
		settings: {
			certificate: {
				id: "{certificate_id}",
				in_use: true,
			},
		},
	}}
/>

Once `in-use` is set to `true`, Gateway will sign your traffic using the custom root certificate and private key. If you turn off or deactivate the custom certificate, Gateway will revert to the next available Cloudflare certificate generated for your Zero Trust account.

</TabItem>

</Tabs>

:::caution[Private key visibility]
When you upload a private key to Zero Trust, Cloudflare encrypts the key and stores it at rest. Only your delegated apps have the necessary decryption keys to decrypt the private key. You will not be able to retrieve the private key after upload.
:::

## Use a custom root certificate

To use a custom root certificate you generated and uploaded to Cloudflare, refer to [Activate a root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/#activate-a-root-certificate).

## Troubleshooting

### Error 526: Invalid SSL certificate

If Gateway returns an **HTTP Response Code: 526** after deploying a custom certificate, refer to the [Error 526 documentation](/cloudflare-one/traffic-controls/troubleshooting/#error-526-invalid-ssl-certificate).

### Python 3.13+ SSL errors with the Cloudflare One Client

Python 3.13 and later enable `ssl.VERIFY_X509_STRICT` by default, which requires CA certificates to comply with [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280). If your BYOPKI certificate was generated without the `keyUsage` and `basicConstraints` extensions, Python HTTPS requests will fail when the Cloudflare One Client is active. To resolve the issue, [generate a new custom root CA](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/#generate-a-custom-root-ca) and upload it to Cloudflare.

# User-side certificates

import { Tabs, TabItem, APIRequest } from "~/components";

Advanced security features such as [HTTPS traffic inspection](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), [Data Loss Prevention](/cloudflare-one/data-loss-prevention/), [anti-virus scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/), [Access for Infrastructure](/cloudflare-one/access-controls/applications/non-http/infrastructure-apps/), and [Browser Isolation](/cloudflare-one/remote-browser-isolation/) require users to install and trust a root certificate on their device. You can either install the certificate provided by Cloudflare (default option), or generate your own custom certificate and upload it to Cloudflare.

Zero Trust [generates a unique root CA](#generate-a-cloudflare-root-certificate) for each account and deploys it across the Cloudflare global network. Alternatively, Enterprise users can upload and deploy their own [custom certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/).

:::caution[Default certificate expired on 2025-02-02]
The default Cloudflare certificate expired on 2025-02-02 at 16:05 UTC.

Review how this change impacts certificate propagation to your end-user devices and how to address browser issues in [Troubleshooting](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/common-issues/#browser-and-certificate-issues).
:::

## Certificate status

Zero Trust will indicate if a certificate is ready for use in inspection based on its deployment status:

| Deployment status    | Description                                                                                                                                                      |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Inactive             | The certificate has been generated by or uploaded to Cloudflare but is not deployed across the global network.                                                   |
| Pending              | The certificate is being activated or deactivated for use.                                                                                                       |
| Available            | The certificate is deployed across the Cloudflare global network and ready to be turned on. The Cloudflare One Client will install the certificate on your users' devices. |
| Available and In-Use | The certificate is turned on. Gateway will use the certificate for inspection.                                                                                   |

## Generate a Cloudflare root certificate

To generate a new Cloudflare root certificate for your Zero Trust organization:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. Select **Certificates**.
3. Select **Generate certificate**.
4. Choose a duration of time before the certificate expires. Cloudflare recommends expiration after five years. Alternatively, choose _Custom_ and enter a custom amount in days.
5. Select **Generate certificate**.

</TabItem>

<TabItem label="API">

Send a `POST` request to the [Create Zero Trust certificate](/api/resources/zero_trust/subresources/gateway/subresources/certificates/methods/create/) endpoint.

<APIRequest path="/accounts/{account_id}/gateway/certificates" method="POST" />

The API will respond with the ID and contents of the new certificate.

</TabItem> </Tabs>

The certificate will appear in your list of certificates as **Inactive**. To download a generated certificate, select it, then choose **Download .pem** and/or **Download .crt**. To deploy your certificate and turn it on for inspection, you need to [activate the certificate](#activate-a-root-certificate).

Each Zero Trust account can generate a new root certificate a maximum of three times per day.

## Activate a root certificate

:::note
Zero Trust accounts using the default Cloudflare certificate prior to 2024-10-17 will need to redeploy and activate the newly generated certificate. Zero Trust accounts created during or after 2024-10-17 will use an available certificate by default.
:::

Once a certificate is generated in or uploaded to Zero Trust, you need to activate it. Activating a certificate deploys it across the Cloudflare network and sets its status to **Available**. You can have up to 10 available certificates at once.

To activate your root certificate:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. Select **Certificates**.
3. Select the certificate you want to activate.
4. Select **Activate**.

</TabItem>

<TabItem label="API">

Send a `POST` request to the [Activate a Zero Trust certificate](/api/resources/zero_trust/subresources/gateway/subresources/certificates/methods/activate/) endpoint.

<APIRequest
	path="/accounts/{account_id}/gateway/certificates/{certificate_id}/activate"
	method="POST"
/>

</TabItem> </Tabs>

The status of the certificate will change to **Pending** while it deploys. Once the status of your certificate is **Available**, you can install it on your user's devices either [with the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/) or [manually](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/).

Once you deploy and install your certificate, you can turn it on for use in inspection:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. Select **Certificates**.
3. Select the certificate you want to turn on.
4. In **Basic information**, select **Confirm and turn on certificate**.

</TabItem>

<TabItem label="API">

Send a `PUT` request to the [Update Zero Trust account configuration](/api/resources/zero_trust/subresources/gateway/subresources/configurations/methods/update/) endpoint. For example:

<APIRequest
	path="/accounts/{account_id}/gateway/configuration"
	method="PUT"
	json={{
		settings: {
			certificate: {
				id: "{certificate_id}",
				in_use: true,
			},
		},
	}}
/>

</TabItem> </Tabs>

You can set multiple certificates to **Available**, but you can only turn on one certificate for use in inspection at a time. Setting a certificate as **In-Use** will set any other in-use certificates as **Available** only and prevent them from being used for inspection until turned on again.

# Install certificate manually

import { Details, Render, TabItem, Tabs } from "~/components";

:::note
This procedure is only required to enable specific Cloudflare Zero Trust features, and should only be done at the direction of your IT department. This procedure is not required to enable the Cloudflare One Client for consumers.
:::

If your device does not support [certificate installation via the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/), you can manually install a Cloudflare certificate. You must add the certificate to both the [system keychain](#add-the-certificate-to-operating-systems) and to [individual application stores](#add-the-certificate-to-applications). These steps must be performed on each new device that is to be subject to HTTP filtering.

Zero Trust will only inspect traffic using installed certificates set to [**Available** and **In-Use**](/cloudflare-one/team-and-resources/devices/user-side-certificates/#activate-a-root-certificate).

To install a certificate manually, you must:

1. Download a Cloudflare certificate and verify it.
2. Install the certificate in your operating system's certificate store.
3. If a target application does not accept certificates from the operating system, you must install the certificate in the application's certificate store.

## 1. Download a Cloudflare root certificate

:::note[Download limitation]
You can only download Cloudflare-generated certificates from the Cloudflare One dashboard or with the Cloudflare One Client.
:::

First, [generate](/cloudflare-one/team-and-resources/devices/user-side-certificates/#generate-a-cloudflare-root-certificate) and download a Cloudflare certificate. The certificate is available in both `.pem` and `.crt` file format. Certain applications require the certificate to be in a specific file type, so ensure you download the most appropriate file for your use case.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. Select **Certificates**.
3. Select the certificate you want to download.
4. Select **More actions**.
5. Depending on which format you want, choose **Download .pem** and/or **Download .crt**.

Alternatively, you can download and install a certificate [using the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/#install-a-certificate-using-the-cloudflare-one-client). The Cloudflare One Client will add the certificates to the device's system certificate store in `installed_certs/<certificate_id>.pem`.

## 2. Verify the downloaded certificate

To verify your download, use a terminal to check that the downloaded certificate's hash matches the thumbprint listed under **Certificate thumbprint**. For example:

### SHA1

```sh title="SHA1 .crt example"
openssl x509 -noout -fingerprint -sha1 -inform der -in <certificate.crt>
```

```sh output
SHA1 Fingerprint=BB:2D:B6:3D:6B:DE:DA:06:4E:CA:CB:40:F6:F2:61:40:B7:10:F0:6C
```

```sh title="SHA1 .pem example"
openssl x509 -noout -fingerprint -sha1 -inform pem -in <certificate.pem>
```

```sh output
SHA1 Fingerprint=BB:2D:B6:3D:6B:DE:DA:06:4E:CA:CB:40:F6:F2:61:40:B7:10:F0:6C
```

### SHA256

```sh title="SHA256 .crt example"
openssl x509 -noout -fingerprint -sha256 -inform der -in <certificate.crt>
```

```sh output
sha256 Fingerprint=F5:E1:56:C4:89:78:77:AD:79:3A:1E:83:FA:77:83:F1:9C:B0:C6:1B:58:2C:2F:50:11:B3:37:72:7C:62:3D:EF
```

```sh title="SHA256 .pem example"
openssl x509 -noout -fingerprint -sha256 -inform pem -in <certificate.pem>
```

```sh output
sha256 Fingerprint=F5:E1:56:C4:89:78:77:AD:79:3A:1E:83:FA:77:83:F1:9C:B0:C6:1B:58:2C:2F:50:11:B3:37:72:7C:62:3D:EF
```

## 3. (Optional) Convert the certificate

Some applications require a certificate formatted in the `.cer` file type. You can convert your downloaded certificate using [OpenSSL](https://www.openssl.org/):

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

1. [Install OpenSSL](https://wiki.openssl.org/index.php/Compilation_and_Installation).
2. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
3. In a terminal, convert the certificate to DER format with the `.cer` file type:
   ```sh
   openssl x509 -inform PEM -in ~/Downloads/certificate.pem -outform DER -out ~/Downloads/certificate.cer
   ```

</TabItem>

<TabItem label="Windows" icon="seti:windows">

1. [Install OpenSSL for Windows](https://slproweb.com/products/Win32OpenSSL.html).
2. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
3. In a PowerShell terminal, convert the certificate to DER format with the `.cer` file type:
   ```powershell
   openssl x509 -inform PEM -in "$HOME\Downloads\certificate.pem" -outform DER -out "$HOME\Downloads\certificate.cer"
   ```

</TabItem>
</Tabs>

## 4. Add the certificate to operating systems

If you are deploying the Cloudflare certificate to desktop devices, use the [Install certificate using the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/) method.

Mobile devices require manual installations detailed in the instructions below.

### macOS

In macOS, you can choose the keychain in which you want to install the certificate. Each keychain impacts which users will be affected by trusting the root certificate.

| Keychain    | Access scope                                 |
| ----------- | -------------------------------------------- |
| login       | The logged in user                           |
| Local Items | Users with access to cached iCloud passwords |
| System      | All users on the system                      |

To install a Cloudflare certificate in macOS, you can use either the Keychain Access application or a terminal. Both methods require you to [download a certificate](#download-a-cloudflare-root-certificate) in `.crt` format.

<Tabs>
<TabItem label="Keychain Access">

1. Download a Cloudflare certificate.
   <Render
   	file="keychain-access"
   	params={{ cert: ".crt" }}
   	product="cloudflare-one"
   />

The root certificate is now installed and ready to be used.

</TabItem>

<TabItem label="Terminal">

1. Download a Cloudflare certificate.
2. Open Terminal.
3. Add the certificate to your keychain:

```sh
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <path-to-certificate.crt>
```

This keychain will allow all users on the system access to the certificate. If you want to install the certificate to a different keychain, replace `System.keychain` with the name of that keychain.

4. Update the OpenSSL CA Store to include the Cloudflare certificate:

```sh
echo | sudo tee -a /etc/ssl/cert.pem < certificate.pem
```

The root certificate is now installed and ready to be used.

</TabItem>
</Tabs>

:::note[Change certificate access scope]
If you want to change user access to the Cloudflare certificate, you can open Keychain Access and move the certificate to a different keychain on the left sidebar.
:::

### Windows

Windows offers two locations to install the certificate, each impacting which users will be affected by trusting the root certificate.

| Store location      | Access scope            |
| ------------------- | ----------------------- |
| Current User Store  | The logged in user      |
| Local Machine Store | All users on the system |

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate).
2. Right-click the certificate file.
3. Select **Open**. If a security warning appears, choose **Open** to proceed.
4. The **Certificate** window will appear. Select **Install Certificate**.
5. Now choose a Store Location. If a security warning appears, choose **Yes** to proceed.
6. On the next screen, select **Browse**.
7. In the list, choose the _Trusted Root Certification Authorities_ store.
8. Select **OK**, then select **Finish**.

The root certificate is now installed and ready to be used.

:::caution
If your certificate is installed in the **Local Machine Store**, the [device posture check](/cloudflare-one/reusable-components/posture-checks/client-checks/) looking for a certificate will fail. Install the certificate in the **Current User Store** to ensure a successful posture device check.
:::

### Linux

The location where the root certificate should be installed is different depending on your Linux distribution. Follow the specific instructions for your distribution.

<Tabs>
<TabItem label="Debian-based" icon="seti:debian">

The following procedure applies to Debian-based systems, such as Debian, Ubuntu, and Kali Linux.

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Install the `ca-certificates` package.

   ```sh
   sudo apt-get install ca-certificates
   ```

3. Copy the certificate to the system, changing the file extension to `.crt`.

   ```sh
   sudo cp certificate.pem /usr/share/ca-certificates/certificate.crt
   ```

4. Import the certificate.

   ```sh
   sudo dpkg-reconfigure ca-certificates
   ```

</TabItem>

<TabItem label="Red Hat-based" icon="seti:redhat">

The following procedure applies to Red Hat-based systems, such as CentOS and Red Hat Enterprise Linux (RHEL).

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in both `.crt` and `.pem` format.
2. Install the `ca-certificates` package.

   ```sh
   sudo dnf install ca-certificates
   ```

3. Copy both certificates to the trust store.

   ```sh
   sudo cp certificate.crt certificate.pem /etc/pki/ca-trust/source/anchors
   ```

4. Import the certificate.

   ```sh
   sudo update-ca-trust
   ```

</TabItem>

<TabItem label="NixOS" icon="seti:nix">

NixOS does not use the system certificate store for self updating and instead relies on the certificates found in `~/.nix-profile/etc/ssl/certs` or provided by `NIX_SSL_CERT_FILE` at runtime.

</TabItem>
</Tabs>

### iOS

1. In Safari, [download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Open Files and go to **Recents**.
3. Find and open the downloaded certificate file. A message will appear confirming the profile was downloaded. Select **Close**.
4. Open Settings. Select the **Profile Downloaded** section beneath your Apple Account info. Alternatively, go to **General** > **VPN & Device Management** and select the **Gateway CA - Cloudflare Managed G1** profile.
5. Select **Install**. If the iOS device is passcode-protected, you will be prompted to enter the passcode.
6. A certificate warning will appear. Select **Install**. If a second prompt appears, select **Install** again.
7. The Profile Installed screen will appear. Select **Done**. The certificate is now installed. However, before it can be used, it must be trusted by the device.
8. In Settings, go to **General** > **About** > **Certificate Trust Settings**. The installed root certificates will be displayed under Enable full trust for root certificates.
9. Turn on the Cloudflare certificate.
10. A security warning message will appear. Choose **Continue**.

The root certificate is now installed and ready to be used.

### Android

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate).
2. In Settings, go to **Security** > **Advanced** > **Encryption & credentials** > **Install a certificate**.
3. Select **CA certificate**.
4. Select **Install anyway**.
5. Verify your identity.
6. Choose the certificate file you want to install.

The root certificate is now installed and ready to be used.

### ChromeOS

ChromeOS devices use different methods to store and deploy root certificates. Certificates may fall under the **VPN and apps** or **CA certificate** settings. Follow the procedure that corresponds with your device.

<Tabs> <TabItem label="VPN and apps">

<Render
	file="chromeos-cert-settings"
	product="cloudflare-one"
	params={{ one: "Install from SD card" }}
/>

5. In the file open dialog, choose the `certificate.crt` file you downloaded. Select **Open**.
6. Enter a name to identify the certificate. Ensure **Credential use** is set to _VPN and apps_.
7. Select **OK**.

</TabItem>

<TabItem label="CA certificate">

<Render
	file="chromeos-cert-settings"
	product="cloudflare-one"
	params={{ one: "Install a certificate > CA certificate" }}
/>

5. When prompted with a privacy warning, select **Install anyway**.
6. In the file open dialog, choose the `certificate.crt` file you downloaded. Select **Open**.
7. To verify the certificate is installed and trusted, go to **Settings** > **Apps** > **Google Play Store** > **Manage Android Preferences** > **Security** > **Credentials** > **Trusted credentials** > **User**.

</TabItem> </Tabs>

After adding the Cloudflare certificate to ChromeOS, you may also have to [install the certificate in your browser](#browsers).

## 5. Add the certificate to applications

Some applications do not use the system certificate store and therefore require the certificate to be added to the application directly. For certain applications like the ones below, you will need to follow the steps in this section and add the Cloudflare certificate to the application for TLS decryption to function properly.

If you do not update the application to trust the Cloudflare certificate, the application will refuse to connect and you will receive an untrusted certificate error.

All of the applications below first require downloading a Cloudflare certificate with [the instructions above](#download-the-cloudflare-root-certificate). On macOS, the default path to the system keychain database file is `/Library/Keychains/System.keychain`. On Windows, the default path is `\Cert:\CurrentUser\Root`.

:::note
Some applications require the use of a publicly trusted certificate — they do not trust the system certificate, nor do they have a configurable private store. For these applications to function, you must add a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) for the domains or IPs that the application relies on.
:::

:::caution
Even if you deployed WARP through the [Install certificate using the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/user-side-certificates/automated-deployment/) method, you may still need to add the Cloudflare certificate to certain applications. The Install certificate using the Cloudflare One Client method only installs the Cloudflare certificate to the operating system certificate store.
:::

### Browsers

Browsers may use their own certificate stores or rely on the operating system certificate store.

#### Chrome

Versions of Chrome before Chrome 113 use the [operating system root store](https://support.google.com/chrome/answer/95617?visit_id=638297158670039236-3119581239&p=root_store&rd=1#zippy=%2Cmanage-device-certificates-on-mac-windows) on macOS and Windows. Chrome 113 and newer on macOS and Windows -- and all versions on Linux and ChromeOS -- use the [Chrome internal trust store](https://www.chromium.org/Home/chromium-security/root-ca-policy/#introduction).

To install a Cloudflare certificate to Chrome manually:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. In Chrome, go to **Settings** > **Privacy and security** > **Security**.
3. Select **Manage certificates**.
4. Go to **Authorities**. Select **Import**.
5. In the file open dialog, choose the `certificate.pem` file you downloaded.
6. In the dialog box, turn on _Trust this certificate for identifying websites_, _Trust this certificate for identifying email users_, and _Trust this certificate for identifying software makers_. Select **OK**.
7. To verify the certificate was installed and trusted, locate it in **Authorities**.

For information on installing a Cloudflare certificate for organizations, refer to [Google's Chrome Enterprise and Education documentation](https://support.google.com/chrome/a/answer/3505249).

#### Firefox

To install a Cloudflare certificate to Firefox manually:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. In Firefox, go to **Settings** > **Privacy & Security**.
3. In **Security**, select **Certificates** > **View Certificates**.
4. In **Authorities**, select **Import**.
5. In the file open dialog, choose the `certificate.pem` file you downloaded.
6. In the dialog box, turn on _Trust this CA to identify websites_ and _Trust this CA to identify email users_. Select **OK**.
7. To verify the certificate was installed and trusted, locate it in the table under **Cloudflare**.

For information on installing a Cloudflare certificate for organizations, refer to this [Mozilla support article](https://support.mozilla.org/en-US/kb/setting-certificate-authorities-firefox).

### Mobile device management (MDM) software

Zero Trust integrates with several [mobile device management (MDM) software partners](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/partners/) to deploy the Cloudflare One Client across devices.

#### Microsoft Intune

To upload and deploy a Cloudflare certificate in Microsoft Intune:

1. [Download and convert a Cloudflare certificate](#convert-the-certificate) to DER format with the `.cer` file type.
2. In Microsoft Intune, [create a trusted certificate profile](https://learn.microsoft.com/mem/intune/protect/certificates-trusted-root#to-create-a-trusted-certificate-profile) with your converted certificate.

For more information, refer to the [Microsoft documentation](https://learn.microsoft.com/mem/intune/protect/certificates-trusted-root).

#### Jamf Pro

To upload and deploy a Cloudflare certificate in Jamf Pro:

1. [Download and convert a Cloudflare certificate](#convert-the-certificate) to DER format with the `.cer` file type.
2. In Jamf Pro, go to **Computers** > **Configuration Profiles** to create a computer configuration profile, or go to **Devices** > **Configuration Profiles** to create a mobile device configuration profile. Select **New**.
3. Add a name and description for the profile.
4. Choose whether you would like Jamf to install the certificate automatically or with self-service, and whether you would like to install the certificate for a single user or all users on the device.
5. Select **Add** > **Certificate**. Choose the certificate file.
6. Uncheck **Allow export from keychain**.
7. Select **Scope**, then choose which devices or groups to deploy the certificate to.
8. Select **Save**.

For more information, refer to the [Jamf Pro documentation](https://learn.jamf.com/bundle/jamf-pro-documentation-current/page/PKI_Certificates.html).

#### Kandji

To upload and deploy a Cloudflare certificate in Kandji:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.crt` format.
2. In Kandji, [upload the certificate](https://support.kandji.io/support/solutions/articles/72000558739-certificate-profile) as a PKCS #1-formatted certificate.

#### Hexnode

To upload and deploy a Cloudflare certificate in Hexnode:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. In Hexnode, follow the directions for adding the certificate to [macOS](https://www.hexnode.com/mobile-device-management/help/how-to-add-certificates-for-mac-devices-with-hexnode-mdm/), [iOS](https://www.hexnode.com/mobile-device-management/help/add-certificates-for-ios-devices-with-hexnode-mdm/), and/or [Android](https://www.hexnode.com/mobile-device-management/help/how-to-add-certificates-for-android-devices-using-hexnode-mdm/) devices.

#### JumpCloud

To upload and deploy a Cloudflare certificate in JumpCloud:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. In JumpCloud, [upload the certificate](https://jumpcloud.com/support/manage-device-trust-certificates#distributing-global-device-certificates-).
3. [Configure a conditional access policy](https://jumpcloud.com/support/configure-a-conditional-access-policy) to deploy the certificate across devices.

### Programming languages and runtimes

Programming language runtimes often maintain their own certificate stores or use language-specific certificate management tools.

#### Python

Depending on which version of Python you have installed and your configuration, you may need to use either the `python` or `python3` command. If you use [virtual environments](https://docs.python.org/3/library/venv.html), you will need to repeat the following steps within each virtual environment.

<Tabs>
<TabItem label="Windows" icon="seti:windows">

The command to install the certificate with Python on Windows automatically includes `pip` and `certifi` (the default certificate bundle for certificate validation).

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.crt` format.
2. In a PowerShell terminal, install the `certifi` package:
   ```powershell
   python -m pip install certifi
   ```
3. Identify the Python CA store:
   ```powershell
   $CERT_PATH = python -c "import certifi; print(certifi.where())"
   ```
4. Update the bundle to include the Cloudflare certificate:
   ```powershell
   gc "$env:USERPROFILE\Downloads\certificate.crt" | ac $CERT_PATH
   ```
5. (Optional) Configure your system variables to point to the CA store by adding them to PowerShell's configuration file:
   ```powershell
   [System.Environment]::SetEnvironmentVariable('CERT_PATH', $CERT_PATH, 'Machine')
   [System.Environment]::SetEnvironmentVariable('SSL_CERT_FILE', $CERT_PATH, 'Machine')
   [System.Environment]::SetEnvironmentVariable('REQUESTS_CA_BUNDLE', $CERT_PATH, 'Machine')
   ```
6. Restart your terminal.

</TabItem>

<TabItem label="macOS and Linux" icon="seti:shell">

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. In a terminal, install the `certifi` package:
   ```sh
   python -m pip install certifi
   ```
3. Append the Cloudflare certificate to this CA store by running:
   ```sh
   echo | cat - certificate.pem >> $(python -m certifi)
   ```
4. (Optional) Configure your system variables to point to the CA store by adding them to your shell's configuration file (such as `~/.zshrc` or `~/.bash_profile`). For example:
   ```sh
   echo 'export CERT_PATH=$(python -c "import certifi; print(certifi.where())")
   export SSL_CERT_FILE=${CERT_PATH}
   export REQUESTS_CA_BUNDLE=${CERT_PATH}' >> ~/.zshrc
   ```
5. Restart your terminal.

</TabItem>
</Tabs>

#### Java

Java may have multiple certificate keystore locations depending on different installations or applications that include Java. Depending on your Java Virtual Machine (JVM) installation, you may need to install the certificate for each instance. You may also need to manually configure each Java application to use and trust the certificate.

To install a Cloudflare root certificate in the system JVM, follow the procedure for your operating system. These steps require you to [download a `.pem` certificate](#download-a-cloudflare-root-certificate).

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

1. Install [OpenSSL](https://www.openssl.org/).

2. In a terminal, format the Cloudflare certificate for Java.

   ```sh
   openssl x509 -in Cloudflare_CA.pem -inform pem -out Cloudflare_CA.der -outform der
   ```

3. Import the converted certificate into the Java keystore.

   ```sh
   sudo $JAVA_HOME/bin/keytool -import -trustcacerts -alias 'Cloudflare Root CA' -file Cloudflare_CA.der -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt 2>&1
   ```

4. Restart any instances of Java.

</TabItem>

<TabItem label="Windows" icon="seti:windows">

1. Install [OpenSSL for Windows](https://slproweb.com/products/Win32OpenSSL.html).

2. In an administrator PowerShell terminal, format the Cloudflare certificate for Java.

   ```powershell
   openssl x509 -in Cloudflare_CA.pem -inform pem -out Cloudflare_CA.der -outform der
   ```

3. Import the converted certificate into the Java keystore.

   ```powershell
   "%JAVA_HOME%\bin\keytool" -import -trustcacerts -alias "Cloudflare Root CA" -file Cloudflare_CA.der -keystore "%JAVA_HOME%\jre\lib\security\cacerts" -storepass changeit -noprompt
   ```

4. Restart any instances of Java.

</TabItem>
</Tabs>

#### Ruby

To trust a Cloudflare root certificate in RubyGems, follow the procedure for your operating system. These steps require you to [download a `.pem` certificate](#download-a-cloudflare-root-certificate).

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

1. Install [OpenSSL](https://www.openssl.org/).

2. In a terminal, format the Cloudflare certificate for Ruby.

   ```sh
   openssl x509 -in ~/Downloads/certificate.pem -out ~/Downloads/ruby-root-ca.crt
   ```

3. Create a RubyGems certificate directory in your home folder.

   ```sh
   mkdir -p ~/.gem/ssl
   ```

4. Copy the Cloudflare certificate to your RubyGems certificate store.

   ```sh
   cp ~/Downloads/ruby-root-ca.crt ~/.gem/ssl/rubygems.org.pem
   ```

5. Configure RubyGems to use the certificate.

   ```sh
   gem sources --add-trusted-cert ~/.gem/ssl/rubygems.org.pem
   ```

   Alternatively, add the following line to your RubyGems configuration file located in `~/.gemrc` file to globally trust the certificate:

   ```yaml
   :ssl_cert: ~/.gem/ssl/rubygems.org.pem
   ```

6. Restart any terminal sessions.

</TabItem>

<TabItem label="Windows" icon="seti:windows">

1. Install [OpenSSL for Windows](https://slproweb.com/products/Win32OpenSSL.html).

2. In a PowerShell terminal, format the Cloudflare certificate for Ruby.

   ```powershell
   openssl x509 -in %UserProfile%\Downloads\certificate.pem -out %UserProfile%\Downloads\ruby-root-ca.crt
   ```

3. Create a RubyGems certificate directory in your home folder.

   ```powershell
   mkdir -Force "$env:USERPROFILE\.gem\ssl"
   ```

4. Copy the Cloudflare certificate to your RubyGems certificate store.

   ```powershell
   Copy-Item "$env:USERPROFILE\Downloads\ruby-root-ca.crt" "$env:USERPROFILE\.gem\ssl\rubygems.org.pem"
   ```

5. Configure RubyGems to use the certificate.

   ```powershell
   gem sources --add-trusted-cert "$env:USERPROFILE\.gem\ssl\rubygems.org.pem"
   ```

   Alternatively, add the following line to your RubyGems configuration file located in `$HOME\.gemrc` to globally trust the certificate:

   ```yaml
   :ssl_cert: C:/Users/<username>/.gem/ssl/rubygems.org.pem
   ```

6. Restart any terminal sessions.

</TabItem>
</Tabs>

#### Rust

Rust's package manager Cargo uses the system certificate store by default on most platforms. However, you may need to configure it explicitly in some cases.

<Tabs>
<TabItem label="Windows" icon="seti:windows">

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Set the `CARGO_HTTP_CAINFO` environment variable to point to the certificate. In PowerShell:
   ```powershell
   [System.Environment]::SetEnvironmentVariable('CARGO_HTTP_CAINFO', "$HOME\Downloads\certificate.pem", 'User')
   ```
3. Restart your terminal.

Alternatively, you can configure this in your Cargo configuration file at `%USERPROFILE%\.cargo\config.toml`:

```toml
[http]
cainfo = "C:\\Users\\<username>\\Downloads\\certificate.pem"
```

</TabItem>

<TabItem label="macOS and Linux" icon="seti:shell">

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Set the `CARGO_HTTP_CAINFO` environment variable by adding it to your shell's configuration file (such as `~/.zshrc` or `~/.bash_profile`):
   ```sh
   export CARGO_HTTP_CAINFO="$HOME/Downloads/certificate.pem"
   ```
3. Restart your terminal.

Alternatively, you can configure this in your Cargo configuration file at `~/.cargo/config.toml`:

```toml
[http]
cainfo = "/path/to/certificate.pem"
```

</TabItem>
</Tabs>

### Development tools and package managers

Development tools and package managers often require certificate configuration for secure package downloads and repository access.

#### Git

<Tabs>
<TabItem label="Windows" icon="seti:windows">

1. Open PowerShell.

2. Run the following command:

   ```powershell
   git config -l
   ```

   This command will output:

   ```txt
   core.symlinks=false
   core.autocrlf=true
   core.fscache=true
   color.diff=auto
   color.status=auto
   color.branch=auto
   color.interactive=true
   help.format=html
   rebase.autosquash=true
   http.sslcainfo=C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
   http.sslbackend=openssl
   diff.astextplain.textconv=astextplain
   filter.lfs.clean=git-lfs clean -- %f
   filter.lfs.smudge=git-lfs smudge -- %f
   filter.lfs.process=git-lfs filter-process
   filter.lfs.required=true
   credential.helper=manager
   ```

3. The `http.sslcainfo` defines the CA Certificate store. To append the Cloudflare certificate to the CA bundle, update `http.sslcainfo`.

   ```powershell
   gc .\certificate.pem | ac $(git config --get http.sslcainfo)
   ```

</TabItem>

<TabItem label="macOS and Linux" icon="seti:shell">

To configure Git to trust a Cloudflare certificate, run the following command:

```sh
git config --global http.sslcainfo [PATH_TO_CLOUDFLARE_CERT]
```

</TabItem>
</Tabs>

#### npm

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Set the `cafile` configuration to use the Cloudflare certificate:
   ```sh
   npm config set cafile [PATH_TO_CLOUDFLARE_CERT.pem]
   ```

On some systems you may need to set the following in your path/export list:

```sh
export NODE_EXTRA_CA_CERTS='[PATH_TO_CLOUDFLARE_CERT.pem]'
```

#### PHP Composer

The command below will set the [`cafile`](https://getcomposer.org/doc/06-config.md#cafile) configuration inside of `composer.json` to use the Cloudflare root certificate. Make sure to [download a certificate](#download-a-cloudflare-root-certificate) in the `.pem` file type.

```sh
composer config cafile [PATH_TO_CLOUDFLARE_CERT.pem]
```

Alternatively, you can add this manually to your `composer.json` file under the `config` key.

#### Docker

To install a certificate for use in a Docker container:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Create a directory for certificates in your Docker project:

   ```sh
   cd docker-project
   mkdir certs
   mv /path/to/downloaded/certificate.pem certs/
   ```

3. Verify the certificate was moved to the directory correctly. Your project should have the following structure:

   ```sh
   docker-project/
   ├── Dockerfile
   └── certs/
       └── certificate.pem
   ```

4. Add the certificate to your Docker image:

   <Tabs> <TabItem label="During build process">

   To add the certificate to your Dockerfile to install it during the build process:
   1. Add the certificate install directions to your Dockerfile. For example:

      ```docker title="Red Hat-based images"
      FROM registry.access.redhat.com/ubi9/ubi:latest
      # Or FROM centos:7 or FROM fedora:38

      # Install necessary certificates package
      RUN dnf install -y ca-certificates

      # Copy and add Cloudflare root certificate
      COPY certs/certificate.pem /etc/pki/ca-trust/source/anchors/certificate.crt
      RUN update-ca-trust extract
      ```

      ```docker title="Debian-based images"
      FROM debian:12
      # Or FROM ubuntu:22.04

      # Install necessary certificates package
      RUN apt-get update && apt-get install -y ca-certificates

      # Copy and add Cloudflare root certificate
      COPY certs/certificate.pem /usr/local/share/ca-certificates/certificate.crt
      RUN update-ca-certificates
      ```

      ```docker title="Alpine-based images"
      FROM alpine:3.18

      # Install necessary certificates package
      RUN apk add --no-cache ca-certificates

      # Copy and add Cloudflare root certificate
      COPY certs/certificate.pem /usr/local/share/ca-certificates/certificate.crt
      RUN update-ca-certificates
      ```

   2. Build the Docker image:

      ```sh
      docker build -t <your-container-name> .
      ```

   3. Verify the certificate was installed:

      ```sh title="Red Hat-based images"
      docker run --rm your-image-name sh -c "cat /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem | grep Cloudflare"
      ```

      ```sh title="Debian and Alpine-based images"
      docker run --rm your-image-name sh -c "cat /etc/ssl/certs/certificate.pem"
      ```

   </TabItem>

   <TabItem label="During runtime">

   To add the certificate to your Docker Compose file to install it during runtime:
   1. Add the certificate install directions to your `docker-compose.yml` file. For example:

      ```yaml title="Red Hat-based containers"
      version: '3'
      services:
      	redhat-app:
      		image: registry.access.redhat.com/ubi9/ubi:latest
       		volumes:
       			- certs/certificate.pem:/etc/pki/ca-trust/source/anchors/certificate.pem
       		entrypoint: /bin/sh -c "dnf install -y ca-certificates && update-ca-trust extract && app start"
      ```

      ```yaml title="Debian-based containers"
      version: '3'
      services:
      	debian-app:
      		image: debian:12
       		volumes:
       			- certs/certificate.pem:/usr/local/share/ca-certificates/certificate.crt
       		entrypoint: /bin/sh -c "apt-get update && apt-get install -y ca-certificates && update-ca-certificates && app start"
      ```

      ```yaml title="Alpine-based containers"
      version: '3'
      services:
      	alpine-app:
      		image: alpine:3.18
       		volumes:
       			- certs/certificate.pem:/usr/local/share/ca-certificates/certificate.pem
       		entrypoint: /bin/sh -c "apk add --no-cache ca-certificates && update-ca-certificates && app start"
      ```

   2. Run the container:

      ```sh
      docker-compose up
      ```

   3. Verify the certificate was installed:

      ```sh title="Red Hat-based containers"
      docker exec -it <container-name> sh -c "cat /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem | grep Cloudflare"
      ```

      ```sh title="Debian and Alpine-based containers"
      docker exec -it <container-name> sh -c "cat /etc/ssl/certs/ca-certificates.crt | grep Cloudflare"
      ```

   </TabItem> </Tabs>

### Command-line tools

Command-line tools typically use the system certificate store but may require specific configuration.

#### cURL

By default, cURL will use your operating system's native certificate store. To force cURL to use your default certificate, add the `--ca-native` flag to the command. For example:

```curl
curl --ca-native https://example.com
```

<Tabs>
<TabItem label="macOS" icon="apple">

To use a Cloudflare root certificate with cURL on macOS, [install the certificate to the operating system keychain](#macos). cURL will use the macOS root certificate by default.

</TabItem>

<TabItem label="Windows" icon="seti:windows">

To use a Cloudflare certificate with the version of cURL preinstalled on Windows or another version that uses the Schannel (WinSSL) backend, [install the certificate to the Windows certificate store](#windows). If you use an older or custom version of cURL built with the OpenSSL backend, you will need to [manually configure cURL to use the certificate](#curl).

</TabItem>

<TabItem label="Linux" icon="seti:linux">

To use a Cloudflare root certificate with cURL on Linux, [install the certificate to your distribution's certificate store](#linux). cURL will use the Linux root certificate by default.

</TabItem>
</Tabs>

#### GNU Wget

By default, GNU Wget will use your operating system's native certificate store. To force Wget to use your default certificate, add the `--ca-certificate` flag to the command.

<Tabs>
<TabItem label="macOS" icon="apple">

To use a Cloudflare root certificate with Wget on macOS, [install the certificate to the operating system keychain](#macos). Wget will use the macOS root certificate by default.

</TabItem>

<TabItem label="Windows" icon="seti:windows">

To use a Cloudflare certificate with Wget on Windows, [install the certificate to the Windows certificate store](#windows). Wget will use the Windows root certificate by default.

</TabItem>

<TabItem label="Linux" icon="seti:linux">

To use a Cloudflare root certificate with Wget on Linux, [install the certificate to your distribution's certificate store](#linux). Wget will use the Linux root certificate by default.

Alternatively, you can specify a certificate file directly:

```sh
wget --ca-certificate=[PATH_TO_CLOUDFLARE_CERT.pem] https://example.com
```

To make this permanent, add the following to your `~/.wgetrc` file:

```sh
ca_certificate = [PATH_TO_CLOUDFLARE_CERT.pem]
```

</TabItem>
</Tabs>

### IDEs and development environments

Integrated development environments often use their own JVMs or certificate stores.

#### Android Studio

Android Studio uses its own JVM and certificate store. To install a Cloudflare root certificate:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate).

2. Find the `java.home` value for your Android Studio installation.
   1. In Android Studio, go to **Help** > **About** (or **Android Studio** > **About Android Studio** on macOS).
   2. Copy the JRE path shown in the dialog. For example:
      ```txt
      /Applications/Android Studio.app/Contents/jbr/Contents/Home
      ```

3. Add the Cloudflare certificate to Android Studio's JVM:

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

1. In a terminal, add the JRE path you copied as an environment variable.

   ```sh
   export JAVA_HOME="/path/to/jre"
   ```

2. Run `keytool` to install and trust the Cloudflare certificate.

   ```sh
   "$JAVA_HOME/bin/keytool" -import -file ~/Downloads/certificate.crt -alias CloudflareRootCA -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit -trustcacerts -noprompt
   ```

3. Restart Android Studio.

</TabItem>

<TabItem label="Windows" icon="seti:windows">

1. In an administrator PowerShell terminal, add the JRE path you copied as an environment variable.

   ```powershell
   $env:JAVA_HOME = "C:\Program Files\Android\Android Studio\jbr"
   ```

2. Run `keytool` to install and trust the Cloudflare certificate.

   ```powershell
   & "$env:JAVA_HOME\bin\keytool.exe" -import -file "$env:USERPROFILE\Downloads\certificate.crt" -alias CloudflareRootCA -keystore "$env:JAVA_HOME\lib\security\cacerts" -storepass changeit -trustcacerts -noprompt
   ```

3. Restart Android Studio.

</TabItem>
</Tabs>

For Gradle builds within Android Studio, you may also need to configure the Gradle JVM to trust the certificate by following the same steps for the Gradle JVM location.

#### JetBrains

To install a Cloudflare root certificate on JetBrains products, refer to the links below:

- [AppCode](https://www.jetbrains.com/help/objc/settings-tools-server-certificates.html)
- [CLion](https://www.jetbrains.com/help/clion/settings-tools-server-certificates.html)
- [DataGrip](https://www.jetbrains.com/help/datagrip/settings-tools-server-certificates.html)
- [DataSpell](https://www.jetbrains.com/help/dataspell/settings-tools-server-certificates.html)
- [GoLand](https://www.jetbrains.com/help/go/settings-tools-server-certificates.html)
- [IntelliJ IDEA](https://www.jetbrains.com/help/idea/settings-tools-server-certificates.html)
- [PhpStorm](https://www.jetbrains.com/help/phpstorm/settings-tools-server-certificates.html)
- [PyCharm](https://www.jetbrains.com/help/pycharm/settings-tools-server-certificates.html)
- [Rider](https://www.jetbrains.com/help/rider/Settings_Tools_Server_Certificates.html)
- [WebStorm](https://www.jetbrains.com/help/webstorm/settings-tools-server-certificates.html)

#### Eclipse

To install a Cloudflare root certificate on Eclipse IDE for Java Developers, you must add the certificate to the Java virtual machine (JVM) used by Eclipse.

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate).

2. Find the `java.home` value for your Eclipse installation.
   1. In Eclipse, go to **Eclipse** > **About Eclipse** (or **Help** > **About Eclipse IDE** on Windows and Linux)
   2. Select **Installation Details**, then go to **Configuration**.
   3. Search for `java.home`, then locate the value. For example:

   ```txt null {2}
   *** System properties:
   java.home=/Users/<username>/.p2/pool/plugins/org.eclipse.justj.openjdk.hotspot.jre.full.macosx.aarch64_17.0.8.v20230831-1047/jre
   ```

   4. Copy the full path after `java.home=`.

3. Add the Cloudflare certificate to Eclipse's JVM:

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

1. In a terminal, add the `java.home` value you copied as an environment variable.

   ```sh
   export JAVA_HOME=$(echo /path/to/java.home)
   ```

2. Run `keytool` to install and trust the Cloudflare certificate.

   ```sh
   "$JAVA_HOME/bin/keytool" -import -file ~/Downloads/certificate.crt -alias CloudflareRootCA -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit -trustcacerts -noprompt
   ```

3. Restart Eclipse.

</TabItem>

<TabItem label="Windows" icon="seti:windows">

1. In a terminal, add the `java.home` value you copied as an environment variable.

   ```powershell
   set JAVA_HOME="\path\to\java.home"
   ```

2. Run `keytool` to install and trust the Cloudflare certificate.

   ```powershell
   "%JAVA_HOME%\bin\keytool.exe" -import -file "%UserProfile%\Downloads\Cloudflare_CA.crt" -alias CloudflareRootCA -keystore "%JAVA_HOME%\lib\security\cacerts" -storepass changeit -trustcacerts -noprompt
   ```

3. Restart Eclipse.

</TabItem>
</Tabs>

For more information on adding certificates to Eclipse with `keytool`, refer to [IBM's documentation](https://www.ibm.com/docs/en/ram/7.5.4?topic=client-adding-server-public-certificate-eclipse).

### Cloud and infrastructure tools

Cloud service providers and infrastructure tools often require certificate configuration for API access and resource management.

#### Google Cloud

##### Google Cloud SDK

The commands below will set the Google Cloud SDK to use a Cloudflare certificate. For more information on configuring the Google Cloud SDK, refer to the [Google Cloud documentation](https://cloud.google.com/sdk/docs/proxy-settings).

1. Get curl's `cacert` bundle.

   ```sh
   curl --remote-name https://curl.se/ca/cacert.pem
   ```

2. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
3. Combine the certs into a single `.pem` file.

   ```sh
   cat cacert.pem certificate.pem > ~/ca.pem
   ```

4. Configure Google Cloud to use the combined `.pem`.

   ```sh
   gcloud config set core/custom_ca_certs_file ~/ca.pem
   ```

:::note

The file at `~/ca.pem` needs to remain in place in order for the `gcloud` utility to leverage it. If the file is moved, then you must re-run step 3 to point `gcloud` to the file's new location.
:::

##### Kaniko

If you use Kaniko with Google Cloud SDK, you must install a Cloudflare certificate in the [Kaniko CA store](https://docs.gitlab.com/ee/ci/docker/using_kaniko.html#using-a-registry-with-a-custom-certificate). For more information, refer to the [`gcloud` documentation](https://cloud.google.com/sdk/gcloud/reference/builds/submit).

##### Google Apps Manager (GAM)

Google Apps Manager (GAM) uses its own certificate store. To add a Cloudflare certificate to GAM, refer to the [GAM documentation](https://github.com/GAM-team/GAM/wiki/#using-gam-with-ssl--tls-mitm-inspection).

#### AWS CLI

##### Global config

To persistently set the location of the certificate:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Locate and open your [AWS configuration file](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html#cli-configure-files-where).
3. Configure the [`ca_bundle` setting](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html#cli-configure-files-settings) with the location of your certificate. For example:

   ```diff lang="ini" title=".aws/config"
   [default]
   region = us-west-1

   +ca_bundle = /path/to/certificate.pem
   ```

4. Restart your terminal.

##### Environment variable

To set the location of the certificate for use as an environment variable:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. In a terminal, set the [`AWS_CA_BUNDLE` environment variable](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) to the location of your certificate depending on your operating system.
3. Restart your terminal.

#### Azure CLI

##### Global config

To persistently set the location of the certificate:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Set the `REQUESTS_CA_BUNDLE` environment variable to point to your certificate depending on your operating system.

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

Add the following to your shell's configuration file (such as `~/.zshrc` or `~/.bash_profile`):

```sh
export REQUESTS_CA_BUNDLE="$HOME/Downloads/certificate.pem"
```

</TabItem>

<TabItem label="Windows" icon="seti:windows">

In PowerShell:

```powershell
[System.Environment]::SetEnvironmentVariable('REQUESTS_CA_BUNDLE', "$HOME\Downloads\certificate.pem", 'User')
```

</TabItem>
</Tabs>

3. Restart your terminal.

##### Per-command

To set the location of the certificate for a single command:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Set the `REQUESTS_CA_BUNDLE` environment variable when running the command:

   ```sh
   REQUESTS_CA_BUNDLE=/path/to/certificate.pem az <command>
   ```

For more information, refer to the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/use-cli-effectively#work-behind-a-proxy).

#### Boto3

Boto3, the AWS SDK for Python, can be configured to use a Cloudflare certificate in several ways.

##### Environment variable

To set the location of the certificate using an environment variable:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Set the `AWS_CA_BUNDLE` environment variable depending on your operating system.

<Tabs>
<TabItem label="macOS and Linux" icon="seti:shell">

Add the following to your shell's configuration file (such as `~/.zshrc` or `~/.bash_profile`):

```sh
export AWS_CA_BUNDLE="$HOME/Downloads/certificate.pem"
```

</TabItem>

<TabItem label="Windows" icon="seti:windows">

In PowerShell:

```powershell
[System.Environment]::SetEnvironmentVariable('AWS_CA_BUNDLE', "$HOME\Downloads\certificate.pem", 'User')
```

</TabItem>
</Tabs>

3. Restart your terminal.

##### AWS config file

To persistently set the location of the certificate in your AWS configuration:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Locate and open your [AWS configuration file](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html#cli-configure-files-where).
3. Configure the [`ca_bundle` setting](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html#cli-configure-files-settings) with the location of your certificate. For example:

   ```diff lang="ini" title=".aws/config"
   [default]
   region = us-west-1

   +ca_bundle = /path/to/certificate.pem
   ```

##### In code

To specify the certificate directly in your Python code:

1. [Download a Cloudflare certificate](#download-a-cloudflare-root-certificate) in `.pem` format.
2. Pass the certificate path when creating a Boto3 client or resource:

   ```python
   import boto3

   client = boto3.client(
       's3',
       verify='/path/to/certificate.pem'
   )
   ```

For more information, refer to the [Boto3 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html).

### Enterprise applications

Enterprise desktop applications and specialized tools may require custom certificate configuration.

#### Google Drive

To trust a Cloudflare root certificate in the Google Drive desktop application, follow the procedure for your operating system. These steps require you to [download a `.pem` certificate](#download-a-cloudflare-root-certificate).

<Tabs>
<TabItem label="macOS" icon="apple">

1. In a terminal, copy the contents of the Google Drive certificate file to a new certificate file in a permanent location, such as your Documents folder. For example:

   ```sh
   cat /Applications/"Google Drive.app"/Contents/Resources/roots.pem > ~/Documents/gdrivecerts.pem
   ```

2. Append the contents of the downloaded certificate to the end of the new file. For example:

   ```sh
   cat ~/Downloads/certificate.pem >> ~/Documents/gdrivecerts.pem
   ```

3. Apply the newly created root certificate to your Google Drive application. For example:

   ```sh
   sudo defaults write /Library/Preferences/com.google.drivefs.settings TrustedRootCertsFile "/Users/$(whoami)/Documents/gdrivecerts.pem"
   ```

You can verify the update with the following command.

```sh
defaults read /Library/Preferences/com.google.drivefs.settings
```

</TabItem>

<TabItem label="Windows" icon="seti:windows">

1. In an administrator PowerShell terminal, copy the contents of the Google Drive certificate file to a new certificate file in a permanent location, such as your Documents folder. For example:

   ```powershell
   Get-Content "C:\Program Files\Google\Drive File Stream\roots.pem" | Set-Content "$HOME\Documents\gdrivecerts.pem"
   ```

2. Append the contents of the downloaded certificate to the end of the new file. For example:

   ```powershell
   Get-Content "$HOME\Downloads\certificate.pem" | Add-Content "$HOME\Documents\gdrivecerts.pem"
   ```

3. Apply the newly created root certificate to your Google Drive application. For example:

   ```powershell
   Set-ItemProperty -Path "HKLM:\SOFTWARE\Google\DriveFS" -Name "TrustedRootCertsFile" -Value "$HOME\Documents\gdrivecerts.pem"
   ```

You can verify the update with the following command.

```powershell
Get-ItemProperty -Path "HKLM:\SOFTWARE\Google\DriveFS" | Select-Object TrustedRootCertsFile
```

</TabItem>
</Tabs>

For more information, refer to the [Google documentation](https://support.google.com/a/answer/7644837) for the `TrustedRootCertsFile` setting.

#### Minikube

To trust a Cloudflare root certificate in Minikube, refer to [x509: certificate signed by unknown authority](https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/#x509-certificate-signed-by-unknown-authority).

# Users

import { DirectoryListing, Render } from "~/components";

Manage users in your Zero Trust organization.

<DirectoryListing />

# Risk score

import { Render } from "~/components";

:::note

Only available on Enterprise plans.
:::

Cloudflare One risk scoring detects user activity and behaviors that could introduce risk to your organization's systems and data. Risk scores add user and entity behavior analytics (UEBA) to the Cloudflare One platform.

## User risk scoring

Cloudflare One assigns a risk score of Low, Medium, or High based on detections of users' activities, posture, and settings. A user's score is equal to the highest-level risk behavior they trigger.

### View a user's risk score

To view a user's risk score:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources**.
2. Select **Users**.
3. Select **Risk score** > **Risk scoring**.
4. Select a user's name to view their instances of risk behaviors, if any. You can select an instance of a risk behavior to view the log associated with the detection.

Users that have had their risk score [cleared](#clear-a-users-risk-score) will not appear in the table unless they trigger another risk behavior.

### Clear a user's risk score

If required, you can reset risk scores for specific users. Once reset, users will not appear in the associated risk table until they trigger another risk behavior.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources**.
2. Select **Risk score** > **Risk scoring**.
3. Select the user you want to clear the risk score for.
4. In **User risk overview**, select **Reset user risk**.
5. Select **Confirm**.

### Send risk score to Okta

In addition to controls in Cloudflare One, Okta users can send risk scores to Okta to apply SSO-level policies.

<Render file="send-risk-scores-okta" product="cloudflare-one" />

For more information on configuring user risk score within Okta, refer to the [Okta documentation](https://help.okta.com/oie/en-us/content/topics/itp/overview.htm).

While the Okta integration is turned on, Cloudflare One will send any user risk score updates to Okta, including score increases and resets. Score update events will appear in your [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/).

## Predefined risk behaviors

By default, all predefined behaviors are disabled. When a behavior is enabled, Cloudflare One will continuously evaluate all users within the organization for the behavior. You can [change the risk level](#change-risk-behavior-risk-levels) for predefined behaviors if the default assignment does not suit your environment.
| Risk behavior                         | Requirements                                                                                                | Description                                                                                                                                                                                                            | Evaluation timing |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| Impossible travel                      | [A configured Access application](/cloudflare-one/access-controls/applications/http-apps/)                                            | User has a successful login from two different locations that they could not have traveled between in that period of time. Matches will appear in your [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/). | Evaluated at each authentication and session-refresh event. |
| High number of DLP policies triggered  | [A configured DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/)                     | User has created a high number of DLP policy matches within a narrow frame of time. Matches will appear in your [Gateway activity logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/).                                  | Evaluated per-request in milliseconds. |
| SentinelOne threat detected on machine | [SentinelOne service provider integration](/cloudflare-one/integrations/service-providers/sentinelone/) | SentinelOne returns one or more configured [device posture attributes](/cloudflare-one/integrations/service-providers/sentinelone/#device-posture-attributes) for a user.                                          | Ingested via service-to-service API. Frequency is administrator-configurable during device posture setup to align with SentinelOne's API rate limits. |
| CrowdStrike Low ZTA security score| [CrowdStrike integration](/cloudflare-one/integrations/service-providers/crowdstrike/)| A user's device reports a score between 0-50 for any CrowdStrike Zero Trust Assessment attribute (OS Score, Overall Score, or Sensor Config score). Refer to [CrowdStrike device posture attributes](/cloudflare-one/integrations/service-providers/crowdstrike/#device-posture-attributes) for more information.| Ingested via service-to-service API. Frequency is administrator-configurable during device posture setup to align with CrowdStrike's API rate limits. |
| CrowdStrike Medium ZTA security score| [CrowdStrike integration](/cloudflare-one/integrations/service-providers/crowdstrike/)| A user's device reports a score between 50-79 for any CrowdStrike Zero Trust Assessment attribute (OS Score, Overall Score, or Sensor Config score). Refer to [CrowdStrike device posture attributes](/cloudflare-one/integrations/service-providers/crowdstrike/#device-posture-attributes) for more information. | Ingested via service-to-service API. Frequency is administrator-configurable during device posture setup to align with CrowdStrike's API rate limits. |
| Interaction with Malicious File | [Gateway AV scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/) or [File sandboxing](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/) | User uploads or downloads a file flagged as malicious by Gateway's AV scanner or file sandboxing. Risk is elevated even if the file is blocked. | Evaluated per-request in milliseconds. |
| Suspicious Security Domain Visited  | [Gateway DNS policies](/cloudflare-one/traffic-controls/dns-policies/)    | User visits a domain categorized as a security risk or security threat. Refer to [domain categories](/cloudflare-one/traffic-controls/domain-categories/) for the full list. Risk is elevated even if the traffic is blocked. | Evaluated per-request in milliseconds. |
| High Risk Domain Visited | [Gateway DNS policies](/cloudflare-one/traffic-controls/dns-policies/)    | User visits a domain categorized as questionable content, violence, or CIPA. Refer to [domain categories](/cloudflare-one/traffic-controls/domain-categories/) for the full list. Risk is elevated even if the traffic is blocked. | Evaluated per-request in milliseconds. |
## Manage risk behaviors

To toggle risk behaviors, go to **Risk score** > **Risk behaviors**.

### Enable risk behaviors

When a specific behavior is enabled, Cloudflare One will continuously monitor all users within the organization for any instances of that behavior.

If a user engages in an enabled risk behavior, their risk level is re-evaluated. Cloudflare One will update their risk score to the highest value between the current risk level and the risk level of the behavior they triggered.

### Disable risk behaviors

When a risk behavior is disabled, monitoring for future activity will cease. Previously detected risk behaviors will remain in the logs and associated with a user.

### Change risk behavior risk levels

You can change the risk level for a behavior at any time.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Teams & Resources**.
2. Go to **Users**.
3. Select **Risk score** > **Risk behaviors**.
2. Select the risk behavior you want to modify.
3. In the drop-down menu, choose your desired risk level.
4. Select **Save**.

## Use risk scores in Access policies

You can use risk scores to control access to applications protected by [Cloudflare Access](/cloudflare-one/access-controls/). This enables adaptive access control that responds to changes in user behavior.

To add a risk score requirement to an Access policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Policies**.
2. Create a new policy or select an existing policy to edit.
3. Add a rule with the _User Risk Score_ selector.
4. For **Value**, select one or more risk levels (Low, Medium, High, or Unscored). The rule matches a user only when their current risk level is one of the selected values.
5. Save the policy.

### Example: Block high-risk users

To prevent users with elevated risk scores from accessing sensitive applications, create a policy with the following configuration:

<Render file="access/example-exclude-risk-score" product="cloudflare-one" />

Users with a High risk score will be blocked, while users with Low or Medium scores can access the application.

For more information on Access policies, refer to [Access policies](/cloudflare-one/access-controls/policies/).

# View SCIM users and groups

import { CURL, Steps } from "~/components";

The SCIM population viewer shows users and groups provisioned through an identity provider (IdP). Use it to check which resources Cloudflare has received, inspect their attributes, and investigate provisioning issues.

Each view belongs to one identity provider. Its resource counts describe that provider's SCIM population, not your account's active users or occupied seats. For user activity and identity history, refer to the [User Registry](/cloudflare-one/team-and-resources/users/users/).

## Prerequisites

- [Configure SCIM provisioning](/cloudflare-one/team-and-resources/users/scim/) for your identity provider.
- Use an account role with permission to read the identity provider's SCIM population. Deleting resources requires permission to edit the identity provider.

## Open the population viewer

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Integrations** > **Identity providers**.
2. Find an identity provider with SCIM turned on.
3. In **SCIM resources**, select the user or group count.
4. Select **Users** or **Groups** to switch resource types.
</Steps>

![SCIM users table with search controls and blurred personal information](~/assets/images/cloudflare-one/identity/scim-population-viewer.png)

The **Users** table shows each user's name, username, email, and active state when those attributes are available. The active state reflects the SCIM `active` attribute. It does not indicate recent user activity or whether the latest provisioning request succeeded.

The **Groups** table shows group names and IdP resource IDs.

## Search for a resource

<Steps>
1. Select **Users** or **Groups**.
2. Choose a search field and enter a search value.
3. Select **Search**. To remove the search, select **Clear**.
</Steps>

| Resource type | Search fields                                                                     |
| ------------- | --------------------------------------------------------------------------------- |
| Users         | Username contains, Username, Email, Name, Cloudflare resource ID, IdP resource ID |
| Groups        | Name contains, Name, Cloudflare resource ID, IdP resource ID                      |

Use **Cloudflare resource ID** to match a resource with a [SCIM provisioning log](/cloudflare-one/insights/logs/dashboard-logs/scim-logs/) entry. Use **IdP resource ID** to match it with the corresponding record in your identity provider.

## Inspect resource details

For a user or group, select **View details**. Switch between **Formatted** and **JSON** to inspect the stored resource.

Details include resource IDs, profile attributes, membership, and metadata when present. The SCIM `id` is the Cloudflare resource ID. The `externalId` is the IdP resource ID. Metadata timestamps describe when a resource was created or modified. They do not report whether the latest provisioning request succeeded.

For a group with members, select **View all group members** to inspect its member list.

## Delete a resource

Deleting a resource removes it from the identity provider's SCIM population in Cloudflare. It does not delete the upstream user or group in your IdP. Make permanent provisioning changes in your identity provider.

:::caution
Do not use population cleanup as a substitute for revoking access. To remove a user's access, follow your [SCIM deprovisioning configuration](/cloudflare-one/team-and-resources/users/scim/#sync-users-and-groups-in-zero-trust-policies).
:::

<Steps>
1. Find the user or group you want to remove.
2. Open its context menu and select **Delete**.
3. Check the resource name in the confirmation dialog, then select **Delete**.
</Steps>

## Query the population through the API

Use the Cloudflare API to retrieve resources for an identity provider. Create an API token with **Access: Organizations, Identity Providers, and Groups Read** permission for the target account. Use this Cloudflare API token, not the SCIM bearer token configured in your IdP.

To list users:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/identity_providers/{identity_provider_id}/scim/users"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	query={{ page: "1", per_page: "25" }}
/>

To list groups:

<CURL
	url="https://api.cloudflare.com/client/v4/accounts/{account_id}/access/identity_providers/{identity_provider_id}/scim/groups"
	headers={{ Authorization: "Bearer $CLOUDFLARE_API_TOKEN" }}
	query={{ page: "1", per_page: "25" }}
/>

Responses contain a `result` array and `result_info` pagination metadata. Increase `page` to retrieve subsequent pages. Both endpoints support up to 100 resources per page.

For filters, individual resource lookups, and deletion endpoints, refer to the [SCIM API reference](/api/resources/zero_trust/subresources/identity_providers/subresources/scim/).

## Troubleshoot missing resources

- **Resource counts differ from your IdP:** Check which users and groups the IdP assigns to the SCIM application. The viewer shows resources provisioned to this integration, not the entire IdP directory.
- **Users appear, but groups do not:** Check the IdP's group provisioning settings. Okta requires **Push Groups**. Microsoft Entra ID provisions groups within the configured provisioning scope.
- **A count shows Users unavailable or Groups unavailable:** Cloudflare could not retrieve that resource count. This is not a zero-resource result. Open the viewer and check for an error.
- **A resource is missing or has unexpected attributes:** Compare [SCIM provisioning logs](/cloudflare-one/insights/logs/dashboard-logs/scim-logs/) with your IdP's provisioning logs. Match records by resource ID to investigate failed requests or attribute differences.

# SCIM provisioning

import { Render } from "~/components";

System for Cross-domain Identity Management (SCIM) is an open standard protocol that allows identity providers to synchronize user identity information with cloud applications and services. After configuring SCIM, user identities that you create, edit, or delete in the identity provider are automatically updated across all supported applications. This makes it easier for IT admins to onboard new users, update their groups and permissions, and revoke access in the event of an employee termination or security breach.

:::note
This section covers SCIM provisioning for Cloudflare Zero Trust only. To provision access to your Cloudflare account, you will need to set up a distinct [dashboard SSO SCIM integration](/fundamentals/account/account-security/scim-setup/) in your IdP. You can assign users and groups to this new SCIM application to define who can access the Cloudflare dashboard.

Users provisioned via the [Zero Trust SCIM integration](#sync-users-and-groups-in-zero-trust-policies) will not have access to your Cloudflare dashboard unless you have manually added them to your [Cloudflare dashboard SSO application](/fundamentals/manage-members/dashboard-sso/).
:::

## Supported identity providers

Cloudflare One supports SCIM provisioning for all SAML and OIDC identity providers that use SCIM version `2.0`.

For Google's built-in SCIM provisioning, you must use a [Google Workspace (SAML/SCIM) integration](/cloudflare-one/integrations/identity-providers/google-workspace-saml/), not the dedicated Google Workspace integration. Google's custom SCIM profiles are in beta and provision users; configure [SAML group membership mapping](/cloudflare-one/integrations/identity-providers/google-workspace-saml/#1-create-a-custom-saml-app-in-google-workspace) separately to send groups during authentication.

## Sync users and groups in Zero Trust policies

Cloudflare Access can automatically deprovision users from Zero Trust after they are deactivated in the identity provider and display synchronized group names in the Access and Gateway policy builders. Cloudflare does not provision new users in Zero Trust when they are added to the identity provider -- users must first register a device with the Cloudflare One Client or authenticate to an Access application.

<Render file="access/scim-policy-behavior" product="cloudflare-one" />

To set up SCIM for Zero Trust, refer to our [SSO integration](/cloudflare-one/integrations/identity-providers/) guides.

## View provisioned users and groups

Use the [SCIM population viewer](/cloudflare-one/team-and-resources/users/scim-population-viewer/) to inspect users and groups provisioned through each identity provider. You can search for resources, inspect their attributes and group membership, and compare resource IDs with SCIM provisioning logs.

## Common provider-specific issues

SCIM behavior depends on the identity provider configuration as well as Cloudflare.

Common issues include:

- **Okta**: User sync and group sync are separate. Make sure **Push Groups** is configured if you expect groups to appear in Zero Trust policies.
- **Microsoft Entra ID**: Group sync only occurs for groups included in the provisioning scope. The `userName` attribute should match the user's email address in Cloudflare One.
- **Google Workspace**: SCIM requires a generic SAML integration. Selecting Google groups as a provisioning scope limits which users are provisioned; it does not send group membership. Configure the SAML app to send a `groups` attribute for group-based policies.

If users appear but groups do not, verify the IdP-side SCIM app first before troubleshooting Cloudflare policy behavior.

# Seat management

import { Render } from "~/components";

Cloudflare One subscriptions consist of seats that active users in your account consume. Active users are added to Cloudflare One through any [authentication event](#authentication-events).

The amount of seats available in your Cloudflare One account depends on the amount of users you purchase. If you want to increase the number of seats available, you will have to purchase more users. Learn more about adding and removing seats from your account in the [Cloudflare One FAQ](/cloudflare-one/faq/getting-started-faq/#how-do-i-change-my-subscription-plan).

## Authentication events

A user consumes a seat when they perform an authentication event. For Access, this is any Cloudflare Access authentication event, such as a login to the [App Launcher](/cloudflare-one/access-controls/access-settings/app-launcher/) or an application. For Gateway, this is when any devices associated with the user connect to Cloudflare One within the [specified period](#enable-seat-expiration).

If either one of these events occurs, that user's identity is added as an Active user to Cloudflare One and consumes one seat from your plan. The user will occupy and consume a single seat regardless of the number of applications accessed or login events from their user account. Once the total amount of seats in the subscription has been consumed, additional users who attempt to log in are blocked.

A user who authenticates will hold their seat until you [remove the user](#remove-a-user) from your account. By default, inactive users will not be [automatically removed](#enable-seat-expiration) from your account. You can remove a single user or all users at any time, and those users will immediately stop counting against the seat count defined in your subscription.

If you notice a number of accounts greater than the number of your users, you may need to configure an Access [bypass policy](/cloudflare-one/access-controls/policies/#bypass). Alternatively, you can use Access [service tokens](/cloudflare-one/access-controls/service-credentials/service-tokens/) to allow access to applications without consuming seats.

## Seat management and device registrations

[Removing a user](/cloudflare-one/team-and-resources/users/seat-management/#remove-a-user) determines whether that user consumes a billable seat, but does not [prevent users from accessing resources](/cloudflare-one/team-and-resources/devices/device-registration/#remove-user-access) behind Cloudflare Access.

Removing a user will delete all device registrations associated with the user. For more information about managing device registrations, refer to [Device registration](/cloudflare-one/team-and-resources/devices/device-registration/).

## Manage users

### Check number of seats used

To check the number of seats consumed by active users in your organization, log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust**. **Cloudflare One overview** will display the amount of seats consumed and the remaining amount available. For more details on your users, go to **Team & Resources** > **Users**.

### Revoke a user

When you revoke a user, this action will terminate active sessions, but will not remove the user's consumption of an active seat.

To revoke a user from your Zero Trust Organization:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Users**.
2. Select the checkbox next to a user with an **Active** status in the **Seat usage** column.
3. Select **Action** > **Revoke**.
4. Select **Revoke sessions**.

Revoked users can still log in if your policies allow them. To prevent a user from authenticating, you must remove them from your [device enrollment policies](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/) or from your Identity Provider (IdP).

### Remove a user

<Render file="users/remove-user-from-seat" product="cloudflare-one" />

### Enable seat expiration

Cloudflare One can automatically remove any user who does not log in to an Access application or whose device does not show any Gateway activity for the specified period (between one month and one year). To determine if a user will be removed, Cloudflare looks for any authentication events and checks the **Last seen** value for all of the user's devices. If both of those are outside the expiration window, the user will be removed and will no longer count against your number of seats. This process occurs once daily for an account.

To enable user seat expiration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Settings** > **Admin controls**.
2. In **Remove inactive users from seats**, select **Edit**.
3. Select an inactivity time from the dropdown menu.
4. Select **Save**.

If a user is removed but authenticates later, they will consume a seat again.

For more information about removing a user for Access and Gateway, refer to the [FAQ](/cloudflare-one/faq/getting-started-faq/#removing-users).

# User logs

import { GlossaryTooltip } from "~/components"

User logs show a list of all users who have authenticated to Cloudflare One. For each user who has logged in, you can view their enrolled devices, login history, seat usage, and identity used for policy enforcement.

## View user logs

In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Users**.

This page lists all users who have registered the Cloudflare One Client or authenticated to a Cloudflare Access application. You can select a user's name to view detailed logs, [revoke their session](/cloudflare-one/access-controls/access-settings/session-management/#revoke-user-sessions), or [remove their seat](/cloudflare-one/team-and-resources/users/seat-management/).

### Available logs

* **User Registry identity**: Select the user's name to view their last seen identity. This identity is used to evaluate Gateway policies and Cloudflare One Client [device profiles](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/). A refresh occurs when the user re-authenticates the device client, logs into an Access application, or has their IdP group membership updated via <GlossaryTooltip term="SCIM" link="/cloudflare-one/team-and-resources/users/scim/">SCIM provisioning</GlossaryTooltip>. To track how the user's identity has changed over time, go to the **Audit logs** tab.
* **Session identities**: The user's active sessions, the identity used to authenticate each session, and when each session will [expire](/cloudflare-one/access-controls/access-settings/session-management/).
* **Devices**: Devices registered to the user via the Cloudflare One Client.
* **Recent activities**: The user's five most recent Access login attempts. For more details, refer to your [authentication audit logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/#authentication-logs).

# Applications and app types

import { GlossaryDefinition, GlossaryTooltip } from "~/components";
import GranularControlApplicationsList from "~/components/cf/GranularControlApplicationsList.astro";

Gateway allows you to create DNS, Network, and HTTP policies based on applications and application types. Because a single application often spans multiple hostnames, selecting an application by name is easier than writing separate rules for each hostname. You can select individual applications or application types to filter specific traffic on your network.

## Applications

When you choose the _Application_ selector in a Gateway policy builder, the **Value** field will include all supported applications and their respective app types. Alternatively, you can use the [Gateway API](/api/resources/zero_trust/subresources/gateway/subresources/app_types/methods/list/) to fetch a list of applications, app types, and ID numbers.

To manage a consolidated list of applications across Cloudflare One, you can use the [Application Library](/cloudflare-one/team-and-resources/app-library/).

### Custom applications

You can [create a custom application in the Application Library](/cloudflare-one/team-and-resources/app-library/custom-applications/#create-a-custom-application) and select it by name in the existing **Application** selector for DNS, HTTP, and network policies. Custom and Cloudflare-managed applications can appear in the same condition. Custom applications are available only in the account that owns them.

For API and Terraform rules, use the application's assigned numeric ID with the existing application fields, such as `app.ids`, `app.hosts_ids`, and `app.supports_ids`. The human-readable **Application ID** entered during creation is not the numeric ID used in a rule expression.

To retrieve custom application IDs, list your account's applications with `GET /accounts/{account_id}/resource-library/applications`. The legacy Gateway `app_types` endpoint returns the Cloudflare-managed catalog and does not include custom applications.

For example, replace `<CUSTOM_APPLICATION_NUMERIC_ID>` with the numeric ID to match the application:

```txt
any(app.ids[*] in {<CUSTOM_APPLICATION_NUMERIC_ID>})
```

Editing the application's network identifiers updates what referencing rules match after the definition propagates to Gateway. You do not need to replace its ID or recreate the rules. A custom application cannot be deleted while a Gateway rule or reusable profile references it, including a rule that is turned off. Refer to [Delete a custom application](/cloudflare-one/team-and-resources/app-library/custom-applications/#delete-a-custom-application).

#### Custom application matching

Gateway matches a custom application when the hostname or destination IP matches its definition. If the definition includes both hostnames and IP subnets, either can match independently. Multiple entries within each field are also alternatives.

Hostnames match the specified domain and its subdomains. For example, `example.com` matches both `example.com` and `api.example.com`. Choose specific hostnames when you need to limit the scope of a custom application.

In HTTP and network policies, configured port protocols add a restriction to the hostname or IP match. The destination must also match one of the configured port/protocol pairs or inclusive ranges. If **Port protocols** is empty, the definition applies to any port and protocol. A port/protocol match alone does not identify an application.

For example, define a custom application with the hostname `crm.example.com`, the IP subnet `192.0.2.0/24`, and the port protocol `tcp/443`:

| Traffic                                                                                     | Matches the custom application in an HTTP or network policy? |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `crm.example.com` on TCP port `443`, outside the configured IP subnet                       | Yes. The hostname and port/protocol match.                   |
| A destination in `192.0.2.0/24` on TCP port `443`, with a different hostname or no hostname | Yes. The IP subnet and port/protocol match.                  |
| `crm.example.com` on TCP port `8443`                                                        | No. The port does not match.                                 |
| A destination in `192.0.2.0/24` on UDP port `443`                                           | No. The protocol does not match.                             |
| An unrelated hostname and IP on TCP port `443`                                              | No. Neither the hostname nor the IP subnet matches.          |

DNS policies match the query hostname before resolution and can also match resolved IP addresses after resolution. DNS application matching does not use the application's port/protocol constraints because a DNS query does not identify the destination port of the subsequent connection. For the example application, a query for `crm.example.com` can match even when the subsequent connection would use port `8443`.

[Support hostnames](#support-hostnames) identify application dependencies. Gateway includes them in Allow policies and excludes them from Block policies, using the same behavior as support hostnames for Cloudflare-managed applications. Adding support hostnames does not replace the requirement for a primary hostname or IP subnet. Refer to [custom application limits](/cloudflare-one/team-and-resources/app-library/custom-applications/#custom-application-limits) for the combined hostname limit, IP subnet limits, and port formats.

## App types

Gateway sorts applications into the following app type groups:

| Value                                          | Definition                                                                                                                  |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Artificial Intelligence                        | AI assistance applications                                                                                                  |
| Business                                       | Applications used for general business purposes                                                                             |
| Collaboration & Online Meetings                | Business communication and collaboration applications                                                                       |
| Dating                                         | Online dating applications                                                                                                  |
| Development                                    | Software development and development operations applications                                                                |
| Education                                      | Applications used for educational purposes and e-learning                                                                   |
| Email                                          | Email applications                                                                                                          |
| Entertainment & Events                         | Applications used for entertainment content and event information                                                           |
| Encrypted DNS                                  | DNS encryption applications                                                                                                 |
| File Sharing                                   | File sharing applications                                                                                                   |
| Finance & Accounting                           | Financial and accounting applications                                                                                       |
| Food & Drink                                   | Applications related to food delivery and recipe services                                                                   |
| Gaming                                         | Games and gaming applications                                                                                               |
| Health & Fitness                               | Applications used for health monitoring and fitness tracking                                                                |
| Human Resources                                | Employee management applications and workforce tools                                                                        |
| Instant Messaging                              | Instant messaging applications                                                                                              |
| IT Management                                  | IT deployment management applications                                                                                       |
| Legal                                          | Legal tools and applications                                                                                                |
| Lifestyle                                      | Applications related to lifestyle and personal interests                                                                    |
| Music & Audio Streaming                        | Applications used for streaming music and audio                                                                             |
| Navigation                                     | Applications used for maps and navigation services                                                                          |
| News, Books, & Magazines                       | Applications delivering news, books, and magazine content                                                                   |
| Photography & Graphic Design                   | Applications used for photography and graphic design                                                                        |
| Productivity                                   | Business and productivity applications                                                                                      |
| Public Cloud                                   | Public cloud infrastructure management applications                                                                         |
| Sales & Marketing                              | Sales and marketing applications                                                                                            |
| Search Engines                                 | Web search engines and applications                                                                                         |
| Security                                       | Information security applications, including <GlossaryTooltip term="shadow IT">shadow IT</GlossaryTooltip>                  |
| Shopping                                       | Online shopping applications                                                                                                |
| Social Networking                              | Social networking applications                                                                                              |
| Sports                                         | Sports streaming and news applications                                                                                      |
| Travel                                         | Travel related applications                                                                                                 |
| Video Streaming & Editing                      | Applications used for streaming and editing video                                                                           |
| [Do Not Inspect](#do-not-inspect-applications) | Applications incompatible with the TLS certificate required by the [Gateway proxy](/cloudflare-one/traffic-controls/proxy/) |

## Application hostnames

An application like Google Drive uses its own hostnames (for example, `drive.google.com`) and shared resources used by other applications (for example, `accounts.google.com` for login). Gateway separates these into [hostnames](#hostnames) and [support hostnames](#support-hostnames) so you can control the behavior of each application independently.

### Hostnames

Hostnames are domains that are core to the application and not [used by other applications](#overlapping-hostnames). These are the domains that Gateway blocks when you block an application. The App Library surfaces these hostnames in the [Hostnames table](/cloudflare-one/team-and-resources/app-library/#overview) for an application.

### Support hostnames

Support hostnames are shared resources that applications depend on for content delivery, authentication, or third-party integrations. Because multiple applications share these hostnames, blocking them can cause unexpected side effects.

For example, assume that `file-sharing-service.com` relies on `content-delivery.com`. If you allow access to `file-sharing-service.com` and its associated subdomains but not `content-delivery.com`, some of the functionality of `file-sharing-service.com` may break when Gateway matches the traffic.

To prevent this, Gateway only uses support hostnames in Allow policies — it will allow support hostname connections but will not block them. For example, many Google applications use `accounts.google.com` for authentication. If you create an Allow policy for an application that lists `accounts.google.com` as a support hostname, Gateway will allow both `accounts.google.com` and the application's own domains.

## Application controls

When you use the [_Application_ selector](/cloudflare-one/traffic-controls/http-policies/#granular-controls) in an HTTP policy with the _is_ operator, you can choose specific actions and operations to match application traffic. Supported applications and operations include:

<GranularControlApplicationsList />

For more information, refer to [Application Granular Controls](/cloudflare-one/traffic-controls/http-policies/granular-controls/).

## Usage

### Overlapping hostnames

Overlapping hostnames are most common for vendors with many applications, such as Google or Meta. When you use the Application selector in Gateway policies, actions taken by Gateway will be limited to the specific application defined. Gateway will also log other applications that use the same hostnames, but it will not take action unless the application was matched by the policy. For example, both the Facebook and Facebook Messenger apps use the `chat-e2ee.facebook.com` hostname. When evaluating traffic to the Facebook Messenger app, Gateway will only take action on Facebook Messenger traffic but may log both the Facebook and Facebook Messenger apps.

To ensure Gateway evaluates traffic with your desired precedence, order your most specific policies with the highest priority according to [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#priority-within-a-policy-builder).

### Do Not Inspect applications

Gateway automatically groups applications incompatible with TLS decryption into the _Do Not Inspect_ app type. As Cloudflare identifies incompatible applications, Gateway will periodically update this app type to add new applications. To ensure Gateway does not intercept any current or future incompatible traffic, you can [create a Do Not Inspect HTTP policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) with the entire _Do Not Inspect_ app type selected.

When managing applications with the [Application Library](/cloudflare-one/team-and-resources/app-library/), Do Not Inspect applications will appear under the corresponding application. For example, the App Library will group _Google Drive (Do Not Inspect)_ under **Google Drive**.

:::note[Install Cloudflare certificate manually to allow TLS decryption]
Instead of creating a Do Not Inspect policy for an application, you may be able to configure the application to [trust a Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/#5-add-the-certificate-to-applications). Doing so will allow the application to function without losing visibility into your traffic.
:::

#### TLS decryption limitations

Applications can be incompatible with [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) for various reasons:

- <GlossaryDefinition
  	term="certificate pinning"
  	prepend="**Certificate pinning**: Certificate pinning is "
  />
- **Non-web traffic**: Some applications send non-web traffic over TLS, such as Session Initiation Protocol (SIP) for voice and video calls and Extensible Messaging and Presence Protocol (XMPP) for chat. Gateway cannot inspect these protocols.

#### Microsoft 365 integration

To optimize performance for Microsoft 365 applications and services, you can bypass TLS decryption by turning on the Microsoft 365 traffic integration. This will create a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) for all [Microsoft 365 domains and IP addresses](https://docs.microsoft.com/en-us/microsoft-365/enterprise/microsoft-365-ip-web-service) specified by Microsoft. This policy also uses Cloudflare intelligence to identify other Microsoft 365 traffic not explicitly defined.

To turn on the Microsoft 365 integration:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings** > **Policy settings**.
2. In **Bypass decryption of Microsoft 365 traffic**, select **Create policy**.
3. To verify the policy was created, select **View policy**. Alternatively, go to **Traffic policies** > **HTTP policies**. A policy named Microsoft 365 Auto Generated will be enabled in your list.

All future Microsoft 365 traffic will bypass Gateway logging and filtering. To disable this behavior, turn off or delete the policy.

### Terraform

Terraform users can retrieve the app types list with the `cloudflare_zero_trust_gateway_app_types_list` data source. This allows you to create Gateway policies with the application's name rather than its numeric ID. For example:

```tf
data "cloudflare_zero_trust_gateway_app_types_list" "gateway_apptypes" {
  account_id = var.cloudflare_account_id
}

locals {
  apptypes_map = merge([
    for c in data.cloudflare_zero_trust_gateway_app_types_list.gateway_apptypes.result :
    { (c.name) = c.id }
  ]...)
}

resource "cloudflare_zero_trust_gateway_policy" "zt_block_dns_apps" {
  account_id = var.cloudflare_account_id
  name       = "DNS Blocked apps"
  action     = "block"
  traffic    = "any(app.ids[*] in {${join(" ", [
    local.apptypes_map["Discord"],
    local.apptypes_map["GoToMeeting"],
    local.apptypes_map["Greenhouse"],
    local.apptypes_map["Zelle"],
    local.apptypes_map["Microsoft Visual Studio"]
  ])}})"
}
```

# Domain categories

Cloudflare Gateway allows you to block known and potential security risks on the public Internet, as well as specific categories of content. Domains are categorized by [Cloudforce One](/security-center/cloudforce-one/), Cloudflare's threat intelligence solution. To review the categories for a specific domain, use [Cloudflare Radar](/radar/glossary/#content-categories).

Cloudflare categorizes domains into content categories and security categories, which cover security risks and security threats:

- **Content categories**: An upstream vendor supplies content categories for domains. These categories help us organize domains into broad topic areas. However, the specific criteria and methods used by our vendor may not be disclosed.
- **Security risks**: Cloudflare determines security risks for domains using internal models. These models analyze various factors, including the age of a domain and its reputation. This allows us to identify potentially risky domains.
- **Security threats**: To identify malicious domains that pose security threats, Cloudflare employs a mix of internal data sources, machine learning models, commercial feeds, and open-source threat intelligence.

You can block security and content categories by creating DNS or HTTP policies. Once you have configured your policies, you will be able to inspect network activity and the associated categories in your Gateway logs.

To request changes to a domain's categorization, refer to [Change categorization](/security-center/investigate/change-categorization/). For more information on investigating potentially risky domains, refer to [Investigate threats](/security-center/investigate/investigate-threats/#domain).

:::note[Subdomain category]

Subdomains that have not been assigned a category will inherit the category of their parent domain. When Gateway categorizes a subdomain, the subdomain will carry only its own category. Categorized subdomains will not inherit their parent domain's categories.
:::

## Security categories

| Category                      | Definition                                                                                                                                                                                                            |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Anonymizer                    | Sites that allow users to surf the Internet anonymously.                                                                                                                                                              |
| Brand Embedding               | Sites that imitate a verified brand, for example `facobook.com`.                                                                                                                                                      |
| Command and Control & Botnet  | Sites that are queried by compromised devices to exfiltrate information or potentially infect other devices in a network.                                                                                             |
| Compromised Domain            | Sites where a legitimate domain has been compromised or taken over and had malicious content planted or injected.                                                                                                     |
| Cryptomining                  | Sites that mine cryptocurrency by taking over the user's computing resources.                                                                                                                                         |
| DGA Domains                   | Domains generated programmatically by Domain Generation Algorithms (DGA) associated with malware. These algorithmically created domain names change frequently, making them harder to block individually.             |
| DNS Tunneling                 | Domains with detected DNS tunneling activity, including attempts to encode or exfiltrate data in DNS queries and responses (for example, in `TXT` records) or to use DNS for command-and-control (C2) communications. |
| Malware                       | Sites hosting malicious content and other compromised websites.                                                                                                                                                       |
| Phishing                      | Domains that are known for stealing personal information.                                                                                                                                                             |
| Potentially Unwanted Software | Domains that distribute software that may come bundled with other less legitimate software or functionality, like toolbars, adware, and grayware.                                                                     |
| Private IP Address            | Domains that resolve to private IP Addresses.                                                                                                                                                                         |
| Scam                          | Fraudulent websites and schemes designed to trick victims into giving away money or personal information.                                                                                                             |
| Spam                          | Sites that are known for targeting users with unwanted sweepstakes, surveys, and advertisements.                                                                                                                      |
| Spyware                       | Sites that are known to distribute or contain code that displays unwanted advertisements or that gathers user information without the user's knowledge.                                                               |

## Content categories

| Category               | Definition                                                                                                                                                                        |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ads                    | Sites that are hosting content related to advertising.                                                                                                                            |
| Adult Themes           | Sites that are hosting content related to pornography, nudity, sexuality, and other adult themes.                                                                                 |
| Business & Economy     | Sites that are related to business, economy, finance, education, science and technology.                                                                                          |
| Child Abuse            | Sites hosting child abuse content.                                                                                                                                                |
| CIPA                   | Sites related to aiding schools and organizations in abiding by Children's Internet Protection Act (CIPA) requirements.                                                           |
| Education              | Sites hosting educational content that are not included in other categories like Science, Technology or Educational institutions.                                                 |
| Entertainment          | Sites that are hosting entertaining content that are not included in other categories like Comic books, Audio streaming, Video streaming etc.                                     |
| Gambling               | Sites that are providing online gambling or are related to gambling.                                                                                                              |
| Government & Politics  | Sites related to government and politics.                                                                                                                                         |
| Health                 | Sites containing information about health and fitness.                                                                                                                            |
| Information Technology | Sites related to information technology.                                                                                                                                          |
| Internet Communication | Sites hosting applications that are used for communication like chat, mail etc.                                                                                                   |
| Job Search & Careers   | Sites that facilitate searching for jobs and careers.                                                                                                                             |
| Miscellaneous          | Sites that are not included in the listed security and content categories.                                                                                                        |
| Questionable Content   | Sites hosting content that are related to hacking, piracy, profanity and other questionable activities.                                                                           |
| Real Estate            | Sites related to real estate.                                                                                                                                                     |
| Religion               | Sites hosting content about religion, alternative religion, religious teachings, religious groups, and spirituality.                                                              |
| Security Risks         | Sites that are [new or misconfigured](#security-risk-subcategories). We recommend that you allow or isolate this content category to avoid accidentally blocking trusted domains. |
| Shopping & Auctions    | Sites that are hosting content related to ecommerce, coupons, shopping, auctions and marketplaces.                                                                                |
| Social & Family        | Sites related to society and lifestyle.                                                                                                                                           |
| Society & Lifestyle    | Sites hosting information about lifestyle that are not included in other categories like fashion, food & drink etc.                                                               |
| Sports                 | Sites related to sports & recreation.                                                                                                                                             |
| Technology             | Sites hosting information about technology that are not included in the science category.                                                                                         |
| Travel                 | Sites that contain information about listings, reservations, services for travel.                                                                                                 |
| Vehicles               | Sites related vehicles, automobiles, including news, reviews, and other hobbyist information.                                                                                     |
| Violence               | Sites hosting and/or promoting violent content.                                                                                                                                   |
| Weather                | Sites related to weather.                                                                                                                                                         |

### Miscellaneous subcategories

| Category      | Definition                                                                   |
| ------------- | ---------------------------------------------------------------------------- |
| Login Screens | Sites hosting login screens that might also be included in other categories. |
| Miscellaneous | Sites that do not belong to other content categories.                        |
| No Content    | Sites that have no content.                                                  |
| Redirect      | Domains that redirect to other sites.                                        |
| Unreachable   | Domains that resolve to unreachable IP addresses.                            |

### Security risk subcategories

| Category                  | Definition                                                             |
| ------------------------- | ---------------------------------------------------------------------- |
| New Domains               | Domains registered within the past 30 days.                            |
| Newly Seen Domains        | Domains that were resolved for the first time within the past 30 days. |
| Parked & For Sale Domains | Domains that are not connected to a hosting service.                   |

### Category and subcategory IDs

| Category ID | Category Name          | Subcategory ID | Subcategory Name                           |
| ----------- | ---------------------- | -------------- | ------------------------------------------ |
| 1           | Ads                    | 66             | Advertisements                             |
| 2           | Adult Themes           | 67             | Adult Themes                               |
| 2           | Adult Themes           | 125            | Nudity                                     |
| 2           | Adult Themes           | 133            | Pornography                                |
| 3           | Business & Economy     | 186            | Brokerage & Investing                      |
| 3           | Business & Economy     | 75             | Business                                   |
| 3           | Business & Economy     | 89             | Economy & Finance                          |
| 3           | Business & Economy     | 183            | Cryptocurrency                             |
| 3           | Business & Economy     | 185            | Personal Finance                           |
| 6           | Education              | 90             | Education                                  |
| 6           | Education              | 91             | Educational Institutions                   |
| 6           | Education              | 189            | Reference                                  |
| 6           | Education              | 144            | Science                                    |
| 6           | Education              | 150            | Space & Astronomy                          |
| 7           | Entertainment          | 70             | Arts                                       |
| 7           | Entertainment          | 74             | Audio Streaming                            |
| 7           | Entertainment          | 76             | Cartoons & Anime                           |
| 7           | Entertainment          | 79             | Comic Books                                |
| 7           | Entertainment          | 92             | Entertainment                              |
| 7           | Entertainment          | 96             | Fine Art                                   |
| 7           | Entertainment          | 100            | Gaming                                     |
| 7           | Entertainment          | 106            | Home Video/DVD                             |
| 7           | Entertainment          | 107            | Humor                                      |
| 7           | Entertainment          | 116            | Magazines                                  |
| 7           | Entertainment          | 120            | Movies                                     |
| 7           | Entertainment          | 121            | Music                                      |
| 7           | Entertainment          | 122            | News & Media                               |
| 7           | Entertainment          | 127            | Paranormal                                 |
| 7           | Entertainment          | 139            | Radio                                      |
| 7           | Entertainment          | 156            | Television                                 |
| 7           | Entertainment          | 164            | Video Streaming                            |
| 8           | Gambling               | 99             | Gambling                                   |
| 9           | Government & Politics  | 190            | Charity and Non-profit                     |
| 9           | Government & Politics  | 101            | Government/Legal                           |
| 9           | Government & Politics  | 137            | Politics, Advocacy, and Government-Related |
| 10          | Health                 | 103            | Health & Fitness                           |
| 10          | Health                 | 146            | Sex Education                              |
| 12          | Internet Communication | 77             | Chat                                       |
| 12          | Internet Communication | 98             | Forums                                     |
| 12          | Internet Communication | 108            | Information Security                       |
| 12          | Internet Communication | 110            | Instant Messengers                         |
| 12          | Internet Communication | 111            | Internet Phone & VOIP                      |
| 12          | Internet Communication | 118            | Messaging                                  |
| 12          | Internet Communication | 126            | P2P                                        |
| 12          | Internet Communication | 129            | Personal Blogs                             |
| 12          | Internet Communication | 168            | Webmail                                    |
| 12          | Internet Communication | 172            | Photo Sharing                              |
| 13          | Job Search & Careers   | 113            | Job Search & Careers                       |
| 15          | Miscellaneous          | 115            | Login Screens                              |
| 15          | Miscellaneous          | 119            | Miscellaneous                              |
| 15          | Miscellaneous          | 124            | No Content                                 |
| 15          | Miscellaneous          | 141            | URL Alias/Redirect                         |
| 15          | Miscellaneous          | 161            | Unreachable                                |
| 17          | Questionable Content   | 85             | Deceptive Ads                              |
| 17          | Questionable Content   | 87             | Drugs                                      |
| 17          | Questionable Content   | 102            | Hacking                                    |
| 17          | Questionable Content   | 135            | Profanity                                  |
| 17          | Questionable Content   | 138            | Questionable Activities                    |
| 17          | Questionable Content   | 157            | Militancy, Hate & Extremism                |
| 17          | Questionable Content   | 162            | Unreliable Information                     |
| 18          | Real Estate            | 140            | Real Estate                                |
| 19          | Religion               | 142            | Religion & Spirituality                    |
| 20          | Safe for Kids          | 143            | Safe for Kids                              |
| 21          | Security threats       | 68             | Anonymizer                                 |
| 21          | Security threats       | 80             | Command and Control & Botnet               |
| 21          | Security threats       | 187            | Compromised Domain                         |
| 21          | Security threats       | 83             | Cryptomining                               |
| 21          | Security threats       | 117            | Malware                                    |
| 21          | Security threats       | 131            | Phishing                                   |
| 21          | Security threats       | 188            | Potentially unwanted software              |
| 21          | Security threats       | 134            | Private IP Address                         |
| 21          | Security threats       | 151            | Spam                                       |
| 21          | Security threats       | 153            | Spyware                                    |
| 21          | Security threats       | 175            | DNS Tunneling                              |
| 21          | Security threats       | 176            | Domain Generation Algorithm                |
| 21          | Security threats       | 178            | Brand Embedding                            |
| 21          | Security threats       | 191            | Scam                                       |
| 22          | Shopping & Auctions    | 73             | Auctions & Marketplaces                    |
| 22          | Shopping & Auctions    | 82             | Coupons                                    |
| 22          | Shopping & Auctions    | 88             | Ecommerce                                  |
| 22          | Shopping & Auctions    | 148            | Shopping                                   |
| 24          | Society & Lifestyle    | 71             | Arts & Crafts                              |
| 24          | Society & Lifestyle    | 72             | Astrology                                  |
| 24          | Society & Lifestyle    | 78             | Clothing                                   |
| 24          | Society & Lifestyle    | 84             | Dating & Relationships                     |
| 24          | Society & Lifestyle    | 86             | Digital Postcards                          |
| 24          | Society & Lifestyle    | 93             | Parenting                                  |
| 24          | Society & Lifestyle    | 94             | Fashion                                    |
| 24          | Society & Lifestyle    | 97             | Food & Drink                               |
| 24          | Society & Lifestyle    | 104            | Hobbies & Interests                        |
| 24          | Society & Lifestyle    | 105            | Home & Garden                              |
| 24          | Society & Lifestyle    | 114            | Lifestyle                                  |
| 24          | Society & Lifestyle    | 130            | Pets                                       |
| 24          | Society & Lifestyle    | 132            | Photography                                |
| 24          | Society & Lifestyle    | 136            | Professional Networking                    |
| 24          | Society & Lifestyle    | 147            | Sexuality                                  |
| 24          | Society & Lifestyle    | 149            | Social Networks                            |
| 24          | Society & Lifestyle    | 154            | Swimsuits                                  |
| 24          | Society & Lifestyle    | 158            | Tobacco                                    |
| 24          | Society & Lifestyle    | 173            | Body Art                                   |
| 24          | Society & Lifestyle    | 174            | Lingerie & Bikini                          |
| 24          | Society & Lifestyle    | 181            | Alcohol                                    |
| 25          | Sports                 | 152            | Sports                                     |
| 26          | Technology             | 69             | APIs                                       |
| 26          | Technology             | 81             | Content Servers                            |
| 26          | Technology             | 95             | File Sharing                               |
| 26          | Technology             | 109            | Information Technology                     |
| 26          | Technology             | 123            | News, Portal & Search                      |
| 26          | Technology             | 145            | Search Engines                             |
| 26          | Technology             | 155            | Technology                                 |
| 26          | Technology             | 159            | Translator                                 |
| 26          | Technology             | 184            | Artificial Intelligence                    |
| 26          | Technology             | 192            | Remote Access                              |
| 26          | Technology             | 193            | Shareware/Freeware                         |
| 26          | Technology             | 194            | Keep Awake Software                        |
| 27          | Travel                 | 160            | Travel                                     |
| 28          | Vehicles               | 163            | Vehicles                                   |
| 29          | Violence               | 165            | Violence                                   |
| 29          | Violence               | 166            | Weapons                                    |
| 30          | Weather                | 167            | Weather                                    |
| 31          | Always blocked         | 170            | Child Abuse                                |
| 32          | Security Risks         | 128            | Parked & For Sale Domains                  |
| 32          | Security Risks         | 169            | New Domains                                |
| 32          | Security Risks         | 177            | Newly Seen Domains                         |
| 34          | CIPA                   | 182            | CIPA Filter                                |

## Filtering options

### Filter traffic by resolved IP category

When creating a DNS policy for security or content categories, you can optionally turn on **Filter traffic by resolved IP category** in the policy settings. When turned on, Gateway will block queries based on their resolved IP address in addition to the domain name. This setting may increase the number of false positives because domains in the blocked category can share IP addresses with legitimate domains.

### Ignore `CNAME` domain categories

The categories for a site's Canonical Name (`CNAME`) records may differ from its `A` record. For example, `blog.example.com` may be categorized under Personal Blogs, while `example.com` is categorized under Technology. To limit matches for a DNS policy to only the root domain's categories, turn on **Ignore CNAME domain categories**.

Regardless of this setting, `CNAME` domain categories will still appear in your Gateway [Logpush](/cloudflare-one/insights/logs/logpush/) logs.

## Categorization process

Cloudflare's domain categorization engine begins with multiple data sources, including:

1. Cloudflare's proprietary data using our global network.

2. Third-party intelligence feeds. Cloudflare uses data from over 30 open-source intelligence feeds and premium commercial feeds, such as Avira and Zvelo.

Then, the initial categorization is refined via:

3. Machine learning models. Our algorithms, including DGA Domains, DNS tunneling, and phishing detection models analyze patterns and behaviors to detect new and evolving threats.

4. Community feedback. Through a review process, Cloudflare assesses feedback by both our internal models and threat analysts. This ensures that our categorizations reflect the most current and accurate threat intelligence.

## Terraform

Terraform users can retrieve the category list with the `cloudflare_zero_trust_gateway_categories_list` data source. This allows you to create Gateway policies with the category's name rather than its numeric ID. For example:

```tf
data "cloudflare_zero_trust_gateway_categories_list" "categories" {
  account_id = var.cloudflare_account_id
}

locals {
  main_categories_map = {
    for idx, c in data.cloudflare_zero_trust_gateway_categories_list.categories.result :
    c.name => c.id
  }

  subcategories_map = merge(flatten([
    for idx, c in data.cloudflare_zero_trust_gateway_categories_list.categories.result : {
      for k, v in coalesce(c.subcategories, []) :
      v.name => v.id
    }
  ])...)
}

resource "cloudflare_zero_trust_gateway_policy" "zt_block_dns_tech_categories" {
  account_id = var.cloudflare_account_id
  name       = "DNS Blocked"
  action     = "block"
  traffic    = "any(dns.content_category[*] in {${join(" ", [
    local.main_categories_map["Technology"],
    local.subcategories_map["APIs"],
    local.subcategories_map["Artificial Intelligence"],
    local.subcategories_map["Content Servers"],
    local.subcategories_map["Translator"]
  ])}})"
}
```

# Enable IDS

import { TabItem, Tabs, DashButton } from "~/components";

Cloudflare's Intrusion Detection System (IDS) is a Cloudflare Advanced Network Firewall feature you can use to actively monitor for a wide range of known threat signatures in your traffic. An IDS expands the security coverage of a firewall to analyze traffic against a broader threat database, detecting a variety of sophisticated attacks such as ransomware, data exfiltration, and network scanning based on signatures or “fingerprints” in network traffic.

With Cloudflare's global anycast network, you get:

- Cloudflare's entire global network capacity is now the capacity of your IDS.
- Built-in redundancy and failover. Every server runs Cloudflare's IDS software, and traffic is automatically attracted to the closest network location to its source.
- Continuous deployment for improvements to Cloudflare's IDS capabilities.

Refer to [Enable IDS](/cloudflare-one/traffic-controls/enable-ids/#enable-ids) for more information on enabling IDS and creating new rulesets. After IDS is enabled, your traffic will be scanned to find malicious traffic. The detections are logged to destinations that can be configured from the dashboard. Refer to [IDS logs](/cloudflare-one/insights/logs/logpush/ids-logs/) for instructions on configuring a destination to receive the detections. Additionally, all traffic that is analyzed can be accessed via [network analytics](/analytics/network-analytics/). Refer to [GraphQL Analytics](/cloudflare-network-firewall/tutorials/graphql-analytics/) to query the analytics data.

Cloudflare's IDS takes advantage of the threat intelligence powered by our global network and extends the capabilities of the Cloudflare Firewall to monitor and protect your network from malicious actors.

## Enable IDS

You can enable IDS through the dashboard or via the API.

:::note
This feature is available for Cloudflare Advanced Network Firewall users. For access, contact your account team.
:::

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies**.
2. Select **Policy settings** and turn on **IDS**.

</TabItem> <TabItem label="API">

To start using IDS via the API, first create a new ruleset in the `magic-transit-ids-managed` phase with a rule which is enabled.

1. Follow instructions in the [Rulesets Engine Page](/ruleset-engine/basic-operations/view-rulesets/) to view all rulesets for your account. You must see a ruleset with phase `magic-transit-ids-managed` and kind `managed`. If not, please contact your account team. The managed ruleset ID will be used in the next step.

2. Create a new root ruleset with a single rule in the `magic_transit_ids_managed` phase by running:

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "name": "IDS Execute ruleset",
  "description": "Ruleset to enable IDS",
  "kind": "root",
  "phase": "magic_transit_ids_managed",
  "rules": [
    {
      "enabled": true,
      "expression": "true",
      "action": "execute",
      "description": "enable ids",
      "action_parameters": {
        "id": "${managed_ruleset_id}"
      }
    }
  ]
}'
```

With this ruleset added, IDS will start inspecting packets and report any anomalous traffic. Next, you can [configure Logpush](/cloudflare-network-firewall/how-to/use-logpush-with-ids/) to start receiving details about the anomalous traffic.

3. Use the rule created in the previous step to enable or disable IDS. The Rulesets API documentation describes [how to patch a rule](/ruleset-engine/rulesets-api/update-rule/). <br/><br/> For example, the following patch request to set the `enabled` field to `false` will disable IDS. The ruleset and rule ID from the ruleset created in the previous step are used below.

```bash
curl --request PATCH \
https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets/{root_ruleset_id}/rules/{rule_id} \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "enabled": false,
  "expression": "true",
  "action": "execute",
  "action_parameters": {
    "id": "${managed_ruleset_id}"
  }
}'
```

Similarly, sending a patch request with the `enabled` field set to `true` will enable IDS.

</TabItem> </Tabs>

## IDS rules

IDS rules are run on a subset of packets. IDS also supports the current flows:

- Cloudflare WAN to Cloudflare WAN.
- Magic Transit ingress traffic (when egress traffic is handled through direct server return).
- Magic Transit ingress and egress traffic when Magic Transit has the [Egress option enabled](/reference-architecture/architectures/magic-transit/#magic-transit-with-egress-option-enabled).

## Next steps

You must configure Logpush to log detected risks. Refer to [Configure a Logpush destination](/cloudflare-network-firewall/how-to/use-logpush-with-ids/) for more information. Additionally, all traffic that is analyzed can be accessed via [network analytics](/analytics/network-analytics/). Refer to [GraphQL Analytics](/cloudflare-network-firewall/tutorials/graphql-analytics/) to query the analytics data.

# Gateway policy expressions

import { Render } from "~/components";

Gateway policies use a wirefilter-based expression language to match traffic against selectors (criteria). This syntax is similar to, but distinct from, the [Rules language](/ruleset-engine/rules-language/) used by WAF, Rules, and other Cloudflare products. Refer to [Gateway versus Ruleset Engine](#gateway-versus-ruleset-engine) for details on the differences.

:::caution[Important]

The [Ruleset Engine documentation](/ruleset-engine/rules-language/) does not apply to Gateway policies. Gateway has its own set of selectors and fields specific to Zero Trust traffic filtering. For available selectors, refer to the documentation for each policy type:

- [DNS policy selectors](/cloudflare-one/traffic-controls/dns-policies/#selectors)
- [HTTP policy selectors](/cloudflare-one/traffic-controls/http-policies/#selectors)
- [Network policy selectors](/cloudflare-one/traffic-controls/network-policies/#selectors)
- [Egress policy selectors](/cloudflare-one/traffic-controls/egress-policies/#selectors)
- [Resolver policy selectors](/cloudflare-one/traffic-controls/resolver-policies/#selectors)

:::

## Expression syntax

Gateway expressions follow this pattern:

```txt
<field> <operator> <value>
```

For example:

```txt
dns.fqdn == "example.com"
http.request.host == "api.example.com"
identity.email == "user@company.com"
```

### Operators

Gateway supports the following operators:

| Operator  | Name                       | Example                                          |
| --------- | -------------------------- | ------------------------------------------------ |
| `==`      | Equals                     | `dns.fqdn == "example.com"`                      |
| `!=`      | Does not equal             | `http.request.host != "blocked.com"`             |
| `in`      | Value is in set            | `net.dst.port in {80 443}`                       |
| `matches` | Matches regular expression | `http.request.host matches ".*\\.example\\.com"` |
| `>`       | Greater than               | `http.upload.file.size > 10`                     |
| `>=`      | Greater than or equal to   | `http.download.file.size >= 100`                 |
| `<`       | Less than                  | `http.upload.file.size < 50`                     |
| `<=`      | Less than or equal to      | `http.download.file.size <= 200`                 |

### Logical operators

Combine multiple conditions using logical operators:

| Operator | Name        | Example                                                               |
| -------- | ----------- | --------------------------------------------------------------------- |
| `and`    | Logical AND | `dns.fqdn == "example.com" and identity.email == "admin@company.com"` |
| `or`     | Logical OR  | `net.dst.port == 80 or net.dst.port == 443`                           |
| `not`    | Logical NOT | `not(identity.email == "guest@company.com")`                          |

## Array handling

Some Gateway fields return arrays (multiple values). Use the `any()` function to match if any element in the array meets the condition:

```txt
any(http.request.uri.content_category[*] in {17 85 102})
```

```txt
any(identity.groups[*].name in {"Engineering" "Security"})
```

```txt
any(http.request.domains[*] == "example.com")
```

The `[*]` notation indicates that the function should evaluate all elements in the array.

## List handling

You can reference [lists](/cloudflare-one/reusable-components/lists/) in your expressions using the list UUID:

```txt
http.request.host in $<LIST_UUID>
```

```txt
any(http.request.domains[*] in $<LIST_UUID>)
```

To find a list's UUID, go to **My Team** > **Lists** in Zero Trust and select the list. The UUID appears in the browser URL.

## Common field patterns

Each Gateway policy type has its own set of available fields. The following table shows the field prefixes used by each policy type:

| Policy type    | Field prefix      | Example fields                                                  |
| -------------- | ----------------- | --------------------------------------------------------------- |
| DNS            | `dns.`            | `dns.fqdn`, `dns.content_category`, `dns.src_ip`                |
| HTTP           | `http.`           | `http.request.host`, `http.request.uri`, `http.request.domains` |
| Network        | `net.`            | `net.dst.ip`, `net.dst.port`, `net.src.ip`                      |
| Identity       | `identity.`       | `identity.email`, `identity.groups`, `identity.name`            |
| Device posture | `device_posture.` | `device_posture.checks.passed`                                  |

For a complete list of available fields for each policy type, refer to the selectors documentation linked at the top of this page.

## Example expressions

### Block a domain in a DNS policy

```txt
dns.fqdn == "example.com"
```

### Block multiple content categories in an HTTP policy

```txt
any(http.request.uri.content_category[*] in {17 85 102})
```

### Allow traffic from a specific user group

```txt
any(identity.groups[*].name in {"Engineering"})
```

### Block traffic to a destination IP range in a Network policy

```txt
net.dst.ip in {10.0.0.0/8}
```

### Combine identity and traffic conditions

```txt
http.request.host == "internal.example.com" and identity.email matches ".*@company.com"
```

## Gateway versus Ruleset Engine

The following table summarizes the key differences between the Rules language](/ruleset-engine/rules-language/) (supported by the Ruleset Engine) and Gateway policy expressions:

|                     | Ruleset Engine                                               | Gateway                                                      |
| ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **Products**        | WAF, Transform Rules, Cache Rules, Configuration Rules       | DNS, HTTP, Network, Egress, Resolver policies                |
| **Field examples**  | `http.request.uri.path`, `cf.bot_management.score`, `ip.src` | `dns.fqdn`, `http.request.host`, `identity.email`            |
| **Identity fields** | Not available                                                | Available (for example, `identity.email`, `identity.groups`) |
| **DNS fields**      | Not available                                                | Available (for example, `dns.fqdn`, `dns.content_category`)  |
| **Documentation**   | [Rules language](/ruleset-engine/rules-language/)            | [Traffic policies](/cloudflare-one/traffic-controls/)        |

:::note

Do not reference the [Ruleset Engine fields reference](/ruleset-engine/rules-language/fields/) when building Gateway policies. Gateway has its own field set documented on each policy type page.

:::

## Related resources

- [DNS policies](/cloudflare-one/traffic-controls/dns-policies/)
- [HTTP policies](/cloudflare-one/traffic-controls/http-policies/)
- [Network policies](/cloudflare-one/traffic-controls/network-policies/)
- [Identity-based policies](/cloudflare-one/traffic-controls/identity-selectors/)
- [Lists](/cloudflare-one/reusable-components/lists/)

# Global policies

Cloudflare Zero Trust applies a set of global policies to all accounts. These policies prevent you from accidentally blocking Cloudflare services that Zero Trust depends on, such as the dashboard, API, and client registration.

Zero Trust logs prepend an identifier to global policy names. For example, matches for the global policy **Allow Zero Trust Services** will appear in your logs with the name **Global Policy - Allow Zero Trust Services**.

The following policies are sorted by [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence) within each policy type.

## DNS resolution policies

Gateway enforces global DNS and resolver policies before any other policies. This ensures the traffic is not blocked by user policies and gets resolved with Cloudflare's public DNS resolver, [1.1.1.1](/1.1.1.1/). Each global DNS policy evaluates traffic based on the domain in the query.

| Name                                                                                      | ID                                     | Value                                                                                                                                                     | Action  |
| ----------------------------------------------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Allow DNS queries for cloudflareclient.com domain                                         | `00000001-e139-4a1b-90d5-698d8fa371e0` | `cloudflareclient.com`                                                                                                                                    | allow   |
| Resolve cloudflareclient.com through 1.1.1.1                                              | `00000001-e738-4554-823b-0b2c75af2c66` | `cloudflareclient.com`                                                                                                                                    | resolve |
| Allow DNS queries for content.browser.run domain                                          | `00000001-9bff-4d83-a9e4-e5ed321fe0b9` | `content.browser.run`                                                                                                                                     | allow   |
| Resolve content.browser.run through 1.1.1.1                                               | `00000001-0df5-472b-80c0-02888e7167ee` | `content.browser.run`                                                                                                                                     | resolve |
| Allow DNS queries for edge.browser.run and cloudflarebrowser.com domains                  | `00000001-e2f1-4e99-bab3-91df88879587` | `edge.browser.run` and `cloudflarebrowser.com`                                                                                                            | allow   |
| Resolve edge.browser.run and cloudflarebrowser.com through 1.1.1.1                        | `00000001-b103-44c6-a114-7a784cdf3fb7` | `edge.browser.run` and `cloudflarebrowser.com`                                                                                                            | resolve |
| Allow DNS queries for help.teams.cloudflare.com and help.one.cloudflare.com domains       | `00000001-b2fc-46db-b0f1-69ef3553bd7a` | `help.teams.cloudflare.com` and `help.one.cloudflare.com`                                                                                                 | allow   |
| Resolve help.teams.cloudflare.com and help.one.cloudflare.com through 1.1.1.1             | `00000001-ce13-486a-b006-ba0435ccb013` | `help.teams.cloudflare.com` and `help.one.cloudflare.com`                                                                                                 | resolve |
| Allow DNS queries for cloudflare-gateway.com domain                                       | `00000001-e83d-492b-995e-351970cd5e8e` | `cloudflare-gateway.com`                                                                                                                                  | allow   |
| Resolve cloudflare-gateway.com through 1.1.1.1                                            | `00000001-d9bc-4913-a2f5-905dbb3ecf9a` | `cloudflare-gateway.com`                                                                                                                                  | resolve |
| Allow DNS queries for cloudflarestatus.com domain                                         | `00000001-78da-4f8a-b9ee-76563f1ec46b` | `cloudflarestatus.com`                                                                                                                                    | allow   |
| Resolve cloudflarestatus.com through 1.1.1.1                                              | `00000001-4d1d-43a3-9015-c49fc3a6da31` | `cloudflarestatus.com`                                                                                                                                    | resolve |
| Allow DNS queries for nel.cloudflare.com domain                                           | `00000001-af28-4afa-8987-eadc21187e14` | `nel.cloudflare.com`                                                                                                                                      | allow   |
| Resolve nel.cloudflare.com through 1.1.1.1                                                | `00000001-0034-45a0-8333-f339451fba46` | `nel.cloudflare.com`                                                                                                                                      | resolve |
| Allow DNS queries for api.cloudflare.com domain                                           | `00000001-5eea-4932-8dd5-8e1ec9770396` | `api.cloudflare.com`                                                                                                                                      | allow   |
| Resolve api.cloudflare.com through 1.1.1.1                                                | `00000001-4f0c-4f86-9b96-5d26123a194b` | `api.cloudflare.com`                                                                                                                                      | resolve |
| Allow DNS queries for one.dash.cloudflare.com domain                                    | `00000001-0f75-48a9-b3e1-925a974d2b65` | `one.dash.cloudflare.com`                                                                                                                               | allow   |
| Resolve one.dash.cloudflare.com through 1.1.1.1                                         | `00000001-3d84-41a6-bc84-3014685c0d81` | `one.dash.cloudflare.com`                                                                                                                               | resolve |
| Allow DNS queries for one.dash.cloudflare.com domain                                      | `00000001-a9fd-40de-a662-51d3a3ae0ad8` | `one.dash.cloudflare.com` and `one.dash.fed.cloudflare.com`                                                                                               | allow   |
| Resolve one.dash.cloudflare.com through 1.1.1.1                                           | `00000001-70f2-4eea-b711-201bca434ed4` | `one.dash.cloudflare.com` and `one.dash.fed.cloudflare.com`                                                                                               | resolve |
| Allow DNS queries for dash.cloudflare.com domain                                          | `00000001-0c2a-4b31-8606-3e5a1d87c1bf` | `dash.cloudflare.com` and `dash.fed.cloudflare.com`                                                                                                       | allow   |
| Resolve dash.cloudflare.com through 1.1.1.1                                               | `00000001-c47f-41f3-b234-d66c82b8d422` | `dash.cloudflare.com` and `dash.fed.cloudflare.com`                                                                                                       | resolve |
| Allow DNS queries for cloudflareportal.com, cloudflareok.com and cloudflarecp.com domains | `00000001-1c6c-4793-b48f-799eee6e0e31` | `cloudflareportal.com`, `cloudflareok.com`, and `cloudflarecp.com`                                                                                        | allow   |
| Resolve cloudflareportal.com, cloudflareok.com and cloudflarecp.com through 1.1.1.1       | `00000001-8c35-4d7d-9dbb-cb7350375b7b` | `cloudflareportal.com`, `cloudflareok.com`, and `cloudflarecp.com`                                                                                        | resolve |
| Allow DNS queries for cloudflareaccess.com domain                                         | `00000001-d738-4dad-bac4-1a50201d9503` | `cloudflareaccess.com`                                                                                                                                    | allow   |
| Resolve cloudflareaccess.com through 1.1.1.1                                              | `00000001-4404-4572-80f6-f7b098909460` | `cloudflareaccess.com`                                                                                                                                    | resolve |
| Allow DNS queries for blocked.teams.cloudflare.com domain                                 | `00000001-76f4-4438-b8ab-a9da53f4a2f1` | `blocked.teams.cloudflare.com` and `blocked.teams.fed.cloudflare.com`                                                                                     | allow   |
| Resolve blocked.teams.cloudflare.com through 1.1.1.1                                      | `00000001-af3c-458f-aeb2-b3bb5d3fe1d5` | `blocked.teams.cloudflare.com` and `blocked.teams.fed.cloudflare.com`                                                                                     | resolve |
| Allow DNS queries for developers.cloudflare.com domain                                    | `00000001-4263-4808-8457-4d4329c91f66` | `developers.cloudflare.com`                                                                                                                               | allow   |
| Resolve developers.cloudflare.com through 1.1.1.1                                         | `00000001-9f91-4462-9270-78beca5b4dbc` | `developers.cloudflare.com`                                                                                                                               | resolve |
| Allow DNS queries for speed.cloudflare.com domain                                         | `00000001-4fc0-4286-b783-6c442adda171` | `speed.cloudflare.com`                                                                                                                                    | allow   |
| Resolve speed.cloudflare.com through 1.1.1.1                                              | `00000001-ec51-4471-9e78-bd47d46a3002` | `speed.cloudflare.com`                                                                                                                                    | resolve |
| Allow DNS requests to browser-rendered Access Apps                                        | `00000001-1232-4a9f-a165-1e8ed59483c4` | `*.zero-trust-apps.cfdata.org`, `*.zero-trust-apps-staging.cfdata.org`, `*.zero-trust-apps.fed.cfdata.org`, or `*.zero-trust-apps-staging.fed.cfdata.org` | allow   |
| Resolve browser-rendered Access Apps domains through 1.1.1.1                              | `00000001-9461-43c7-ba63-d0fdf9376bd4` | `*.zero-trust-apps.cfdata.org`, `*.zero-trust-apps-staging.cfdata.org`, `*.zero-trust-apps.fed.cfdata.org`, or `*.zero-trust-apps-staging.fed.cfdata.org` | resolve |

## Network proxy policies

| Name                                                | ID                                     | Criteria | Value                                                                                                                                                                                                                                                                                                                                                                                                    | Action | Description                                                                                                                                                   |
| --------------------------------------------------- | -------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Allow CF Network Error Logging L4                   | `00000001-e4af-4b82-8f8c-c79c1d5d212e` | Hostname | `*.nel.cloudflare.com`                                                                                                                                                                                                                                                                                                                                                                                   | allow  | Allows SNI domains for Cloudflare One Client registration.                                                                                                    |
| Allow CF Client                                     | `00000001-8c3d-4e27-a01b-af8418000077` | Hostname | `*.cloudflareclient.com` and `*.fed.cloudflareclient.com`                                                                                                                                                                                                                                                                                                                                                | allow  | Allows Zero Trust client.                                                                                                                                     |
| Allow Gateway Proxy PAC                             | `00000001-776e-438d-9856-987d7053762b` | Hostname | `*.cloudflare-gateway.com` and `*.fed.cloudflare-gateway.com`                                                                                                                                                                                                                                                                                                                                            | allow  | Allows Gateway proxy with [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/).                                                       |
| Allow Zero Trust Services                           | `00000001-e1e8-421b-a0fe-895397489f28` | Hostname | `one.dash.cloudflare.com`, `help.teams.cloudflare.com`, `blocked.teams.cloudflare.com`, `blocked.teams.fed.cloudflare.com`, `api.cloudflare.com`, `api.fed.cloudflare.com`, `cloudflarestatus.com`, `www.cloudflarestatus.com`, `one.dash.cloudflare.com`, `one.dash.fed.cloudflare.com`, `help.one.cloudflare.com`, `dash.cloudflare.com`, `dash.fed.cloudflare.com`, and `developers.cloudflare.com` | allow  | Allows Cloudflare Zero Trust services.                                                                                                                        |
| Allow Access Apps L4                                | `00000001-daa2-41e2-8a88-698af4066951` | Hostname | `*.cloudflareaccess.com` and `*.fed.cloudflareaccess.com`                                                                                                                                                                                                                                                                                                                                                | allow  | Allows [Cloudflare Access](/cloudflare-one/access-controls/policies/) applications.                                                                           |
| Allow HTTP requests to browser-rendered Access Apps | `00000001-1f93-4476-8f92-9aa4407d1c5f` | Hostname | `*.zero-trust-apps.cfdata.org`, `*.zero-trust-apps-staging.cfdata.org`, `*.zero-trust-apps.fed.cfdata.org`, or `*.zero-trust-apps-staging.fed.cfdata.org`                                                                                                                                                                                                                                                | allow  | Allows Cloudflare Access terminal applications [rendered in a browser](/cloudflare-one/access-controls/applications/non-http/browser-rendering/#ssh-and-vnc). |

## HTTP inspection policies

| Name                                   | ID                                     | Criteria         | Value                                                                                                                                           | Action    | Description                                                                                                                                              |
| -------------------------------------- | -------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prevent Account Change Block           | `00000001-d1f2-461a-8253-501c8d882a15` | Hostname         | `*.cloudflareclient.com` and `*.fed.cloudflareclient.com`; not `notifications.cloudflareclient.com` or `notifications.fed.cloudflareclient.com` | bypass    | Ensures users cannot accidentally block themselves from making account changes.                                                                          |
| Bypass RBI Assets                      | `00000001-df61-4068-aa6c-0f684c3cd4e6` | Hostname         | `*.content.browser.run`                                                                                                                         | bypass    | Required for [Browser Isolation](/cloudflare-one/remote-browser-isolation/).                                                                             |
| Inspect RBI Urls                       | `00000001-3faa-4f59-98d4-0f6d6af4b6d0` | Hostname         | `*.edge.browser.run` and `*.cloudflarebrowser.com`                                                                                              | bypass    | Required for Browser Isolation.                                                                                                                          |
| Allow Gateway Help Page                | `00000001-8e9a-4429-b3c2-d267d0ce6114` | Hostname         | `help.teams.cloudflare.com` and `help.one.cloudflare.com`                                                                                       | allow     | Used by the Cloudflare One Client to check if Gateway is on by inspecting the certificate and checking if it is properly installed on the client device. |
| Allow Gateway Services                 | `00000001-346f-4710-b444-eb62e369b5f7` | Capability       | Gateway Block Page                                                                                                                              | allow     | Ensures HTTP requests to render the Gateway block page are always allowed.                                                                               |
| Bypass Gateway DNS                     | `00000001-d9c0-46b0-8704-2ea5b9d7bdfc` | Hostname         | `*.cloudflare-gateway.com` and `*.fed.cloudflare-gateway.com`                                                                                   | bypass    | Ensures requests to the `cloudflare-gateway.com` DNS endpoint will not be inspected.                                                                     |
| Bypass CF Status                       | `00000001-5399-4b71-a9fc-d4d90ccf0758` | Hostname         | `*.cloudflarestatus.com`                                                                                                                        | bypass    | Bypasses `cloudflarestatus.com` so users can reach the status page in case of a Gateway outage.                                                          |
| Bypass CF Network Error Logging        | `00000001-dfe0-4737-8d1e-8191e8f637df` | Hostname         | `*.nel.cloudflare.com`                                                                                                                          | bypass    | Bypasses `*.nel.cloudflarestatus.com` for Cloudflare's network error logging feature.                                                                    |
| Bypass CF API                          | `00000001-a424-43fb-b1f1-d3eb35ed7ddd` | Hostname         | `api.cloudflare.com` and `api.fed.cloudflare.com`                                                                                               | bypass    | Bypasses Cloudflare's API endpoint.                                                                                                                      |
| Prevent ZT Dashboard Lockout           | `00000001-d38e-42db-96fe-60613b6b308f` | Hostname         | `dash.teams.cloudflare.com`, `one.dash.cloudflare.com`, and `one.dash.fed.cloudflare.com`                                                       | bypass    | Prevents users from being locked out of the Zero Trust dashboard.                                                                                        |
| Bypass CF Dashboard                    | `00000001-d343-4ded-908e-b3fe43c5e61e` | Hostname         | `*.dash.cloudflare.com` and `*.dash.fed.cloudflare.com`                                                                                         | bypass    | Bypasses the Cloudflare dashboard and subdomains.                                                                                                        |
| Bypass Zero Trust Captive Portal Sites | `00000001-8b62-4367-919e-5c160a06ddf7` | Hostname         | `cloudflareportal.com`, `cloudflareok.com`, and `cloudflarecp.com`                                                                              | bypass    | Bypasses the Zero Trust captive portal detection sites.                                                                                                  |
| Bypass OCSP                            | `00000001-34ce-47c7-ad0f-199f46eba194` | Application      | Online Certificate Status Protocol                                                                                                              | bypass    | Enables OCSP stapling.                                                                                                                                   |
| Allow Access Apps L7                   | `00000001-8d6b-4951-8a18-3bbc9010976c` | Hostname         | `*.cloudflareaccess.com` and `*.fed.cloudflareaccess.com`                                                                                       | allow     | Allows Cloudflare Access applications.                                                                                                                   |
| Prevent Block Page Loop                | `00000001-48b1-4ade-93c1-f0f3759dc19c` | Hostname         | `blocked.teams.cloudflare.com` and `blocked.teams.fed.cloudflare.com`                                                                           | bypass    | Prevents an infinite loop on the Gateway block page.                                                                                                     |
| Always Blocked Categories              | `00000001-bed5-462e-b0f1-2e2c3555e9f7` | Content Category | [Child Abuse category](/cloudflare-one/traffic-controls/domain-categories/#category-and-subcategory-ids)                                        | block     | Blocks child abuse materials (CSAM).                                                                                                                     |
| Don't Isolate RBI Help Pages           | `00000001-1a18-431f-9c9d-bce431f1002a` | Hostname         | `developers.cloudflare.com` and `help.cloudflarebrowser.com`                                                                                    | noisolate | Prevents browser isolation of Cloudflare developer docs and help pages to help users troubleshoot configuration issues.                                  |
| Don't AV Scan CF Speed                 | `00000001-c194-408f-87dd-9a366ce76e12` | Hostname         | `speed.cloudflare.com`                                                                                                                          | noscan    | Allows files transferred by the Cloudflare speed test.                                                                                                   |

# Granular permissions for Gateway

import { Steps } from "~/components";

You can scope Cloudflare member permissions to individual Gateway [traffic policies](/cloudflare-one/traffic-controls/) and [lists](/cloudflare-one/traffic-controls/expression-syntax/#selectors-by-field-type), instead of granting account-wide access to every Gateway resource. For example, you can let a security analyst manage a specific set of HTTP policies without exposing DNS policies or account-wide configuration.

Granular permissions are a parallel layer to [account-scoped roles](/fundamentals/manage-members/roles/#account-scoped-roles) and do not replace them. Members who already hold an account-level role like `Cloudflare Gateway` or `Cloudflare Zero Trust` continue to have write access to every Gateway resource in the account.

## Account-level roles

Gateway provides dedicated account-level roles that grant access to specific resource types across the entire account, without requiring the broader `Cloudflare Gateway` or `Cloudflare Zero Trust` role.

| Role                               | Access                                                                  |
| ---------------------------------- | ----------------------------------------------------------------------- |
| Zero Trust Gateway Read Only       | Read-only access to all Gateway resources.                              |
| Zero Trust Account Lists Admin     | Full create, edit, and delete access to all Gateway lists.              |
| Zero Trust DNS Location Admin      | Full create, edit, and delete access to all DNS locations.              |
| Zero Trust DNS Policies Admin      | Full create, edit, delete, and reorder access to all DNS policies.      |
| Zero Trust HTTP Policies Admin     | Full create, edit, delete, and reorder access to all HTTP policies.     |
| Zero Trust Network Policies Admin  | Full create, edit, delete, and reorder access to all network policies.  |
| Zero Trust Resolver Policies Admin | Full create, edit, delete, and reorder access to all resolver policies. |
| Zero Trust Egress Policies Admin   | Full create, edit, delete, and reorder access to all egress policies.   |
| Zero Trust Proxy Endpoints Admin   | Full create, edit, and delete access to all proxy endpoints.            |

These roles appear in the [roles reference](/fundamentals/manage-members/roles/#account-scoped-roles).

## Resource-scoped roles

In addition to account-level roles, you can grant members access to **specific individual policies or lists**. For example, you can allow a team member to edit one HTTP isolation policy without exposing any other policies in the account.

Resource-scoped roles cover read and edit access to existing resources. They do not grant the ability to create new policies or lists. To create new resources, a member needs an account-level role.

Gateway supports resource-scoped permissions for:

- **Individual Gateway lists**, allowing a member to edit a specific list used in policy expressions.
- **Individual Gateway policies**, allowing a member to edit a specific traffic policy. This applies to all policy families:
  - DNS policies
  - HTTP policies (including [isolation policies](/cloudflare-one/remote-browser-isolation/isolation-policies/))
  - Network policies
  - Resolver policies
  - Egress policies

:::note
Resource-scoped Gateway permissions do not include DNS locations. DNS locations are managed through the account-level `Zero Trust DNS Location Admin` role.
:::

## How it works

For any API request on a specific Gateway policy or list, access is granted if the principal has **either**:

- An account-level role that covers the resource (for example, `Zero Trust HTTP Policies Admin`, `Cloudflare Gateway`, or `Cloudflare Zero Trust`), **or**
- A [resource-scoped role](/fundamentals/manage-members/roles/#resource-scoped-roles) bound to that specific policy or list.

[Listing endpoints](#resource-enumeration) return only the policies and lists the principal has at least read access to.

## Grant a granular permission

Granular permissions are assigned through the standard [member management](/fundamentals/manage-members/manage/) flow.

<Steps>
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Manage Account** > **Members** and select **Invite Members**, or open an existing member to edit their permissions.
2. Add a permission policy and choose a [resource-scoped role](/fundamentals/manage-members/roles/#resource-scoped-roles) that targets Gateway policies or lists.
3. In the **Scope** section, choose **Specific resources**.
4. Set **Resource type** to one of:
   - **Gateway policies** to scope access to individual traffic policies (DNS, HTTP, network, resolver, or egress).
   - **Gateway lists** to scope access to individual lists.
5. Select one or more specific policies or lists from the resource picker.
6. Save the policy.
</Steps>

You can attach multiple granular policies to the same member to cover different Gateway resources with different roles.

## Resource enumeration

Listing endpoints are authorization-aware. When a principal calls a listing endpoint, the response is filtered to the resources they have at least read access to.

| Endpoint                               | Method | Returns                                            |
| -------------------------------------- | ------ | -------------------------------------------------- |
| `/accounts/{account_id}/gateway/rules` | `GET`  | Gateway policies the principal can read or manage. |
| `/accounts/{account_id}/gateway/lists` | `GET`  | Gateway lists the principal can read or manage.    |

Members with an account-level role that covers Gateway continue to see all resources in the account.

## Browser Isolation policies

[Isolation policies](/cloudflare-one/remote-browser-isolation/isolation-policies/) are Gateway HTTP policies with the _Isolate_ or _Do Not Isolate_ action. All Gateway HTTP policy permissions apply identically to isolation policies, and there is no separate permission surface for Browser Isolation.

The per-policy [settings](/cloudflare-one/remote-browser-isolation/isolation-policies/#policy-settings) (copy/paste, file download/upload, keyboard, printing) are part of the HTTP policy object, so whoever can edit the policy can also edit those settings.

To delegate management of specific isolation policies, grant a member a resource-scoped role for the individual HTTP policy.

## Backward compatibility

- Existing account-level roles and API tokens continue to function as before.
- Existing automation that authenticates with an account-level token (for example, Terraform pipelines using a `Cloudflare Gateway` token) is unaffected.
- Granular permissions are opt-in. Granting one to a member adds capability; it never removes capability that the member already has from an account-level role.

## Related resources

- [Roles reference](/fundamentals/manage-members/roles/) -- the full list of Cloudflare roles, including resource-scoped roles for Gateway policies and lists.
- [Role scopes](/fundamentals/manage-members/scope/) -- how policy scopes work across account, domain, and resource layers.
- [Manage account members](/fundamentals/manage-members/manage/) -- the member invite and edit flow.

# Identity-based policies

import { Render } from "~/components";

With Cloudflare One, you can create Secure Web Gateway policies that filter outbound traffic down to the user identity level. To do that, you can build DNS, HTTP or Network policies using a set of [identity-based selectors](#identity-based-selectors). These selectors require you to deploy the Cloudflare One Client in [Traffic and DNS mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/).

For example, you can create different security rules for different teams — block social media for contractors but allow it for marketing.

You may also filter outbound traffic based on additional signals from [device posture checks](/cloudflare-one/reusable-components/posture-checks/).

## Gateway identity checks

Gateway checks identity when a user logs in or re-authenticates. To check your users' identities and require re-authentication at regular intervals, you can [enforce a Cloudflare One Client session duration](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/).

:::caution
Unless you use an [identity provider (IdP) that supports SCIM provisioning](#automatic-scim-idp-updates), Gateway will not detect when you add or remove a user from a group in your IdP until the user re-authenticates to your Zero Trust instance.
:::

There are two ways a user can re-authenticate:

- Log out from an Access-protected application and log back in.
- In the Cloudflare One Client, re-authenticate the session by going to **Profile** > **Account information** > **Re-authenticate** [^1]. This will open a browser window and prompt the user to log in.

[^1]: In Cloudflare One Client version 2026.1 and earlier, select **Preferences** > **Account** > **Re-Authenticate Session**.

To view the identity that Gateway will use when evaluating policies, check the [user registry](/cloudflare-one/team-and-resources/users/users/).

### Automatic SCIM IdP updates

Gateway will automatically detect changes in user name, title, and group membership for IdPs configured with System for Cross-domain Identity Management (SCIM) provisioning. For more information, refer to [SCIM provisioning](/cloudflare-one/team-and-resources/users/scim/).

### Extended email addresses

<Render file="gateway/extended-email" product="cloudflare-one" />

## Identity-based selectors

### OIDC Claims

Specify a value from a [custom OIDC claim](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) configured on your identity provider.

:::note
This selector is only available for the [Generic OIDC](/cloudflare-one/integrations/identity-providers/generic-oidc/) identity provider integration. Named OIDC providers such as Okta and Microsoft Entra ID do not support custom OIDC claims in Gateway policies — use the [User Group Names](#user-group-names) or [User Group IDs](#user-group-ids) selectors for those providers instead.
:::

<Render file="gateway/selectors/oidc-claims" product="cloudflare-one" />

### SAML Attributes

Specify a value from the SAML Attribute Assertion.

<Render file="gateway/selectors/saml-attributes" product="cloudflare-one" />

### User Email

Use this selector to create identity-based Gateway policies based on a user's email.

<Render
	file="gateway/selectors/user-email"
	product="cloudflare-one"
	params={{ UIname: "User Email" }}
/>

### User Group IDs

Use this selector to create identity-based Gateway policies based on an IdP group ID of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-ids"
	product="cloudflare-one"
	params={{ UIname: "User Group IDs" }}
/>

### User Group Email

Use this selector to create identity-based Gateway policies based on an IdP group email address of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-email"
	product="cloudflare-one"
	params={{ UIname: "User Group Email" }}
/>

### User Group Names

Use this selector to create identity-based Gateway policies based on an IdP group name of which the user is configured as a member in the IdP.

<Render
	file="gateway/selectors/user-group-names"
	product="cloudflare-one"
	params={{ UIname: "User Group Names" }}
/>

### User Name

Use this selector to create identity-based Gateway policies based on an IdP username for a particular user in the IdP.

<Render
	file="gateway/selectors/user-name"
	product="cloudflare-one"
	params={{ UIname: "User Name" }}
/>

:::note[Gateway groups vs. Access rule groups]

In Gateway, a **User Group** refers to a group in your IdP (for example, an Okta group). Gateway does not currently support applying DNS, HTTP, and Network policies to [Access rule groups](/cloudflare-one/access-controls/policies/groups/). This is because Access rule groups may include criteria not available through the IdP, such as device location or IP address.

:::

## IdP groups in Gateway

Cloudflare Gateway can integrate with your organization's identity providers (IdPs). Before building a Gateway policy for IdP users or groups, be sure to [add the IdP as an authentication method](/cloudflare-one/integrations/identity-providers/).

Because IdPs expose user groups in different formats, reference the list below to choose the appropriate identity-based selector.

### Microsoft Entra ID

| Selector       | Value                                 |
| -------------- | ------------------------------------- |
| User Group IDs | `61503835-b6fe-4630-af88-de551dd59a2` |

**Value** is the [Object Id](/cloudflare-one/integrations/identity-providers/entra-id/#entra-groups-in-zero-trust-policies) for an Entra group.

If you enabled user and group synchronization with [SCIM](/cloudflare-one/integrations/identity-providers/entra-id/#synchronize-users-and-groups), the synchronized groups will appear under _User Group Names_:

| Selector         | Value        |
| ---------------- | ------------ |
| User Group Names | `SCIM group` |

### GitHub

| Selector         | Value       |
| ---------------- | ----------- |
| User Group Names | `Marketing` |

### Google

| Selector         | Value       |
| ---------------- | ----------- |
| User Group Names | `Marketing` |

### Okta (OIDC)

If you added Okta as an [OIDC provider](/cloudflare-one/integrations/identity-providers/okta/), use the User Group Names selector:

| Selector         | Value       |
| ---------------- | ----------- |
| User Group Names | `Marketing` |

The Okta OIDC integration supports user and group synchronization with [SCIM](/cloudflare-one/integrations/identity-providers/okta/#synchronize-users-and-groups).

### Okta (SAML)

If you added Okta as a [SAML provider](/cloudflare-one/integrations/identity-providers/okta-saml/), use the SAML Attributes selector:

| Selector        | Attribute name | Attribute value |
| --------------- | -------------- | --------------- |
| SAML Attributes | `groups`       | `Marketing`     |

### Generic SAML IdP

For a [generic SAML provider](/cloudflare-one/integrations/identity-providers/generic-saml/), use the SAML Attribute selector:

| Selector        | Attribute name | Attribute value |
| --------------- | -------------- | --------------- |
| SAML Attributes | `department`   | `Marketing`     |

### Generic OIDC IdP

For a [generic OIDC provider](/cloudflare-one/integrations/identity-providers/generic-oidc/), use the OIDC Claims selector to filter traffic based on [custom OIDC claims](/cloudflare-one/integrations/identity-providers/generic-oidc/#custom-oidc-claims) configured on your IdP:

| Selector    | Claim name   | Claim value   |
| ----------- | ------------ | ------------- |
| OIDC Claims | `department` | `Engineering` |

# Traffic controls

import { Details, Stream } from "~/components";

A Secure Web Gateway (SWG) is a security service that sits between an organization's users and the Internet. It inspects outbound traffic to enforce security policies, block threats, and prevent data loss. Core SWG capabilities include:

- **URL and domain filtering** – Controls which websites users can access.
- **Anti-malware scanning** – Inspects files in transit for malicious code.
- **Application control** – Manages which applications users can reach and what actions they can perform.
- **Data Loss Prevention (DLP)** – Detects and blocks sensitive data before it leaves the network.fprotecting
- **Traffic inspection** – Decrypts and examines encrypted (HTTPS) traffic for hidden threats.

## The need for an SWG

Traditional network security relied on hardware firewalls at the perimeter of a corporate network. That model assumed users, applications, and data all lived inside the same network boundary. Modern organizations face a different reality:

- **Distributed workforce** – Employees connect from home networks, public Wi-Fi, and mobile devices, outside any corporate perimeter.
- **Cloud and SaaS adoption** – Business-critical applications and data have moved to cloud platforms like Microsoft 365, Google Workspace, and Salesforce.
- **Expanding threat surface** – Phishing, ransomware, command-and-control botnets, and data exfiltration attempts target users regardless of their location.

Without an SWG, organizations lose visibility into what websites and applications users access, what threats reach user devices, and what data leaves the organization. An SWG restores that visibility and control by inspecting traffic in the cloud, close to users, rather than forcing all traffic through a central data center.

Cloudflare Gateway is Cloudflare's SWG, built into the [Cloudflare One](https://www.cloudflare.com/learning/access-management/what-is-a-secure-web-gateway/) SASE platform. It inspects and filters traffic at the DNS, network (Layer 4), and HTTP (Layer 7) layers. 

For more information on how SWGs work, refer to the [Cloudflare Learning Center](https://www.cloudflare.com/learning/access-management/what-is-a-secure-web-gateway/).

## Traffic policy types

Every organization needs a way to control what users can reach on the Internet — blocking malware sites, restricting risky applications, and deciding how traffic exits the corporate network. Think of traffic policies as a set of security checkpoints, each inspecting a different layer of your traffic before it is allowed through.

### How Gateway relates to traditional firewalls

If you are familiar with traditional network security, Gateway's policy layers map to familiar firewall functions:

- **DNS policies** correspond to DNS-layer filtering (blocking domains before connections are established).
- **Network policies** correspond to a Layer 4 stateful firewall, sometimes called Firewall-as-a-Service (FWaaS), filtering by IP address, port, and protocol.
- **HTTP policies** correspond to a Layer 7 application firewall (forward proxy with TLS decryption and deep packet inspection).

Unlike hardware firewalls that sit at a single network perimeter, Gateway enforces these policies across Cloudflare's global network, protecting traffic regardless of where users connect.

Gateway supports several policy types because network traffic can be inspected at different layers — from raw packets up to full HTTP requests. Each policy type gives you control at a specific layer:

<Details header="Packet filtering">

**[Packet filtering](/cloudflare-one/traffic-controls/packet-filtering/network-firewall-overview/)** inspects raw network packets and blocks traffic based on properties like source IP address or protocol. It does not need to know who the user is or what session they belong to.

Use packet filtering to drop unwanted traffic before it reaches any other policy.

</Details>

<Details header="DNS policies">

**[DNS policies](/cloudflare-one/traffic-controls/dns-policies/)** check every DNS query your users make. When a query matches a policy rule, Gateway can block the domain from resolving — the site never loads because the domain name is never translated to an IP address.

DNS policies act at the earliest stage of a connection, before any content is fetched. This makes them the fastest policy type to deploy and the broadest in scope. For more information on [DNS filtering](https://www.cloudflare.com/learning/access-management/what-is-dns-filtering/), refer to the Cloudflare Learning Center.

Use DNS policies to block malicious domains, restrict content categories, or prevent entire sites from loading. For full threat protection, pair DNS policies with HTTP policies — DNS blocks known bad domains, while HTTP catches threats hidden in allowed traffic.

</Details>

<Details header="Network policies">

**[Network policies](/cloudflare-one/traffic-controls/network-policies/)** inspect individual TCP, UDP, and Generic Routing Encapsulation (GRE) packets. They can match on IP addresses, ports, protocols, and the server name sent at the start of an encrypted connection (Server Name Indication, or SNI).

Use network policies to block access to specific ports or non-HTTP services such as SSH and RDP.

</Details>

<Details header="HTTP policies">

**[HTTP policies](/cloudflare-one/traffic-controls/http-policies/)** inspect the full content of web requests — including URLs, headers, and uploaded or downloaded files. Gateway decrypts HTTPS traffic so it can examine what DNS and network policies cannot see. This requires installing a [Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on user devices.

Use HTTP policies to block specific URLs, scan file uploads for sensitive data, block malware in downloads, [quarantine suspicious files](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/) for sandbox analysis, and control which accounts users can sign in to. For example, allow your company Google Workspace account but block personal Gmail.

</Details>

<Details header="Egress policies">

**[Egress policies](/cloudflare-one/traffic-controls/egress-policies/)** control how traffic leaves your network by assigning fixed IP addresses that belong to your organization. Third-party services can recognize these IPs as yours.

Use egress policies to connect to partners or services that only allow traffic from a known list of IP addresses.

</Details>

<Details header="Integrate Workers (beta)">

Use the HTTP [Forward action](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) to run a Worker on permitted requests and responses. Gateway handles proxy connections and routing while your Worker runs custom transformations or background tasks. Refer to [Integrate Workers](/cloudflare-one/traffic-controls/integrate-workers/).

</Details>

<Details header="Resolver policies">

**[Resolver policies](/cloudflare-one/traffic-controls/resolver-policies/)** send DNS queries to specific DNS servers instead of the default Cloudflare resolver.

Use resolver policies to resolve private hostnames on your internal network, route queries to your own DNS servers for compliance, or reach internal resources while connected through Cloudflare One.

</Details>

### Identity and device context

Gateway policies can go beyond network attributes (domains, IPs, ports) and incorporate user identity and device health into every decision.

When users connect through the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), Gateway can evaluate:

- **User identity** – Email address, group membership, and authentication method from your [identity provider](/cloudflare-one/integrations/identity-providers/) (for example, Okta, Microsoft Entra ID, or Google Workspace).
- **Device posture** – Signals such as operating system version, disk encryption status, firewall state, and whether the device serial number matches a managed device list. For the full list of available checks, refer to [Device posture](/cloudflare-one/reusable-components/posture-checks/).

These signals can be combined with traffic selectors to create context-aware policies. For example, you can create an HTTP policy that allows access to a sensitive SaaS application only when the user belongs to a specific group **and** the device has disk encryption turned on.

For details on building policies with identity selectors, refer to [Identity-based policies](/cloudflare-one/traffic-controls/identity-selectors/).

:::note
When creating or editing policies, it may take up to 60 seconds for that policy to be updated across all of Cloudflare's data centers.
:::

<Stream
	id="48a3b49b7cdfaef0b3044d1530c82c19"
	title="SASE - Protect your users from Internet risks"
	thumbnail="https://imagedelivery.net/xDOJvHcv1KwTQn6S-BGFIw/a6fd20a9-4cdf-4640-81b1-cada4c4f3f00/public"
/>

## Set up Cloudflare Gateway traffic policies

Before you create Cloudflare Gateway traffic policies, you need connect the devices or networks you want to protect and confirm that Cloudflare Gateway can inspect their traffic. For each traffic policy type, follow this workflow:

1. Connect the devices or networks you want to protect.
2. Verify that Gateway is receiving traffic from your devices.
3. Set up recommended security policies — for example, block all [security threat categories](/cloudflare-one/traffic-controls/domain-categories/#security-categories) with a DNS policy.
4. Add policies specific to your organization's needs.

For example, if your goal is to prevent employees from accessing known malware domains, you would start by enrolling devices with the Cloudflare One Client (step 1), confirm DNS queries appear in your Gateway logs (step 2), then create a DNS policy that blocks all security-risk categories (step 3).

For step-by-step setup guides, refer to [DNS](/cloudflare-one/traffic-controls/get-started/dns/), [Network](/cloudflare-one/traffic-controls/get-started/network/), and [HTTP](/cloudflare-one/traffic-controls/get-started/http/) policies.

### How to choose a Cloudflare Gateway policy type

The following table maps common traffic-filtering goals to the best Cloudflare Gateway policy type:

| Filtering goal                         | Policy type      | Why                                                                    |
| -------------------------------------- | ---------------- | ---------------------------------------------------------------------- |
| Block websites by URL                  | HTTP             | Inspects the full URL path, not just the domain                        |
| Block domains (all pages)              | DNS              | Prevents the domain from resolving                                     |
| Block non-HTTP traffic (SSH, RDP)      | Network          | Inspects TCP/UDP packets on any port                                   |
| Block malware and threats              | DNS _and_ HTTP   | DNS blocks known-bad domains. HTTP catches threats in allowed traffic. |
| Run custom logic on permitted web traffic | HTTP Forward (beta) | Invokes a Worker to transform requests or responses, or start background work |
| Assign static egress IPs               | Egress           | Lets third-party services identify your organization                   |
| Drop traffic before other policies run | Packet filtering | Blocks by packet attributes without user context                       |
| Route DNS to custom nameservers        | Resolver         | Overrides the default Cloudflare resolver                              |

After you choose a Cloudflare Gateway policy type, continue with the matching setup guide to create the policy that fits your traffic-filtering goal.

### Choose a connection method

The connection method (on-ramp) you use determines which policy types Gateway can enforce. The following table summarizes each method:

| Connection method | DNS policies | Network policies | HTTP policies | Best for |
| --- | --- | --- | --- | --- |
| [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) (WARP) | Yes | Yes | Yes | Roaming users on managed devices (laptops, phones) |
| [DNS resolver](/cloudflare-one/traffic-controls/resolver-endpoints/) configuration | Yes | No | No | Unmanaged devices, entire networks, or initial rollout |
| [Proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/) (PAC file) | No | No | Yes (browser only) | Browser-level HTTP filtering without a device agent |
| [Network tunnel](/cloudflare-one/networks/) (IPsec/GRE via Magic WAN) | Yes | Yes | Yes | Branch offices, data centers, and site-level connectivity |

- The **Cloudflare One Client** provides the broadest coverage and is the recommended method for per-device deployments.
- **DNS resolver** configuration is the easiest to deploy (change a DNS setting on your router or device) and provides immediate protection, but it only enforces DNS policies.
- **Proxy endpoints** enable HTTP inspection through browser proxy configuration without installing an agent, but they are limited to browser traffic.
- **Network tunnels** route all site traffic through Gateway and are best for protecting entire office locations or data centers.

You can combine multiple on-ramps. For example, use the Cloudflare One Client for remote employees and network tunnels for branch offices.

## How Gateway processes traffic

When a user makes a request, Gateway inspects it at multiple layers before allowing the connection through. The following diagram shows the end-to-end flow:

```mermaid
flowchart LR
    accTitle: Gateway traffic flow
    accDescr: Diagram showing how traffic flows from user device through an on-ramp to Cloudflare Gateway for policy evaluation, then to the destination.

    A["User device"] --> B["On-ramp"]
    B --> C["Cloudflare edge<br/>(nearest location)"]
    C --> D["Policy evaluation"]
    D --> E["Destination<br/>server"]
    E --> D
    D --> C
    C --> B
    B --> A
```

1. The user's device sends a request (DNS query, TCP connection, or HTTP request).
2. The request reaches Cloudflare through an **on-ramp** — the Cloudflare One Client, a DNS resolver configuration, a proxy endpoint, or a network tunnel.
3. Cloudflare processes the request at the **nearest edge location**, not a centralized data center. This keeps latency low regardless of where the user connects from.
4. Gateway evaluates the request against your configured policies in [order of enforcement](/cloudflare-one/traffic-controls/order-of-enforcement/): DNS policies first, then network policies, then HTTP policies.
5. For permitted HTTP requests, Gateway invokes the Worker selected by the first matching policy using the [Forward action](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/). Permitted traffic without a Forward match is proxied directly. Gateway inspects the final response on the return path.

For details on how Gateway proxies traffic and establishes connections, refer to [Proxy](/cloudflare-one/traffic-controls/proxy/).

## Troubleshoot Cloudflare Gateway policies

For help resolving common issues with Cloudflare Gateway policies, refer to [Troubleshooting](/cloudflare-one/traffic-controls/troubleshooting/).

# Order of enforcement

import { Render } from "~/components";

With Cloudflare Gateway, you can [enable and configure](/cloudflare-one/traffic-controls/get-started/) any combination of DNS, network, and HTTP policies.

<Render file="gateway/order-of-enforcement" product="cloudflare-one" />

# Proxy

import { Tabs, TabItem, Render } from "~/components";

You can forward [HTTP](/cloudflare-one/traffic-controls/get-started/http/) and [network](/cloudflare-one/traffic-controls/get-started/network/) traffic to Gateway for logging and filtering. Gateway can proxy both outbound traffic and traffic directed to resources connected via a Cloudflare Tunnel, Generic Routing Encapsulation (GRE) tunnel, or IPsec tunnel. When a user connects to the Gateway proxy, Gateway will accept the connection and establish a new, separate connection to the origin server.

The Gateway proxy is required for filtering HTTP and network traffic via the Cloudflare One Client in Traffic and DNS mode. To proxy HTTP traffic without deploying the Cloudflare One Client, you can configure [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/) on your devices.

## Proxy algorithm

Gateway uses the [Happy Eyeballs algorithm](https://datatracker.ietf.org/doc/html/rfc6555), which tries IPv4 and IPv6 connections with a staggered fallback and uses whichever address family responds first, to proxy traffic in the following order:

1. The user's browser initiates the TCP handshake by sending Gateway a TCP SYN segment.
2. Gateway sends a SYN segment to the origin server.
3. If the origin server sends a SYN-ACK segment back, Gateway establishes separate TCP connections between the user and Gateway and between Gateway and the origin server.
4. Gateway inspects and filters traffic received from the user.
5. If the traffic passes inspection, Gateway proxies traffic bidirectionally between the user and the origin server.

<Render file="gateway/tcp-flowchart" product="cloudflare-one" />

## Supported protocols

Gateway supports proxying TCP, UDP, and ICMP traffic.

### TCP

When the proxy is enabled, Gateway will always forward TCP traffic.

By default, TCP connection attempts will timeout after 30 seconds and idle connections will disconnect after 8 hours.

### UDP

The UDP proxy forwards UDP traffic such as VoIP, [internal DNS requests](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/private-dns/), and thick client applications.

HTTP/3 uses the QUIC protocol over UDP. To inspect HTTP/3 traffic, turn on both TLS decryption and the UDP proxy. Gateway will then intercept the HTTP/3 connection and connect to the origin server over HTTP/2. Otherwise, HTTP/3 traffic will bypass inspection. For more information on browser-specific behavior, refer to [HTTP/3 inspection](/cloudflare-one/traffic-controls/http-policies/http3/).

### ICMP (Internet Control Message Protocol)

The ICMP proxy allows ICMP traffic to reach your private network through Gateway. For example, this would allow a Cloudflare One Client user to run diagnostic commands such as `ping` and `traceroute` to an internal server IP.

:::caution[Limitation]
Gateway cannot log or filter ICMP traffic.
:::

#### Allow ICMP traffic through `cloudflared`

To use the ICMP proxy with Cloudflare Tunnel, you may need to configure the `cloudflared` host to allow ICMP traffic through `cloudflared`.

<Tabs> <TabItem label="Linux" icon="linux">

1.  Ensure that `ping_group_range` includes the Group ID (GID) of the user running `cloudflared`:

    a. Find the user that owns the `cloudflared` process:

        ```sh
        ps -aux | grep cloudflared
        ```

        ```sh output
        johndoe         407  0.8  1.7 1259904 35296 ?       Ssl  21:02   0:00 /usr/bin/cloudflared --no-autoupdate tunnel run --token eyJhI...
        ```

    b. Get the Group ID of the `cloudflared` user:

        ```sh
        id -g johndoe
        ```

        ```sh output
        10001
        ```

    c. Determine the Group IDs that are allowed to use ICMP:

    ```sh
    sudo sysctl net.ipv4.ping_group_range
    ```

    ```sh output
    net.ipv4.ping_group_range= 0 10000
    ```

    d. Either add the user to a group within that range, or update the range to encompass a group the user is already in. To update `ping_group_range`:

    ```sh
    echo 0 10001 | sudo tee /proc/sys/net/ipv4/ping_group_range
    ```

    e. If you need to make the change apply to an already running process, you need to restart `cloudflared`. To make the change persist on reboot, update your `systcl` parameters:

    ```sh
    echo "net.ipv4.ping_group_range = 0 10001" | sudo tee -a /etc/sysctl.d/99-cloudflared.conf
    ```

2.  If you are running multiple network interfaces (for example, `eth0` and `eth1`), configure `cloudflared` to use the external Internet-facing interface:

    ```sh
    cloudflared tunnel run --icmpv4-src <IP of primary interface>
    ```

</TabItem> <TabItem label="Docker" icon="seti:docker">

In your environment, modify the `ping_group_range` parameter to include the Group ID (GID) of the user running `cloudflared`.

By default the [`cloudflared` Docker container](https://github.com/cloudflare/cloudflared/blob/master/Dockerfile#L29C6-L29C13) executes as a user called `nonroot` inside of the container. `nonroot` is a specific user that exists in the [base image](https://github.com/GoogleContainerTools/distroless/blob/859eeea1f9b3b7d59bdcd7e24a977f721e4a406c/base/base.bzl#L8) we use, and its Group ID is hardcoded to 65532.

</TabItem> </Tabs>

## Turn on the Gateway proxy

The Gateway proxy toggle only applies to traffic from Cloudflare One Client devices. Gateway will always proxy traffic sent with [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/) or [Browser Isolation](/cloudflare-one/remote-browser-isolation/) regardless of this setting.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Proxy and inspection settings**, turn on **Allow Secure Web Gateway to proxy traffic**.
3. Select **TCP**.
4. (Optional) Depending on your use case, you can select **UDP** and/or **ICMP**.

# Resolver policies

import { Render, Badge, GlossaryTooltip } from "~/components";

:::note
Only available on Enterprise plans.
:::

By default, Gateway sends DNS requests to [1.1.1.1](/1.1.1.1/), Cloudflare's public DNS resolver, for resolution. Enterprise users can instead create Gateway policies to route DNS queries to custom resolvers.

```mermaid
flowchart TD
    %% Accessibility
    accTitle: How Gateway routes DNS queries
    accDescr: Flowchart describing the order Cloudflare Gateway routes a DNS query from an endpoint through DNS and resolver policies back to the user.

    %% Flowchart
    user(["User"])-->endpoint[/"Gateway DNS endpoint"/]

    endpoint-->query["DNS policy (query)"]

    query-->resolver["Resolver policy"]

    resolver--"Routes to </br>custom resolver"-->response["DNS policy (response)"]

    response--"Returns response"-->user
```

Gateway will route user traffic to your configured DNS resolver based on the matching policy, even if your resolvers' IP addresses overlap.

## Use cases

You may use resolver policies if you require access to non-publicly routed domains, such as private network services or internal resources. You may also use resolver policies if you need to access a protected DNS service or want to simplify DNS management for multiple locations.

### Internal DNS

[Cloudflare Internal DNS](/dns/internal-dns/) allows you to manage DNS records for internal resources on a private network. DNS zones configured in Internal DNS can only be queried by the Gateway resolver. With resolver policies, you can determine how Gateway resolves your organization's DNS queries to resolve to internal resources based on the context of the query, such as known source IPs for a geographic location.

To get started with resolving internal DNS queries with resolver policies, refer to [Get started](/dns/internal-dns/get-started/).

### Local Domain Fallback

Use resolver policies when your DNS server is reachable from Cloudflare's network — for example, through a Cloudflare Tunnel, IPsec/GRE tunnel, or the public Internet. Use [Local Domain Fallback](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/local-domains/) when the DNS server is only reachable from the user's device.

If both Local Domain Fallback and resolver policies are configured for the same device, Cloudflare will apply your client-side Local Domain Fallback rules first. If you onboard DNS queries to Gateway with the Cloudflare One Client and route them with resolver policies, the source IP of the queries will be the IP address assigned by the Cloudflare One Client.

<Render file="warp/ldf-best-practice" product="cloudflare-one" />

## Resolver connections

Resolver policies support TCP and UDP connections. Custom resolvers can point to the Internet via IPv4 or IPv6, or to a private network service, such as a [Magic tunnel](/magic-transit/how-to/configure-tunnel-endpoints/). Policies default to port `53`. You can change which port your resolver uses by customizing it in your policy.

You can protect your authoritative nameservers from DDoS attacks by enabling [DNS Firewall](/dns/dns-firewall/).

### Cloudflare Tunnel

You can configure connections to a private resolver connected to Cloudflare with [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/). To ensure `cloudflared` can route UDP traffic to your resolver, connect your tunnel via [QUIC](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#protocol).

For more information on connecting a private DNS resolver to Cloudflare with Cloudflare Tunnel, refer to [Private DNS](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/private-dns/).

### Cloudflare WAN

To enable connections to a private resolver connected to Cloudflare via [Cloudflare WAN](/cloudflare-wan/), contact your account team.

### Available DNS endpoints

Resolver policies can route queries for resolution from the following DNS endpoints:

- IPv4
- IPv6
- [DNS over HTTPS (DoH)](/cloudflare-one/traffic-controls/resolver-endpoints/dns-over-https/)
- [DNS over TLS (DoT)](/cloudflare-one/traffic-controls/resolver-endpoints/dns-over-tls/)
- DNS queries generated by Cloudflare [Browser Isolation](/cloudflare-one/remote-browser-isolation/) and [Clientless Web Isolation](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/)
- DNS queries generated by [proxy endpoints](/cloudflare-one/traffic-controls/proxy-endpoints/)

Gateway will filter, resolve, and log your queries regardless of endpoint.

## Create a resolver policy

<Render file="gateway/create-resolver-policy" product="cloudflare-one" />

For more information on creating a DNS policy, refer to [DNS policies](/cloudflare-one/traffic-controls/dns-policies/).

<Render file="gateway/terraform-precedence-warning" product="cloudflare-one" />

## Send DNS queries sourced from dedicated egress IPs <Badge text="Closed beta" variant="caution"/>

:::note
Available in closed beta for Enterprise accounts with [dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/). Contact your account team to request access.
:::

By default, DNS requests that Gateway sends to your custom resolvers use shared Cloudflare source IP addresses. If your upstream resolver restricts access by source IP, you can configure a resolver policy to send those requests from your [dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/) instead.

### Prerequisites

- Your account must have dedicated egress IPs provisioned.
- The resolver policy must include at least one public resolver. Dedicated egress IPs cannot be used when all resolvers route through private networks.

### Enable dedicated egress on a resolver policy

1. In the [Cloudflare One dashboard](https://dash.cloudflare.com/one), go to **Traffic policies** > **Resolver policies**.
2. Create or edit a resolver policy that uses custom DNS resolvers.
3. Under the custom resolver configuration, turn on **Use dedicated egress IPs for DNS requests to custom resolvers**.
4. Select a **Primary IPv4**, **IPv6**, and optionally a **Secondary IPv4** address from the available dedicated egress IPs.
5. Save the policy.

### API configuration

To configure dedicated egress on a resolver policy via the API, add an `egress` object to `rule_settings`:

```json
{
	"rule_settings": {
		"dns_resolvers": {
			"ipv4": [{ "ip": "198.51.100.1" }]
		},
		"egress": {
			"ipv4": "104.30.133.232",
			"ipv6": "2a09:bac0:1000:3ec::/64",
			"ipv4_fallback": "104.30.134.156"
		}
	}
}
```

The `egress` object uses the same schema as [egress policies](/cloudflare-one/traffic-controls/egress-policies/).

## Selectors

### Content Categories

<Render
	file="gateway/selectors/dns-content-categories"
	product="cloudflare-one"
/>

### DNS Resolver IP

<Render file="gateway/selectors/dns-resolver-ip" product="cloudflare-one" />

### DoH Subdomain

<Render file="gateway/selectors/doh-subdomain" product="cloudflare-one" />

### Domain

<Render
	file="gateway/selectors/domain-dns"
	product="cloudflare-one"
	params={{ APIendpoint: "dns.domains" }}
/>

### Host

<Render
	file="gateway/selectors/host-dns"
	product="cloudflare-one"
	params={{ APIendpoint: "dns.fqdn" }}
/>

### Location

<Render file="gateway/selectors/location" product="cloudflare-one" />

### Query Record Type

<Render file="gateway/selectors/query-record-type" product="cloudflare-one" />

### Security Categories

<Render file="gateway/selectors/security-categories" product="cloudflare-one" />

### Source Continent

Use this selector to filter based on the continent where the query arrived to Gateway from. <Render product="cloudflare-one" file="gateway/selectors/source-continent-dns" params={{ one: "dns.src" }} />

### Source Country

Use this selector to filter based on the country where the query arrived to Gateway from. <Render product="cloudflare-one" file="gateway/selectors/source-country-dns" params={{ one: "dns.src" }} />

### Source IP

<Render file="gateway/selectors/source-ip-resolver" product="cloudflare-one" />

### Users

<Render file="gateway/selectors/users-dns" product="cloudflare-one" />

## Comparison operators

<Render file="gateway/comparison-operators" product="cloudflare-one" />

## Value

<Render
	file="gateway/value"
	product="cloudflare-one"
	params={{
		selector: "Host",
		selectordescription: "hostname",
		operator: "matches regex",
		valueOne: ".*whispersystems.org",
		valueTwo: ".*signal.org",
	}}
/>

## Logical operators

<Render
	file="gateway/logical-operators"
	product="cloudflare-one"
	params={{ one: "Identity" }}
/>

# Troubleshoot Gateway

import { TabItem, Tabs } from "~/components";

This guide helps you troubleshoot common issues with Cloudflare Gateway policies. The issues are ordered by the most frequent problems.

## Egress policies do not work as expected

Egress policies are the most common category of issues for Gateway. Symptoms include traffic not using your dedicated egress IP, incorrect failover behavior, or high latency due to Gateway routing traffic through a distant data center.

### Symptom: traffic is not using your dedicated egress IP

Even with an active egress policy, you may find that traffic is egressing from a default Cloudflare IP address instead of your dedicated egress IP.

| Common cause                                | Solution                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DNS resolution to an initial resolved IP | When an egress policy uses a _Domain_ or _Host_ selector, Gateway must first resolve that domain to an [initial resolved IP](/cloudflare-one/networks/routes/reserved-ips/#gateway-initial-resolved-ips). If your account still uses a legacy range within CGNAT (carrier-grade NAT) address space, this IP may be treated as internal to Cloudflare's network and may not be subject to egress policies, which apply to traffic leaving the network. Change the selector in your egress policy from _Domain_ or _Host_ to _Destination IP_ (using the public IP addresses of the service you are trying to reach), or [move your initial resolved IP range off CGNAT](/cloudflare-one/networks/routes/configure-initial-resolved-ips/). |
| Policy precedence                           | A different egress policy with a higher precedence (a lower number) is matching the traffic first. Remember that egress policies follow the same first-match-wins logic.                                                                                                                                                                                                                                                                                                                                                  |
| Split Tunnel configuration                  | The destination IP or domain is excluded from the WARP tunnel via your [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) configuration (which controls whether traffic for specific IPs or domains is sent through or excluded from the WARP tunnel). Traffic that is excluded from the tunnel will not be subject to any Gateway policies, including egress.                                                                                       |
| No egress logs                              | Egress logging is available via Logpush with the Gateway Egress dataset. This is essential for troubleshooting. You can also use a third-party IP check service to verify the egress IP from a test device.                                                                                                                                                                                                                                                                                                               |

### Symptom: failover is not working or is using the wrong IP

Your primary dedicated egress IP becomes unavailable, but instead of using your configured secondary dedicated IP, traffic fails over to a default Cloudflare shared IP.

| Common cause                                          | Solution                                                                                                                                                                                                                                                                |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Routing or configuration issue on the Cloudflare side | Document the time of the incident and collect Request IDs from Gateway HTTP or DNS logs for affected users. Open a support ticket and provide this information. Temporarily, you can edit the egress policy to set your secondary IP as the primary to restore service. |

### Symptom: users are egressing from a geographically distant location

Gateway routes your users in one country (such as Australia) through a dedicated egress IP located in another region (such as Germany), causing high latency and breaking access to geo-restricted content.

Common causes and solutions:

| Common cause               | Solution                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Single egress policy       | You may have one broad egress policy that applies to all users regardless of their location. Create location-aware egress policies. Use the _User Location_ selector in your policy to tie specific user locations to their nearest dedicated egress IP. For example, create one policy for when _User Location_ is `United Kingdom`, egress via London IP; create a second policy for when _User Location_ is `Australia`, egress via Sydney IP. |
| Incorrect geolocation data | The IP address of the user's ISP may not be correctly geolocated. Check the user's location as seen by Cloudflare in the Gateway logs. If it appears incorrect, you can report it to Cloudflare Support.                                                                                                                                                                                                                                          |

## Gateway does not apply policies in the correct order

A common point of confusion is how Gateway evaluates its different policy types and the rules within them.

### Symptom: a Block policy is overriding a more specific Allow or Do Not Scan policy

You have a high-precedence Allow or Do Not Scan policy for a specific application (such as Allow finance.example.com), but Gateway still block traffic with a low-precedence Block policy (such as Block All High-Risk Sites).

The most important concept is [Gateway policy precedence](/cloudflare-one/traffic-controls/order-of-enforcement/), which Gateway enforces based on the policy's order number. A lower order number in the list means a higher precedence. Gateway stops processing further policies when it encounters the first rule that matches.

To resolve Gateway policy precedence issues:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**.
2. Review the order of your DNS, Network, and HTTP policies.
3. Ensure that your most specific Allow, Do Not Scan, or Do Not Inspect policies have a lower order number than your general Block policies.
4. Drag and drop policies to reorder them as needed. An Allow policy for `teams.microsoft.com` should be placed before a general Block policy for all file sharing applications.

## TLS decryption breaks applications

Turning on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) is required for Gateway features such as Data Loss Prevention (DLP), Browser Isolation, and application-aware HTTP policies. However, it can cause issues with certain types of software.

### Symptom: command-line tools (CLI tools) or native applications fail with certificate errors

If after turning on TLS decryption, command-line tools (such as `git`, `aws`, `kubectl`, and `terraform`) or desktop applications (such as ChatGPT or Docker) stop working, this may be due to certificate errors. Applications may return errors such as `SSL: CERTIFICATE_VERIFY_FAILED`, `self-signed certificate in certificate chain`, or similar TLS errors.

These applications do not use the operating system's trust store and therefore do not trust the Cloudflare root certificate that you installed. They often have their own certificate trust store or use certificate pinning, which expects the server's original certificate, not one re-signed by Cloudflare.

To resolve this issue:

<Tabs>
<TabItem label="Recommended">

Create a targeted HTTP policy to bypass decryption for the specific domains these tools need to access. Place this policy at a higher precedence (lower order number) than your main TLS decryption policy.

Create a [list](/cloudflare-one/reusable-components/lists/) that includes hosts such as `github.com`, `*.amazonaws.com`, and `*.docker.io`.

| Selector | Operator | Value              | Action         |
| -------- | -------- | ------------------ | -------------- |
| Domain   | in list  | _CLI Tool Domains_ | Do Not Inspect |

</TabItem>
<TabItem label="Workaround">

You can configure some tools to trust a custom CA or disable SSL verification. This is less secure and harder to manage at scale. For more information, refer to [Install certificate manually](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/).

</TabItem>
</Tabs>

### Symptom: the custom block page is not displayed

When an HTTP policy blocks a user's request, their browser will return a generic error (`ERR_SSL_PROTOCOL_ERROR`) instead of your configured Gateway block page.

This happens because the browser does not trust the certificate presented by the block page, which is signed by the Cloudflare root certificate. This means the certificate is not installed or not trusted on the user's device.

To resolve this issue:

1. Confirm that a [Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) is installed on the device.
2. Ensure the certificate is placed in the correct system-level trust store (such as, Keychain's System store on macOS, or Trusted Root Certification Authorities for the Local Computer on Windows).
3. If you are using a mobile device management (MDM) tool, verify that your deployment script correctly installs and trusts the certificate.

## Private DNS and internal resources are not working

You have configured Gateway to resolve internal hostnames, but users are unable to access them. For example, a user connected to the Cloudflare One Client tries to access an internal service like `jira.mycompany.local`, but the DNS query fails.

| Common causes                              | Solution                                                                                                                                                                                                                                       |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Missing or incorrect resolver policy       | Go to **Traffic policies** > **Resolver policies**. Create a policy that matches your internal domain suffix and forwards queries to your internal DNS servers' IP addresses.                                                                  |
| Split Tunnel excludes the private IP range | If your internal resources are in a private IP range (such as `10.0.0.0/8`), that range must be included in the tunnel. If it is in the Exclude list of your Split Tunnel configuration, the Cloudflare One Client will not proxy the traffic. |
| Local Domain Fallback misconfiguration     | Use resolver policies for corporate DNS. Only use Local Domain Fallback for domains specific to a user's immediate physical network.                                                                                                           |

# Troubleshooting

import { Badge, Render } from "~/components";

<Render file="troubleshooting/gateway" product="cloudflare-one" />

## Worker errors with the Forward action <Badge text="Beta" variant="caution" />

If a Forward policy does not invoke your Worker or requests fail after enabling it, refer to [Forward troubleshooting](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#troubleshooting). Worker invocation failures fail closed. Check HTTP filtering, TLS decryption, policy order, the named entrypoint, and Workers Logs before widening your policy scope.

Deleting a Worker used by a Forward policy leaves the policy active. Matching requests fail with HTTP `500`. Update the policy to select an existing Worker, or delete the policy.

# Common policies

import { Render, Tabs, TabItem, APIRequest } from "~/components";

The following Cloudflare Gateway DNS policies are commonly used to secure DNS traffic. Each example includes both dashboard and API instructions that you can adapt for your organization.

For a baseline set of recommended policies, refer to [Secure your Internet traffic and SaaS apps](/learning-paths/secure-internet-traffic/build-dns-policies/recommended-dns-policies/).

Refer to the [DNS policies page](/cloudflare-one/traffic-controls/dns-policies/) for a comprehensive list of other selectors, operators, and actions.

## Allow corporate domains

This policy allows users to access official corporate domains. By deploying the policy with high [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence), you ensure that employees can access trusted domains even if they fall under a blocked category like _Newly seen domains_ or _Login pages_.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator | Value             | Action | Precedence |
| -------- | -------- | ----------------- | ------ | ---------- |
| Domain   | in list  | _Allowed domains_ | Allow  | 1          |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow corporate domains",
		description: "Allow any internal corporate domains added to a list",
		precedence: 0,
		enabled: true,
		action: "allow",
		filters: ["dns"],
		traffic: "any(dns.domains[*] in $<LIST_UUID>)",
		identity: "",
	}}
/>

To get the UUIDs of your lists, use the [List Zero Trust lists](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/) endpoint.

</TabItem> </Tabs>

## Block security threats

Block [security categories](/cloudflare-one/traffic-controls/domain-categories/#security-categories) such as Command & Control, Botnet and Malware based on Cloudflare's threat intelligence.

<Render
	file="gateway/policies/dash-plus-api/dns/block-security-categories"
	product="cloudflare-one"
/>

## Block content categories

The categories included in this policy are not always a security threat, but blocking them can help minimize the risk that your organization is exposed to. For more information, refer to [domain categories](/cloudflare-one/traffic-controls/domain-categories/).

<Render
	file="gateway/policies/dash-plus-api/dns/block-content-categories"
	product="cloudflare-one"
/>

## Block a dynamic list of categories

You can add a list of category IDs to the [EDNS (Extension Mechanisms for DNS)](https://datatracker.ietf.org/doc/html/rfc6891) header of a request sent to Gateway as a JSON object using OPT code `65050`. EDNS allows extra metadata to be attached to a DNS query beyond the standard fields. For example:

```json
{
	"categories": [2, 67, 125, 133]
}
```

With the [Request Context Categories](/cloudflare-one/traffic-controls/dns-policies/#request-context-categories) selector, you can block the category IDs sent with EDNS. This is useful to filter by categories not known at the time of creating a policy, or to enforce device-specific DNS content filtering without reaching your account limit. When Gateway uses this selector to block a DNS query, the request will return an Extended DNS Error (EDE) Code 15 (`Blocked`), along with a field containing an array of the matched categories.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector                 | Operator | Value     | Action |
| ------------------------ | -------- | --------- | ------ |
| Request Context Category | is       | _Present_ | Block  |

</TabItem>
<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "All-DNS-Bock-Category-Matches-In-Request",
		description: "Block all category matches in the request EDNS context",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: "dns.categories_in_request_context_matches",
		identity: "",
	}}
/>

</TabItem>
<TabItem label="Terraform">

```tf
resource "cloudflare_zero_trust_gateway_policy" "block_content_categories" {
  account_id  = var.cloudflare_account_id
  name        = "All-DNS-Bock-Category-Matches-In-Request"
  description = "Block all category matches in the request EDNS context"
  enabled     = true
  action      = "block"
  filters     = ["dns"]
  traffic     = "dns.categories_in_request_context_matches"
  identity    = ""
}
```

</TabItem> </Tabs>

## Block unauthorized applications

<Render file="gateway/policies/block-applications" product="cloudflare-one" />

<Render
	file="gateway/policies/dash-plus-api/dns/block-applications"
	product="cloudflare-one"
/>

## Block banned countries

You can implement policies to block websites hosted in countries categorized as high risk. The designation of such countries may result from your organization's requirements or through regulations including [EAR (Export Administration Regulations)](https://www.tradecompliance.pitt.edu/embargoed-and-sanctioned-countries), [OFAC (Office of Foreign Assets Control)](https://orpa.princeton.edu/export-controls/sanctioned-countries), and [ITAR (International Traffic in Arms Regulations)](https://www.tradecompliance.pitt.edu/embargoed-and-sanctioned-countries). This policy blocks DNS queries that resolve to IP addresses geolocated in the countries you specify.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector                        | Operator | Value                                                                                                                                                          | Action |
| ------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| Resolved Country IP Geolocation | in       | _Afghanistan_, _Belarus_, _Congo (Kinshasa)_, _Cuba_, _Iran_, _Iraq_, _Korea, North_, _Myanmar_, _Russian Federation_, _Sudan_, _Syria_, _Ukraine_, _Zimbabwe_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block banned countries",
		description: "Block access to banned countries",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic:
			'any(dns.dst.geo.country[*] in {"AF" "BY" "CD" "CU" "IR" "IQ" "KP" "MM" "RU" "SD" "SY" "UA" "ZW"})',
		identity: "",
	}}
/>

</TabItem> </Tabs>

## Block top-level domains

Blocking [frequently misused](https://www.spamhaus.org/statistics/tlds/) top-level domains (TLDs) — the last segment of a domain name, such as `.com` or `.ru` — can reduce security risks, especially when there is no discernible advantage to be gained from allowing access. Similarly, restricting access to specific country-level TLDs may be necessary to comply with regulations like [ITAR](https://www.tradecompliance.pitt.edu/embargoed-and-sanctioned-countries) or [OFAC](https://orpa.princeton.edu/export-controls/sanctioned-countries).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator      | Value                                                                 | Logic | Action |
| -------- | ------------- | --------------------------------------------------------------------- | ----- | ------ |
| Domain   | matches regex | `[.](cn\|ru)$`                                                        | Or    | Block  |
| Domain   | matches regex | `[.](rest\|hair\|top\|live\|cfd\|boats\|beauty\|mom\|skin\|okinawa)$` | Or    |        |
| Domain   | matches regex | `[.](zip\|mobi)$`                                                     |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block top-level domains",
		description:
			"Block top-level domains that are frequently used for malicious practices",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic:
			'any(dns.domains[*] matches "[.](cn|ru)$") or any(dns.domains[*] matches "[.](rest|hair|top|live|cfd|boats|beauty|mom|skin|okinawa)$") or any(dns.domains[*] matches "[.](zip|mobi)$")',
		identity: "",
	}}
/>

</TabItem> </Tabs>

## Block phishing attacks

To protect against [sophisticated phishing attacks](https://blog.cloudflare.com/2022-07-sms-phishing-attacks/), you could prevent users from accessing phishing domains that are specifically targeting your organization. The following policy blocks specific keywords associated with an organization or its authentication services (such as _okta_, _2fa_, _cloudflare_ or _sso_), while still allowing access to official corporate domains.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator      | Value                                       | Logic | Action |
| -------- | ------------- | ------------------------------------------- | ----- | ------ |
| Domain   | not in list   | _Corporate Domains_                         | And   | Block  |
| Domain   | matches regex | `.*okta.*\|.*cloudflare.*\|.*mfa.*\|.sso.*` |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block phishing attacks",
		description:
			"Block attempts to phish specific domains targeting your organization",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic:
			'not(any(dns.domains[*] in $<LIST_UUID>)) and any(dns.domains[*] matches ".*okta.*\\|.*cloudflare.*\\|.*mfa.*\\|.sso.*")',
		identity: "",
	}}
/>

To get the UUIDs of your lists, use the [List Zero Trust lists](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/) endpoint.

</TabItem> </Tabs>

## Block online tracking

To safeguard user privacy, some organizations will block tracking domains such as `dig.whatsapp.com` as well as other tracking domains embedded at the OS level. This policy is implemented by creating a custom blocklist. Refer to [this repository](https://github.com/nextdns/native-tracking-domains/tree/28991a0d5b2ab6d35588a74af82162ea7caff420/domains) for a list of widespread tracking domains that you can add to your blocklist.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator | Value                  | Action |
| -------- | -------- | ---------------------- | ------ |
| Domain   | in list  | _Top tracking domains_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block online tracking",
		description: "Block domains used for tracking at an OS level",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: "any(dns.domains[*] in $<LIST_UUID>)",
		identity: "",
	}}
/>

To get the UUIDs of your lists, use the [List Zero Trust lists](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/) endpoint.

</TabItem> </Tabs>

## Block malicious IPs

Block specific IP addresses that are known to be malicious or pose a threat to your organization. This policy is usually implemented by creating custom blocklists or by using blocklists provided by threat intelligence partners or regional Computer Emergency and Response Teams (CERTs).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector    | Operator | Value     | Action |
| ----------- | -------- | --------- | ------ |
| Resolved IP | in list  | _DShield_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block malicious IPs",
		description:
			"Block specific IP addresses that are known to be malicious or pose a threat to your organization",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: "any(dns.resolved_ips[*] in $<LIST_UUID>)",
		identity: "",
	}}
/>

To get the UUIDs of your lists, use the [List Zero Trust lists](/api/resources/zero_trust/subresources/gateway/subresources/lists/methods/list/) endpoint.

</TabItem> </Tabs>

## Turn on CIPA filter

The CIPA (Children's Internet Protection Act) Filter is a collection of subcategories that encompass a wide range of topics that could be harmful or inappropriate for minors. It is used as a part of [Project Cybersafe Schools](/fundamentals/reference/policies-compliances/cybersafe/) to block access to unwanted or harmful online content. Upon creating this policy, your organization will have minimum [CIPA compliance](https://www.fcc.gov/consumers/guides/childrens-internet-protection-act).

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value         | Action |
| ------------------ | -------- | ------------- | ------ |
| Content Categories | in       | _CIPA Filter_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Turn on CIPA filter",
		description:
			"Block access to unwanted or harmful online content for children",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: "any(dns.content_category[*] in {182})",
		identity: "",
	}}
/>

</TabItem> </Tabs>

## Hide explicit search results

SafeSearch is a feature of search engines that helps you filter explicit or offensive content. You can force SafeSearch on search engines like Google, Bing, Yandex, YouTube, and DuckDuckGo:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value            | Action      |
| ------------------ | -------- | ---------------- | ----------- |
| Content Categories | in       | _Search Engines_ | Safe Search |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Hide explicit search results",
		description:
			"Force SafeSearch on search engines to filter explicit or offensive content",
		enabled: true,
		action: "safesearch",
		filters: ["dns"],
		traffic: "any(dns.content_category[*] in {145})",
		identity: "",
	}}
/>

</TabItem> </Tabs>

## Check user identity

<Render file="gateway/policies/check-user-identity" product="cloudflare-one" />

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator | Value         | Logic | Action |
| ---------------- | -------- | ------------- | ----- | ------ |
| Application      | in       | _Salesforce_  | And   | Block  |
| User Group Names | in       | `Contractors` |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Check user identity",
		description: "Filter traffic based on a user identity group name",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: "any(app.ids[*] in {606})",
		identity: 'any(identity.groups.name[*] in {"Contractors"})',
	}}
/>

</TabItem> </Tabs>

## Restrict access to specific groups

Filter DNS queries to allow only specific users access.

The following example includes two policies. The first policy allows the specified group, while the second policy blocks all other users. To ensure the policies are evaluated properly, place the Allow policy above the Block policy. For more information, refer to the [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence).

### 1. Allow a group

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value             | Logic | Action |
| ------------------ | -------- | ----------------- | ----- | ------ |
| Content Categories | in       | _Social Networks_ | And   | Allow  |
| User Group Names   | in       | `Marketing`       |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow social media for Marketing",
		description:
			"Allow access to social media sites for users in the Marketing group",
		precedence: 1,
		enabled: true,
		action: "allow",
		filters: ["dns"],
		traffic: "any(dns.content_category[*] in {149})",
		identity: 'any(identity.groups.name[*] in {"Marketing"})',
	}}
/>

</TabItem> </Tabs>

### 2. Block all other users

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value             | Action |
| ------------------ | -------- | ----------------- | ------ |
| Content Categories | in       | _Social Networks_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block social media",
		description: "Block social media for all other users",
		precedence: 2,
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: "any(dns.content_category[*] in {149})",
		identity: "",
	}}
/>

</TabItem> </Tabs>

## Control IP version

Enterprise users can pair these policies with an [egress policy](/cloudflare-one/traffic-controls/egress-policies/) to control which IP version is used when Gateway connects to the destination server.

Optionally, you can use the Domain selector to control the IP version for specific sites.

:::note
To ensure traffic routes through your preferred IP version, turn off **Modify Gateway block behavior**.
:::

### Force IPv4

Force users to connect with IPv4 by blocking `AAAA` (IPv6) record resolution.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector          | Operator | Value  | Action |
| ----------------- | -------- | ------ | ------ |
| Query Record Type | is       | _AAAA_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Force IPv4",
		description: "Force users to connect with IPv4 by blocking IPv6 resolution",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: 'dns.query_rtype == "AAAA"',
		identity: "",
	}}
/>

</TabItem> </Tabs>

### Force IPv6

Force users to connect with IPv6 by blocking `A` (IPv4) record resolution.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector          | Operator | Value | Action |
| ----------------- | -------- | ----- | ------ |
| Query Record Type | is       | _A_   | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Force IPv6",
		description: "Force users to connect with IPv6 by blocking IPv4 resolution",
		enabled: true,
		action: "block",
		filters: ["dns"],
		traffic: 'dns.query_rtype == "A"',
		identity: "",
	}}
/>

</TabItem> </Tabs>

# DNS policies

import { Details, InlineBadge, Render } from "~/components";

DNS policies let you control which websites and services your users can reach by inspecting their DNS queries — the lookups that translate domain names into IP addresses. Because DNS policies act at the lookup stage, they work across all protocols and applications, not just web browsers.

When a user makes a DNS request, [Gateway](/cloudflare-one/traffic-controls/) matches the request against the DNS policies you have set up for your organization. If the domain does not belong to any blocked categories, or if it matches an Allow or Override policy, the user's client receives an address based on DNS resolution from Cloudflare's public DNS resolver (1.1.1.1). You can also use a [resolver policy](/cloudflare-one/traffic-controls/resolver-policies/) to redirect DNS requests to a custom server.

A DNS policy consists of an **Action** as well as a logical expression that determines the scope of the action. To build an expression, you need to choose a **Selector** and an **Operator**, and enter a value or range of values in the **Value** field. You can use **And** and **Or** logical operators to evaluate multiple conditions.

- [Actions](#actions)
- [Selectors](#selectors)
- [Comparison operators](#comparison-operators)
- [Value](#value)
- [Logical operators](#logical-operators)

When creating a DNS policy, you can select as many security risk categories and content categories as needed to fully secure your network. Unless a more specific selector is configured in a policy (for example, _User Email_ or _Source IP_), then the policy will be evaluated against all DNS queries that reach Gateway from your organization.

<Render
	file="gateway/response"
	product="cloudflare-one"
	params={{ type: "query", example: "Source IP", example2: "Resolved IP" }}
/>

<Render file="gateway/terraform-precedence-warning" product="cloudflare-one" />

## Actions

The action determines what Gateway does when a DNS query matches your policy conditions. You can assign one action per policy.

These are the action types you can choose from:

- [Allow](#allow)
- [Block](#block)
- [Override](#override)
- [Safe Search](#safe-search)
- [YouTube Restricted Mode](#youtube-restricted-mode)

### Allow

API value: `allow`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Authoritative Nameserver IP](#authoritative-nameserver-ip)
- [Content Categories](#content-categories)
- [DNS CNAME Response Value](#dns-cname-record)
- [DNS MX Response Value](#dns-mx-record)
- [DNS PTR Response Value](#dns-ptr-record)
- [DNS Resolver IP](#dns-resolver-ip)
- [DNS TXT Response Value](#dns-txt-record)
- [DOH Subdomain](#doh-subdomain)
- [Domain](#domain)
- [Host](#host)
- [Indicator Feeds](#indicator-feeds)
- [Location](#location)
- [Query Record Type](#query-record-type)
- [Resolved Continent IP Geolocation](#resolved-continent)
- [Resolved Country IP Geolocation](#resolved-country)
- [Resolved IP](#resolved-ip)
- [Request Context Categories](#request-context-categories)
- [Security Categories](#security-categories)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source IP](#source-ip)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

</Details>

Policies with Allow actions explicitly permit DNS queries to resolve. Gateway uses a [first-match principle](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence), which means that if an Allow policy matches a query at a higher precedence than a Block policy, the query will be allowed to resolve. For example, the following configuration allows DNS queries to reach domains categorized as belonging to the Education content category:

| Selector           | Operator | Value       | Action |
| ------------------ | -------- | ----------- | ------ |
| Content Categories | in       | _Education_ | Allow  |

#### Disable DNSSEC validation

DNSSEC (Domain Name System Security Extensions) verifies that DNS responses have not been tampered with by checking a cryptographic signature attached to the record. When you select **Disable DNSSEC validation**, Gateway will resolve DNS queries even if the signature cannot be validated. We do not recommend disabling DNSSEC validation unless you know that the validation failure is due to DNSSEC configuration issues and not malicious attacks.

### Block

API value: `block`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Authoritative Nameserver IP](#authoritative-nameserver-ip)
- [Content Categories](#content-categories)
- [DNS CNAME Response Value](#dns-cname-record)
- [DNS MX Response Value](#dns-mx-record)
- [DNS PTR Response Value](#dns-ptr-record)
- [DNS Resolver IP](#dns-resolver-ip)
- [DNS TXT Response Value](#dns-txt-record)
- [DOH Subdomain](#doh-subdomain)
- [Domain](#domain)
- [Host](#host)
- [Indicator Feeds](#indicator-feeds)
- [Location](#location)
- [Query Record Type](#query-record-type)
- [Resolved Continent IP Geolocation](#resolved-continent)
- [Resolved Country IP Geolocation](#resolved-country)
- [Resolved IP](#resolved-ip)
- [Request Context Categories](#request-context-categories)
- [Security Categories](#security-categories)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source IP](#source-ip)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

</Details>

Policies with Block actions prevent DNS queries from resolving for destinations you specify within the Selector and Value fields. For example, the following configuration blocks DNS queries from reaching domains categorized as belonging to the Adult Themes content category:

| Selector           | Operator | Value          | Action |
| ------------------ | -------- | -------------- | ------ |
| Content Categories | in       | _Adult Themes_ | Block  |

#### Custom block page

When choosing the Block action, turn on **Modify Gateway block behavior** to respond to queries with a block page to display to users who go to blocked websites. Optionally, you can override your global block page setting with a URL redirect for the specific DNS policy. For more information, refer to [Block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/).

If the block page is turned off for a policy, Gateway will respond to blocked queries with an `A` record (IPv4) of `0.0.0.0` or an `AAAA` record (IPv6) of `::`. Because no server responds at these addresses, the browser will display its default connection error page.

<Render file="gateway/block-page-dns-records" product="cloudflare-one" />

#### Cloudflare One Client block notifications

<Render
	file="gateway/client-notifications"
	product="cloudflare-one"
	params={{ toggleName: "**Display block notification for Cloudflare One Client**" }}
/>

### Override

API value: `override`

<Details header="Available selectors">

The Override action cannot be used with selectors evaluated during or after DNS resolution.

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [DNS Resolver IP](#dns-resolver-ip)
- [DOH Subdomain](#doh-subdomain)
- [Domain](#domain)
- [Host](#host)
- [Location](#location)
- [Query Record Type](#query-record-type)
- [Resolved Continent IP Geolocation](#resolved-continent)
- [Resolved Country IP Geolocation](#resolved-country)
- [Security Categories](#security-categories)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source IP](#source-ip)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

</Details>

Policies with Override actions replace the real DNS answer with a destination you specify. When a user queries a domain that matches the policy, Gateway returns your custom IP address or hostname instead of the actual DNS record. For example, you can provide a custom response IP of `1.2.3.4` for all queries to `www.example.com` with the following policy:

| Selector | Operator | Value             | Action   | Override Hostname |
| -------- | -------- | ----------------- | -------- | ----------------- |
| Hostname | is       | `www.example.com` | Override | `1.2.3.4`         |

:::note
The Override action only supports queries for A, AAAA, and HTTPS records. If a query for a different type of record matches an Override policy, Gateway will return REFUSED.
:::

### Safe Search

API value: `safesearch`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [DNS Resolver IP](#dns-resolver-ip)
- [DOH Subdomain](#doh-subdomain)
- [Domain](#domain)
- [Host](#host)
- [Location](#location)
- [Query Record Type](#query-record-type)
- [Resolved Continent IP Geolocation](#resolved-continent)
- [Resolved Country IP Geolocation](#resolved-country)
- [Security Categories](#security-categories)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source IP](#source-ip)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

</Details>

SafeSearch is a feature of search engines that helps you filter explicit or offensive content. When you enable SafeSearch, the search engine filters explicit or offensive content and returns search results that are safe for children or at work.

You can use Cloudflare Gateway to enable SafeSearch on search engines like Google, Bing, Yandex, YouTube and DuckDuckGo. For example, to enable SafeSearch for Google, you can create the following policy:

| Selector | Operator | Value        | Action      |
| -------- | -------- | ------------ | ----------- |
| Domain   | is       | `google.com` | Safe Search |

### YouTube Restricted Mode

API value: `ytrestricted`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [DNS Resolver IP](#dns-resolver-ip)
- [DOH Subdomain](#doh-subdomain)
- [Domain](#domain)
- [Host](#host)
- [Location](#location)
- [Query Record Type](#query-record-type)
- [Resolved Continent IP Geolocation](#resolved-continent)
- [Resolved Country IP Geolocation](#resolved-country)
- [Security Categories](#security-categories)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source IP](#source-ip)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

</Details>

Similarly, you can enforce YouTube Restricted mode by choosing the _YouTube Restricted_ action. YouTube Restricted Mode is an automated filter for adult and offensive content built into YouTube. To enable YouTube Restricted Mode, you could set up a policy like the following:

| Selector   | Operator | Value         | Action             |
| ---------- | -------- | ------------- | ------------------ |
| DNS Domain | is       | `youtube.com` | YouTube Restricted |

This setup ensures users will be blocked from accessing offensive sites using DNS.

## Selectors

Gateway matches DNS queries against the following selectors, or criteria.

Each selector is evaluated during a specific phase of the DNS resolution process:

- **Before DNS resolution** — Gateway inspects properties of the incoming query (for example, the domain name or source IP) before looking up the answer.
- **During DNS resolution** — Gateway inspects information discovered while resolving the query (for example, the authoritative nameserver IP).
- **After DNS resolution** — Gateway inspects the DNS answer (for example, the resolved IP or CNAME record) after resolution completes.

The Override action cannot be used with selectors evaluated during or after DNS resolution, because the override must be applied before the answer is returned. For more information on how evaluation phase interacts with precedence, refer to [order of enforcement](/cloudflare-one/traffic-controls/order-of-enforcement/#dns-policies).

### Application

<Render
	file="gateway/selectors/application-dns"
	product="cloudflare-one"
	params={{ policyType: "DNS" }}
/>

### Authoritative Nameserver IP

Use this selector to match against the IP address of the authoritative nameserver IP address.

| UI name                     | API example                                | Evaluation phase      |
| --------------------------- | ------------------------------------------ | --------------------- |
| Authoritative Nameserver IP | `dns.authoritative_ns_ips == 198.51.100.0` | During DNS resolution |

### Content Categories

<Render
	file="gateway/selectors/dns-content-categories"
	product="cloudflare-one"
/>

<Render file="gateway/selectors/category-options" product="cloudflare-one" />

### DNS CNAME Record

Use this selector to filter DNS responses by their `CNAME` records.

| UI name                  | API example                                                   | Evaluation phase     |
| ------------------------ | ------------------------------------------------------------- | -------------------- |
| DNS CNAME Response Value | `any(dns.response.cname[*] in {"www.apple.com.edgekey.net"})` | After DNS resolution |

:::note

If one CNAME record points to another CNAME record, each record in the chain will be evaluated. For example, if `abc.example.com` points to `xyz.example.com`, then your DNS policy will evaluate both `abc.example.com` and `xyz.example.com`.
:::

### DNS MX Record

Use this selector to filter DNS responses by their `MX` records.

| UI name               | API example                                                 | Evaluation phase     |
| --------------------- | ----------------------------------------------------------- | -------------------- |
| DNS MX Response Value | `any(dns.response.mx[*] in {"gmail-smtp-in.l.google.com"})` | After DNS resolution |

### DNS PTR Record

Use this selector to filter DNS responses by their `PTR` records.

| UI name                | API example                                                | Evaluation phase     |
| ---------------------- | ---------------------------------------------------------- | -------------------- |
| DNS PTR Response Value | `any(dns.response.ptr[*] in {"255.2.0.192.in-addr.arpa"})` | After DNS resolution |

### DNS Resolver IP

<Render file="gateway/selectors/dns-resolver-ip" product="cloudflare-one" />

### DNS TXT Record

Use this selector to filter DNS responses by their `TXT` records.

| UI name                | API example                                 | Evaluation phase     |
| ---------------------- | ------------------------------------------- | -------------------- |
| DNS TXT Response Value | `any(dns.response.txt[*] in {"your_text"})` | After DNS resolution |

### DoH Subdomain (DNS over HTTPS)

<Render file="gateway/selectors/doh-subdomain" product="cloudflare-one" />

### Domain

<Render
	file="gateway/selectors/domain-dns"
	product="cloudflare-one"
	params={{ APIendpoint: "dns.domains" }}
/>

### Host

<Render
	file="gateway/selectors/host-dns"
	product="cloudflare-one"
	params={{ APIendpoint: "dns.fqdn" }}
/>

### Indicator Feeds

Use this selector to match against custom indicator feeds.

You can use a [publicly available indicator feed](/security-center/indicator-feeds/#publicly-available-feeds) or a custom indicator feed assigned to your account by a designated third-party vendor. For more information on indicator feeds, refer to [Custom Indicator Feeds](/security-center/indicator-feeds/).

| UI name         | API example          | Evaluation phase      |
| --------------- | -------------------- | --------------------- |
| Indicator Feeds | `dns.indicator_feed` | Before DNS resolution |

<Render file="gateway/selectors/category-options" product="cloudflare-one" />

### Location

<Render file="gateway/selectors/location" product="cloudflare-one" />

### Query Record Type

<Render file="gateway/selectors/query-record-type" product="cloudflare-one" />

### Resolved Continent

Use this selector to filter based on the continent that the query resolves to. Geolocation is determined from the IP address in the response. To specify a continent, enter its two-letter code into the **Value** field:

- AF - Africa
- AN - Antarctica
- AS - Asia
- EU - Europe
- NA - North America
- OC - Oceania
- SA - South America

| UI name                           | API example                     | Evaluation phase     |
| --------------------------------- | ------------------------------- | -------------------- |
| Resolved Continent IP Geolocation | `dns.dst.geo.continent == "EU"` | After DNS resolution |

### Resolved Country

Use this selector to filter based on the country that the query resolves to. Geolocation is determined from the IP address in the response. To specify a country, enter its [ISO 3166-1 Alpha 2 code](https://www.iso.org/obp/ui/#search/code/) in the **Value** field.

| UI name                         | API example                   | Evaluation phase     |
| ------------------------------- | ----------------------------- | -------------------- |
| Resolved Country IP Geolocation | `dns.dst.geo.country == "RU"` | After DNS resolution |

### Resolved IP

Use this selector to filter based on the IP addresses that the query resolves to.

| UI name     | API example                                | Evaluation phase     |
| ----------- | ------------------------------------------ | -------------------- |
| Resolved IP | `any(dns.resolved_ips[*] == 198.51.100.0)` | After DNS resolution |

### Request Context Categories

Use this selector to match a dynamic list of [category IDs](/cloudflare-one/traffic-controls/domain-categories/#category-and-subcategory-ids) sent in the [EDNS (Extension Mechanisms for DNS)](https://datatracker.ietf.org/doc/html/rfc6891) portion of a DNS query. EDNS allows extra metadata to be attached to a DNS query beyond the standard fields. Gateway reads category IDs from the EDNS OPT code `65050`.

| UI name                    | API example                                 | Evaluation phase      |
| -------------------------- | ------------------------------------------- | --------------------- |
| Request Context Categories | `dns.categories_in_request_context_matches` | Before DNS resolution |

### Security Categories

<Render file="gateway/selectors/security-categories" product="cloudflare-one" />

<Render file="gateway/selectors/category-options" product="cloudflare-one" />

### Source Continent

Use this selector to filter based on the continent where the query arrived to Gateway from. <Render file="gateway/selectors/source-continent-dns" product="cloudflare-one" params={{ one: "dns.src" }} />

### Source Country

Use this selector to filter based on the country where the query arrived to Gateway from. <Render file="gateway/selectors/source-country-dns" product="cloudflare-one" params={{ one: "dns.src" }} />

### Source IP

<Render file="gateway/selectors/source-ip-resolver" product="cloudflare-one" />

### Source Internal IP

Use this selector to apply policies to the source internal IP address of a DNS query. For example, this could be the private IP address of the hosts behind [Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-gateway/) (formerly Magic WAN) or [Cloudflare Mesh](/mesh/) used by your organization to send queries to Gateway.

| UI name            | API example                        | Evaluation phase      |
| ------------------ | ---------------------------------- | --------------------- |
| Source Internal IP | `dns.src_internal_ip == 10.10.0.1` | Before DNS resolution |

### Users

<Render file="gateway/selectors/users-dns" product="cloudflare-one" />

## Comparison operators

<Render file="gateway/comparison-operators" product="cloudflare-one" />

## Value

<Render
	file="gateway/value"
	product="cloudflare-one"
	params={{
		selector: "Host",
		selectordescription: "hostname",
		operator: "matches regex",
		valueOne: ".*whispersystems.org",
		valueTwo: ".*signal.org",
	}}
/>

## Logical operators

<Render
	file="gateway/logical-operators"
	product="cloudflare-one"
	params={{ one: "Identity" }}
/>

## Limitations

### Third-party filtering conflict

<Render file="gateway/third-party-warning" product="cloudflare-one" />

### Cloudflare WAN forwarding

To apply DNS policies to queries forwarded through [Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-gateway/), you can either point your organization's DNS resolver to an IPv6, DNS over HTTPS (DoH), or DNS over TLS (DoT) endpoint or request a dedicated resolver IPv4 address. For more information, refer to [DNS resolver IPs and hostnames](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/).

### Fallback DNS

Some applications (for example, WhatsApp and Android Studio) have backup DNS servers built into their code. If their primary DNS query is blocked by Gateway, these apps automatically retry the query against their built-in DNS servers (for example, Google's `8.8.8.8`), which bypasses your policies entirely. To mitigate this behavior, you create a [Gateway Network policy](/cloudflare-one/traffic-controls/network-policies/) to block outbound DNS traffic on TCP/UDP port `53` to the fallback DNS servers. For example, to block Google's fallback DNS servers:

| Selector         | Operator | Value                | Logic | Action |
| ---------------- | -------- | -------------------- | ----- | ------ |
| Protocol         | in       | _TCP_, _UDP_         | And   | Block  |
| Destination Port | in       | `53`                 | And   |        |
| Destination IP   | in       | `8.8.8.8`, `8.8.4.4` |       |        |

# Maximum DNS TTL

import { Tabs, TabItem } from "~/components";

Set a maximum time-to-live (TTL) for DNS responses returned by Gateway. When an upstream DNS record has a TTL that exceeds the configured maximum, Gateway caps the TTL to the value you specify. Lower values ensure that DNS policy changes - such as blocking a newly identified malicious domain - take effect faster across all clients, at the cost of increased query volume from reduced caching.

The maximum TTL cap only applies to upstream-derived DNS answers for allowed queries. Gateway-generated responses (blocks, overrides, safe-search answers) are not affected because they already use a short default TTL.

## How it works

Gateway applies a tiered TTL hierarchy. The most specific setting takes precedence:

1. If the DNS location has a per-location override, that value is used.
2. If the location inherits its setting, the account-level maximum TTL is used.
3. If no maximum TTL is configured at any level, upstream TTL values pass through unchanged.

The valid range for any maximum TTL value is **60 to 36,000 seconds** (1 minute to 10 hours).

## Configure the account-level maximum TTL

The account-level setting applies to all DNS locations that do not have a per-location override.

<Tabs> <TabItem label="Dashboard">

1. In [Zero Trust](https://dash.cloudflare.com/one), go to **Traffic Policies** > **Traffic Settings**.
2. Under **Proxy and inspection**, find the **Configure time-to-live for DNS resolution** section.
3. Enter a value in seconds (between 60 and 36,000).
4. Select **Save**.

</TabItem> <TabItem label="API">

```sh
curl --request PUT \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/gateway/configuration \
  --header "Authorization: Bearer {api_token}" \
  --header "Content-Type: application/json" \
  --data '{
    "settings": {
      "max_ttl_secs": 3600
    }
  }'
```

To remove the account-level cap (allow upstream TTLs to pass through), omit the `max_ttl_secs` field or set it to `null`.

</TabItem> </Tabs>

## Configure a per-location maximum TTL

Each [DNS location](/cloudflare-one/traffic-controls/resolver-endpoints/) can override the account-level setting. The per-location setting supports three modes:

| Mode | Behavior |
| --- | --- |
| **Respect account-level setting** (`inherit`) | Uses whatever value is configured at the account level. This is the default for new locations. |
| **Do not set max value** (`disabled`) | Disables the maximum TTL cap for this location, even if one is configured at the account level. Upstream TTL values pass through unchanged. |
| **Custom** (`override`) | Sets a location-specific maximum TTL that overrides the account-level value. Requires a `ttl_secs` value between 60 and 36,000. |

<Tabs> <TabItem label="Dashboard">

1. In [Zero Trust](https://dash.cloudflare.com/one), go to **Traffic controls** > **Resolver endpoints**.
2. Select a location, or create a new one.
3. In the **DNS Endpoints** tab, find the **Configure time-to-live for this location** section.
4. Choose one of:
   - **Respect account-level setting** - inherits the account-level TTL cap.
   - **Do not set max value** - disables the TTL cap for this location.
   - **Custom** - enter a TTL value in seconds.
5. Select **Save**.

</TabItem> <TabItem label="API">

```sh
# Inherit account-level setting (default)
curl --request PUT \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/gateway/locations/{location_id} \
  --header "Authorization: Bearer {api_token}" \
  --header "Content-Type: application/json" \
  --data '{
    "max_ttl": {
      "mode": "inherit"
    }
  }'
```

```sh
# Disable max TTL for this location
curl --request PUT \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/gateway/locations/{location_id} \
  --header "Authorization: Bearer {api_token}" \
  --header "Content-Type: application/json" \
  --data '{
    "max_ttl": {
      "mode": "disabled"
    }
  }'
```

```sh
# Override with a custom value
curl --request PUT \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/gateway/locations/{location_id} \
  --header "Authorization: Bearer {api_token}" \
  --header "Content-Type: application/json" \
  --data '{
    "max_ttl": {
      "mode": "override",
      "ttl_secs": 3600
    }
  }'
```

</TabItem> </Tabs>

## DNS log fields

When a maximum TTL is active, two additional fields appear in Gateway DNS logs:

| Field | Description |
| --- | --- |
| `upstream_record_ttls` | The original TTL values from the upstream DNS response, before any cap was applied. |
| `applied_max_ttl` | The maximum TTL value that Gateway applied to the response. If no cap was applied, this field is absent. |

These fields are visible in the DNS logs column picker under the **DNS Response Details** group in the dashboard, and in Logpush datasets.

# Test DNS filtering

import { Details } from "~/components";

This section covers how to validate your Gateway DNS configuration. Testing your policies after setup helps confirm that queries are being filtered as expected before you rely on them in production.

## Prerequisites

Before you start, make sure your device is sending DNS queries to Gateway. You can do this in one of two ways:

- **Cloudflare One Client** — If your device runs the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), DNS queries route through Gateway automatically.
- **DNS location** — If you are using a DNS-only deployment (without the Cloudflare One Client), verify that your network's DNS resolver points to your [Gateway DNS location's](/cloudflare-one/traffic-controls/resolver-endpoints/) IP address.

## Test a DNS policy

Once you have created a DNS policy to block a domain, you can use either `dig` (a command-line DNS lookup tool, available on macOS and Linux) or `nslookup` (available on Windows) to see if the policy is working as intended.

For example, if you created a policy to block `example.com`, you can do the following to see if Gateway is successfully blocking `example.com`:

1. Open your terminal.

2. Type `dig example.com` (`nslookup example.com` if you are using Windows) and press **Enter**.

3. In the `dig` output, check the `status:` field in the header line (the line starting with `;; ->>HEADER<<-`). If the [block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) is turned off for the policy, you should see `REFUSED` — a DNS response code meaning the server declined to answer the query:

   ```sh
   dig example.com
   ```

   ```sh {4} output

   ; <<>> DiG 9.10.6 <<>> example.com
   ;; global options: +cmd
   ;; Got answer:
   ;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 6503
   ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

   ;; QUESTION SECTION:
   ;example.com.                   IN      A

   ;; Query time: 46 msec
   ;; SERVER: 172.64.36.1#53(172.64.36.1)
   ;; WHEN: Tue Mar 10 20:22:18 CDT 2020
   ;; MSG SIZE  rcvd: 29
   ```

   If the [block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) is enabled for the policy, you should see `NOERROR` (meaning the query was resolved) in the header with `162.159.36.12` and `162.159.46.12` as the answers. These are Cloudflare's block page IP addresses:

   ```sh null
   dig example.com
   ```

   ```sh {4,13,14} output

   ; <<>> DiG 9.10.6 <<>> example.com
   ;; global options: +cmd
   ;; Got answer:
   ;; ->>HEADER<<- opcode: QUERY, status: NOERROR id: 14531
   ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

   ;; OPT PSEUDOSECTION:
   ; EDNS: version: 0, flags:; udp: 1452
   ;; QUESTION SECTION:
   ;example.com.                   IN      A

   ;;ANSWER SECTION:
   example.com.            60      IN      A                  162.159.36.12
   example.com.            60      IN      A                  162.159.46.12

   ;; Query time: 53 msec
   ;; SERVER: 172.64.36.1#53(172.64.36.1)
   ;; WHEN: Tue Mar 10 20:19:52 CDT 2020
   ;; MSG SIZE  rcvd: 83
   ```

### Test a security or content category

If you are blocking a [security category](/cloudflare-one/traffic-controls/dns-policies/#security-categories) or a [content category](/cloudflare-one/traffic-controls/dns-policies/#content-categories), you can test that the policy is working by using the [test domain](#common-test-domains) associated with each category.

Once you have configured your Gateway policy to block the category, the test domain will show a block page when you attempt to visit the domain in your browser, or will return `REFUSED` when you perform `dig` using the command-line interface.

#### Test domain format

- **One-word category** — For categories with one-word names (for example, _Malware_), the test domain uses the following format:

  ```txt
  <NAME_OF_CATEGORY>.testcategory.com
  ```

- **Multi-word category** — For categories with multiple words in the name (for example, _Parked & For Sale Domains_), the test domain uses the following format:
  - Remove any spaces between the words
  - Replace `&` with `and`
  - Lowercase all letters

#### Common test domains

| Category                        | Test domain                                    |
| ------------------------------- | ---------------------------------------------- |
| _Anonymizer_                    | `anonymizer.testcategory.com`                  |
| _Command and Control & Botnet_  | `commandandcontrolandbotnet.testcategory.com`  |
| _compromised Domain_            | `compromiseddomain.testcategory.com`           |
| _Cryptomining_                  | `cryptomining.testcategory.com`                |
| _Malware_                       | `malware.testcategory.com`                     |
| _New Domains_                   | `newdomains.testcategory.com`                  |
| _Parked & For Sale Domains_     | `parkedandforsaledomains.testcategory.com`     |
| _Phishing_                      | `phishing.testcategory.com`                    |
| _Potentially Unwanted Software_ | `potentiallyunwantedsoftware.testcategory.com` |
| _Private IP Address_            | `privateipaddress.testcategory.com`            |
| _Spam_                          | `spam.testcategory.com`                        |
| _Spyware_                       | `spyware.testcategory.com`                     |
| _Unreachable_                   | `unreachable.testcategory.com`                 |

## Test EDNS configuration

EDNS client subnet (ECS) is a DNS extension that sends a portion of the user's IP address to authoritative DNS nameservers, allowing them to return geographically optimal answers. Cloudflare sends the first `/24` of the user's IP address to preserve privacy while still providing location information. If you [enabled EDNS client subnet](/cloudflare-one/traffic-controls/resolver-endpoints/) for your DNS location, you can validate it as follows:

1. Obtain your DNS location's DoH (DNS over HTTPS) subdomain:
   1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic controls** > **Resolver endpoints**.
   2. Select the DNS location you are testing.
   3. Note the value of **DNS over HTTPS**.

2. Open a terminal and run the following command:

   ```sh
   curl 'https://<DOH_SUBDOMAIN>.cloudflare-gateway.com/dns-query?type=TXT&name=o-o.myaddr.google.com' -H 'Accept: application/dns-json' | json_pp
   ```

   The output should contain your EDNS client subnet:

   ```json null {12}
   {
   	"AD": false,
   	"Answer": [
   		{
   			"TTL": 60,
   			"data": "\"108.162.218.211\"",
   			"name": "o-o.myaddr.google.com",
   			"type": 16
   		},
   		{
   			"TTL": 60,
   			"data": "\"edns0-client-subnet 136.62.0.0/24\"",
   			"name": "o-o.myaddr.google.com",
   			"type": 16
   		}
   	],
   	"CD": false,
   	"Question": [
   		{
   			"name": "o-o.myaddr.google.com",
   			"type": 16
   		}
   	],
   	"RA": true,
   	"RD": true,
   	"Status": 0,
   	"TC": false
   }
   ```

3. To verify your EDNS client subnet, obtain your source IP address:

   ```sh
   curl ifconfig.me
   ```

   ```sh output
   136.62.12.156%
   ```

   The source IP address should fall within the /24 range specified by your EDNS client subnet.

## Clear DNS cache

Modern web browsers and operating systems are designed to cache DNS records for a set amount of time. When a request is made for a DNS record, the browser cache is the first location checked for the requested record. A DNS policy may not appear to work if the response is already cached.

To clear your DNS cache:

<Details header="ChromeOS">

1. Go to `chrome://net-internals/#dns`.
2. Select **Clear host cache**.

</Details>

<Details header="Windows">

1. Open the admin command prompt or PowerShell.
2. Run the following command:

```bash
ipconfig /flushdns
```

</Details>

<Details header="macOS">

1. Open Terminal.
2. Run the following commands:

```sh
sudo killall -HUP mDNSResponder
sudo killall mDNSResponderHelper
sudo dscacheutil -flushcache
```

</Details>

# Timed DNS policies

import { APIRequest, Tabs, TabItem } from "~/components";

By default, Cloudflare Gateway policies apply at all times when turned on. With timed DNS policies, you can control when DNS policies are active — for example, to block social media only during work hours or to temporarily allow access to a restricted site for a maintenance window. You can configure a policy to be active during specific time periods or set the policy to expire after a certain duration.

There are two timed DNS policy options:

- [Policy duration](#policy-duration): The policy is active for a specific amount of time after being turned on (for example, 30 minutes).
- [Policy schedule](#policy-schedule): The policy is active during a recurring weekly schedule (for example, weekdays from 9 AM to 5 PM).

## Policy duration

You can use a time-based policy duration to set a specific time frame for the policy to turn on or configure an exact time for the policy to turn off.

To set a duration for a DNS policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies** > **DNS**.
2. Create a new DNS policy or choose an existing policy and select **Edit**.
3. In **Apply durations and schedules**, turn on **Policy duration**.
4. In **Input method**, choose the type of duration:
   - Choose _Duration_ and enter a specific amount of time until the policy turns off.
   - Choose _Exact end date_ and enter a specific date and time in your account's time zone for the policy to turn off.
5. Select **Save policy**.

When a policy turns off, it will remain off until you turn it back on.

:::caution
The duration timer does not pause when you turn the policy off. It is calculated as an absolute end time from when the policy was first turned on.
:::

For example, you can create a policy at 12:00 PM and set it to turn off after six hours. If you turn the policy off at 3:00 PM and turn it back on at 4:00 PM, the policy will still turn off at 6:00 PM — six hours after the original activation time, not six hours of cumulative active time.

### Reset a policy's duration

When a policy's time duration expires, you can turn the policy back on for the duration you originally configured. To reset a policy's duration, select the policy and choose **Reset policy duration**.

For policies with an exact end time, you can change the time before the policy turns off. Once the policy reaches its exact end time, you will need to edit the policy and set a new end time. To set a new exact end time:

1. Select the policy.
2. Choose **Edit**.
3. Turn on **Set a policy duration**.
4. In **Input method**, choose _Exact end date_. In **Date and time**, enter a new date and time for the policy to turn off.
5. Select **Save policy**.

## Policy schedule

You can use Gateway to create a new DNS policy with a schedule or add a schedule to an existing policy.

<Tabs syncKey="dashPlusAPI">
<TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies** > **DNS**.
2. Create a new DNS policy or choose an existing policy and select **Edit**.
3. In **Apply durations and schedules**, turn on **Policy schedule**.
4. (Optional) In **Time Zone**, choose a time zone to apply the policy based on the time zone you select, regardless of the user's location. By default, Gateway will use the end user's time zone to apply the policy based on the local time of the user making the DNS query.
5. In **Schedule template**, choose a preset schedule, or choose _Custom schedule_ to define a custom schedule. You can choose up to three non-overlapping time ranges of 15 minute intervals.
6. Select **Save policy**.

</TabItem>
<TabItem label="API">

To schedule a policy with the API, use the [Create a Zero Trust Gateway rule endpoint](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/create/) with the `schedule` parameter set to your desired days of the week, times of day, and an optional time zone. For example:

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		action: "block",
		name: "Block gambling sites on weekends",
		traffic: 'any(dns.content_category[*] in {"Gambling"})',
		schedule: {
			sat: "08:00-17:00",
			sun: "08:00-17:00",
			timezone: "Europe/Paris",
		},
	}}
/>

</TabItem>
</Tabs>

The policy's schedule will appear in the Cloudflare dashboard under **Zero Trust** > **Traffic policies** > **Firewall policies** > **DNS** when you select the policy.

### How Gateway determines time zone

If you [assign a time zone](#example-fixed-time-zone) to your schedule, Gateway will always use the current time at that time zone regardless of the user's location. This allows you to enable a policy during a certain fixed time period.

If you [do not specify a time zone](#example-users-time-zone), Gateway will enable the DNS policy based on the user's local time zone. The user's time zone is inferred from the IP geolocation of their source IP address. If Gateway is unable to determine the time zone from the source IP, it will fall back to the time zone of the data center where the query was received.

:::note
Users on VPNs or corporate proxies may have their time zone inferred incorrectly, because their source IP geolocates to the VPN exit point rather than their physical location. If consistent enforcement is important, assign a fixed time zone to the schedule.
:::

#### Example: Fixed time zone

The following command creates a DNS policy to block `facebook.com` only on weekdays from 8:00 AM - 12:30 PM and 1:30 PM - 5:00 PM in the Chicago, USA time zone.

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "office-no-facebook-policy",
		action: "block",
		traffic: 'dns.fqdn == "facebook.com"',
		enabled: true,
		schedule: {
			time_zone: "America/Chicago",
			mon: "08:00-12:30,13:30-17:00",
			tue: "08:00-12:30,13:30-17:00",
			wed: "08:00-12:30,13:30-17:00",
			thu: "08:00-12:30,13:30-17:00",
			fri: "08:00-12:30,13:30-17:00",
		},
	}}
/>

Refer to [this table](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) for a list of all time zone identifiers.

#### Example: User's time zone

The following command creates a DNS policy to block `clockin.com` only on weekends in the time zone where the user is currently located.

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "clock-in-policy",
		action: "block",
		traffic: 'dns.fqdn == "clockin.com"',
		enabled: true,
		schedule: {
			sat: "00:00-24:00",
			sun: "00:00-24:00",
		},
	}}
/>

:::note
Gateway will not change the policy's `enabled` status when inside or outside of the time period specified. When enabled, Gateway activates or deactivates the policy according to its schedule. When disabled, the policy is always deactivated.
:::

# Dedicated egress IPs

import { Details, Render } from "~/components";

:::note
Only available as an add-on to Zero Trust Enterprise plans.
:::

Many third-party services require you to allowlist specific source IP addresses before they accept connections. Dedicated egress IPs are static IP addresses assigned exclusively to your account — no other Cloudflare customer shares them.

Each dedicated egress IP consists of an IPv4 address and an IPv6 range, both tied to a specific Cloudflare data center. Cloudflare provisions your account with at least two dedicated egress IPs in two different cities.

You can request additional dedicated egress IPs at any time. Contact your account team to schedule a service window.

## Turn on egress IPs

To start routing traffic through dedicated egress IPs:

1. Contact your account team to obtain a dedicated egress IP.
2. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
3. Turn on **Allow Secure Web Gateway to proxy traffic**.
4. Select **TCP**.
5. (Optional) Select **UDP**. This will allow HTTP/3 traffic to egress with your dedicated IPs.

Dedicated egress IPs are now turned on for all network and HTTP traffic proxied by Gateway. To selectively turn on dedicated egress IPs for a subset of your traffic, refer to [egress policies](/cloudflare-one/traffic-controls/egress-policies/).

## Verify egress IPs

To check if your device is using the correct dedicated egress IP:

1. Verify that the device is connected to your Zero Trust organization through the Cloudflare One Client.
2. Determine the source IPv4 address of your device by going to `https://ipv4.icanhazip.com/`.
3. Determine the source IPv6 address of your device by going to `https://ipv6.icanhazip.com/`.
4. Verify that the source IPv4 and IPv6 addresses match your dedicated egress IP.

When testing against another origin, you may see either an IPv4 or IPv6 address. Gateway does not control which protocol is used — some origins only support one protocol, and when both are available, the client operating system and browser decide. For example, Windows [favors IPv6 by default](https://learn.microsoft.com/troubleshoot/windows-server/networking/configure-ipv6-in-windows).

## IPs

### Bring your own IP address (BYOIP)

If your organization already owns IPv4 or IPv6 addresses from a regional Internet registry, you can use them as dedicated egress IPs instead of Cloudflare-provided addresses. To obtain an IPv6 range, refer to [American Registry for Internet Numbers (ARIN)](https://www.arin.net/resources/guide/ipv6/first_request/) or [Regional Internet Registry for Europe, Middle East and Central Asia (RIPE NCC)](https://www.ripe.net/manage-ips-and-asns/ipv6/request-ipv6/).

After you onboard your IP addresses, they appear as options when you create an [egress policy](/cloudflare-one/traffic-controls/egress-policies/) and choose **Use dedicated egress IPs (Cloudflare or BYOIP)** as the [egress method](/cloudflare-one/traffic-controls/egress-policies/#egress-methods). BYOIP dedicated egress IPs do not support [IP geolocation](#ip-geolocation).

For more information, refer to [Cloudflare BYOIP](/byoip/) or contact your account team.

### Cloudflare IPs

If you do not have your own authority-provided IPv4 and IPv6 addresses, you can use dedicated egress IPs with a Cloudflare IP address.

<Render
	file="check-leased-ips"
	product="byoip"
	params={{
		product: "Gateway dedicated egress IPs",
	}}
/>

## Limitations

### Concurrent connections

Each dedicated egress IP supports up to 40,000 concurrent connections per unique combination of destination IP and destination port. You can configure multiple origins for each combination of dedicated egress IP and source port.

### Unsupported traffic

Dedicated egress IPs do not apply to the following traffic types. These connections use the default shared IPs because Cloudflare identifies them by other means (for example, tunnel ID or account context) rather than source IP.

- Private networks connected to Zero Trust via Cloudflare Tunnel
- Traffic destined for private networks connected to Zero Trust via [Cloudflare WAN](/cloudflare-wan/)
- ICMP traffic (for example, `ping`)

By default, DNS queries that Gateway sends to custom resolvers use shared Cloudflare source IP addresses. You can [configure a resolver policy to send these queries from dedicated egress IPs](/cloudflare-one/traffic-controls/resolver-policies/#send-dns-queries-sourced-from-dedicated-egress-ips).

### Traffic resilience

To improve traffic resilience, assign your dedicated egress IPs to different Cloudflare data center locations. If you have multiple IPs in the same city, choose different data centers within that city. For more information, contact your account team.

<Render file="gateway/secondary-ipv4-requirement" product="cloudflare-one" />

:::tip[Fallback egress IPs]
If the location for your primary egress IPs goes down and there is no secondary backup IP address configured in the egress policy, Gateway will not properly route your traffic. Cloudflare recommends you always configure a fallback egress IP for every egress policy.
:::

### IP geolocation

:::note
IP geolocation will take at least six weeks to update across databases.
:::

Websites and services use third-party IP geolocation databases to determine where a visitor is located. When you turn on dedicated egress IPs, Gateway updates these databases so they associate your new IPs with the correct city. Until the databases finish updating, services like Google Search may show incorrect regional content — for example, directing users in India to the United States landing page.

Your egress traffic geolocates to the city selected in your [egress policies](/cloudflare-one/traffic-controls/egress-policies/). Traffic that does not match an egress policy defaults to the closest dedicated egress location. Create a [catch-all egress policy](/cloudflare-one/traffic-controls/egress-policies/#catch-all-policy) before dedicated egress IPs are assigned to your account to prevent incorrect geolocation while databases update.

To verify that the IP geolocation has updated, check your dedicated egress IP in one of the supported databases:

<Details header="Supported IP geolocation databases">

- [Google](https://developers.google.com/maps/documentation/geolocation/overview)
- [MaxMind GeoIP](https://www.maxmind.com/en/geoip-databases)
- [TransUnion Neustar TruValidate IP Intelligence](https://www.transunion.com/solution/truvalidate/digital-insights/ip-intelligence)
- [Abstract IP Geolocation API](https://www.abstractapi.com/ip-geolocation-api)
- [DB-IP](https://db-ip.com/)
- [Digital Element](https://www.digitalelement.com/)
- [Geo Targetly](https://geotargetly.com/)
- [IP-API.com](https://ip-api.com/)
- [IP2Location](https://lite.ip2location.com/)
- [IPinfo.io](https://ipinfo.io/)
- [ip2c.org](https://ip2c.org/)
- [ipapi](https://ipapi.com/)
- [ipgeolocation.io](https://ipgeolocation.io/)
- [ipify](https://www.ipify.org/)
- [Ipstack](https://ipstack.com/)

</Details>

### Egress location

Where your users' traffic physically exits the Cloudflare network depends on whether the connection uses IPv4 or IPv6.

| Protocol | Destination proxied by Cloudflare | Physical egress location             | IP geolocation                       |
| -------- | --------------------------------- | ------------------------------------ | ------------------------------------ |
| IPv4     | No                                | Data center with dedicated egress IP | Matches dedicated egress IP location |
| IPv4     | Yes                               | Locally connected data center        | Matches dedicated egress IP location |
| IPv6     | No                                | Locally connected data center        | Matches dedicated egress IP location |
| IPv6     | Yes                               | Locally connected data center        | Matches dedicated egress IP location |

#### IPv4

IPv4 addresses are scarce, so Cloudflare must physically route IPv4 traffic to the data center where your dedicated address is provisioned. The user connects to the nearest Cloudflare data center, and Cloudflare internally routes the traffic to the dedicated egress location configured in your [egress policies](/cloudflare-one/traffic-controls/egress-policies/). As a result, the data center shown in the user's Cloudflare One Client preferences may differ from the actual egress location.

Performance is better when users visit domains proxied by Cloudflare ([orange-clouded](/dns/proxy-status/) domains). In this case, IPv4 traffic physically exits from the most performant data center while still appearing to originate from your dedicated egress location.

For example, assume you have a primary dedicated egress IP in Los Angeles and a secondary dedicated egress IP in New York. A user in Las Vegas would see Las Vegas as their connected data center. If they go to a site not proxied by Cloudflare ([gray-clouded](/dns/proxy-status/#dns-only-records)), such as `espn.com`, they will egress from Los Angeles (or whichever city is in the matching egress policy). If they go to an orange-clouded site such as `cloudflare.com`, they will physically egress from Las Vegas but use Los Angeles as their IP geolocation.

:::note[IPv4 and IPv6 behavior]
IPv4 addresses are limited, so Cloudflare must physically route traffic to the data center where your dedicated IPv4 address is provisioned. IPv6 has virtually unlimited address space, so Cloudflare can assign IPv6 ranges from all geolocations to every data center. This means IPv6 traffic can egress locally while still appearing to originate from your configured geolocation.
:::

#### IPv6

Unlike IPv4, IPv6 traffic physically exits from the user's connected data center while still appearing to originate from the dedicated egress IP geolocation. This works because IPv6 has enough address space for Cloudflare to assign IPv6 ranges from all possible geolocations to every data center. Each account receives a /64 IPv6 range.

In the example above, the Las Vegas user would physically egress from Las Vegas but their traffic would IP geolocate to Los Angeles. This means:

| Attribute       | Value                                                                                                         |
| --------------- | ------------------------------------------------------------------------------------------------------------- |
| Physical egress | User's closest Cloudflare data center (Las Vegas)                                                             |
| IP geolocation  | Dedicated egress IP location configured in your egress policy (Los Angeles)                                   |
| Logs            | Correct IP geolocation (Los Angeles) even though the physical egress is from a different location (Las Vegas) |

## Frequently asked questions (FAQ)

### Can I provision the same egress IP address to multiple data centers?

No, egress IPs are limited to a single data center.

### Can my users in different locations egress from their closest data center via a single egress IP?

No, traffic exits from the data center where the egress IP is provisioned. If your users are spread across multiple regions, reserve multiple egress IPs in different data centers and assign each user group to the closest one.

### Can I use dedicated egress IPs with traffic proxied via [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/)?

Yes, your users will egress via their provisioned IP address.

### What happens when I use dedicated egress IPs with [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/)?

Your users will connect to the nearest data center, where the remote browser session will load. The remote browser will then egress via the data center with their provisioned egress IP.

### Do dedicated egress IPs work on the [Cloudflare China Network](/china-network/)?

No, Gateway does not support dedicated egress IPs on the China Network.

# Egress through Cloudflare Tunnel

import { Render, Details, GlossaryTooltip, DashButton } from "~/components";

<Render file="gateway/egress-selector-warp-version" product="cloudflare-one" />

Some third-party services only accept connections from specific source IPs listed in an Access Control List (ACL). If a non-Cloudflare IP (for example, an IP from your ISP or a cloud provider like AWS) is already on their allowlist, you can route traffic through a Cloudflare Tunnel so that it exits using that same IP. This is called source IP anchoring — it allows you to keep your existing egress IPs without purchasing [Cloudflare dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/).

For example, assume your banking service at `app.bank.com` expects traffic from an AWS IP. You install `cloudflared` in your AWS environment and add a public hostname route for `app.bank.com`. When users connect to `app.bank.com` through the Cloudflare One Client, Gateway applies your network policies and routes the filtered traffic through the Cloudflare Tunnel to AWS. The traffic then exits to the public Internet using your AWS egress IP.

```mermaid
    flowchart LR
      subgraph aws["AWS VPC"]
				cloudflared["cloudflared"]
      end
			subgraph cloudflare[Cloudflare]
			  gateway["Gateway"]
			end
			subgraph internet[Internet]
				resolver[1.1.1.1]
				app[Application]
			end
      warp["Cloudflare One
				Client"]--"app.bank.com"-->gateway--"Network traffic"-->cloudflared
			gateway<-.DNS lookup.->resolver
			aws--AWS egress IP -->app
```

To learn more about how Gateway applies hostname-based egress policies, refer to the [Cloudflare blog](https://blog.cloudflare.com/egress-policies-by-hostname/).

## Prerequisites

User traffic must be on-ramped to Gateway using one of the following methods:

<Render file="gateway/egress-selector-onramps" product="cloudflare-one" />

## 1. Connect your private network

[Connect your private network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/) to Cloudflare using `cloudflared`. For example, if you want traffic to egress from AWS, connect the private CIDR block of your AWS VPC.

:::note
Requires `cloudflared` version 2025.7.0 or later.
:::

## 2. Add a public hostname route

To route a public hostname through Cloudflare Tunnel:

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. Select **Create hostname route**.

3. In **Hostname**, enter the public hostname that represents the application (for example, `app.bank.com`). The hostname should be accessible from the public Internet.

4. For **Tunnel**, select the Cloudflare Tunnel that is being used to connect the private network to Cloudflare.

5. Select **Create route**.

## 3. Route network traffic through the Cloudflare One Client

In your WARP [Split Tunnels](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) configuration, route the following IP addresses through the WARP tunnel to Gateway.

### Initial resolved IPs

When users connect to a public hostname route, Gateway will assign an <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip> to the DNS query from the following range:

<Render file="gateway/egress-selector-cgnat-ips" product="cloudflare-one" />

Gateway's network engine operates at Layer 3/Layer 4 of the [OSI model](https://www.cloudflare.com/learning/ddos/glossary/open-systems-interconnection-model-osi/), where only IP addresses are available — not hostnames. The initial resolved IP acts as a signal: when a packet's destination IP falls within this range, Gateway recognizes that the IP maps to a public hostname route and sends the traffic through the corresponding Cloudflare Tunnel.

To route initial resolved IPs through the Cloudflare One Client:

<Render file="gateway/egress-selector-split-tunnels" product="cloudflare-one" />

### Private network IPs

Your private network's CIDR block should also route through the WARP tunnel. For a detailed configuration example, refer to [Connect a private network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/connect-cidr/#3-route-private-network-ips-through-the-cloudflare-one-client).

## 4. (Optional) Configure network policies

You can build [Gateway network policies](/cloudflare-one/traffic-controls/network-policies/) to filter HTTPS traffic to your public hostname on port `443`. For example, to restrict `app.bank.com` so that only certain users or groups can access it through your AWS egress IP, create two policies: one to allow authorized users, and one to block everyone else.

1. Allow company employees:

   <Render
   	file="gateway/policies/restrict-access-to-private-networks-allow"
   	product="cloudflare-one"
   	params={{ selector: "SNI", value: "app.bank.com" }}
   />

2. Block everyone else on port `443`:

   | Selector | Operator | Value          | Action |
   | -------- | -------- | -------------- | ------ |
   | SNI      | in       | `app.bank.com` | Block  |

Gateway does not support hostname-based filtering for traffic on non-`443` ports. To block traffic to `app.bank.com` on all ports, use the [Destination IP](/cloudflare-one/traffic-controls/network-policies/#destination-ip) selector and specify the public IP range of `app.bank.com`.

## 5. Test the connection

From a device, open a browser and go to `app.bank.com`.

You can search for `app.bank.com` in your [Gateway DNS logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/); the **DNS response details** section should show the public resolved IPs as well as an <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>. You can also check your [Cloudflare Tunnel logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/) to confirm that requests are routing through the tunnel to the public resolved IPs.

## Limitations

### Google Chrome restricts local network access

<Render file="gateway/egress-selector-chrome-issue" product="cloudflare-one" />

# Host selectors

import { Tabs, TabItem, Details, APIRequest, Render, GlossaryTooltip } from "~/components";
import HostSelectorEgressDiagram from "~/components/cf/HostSelectorEgressDiagram.astro";

<Details header="Feature availability">

| [Client modes](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/) | [Zero Trust plans](https://www.cloudflare.com/teams-pricing/) |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Traffic and DNS mode                                                                              | Enterprise                                                    |

| System   | Availability | Minimum client version |
| -------- | ------------ | ---------------------- |
| Windows  | ✅           | 2025.4.929.0           |
| macOS    | ✅           | 2025.4.929.0           |
| Linux    | ✅           | 2025.4.929.0           |
| iOS      | ✅           | 1.11                   |
| Android  | ✅           | 2.4.2                  |
| ChromeOS | ✅           | 2.4.2                  |

</Details>

Egress policies are evaluated at Layer 4 (https://www.cloudflare.com/learning/ddos/glossary/open-systems-interconnection-model-osi/) of the OSI model, where only IP addresses are available — not hostnames. The [Application](/cloudflare-one/traffic-controls/egress-policies/#application), [Content Categories](/cloudflare-one/traffic-controls/egress-policies/#content-categories), [Domain](/cloudflare-one/traffic-controls/egress-policies/#domain), and [Host](/cloudflare-one/traffic-controls/egress-policies/#host) selectors need to match traffic by hostname, so Gateway uses a two-step process:

1. When Gateway receives a DNS query for a hostname that matches one of these selectors, it initially resolves the query to a temporary <GlossaryTooltip term="initial resolved IP">initial resolved IP</GlossaryTooltip>. By default, this IP is drawn from a Cloudflare-owned public range (`172.64.128.0/20` for IPv4, or `2606:4700:0cf1:4000::/64` for IPv6). You can [configure a custom IPv4 range](/cloudflare-one/networks/routes/configure-initial-resolved-ips/) if it conflicts with your existing network.
2. When traffic arrives with this temporary destination IP, Gateway can identify which hostname the connection belongs to, apply the correct egress policy, then replace the temporary IP with the real destination IP before forwarding the traffic.

<HostSelectorEgressDiagram />

These selectors require additional configuration before they work.

## Turn on Host selectors

To turn on the selectors for your account:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Policy settings**, turn on **Allow egress policy host selectors**.

</TabItem> <TabItem label="API">

Use the [Patch Zero Trust account configuration](/api/resources/zero_trust/subresources/gateway/subresources/configurations/methods/edit/) endpoint to update your Zero Trust configuration. For example:

<APIRequest
	path="/accounts/{account_id}/gateway/configuration"
	method="PATCH"
	json={{
		settings: {
			host_selector: {
				enabled: true,
			},
		},
	}}
/>

</TabItem> </Tabs>

## Prerequisites

Traffic must be on-ramped to Gateway with the following methods:

| On-ramp method                                                                                      | Compatibility |
| --------------------------------------------------------------------------------------------------- | ------------- |
| [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/)          | ✅            |
| [PAC files](/cloudflare-one/traffic-controls/proxy-endpoints/)                        | ✅            |
| [Browser Isolation](/cloudflare-one/remote-browser-isolation/)                                      | ✅            |
| [Cloudflare Mesh](/mesh/) | ❌            |
| [Cloudflare WAN](/cloudflare-wan/zero-trust/cloudflare-gateway/)                                    | ✅            |

Traffic from unsupported on-ramp methods resolves using your default Gateway settings. If you use DNS locations to send DNS queries to Gateway (over IPv4, IPv6, DNS over TLS, or DNS over HTTPS), Gateway does not return the initial resolved IP and the host selectors do not apply.

### Configuration changes

To configure your Zero Trust organization to use Host selectors with Egress policies:

1.  Make sure you deploy the following version of the Cloudflare One Client on your users' devices:
    - **Desktop**: [Cloudflare One Client version 2025.4.929.0](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) or later
    - **iOS**: [Cloudflare One Client version 1.11](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#ios) or later
    - **Android and Chrome OS**: [Cloudflare One Client version 2.4.2](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/#android) or later.

    If you need to support devices running prior versions of WARP, add and deploy the following key-value pair to your devices' [WARP configuration file](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/) (`mdm.xml` on Windows and Linux or `com.cloudflare.warp.plist` on macOS):

    ```diff lang="xml"
    <array>
    	<dict>
    +		<key>doh_in_tunnel</key>
    +		<true/>
    	</dict>
    </array>
    ```

2.  <Render
    	file="gateway/egress-selector-split-tunnels"
    	product="cloudflare-one"
    />

The Cloudflare One Client must be set to _Traffic and DNS mode_ for traffic affected by these selectors to route correctly.

## Known issues

### DNS resolution location

For the [Application](/cloudflare-one/traffic-controls/egress-policies/#application), [Content Categories](/cloudflare-one/traffic-controls/egress-policies/#content-categories), [Domain](/cloudflare-one/traffic-controls/egress-policies/#domain), and [Host](/cloudflare-one/traffic-controls/egress-policies/#host) selectors, Gateway captures the destination IP address during the initial DNS resolution step described above. The egress policy does not change this IP address, so the destination Gateway connects to is independent of the location of the egress data center or dedicated egress IP you select.

If the resolved destination IP and the egress IP are located in different regions, connections to destinations that enforce geo-restriction or IP-allowlisting based on the connection source may be rejected.

This can affect you if you use Domain or Host egress selectors, your users are located outside the region associated with your egress IP, and the destination applies geo-restriction or IP-based access controls.

### Google Chrome restricts local network access

<Render file="gateway/egress-selector-chrome-issue" product="cloudflare-one" />

### DNS Override policies bypass host selectors

If a domain matches a [DNS Override policy](/cloudflare-one/traffic-controls/dns-policies/#override), Gateway will not apply the initial resolved IP mapping for that domain. This means host-based egress selectors (Application, Content Categories, Domain, and Host) will not evaluate against traffic to the overridden domain. Traffic to these domains will use the default Cloudflare egress method.

### HTTPS DNS records not supported

Host selectors do not support HTTPS DNS record types. When a domain uses HTTPS records for connection establishment, Gateway cannot map the DNS query to a hostname for egress policy evaluation. Traffic to these domains will use the default Cloudflare egress method instead of matching a host-based egress policy.

If you need to apply egress policies to a domain that uses HTTPS records, use an IP-based selector (such as [Destination IP](/cloudflare-one/traffic-controls/egress-policies/#destination-ip)) instead.

# Egress policies

import {
	Render,
	Badge,
	Tabs,
	TabItem,
	Details,
	GlossaryTooltip,
	APIRequest,
} from "~/components";

:::note
Only available on Enterprise plans.
:::

Many third-party services (for example, a bank or partner API) only allow connections from a known list of IP addresses. By default, traffic that exits through Cloudflare Gateway shares a source IP address with all other Cloudflare One Client users, so upstream services cannot identify your organization by IP alone.

[Dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/) solve this problem. They are static IP addresses assigned only to your account, which you can add to upstream allowlists.

Egress policies control which dedicated egress IP is used for a given connection. You can match traffic on attributes such as user identity, source or destination IP address, and geolocation. Traffic that does not match an egress policy defaults to the most performant dedicated egress IP.

Cloudflare does not publish Cloudflare One Client egress IP ranges. Cloudflare One Client egress IPs are not listed at [Cloudflare's IP Ranges](https://cloudflare.com/ips). To obtain a dedicated Cloudflare One Client egress IP, contact your account team.

<Render file="gateway/terraform-precedence-warning" product="cloudflare-one" />

## Load balancing

Traffic that does not match any egress policy attempts to use the closest dedicated egress IP location.

If two data centers are equally close to the user, Gateway splits traffic between them. The load balancer keeps each user on the same egress IP regardless of which data center handles the request.

## Force IP version

Some upstream services only accept connections over a specific IP version. To force all egress traffic to use IPv4 or IPv6 only, first verify you are [filtering DNS traffic](/cloudflare-one/traffic-controls/get-started/dns/), then create a DNS policy to [block AAAA or A records](/cloudflare-one/traffic-controls/dns-policies/common-policies/#control-ip-version).

## Example policies

The following egress policy configures all traffic destined for a third-party network to use a static source IP:

<Render
	file="gateway/policies/egress-destination-ip"
	product="cloudflare-one"
/>

### Secure access to SaaS applications

Many SaaS providers (for example, Microsoft 365, Salesforce, or Workday) allow you to restrict access to connections from specific IP addresses. You can use dedicated egress IPs with Gateway to enforce this restriction:

1. **Obtain dedicated egress IPs** from your account team and note the assigned IPv4 and IPv6 addresses.
2. **Create an egress policy** that routes traffic destined for the SaaS provider through your dedicated egress IP. Use the Destination IP selector with the published IP ranges of the provider. Alternatively, use the Application selector (Beta) to match the provider by name.
3. **Add the egress IPs to the SaaS provider's allowlist** so the provider only accepts connections from your organization's IPs.
4. **Pair with HTTP policies** to add deeper controls. For example, block file uploads to personal accounts, enforce DLP profiles to prevent sensitive data from leaving the organization, or require [device posture checks](/cloudflare-one/reusable-components/posture-checks/) before allowing access.

This pattern ensures that access to the SaaS application is limited to traffic that passes through Gateway, where your security policies are enforced, and that the SaaS provider can verify traffic originates from your organization.

### Catch-all policy

Without a catch-all policy, any traffic that does not match an explicit egress policy will attempt to use the closest dedicated egress IP location. To avoid unexpected IP assignments and maintain the best performance, create a catch-all policy that routes remaining traffic through the default Zero Trust IP range:

| Policy name           | Selector | Operator | Value                    | Egress method                    |
| --------------------- | -------- | -------- | ------------------------ | -------------------------------- |
| Default egress policy | Protocol | in       | `All options (Protocol)` | Cloudflare default egress method |

Gateway policies evaluate from [top to bottom](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence) in the UI. Place the catch-all policy at the bottom of the list so that more specific policies are evaluated first.

## Egress methods

When you configure your egress policy, you can choose whether to egress traffic using the default Cloudflare egress method or dedicated egress IPs.

### Use default Cloudflare egress method

**Use default Cloudflare egress method** routes traffic through the default source IP range shared across all Zero Trust accounts. Traffic exits from the nearest Cloudflare data center, which provides the best performance.

### Use dedicated egress IPs

**Use dedicated egress IPs (Cloudflare or BYOIP)** routes traffic through the primary IPv4 address and IPv6 range you select in the dropdown menus. <Render file="gateway/secondary-ipv4-requirement" product="cloudflare-one" />

If the data center associated with your primary IPv4 address goes down, Gateway fails over to the secondary data center to prevent traffic drops. A secondary IPv6 address is not required because IPv6 traffic can exit from any Cloudflare data center. You can use IPs provided by Cloudflare or [bring your own IP addresses (BYOIP)](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/#bring-your-own-ip-address-byoip).

To learn more about IPv4 and IPv6 egress behavior, refer to [Egress locations](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/#egress-location).

## Selectors

Selectors are the criteria that Gateway uses to match egress traffic against a policy. Gateway evaluates the following selectors:

### Application <Badge text="Beta" variant="caution"/>

<Render
	file="gateway/selectors/application-http"
	product="cloudflare-one"
	params={{ policyType: "egress" }}
/>

<Render
	file="gateway/selectors/egress-selector-limitation"
	product="cloudflare-one"
/>

### Content Categories <Badge text="Beta" variant="caution"/>

<Render
	file="gateway/selectors/net-http-content-categories"
	product="cloudflare-one"
	params={{ APIendpoint: "net.fqdn.content_category" }}
/>

<Render
	file="gateway/selectors/egress-selector-limitation"
	product="cloudflare-one"
/>

### Destination Continent

<Render
	file="gateway/selectors/destination-continent"
	product="cloudflare-one"
	params={{ one: "net.dst" }}
/>

### Destination Country

<Render
	file="gateway/selectors/destination-country"
	product="cloudflare-one"
	params={{ one: "net.dst" }}
/>

### Destination IP

<Render
	file="gateway/selectors/destination-ip"
	product="cloudflare-one"
	params={{ APIendpoint: "net.dst.ip" }}
/>

### Destination Port

<Render file="gateway/selectors/destination-port" product="cloudflare-one" />

### Device Posture

<Render file="gateway/selectors/device-posture" product="cloudflare-one" />

### Domain <Badge text="Beta" variant="caution"/>

<Render
	file="gateway/selectors/domain-http"
	product="cloudflare-one"
	params={{ APIendpoint: "net.fqdn.domains" }}
/>

<Render
	file="gateway/selectors/egress-selector-limitation"
	product="cloudflare-one"
/>

### Host

<Render
	file="gateway/selectors/host-http"
	product="cloudflare-one"
	params={{ APIendpoint: "net.fqdn.host" }}
/>

<Render
	file="gateway/selectors/egress-selector-limitation"
	product="cloudflare-one"
/>

### Protocol

<Render file="gateway/selectors/protocol" product="cloudflare-one" />

### Proxy Endpoint

<Render file="gateway/selectors/proxy-endpoint" product="cloudflare-one" />

### Source Continent

The continent of the user making the request. <Render file="gateway/selectors/source-continent-http" product="cloudflare-one" params={{ one: "net.src" }} />

### Source Country

The country of the user making the request. <Render file="gateway/selectors/source-country-http" product="cloudflare-one" params={{ one: "net.src" }} />

### Source Internal IP

<Render
	file="gateway/selectors/source-internal-ip"
	product="cloudflare-one"
	params={{ one: "egress", two: "net.src.internal_src_ip" }}
/>

### Source IP

<Render
	file="gateway/selectors/source-ip"
	product="cloudflare-one"
	params={{ APIendpoint: "net.src.ip" }}
/>

### Source Port

<Render file="gateway/selectors/source-port" product="cloudflare-one" />

### Users

<Render file="gateway/selectors/users-http" product="cloudflare-one" />

### Virtual Network

<Render
	file="gateway/selectors/virtual-network"
	product="cloudflare-one"
	params={{ one: "net.vnet_id" }}
/>

## Comparison operators

<Render file="gateway/comparison-operators" product="cloudflare-one" />

## Value

You can input a single value or use regular expressions to specify a range of values.

Gateway uses Rust to evaluate regular expressions. The Rust implementation is slightly different than regex libraries used elsewhere. To evaluate if your regex matches, you can use [Rustexp](https://rustexp.lpil.uk/).

## Logical operators

<Render
	file="gateway/logical-operators"
	product="cloudflare-one"
	params={{ one: "**Identity** or **Device Posture**" }}
/>

## Limitations

### Selector prerequisites

The [Application](#application), [Content Categories](#content-categories), [Domain](#domain), and [Host](#host) selectors require additional setup before they work in egress policies. Before deploying policies with these selectors, refer to [Host selectors](/cloudflare-one/traffic-controls/egress-policies/host-selectors/).

These selectors also resolve the destination IP address when Gateway processes the DNS query, not when the egress policy is applied. If the resolved destination IP and your egress IP are in different regions, connections to destinations that enforce geo-restriction or IP-allowlisting may fail. Refer to [DNS resolution location](/cloudflare-one/traffic-controls/egress-policies/host-selectors/#dns-resolution-location) for details.

# DNS filtering

import {
	GlossaryTooltip,
	Render,
	Tabs,
	TabItem,
	APIRequest,
} from "~/components";

Secure Web Gateway allows you to inspect DNS traffic — the queries your devices make to translate domain names like `example.com` into IP addresses — and control which websites users can visit. Because every connection starts with a DNS lookup, DNS filtering blocks threats at the earliest stage of a connection, before the device ever reaches the destination. Use DNS policies to block malware domains, phishing sites, or entire content categories across your organization.

:::note
For a more detailed guide to filtering DNS queries and other traffic for your organization, refer to the [Secure your Internet traffic and SaaS apps](/learning-paths/secure-internet-traffic/concepts/) implementation guide.
:::

## 1. Connect to Gateway

You can filter DNS queries from individual devices (for example, employee laptops) or from entire network locations (for example, an office router). Choose the option that matches your deployment.

### Connect devices

To filter DNS requests from an individual device such as a laptop or phone:

1. [Install the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/download/) on your device. The Cloudflare One Client is a lightweight agent that routes the device's DNS queries through Cloudflare so Gateway can inspect and filter them.
2. [Enroll the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) in your organization's <GlossaryTooltip term="team name">Zero Trust instance</GlossaryTooltip> [^1]. This tells WARP which Gateway policies to enforce.
3. (Optional) If you want to display a [custom block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) instead of a generic browser error when a request is blocked, [install a Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on your device.

### Connect DNS locations

To filter DNS requests from a network location such as an office or data center without installing software on each device:

1. [Add the location](/cloudflare-one/traffic-controls/resolver-endpoints/) to your Cloudflare One settings. A DNS location represents a network (such as an office) whose DNS queries you want to filter.
2. On your router, browser, or OS, change the DNS server setting to point to the Cloudflare address shown in the location setup UI. This forwards all DNS queries from that network through Gateway.

:::note

Gateway uses different methods to identify which location a query comes from, depending on the protocol:

- **IPv4 queries** — Gateway matches the query to a location based on the source IP address of your network. Under **Traffic controls** > **Resolver endpoints**, verify that the **Source IPv4 Address** matches the public IP of the network you want to protect.
- **IPv6, DNS over TLS (DOT), or DNS over HTTPS (DOH) queries** — Because these protocols may obscure the source IP, Gateway instead matches queries using the unique DNS forwarding address assigned to each location. Make sure your resolver is configured with the correct forwarding address for the location you want policies to apply to.
  :::

## 2. Verify device connectivity

To confirm that your device's DNS queries are flowing through Gateway:

<Render
	file="gateway/verify-connectivity"
	product="cloudflare-one"
	params={{ one: "DNS", two: "queries" }}
/>

## 3. Create your first DNS policy

A DNS policy has two parts: a **traffic condition** that defines which queries to match (for example, all queries to gambling sites) and an **action** that defines what to do with matching queries (for example, block them). To create a new DNS policy:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**.
2. In the **DNS** tab, select **Add a policy**.
3. Name the policy.
4. Under **Traffic**, use the condition builder to define which DNS queries this policy applies to. Select a selector (such as **Security Categories**), an operator (such as **in**), and one or more values.
5. Choose an **Action** to take when traffic matches the condition. For example, we recommend adding a policy to block all [security categories](/cloudflare-one/traffic-controls/domain-categories/#security-categories):
   <Render
   	file="gateway/policies/block-security-categories"
   	product="cloudflare-one"
   />
6. Select **Create policy**.

</TabItem>

<TabItem label="API">

1. [Create an API token](/fundamentals/api/get-started/create-token/) with the following permissions:

   | Type    | Item       | Permission |
   | ------- | ---------- | ---------- |
   | Account | Zero Trust | Edit       |

2. (Optional) Configure your API environment variables to include your [account ID](/fundamentals/account/find-account-and-zone-ids/) and API token.
3. Send a `POST` request to the [Create a Zero Trust Gateway rule](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/create/) endpoint. For example, the following request creates a policy that blocks all default [security categories](/cloudflare-one/traffic-controls/domain-categories/#security-categories). The numeric IDs in the `traffic` field (such as `68`, `178`, `80`) correspond to Cloudflare's predefined security threat categories — refer to [domain categories](/cloudflare-one/traffic-controls/domain-categories/#security-categories) for the full mapping. The `precedence` field controls evaluation order when multiple policies match (`0` means this policy is evaluated first).

   <APIRequest
   	path="/accounts/{account_id}/gateway/rules"
   	method="POST"
   	json={{
   		name: "Block security threats",
   		description: "Block all default Cloudflare DNS security categories",
   		precedence: 0,
   		enabled: true,
   		action: "block",
   		filters: ["dns"],
   		traffic:
   			"any(dns.security_category[*] in {68 178 80 83 176 175 117 131 134 151 153})",
   		identity: "",
   	}}
   />

   ```sh output
   {
   	 "success": true,
   	 "errors": [],
   	 "messages": []
   }
   ```

   The API will respond with a summary of the policy and the result of your request.

</TabItem> </Tabs>

For more information, refer to [DNS policies](/cloudflare-one/traffic-controls/dns-policies/).

## 4. Add optional policies

Once your first policy is active, refer to [common DNS policies](/cloudflare-one/traffic-controls/dns-policies/common-policies) for other policies you may want to add. Common additions include blocking specific content categories (such as social media or streaming), enabling SafeSearch on search engines, and restricting DNS queries so devices can only use resolvers that you have approved.

# HTTP filtering

import { GlossaryTooltip, Render } from "~/components";

Secure Web Gateway allows you to inspect HTTP traffic and control which websites users can visit. DNS filtering can only block or allow entire domains (for example, all of `dropbox.com`). HTTP filtering goes deeper — it inspects full URLs and request content, so you can block a specific page like `dropbox.com/shared-folder`, scan file uploads for sensitive data, or enforce acceptable use policies based on what users are actually doing on a site.

:::note
For a more detailed guide to filtering HTTP requests and other traffic for your organization, refer to the [Secure your Internet traffic and SaaS apps](/learning-paths/secure-internet-traffic/concepts/) implementation guide.
:::

## 1. Connect to Gateway

HTTP filtering requires three components working together: the Cloudflare One Client routes device traffic through Cloudflare, a root certificate lets Gateway decrypt HTTPS traffic so it can inspect URLs and content, and the Gateway proxy enables Gateway to intercept and evaluate HTTP requests. Without the certificate, Gateway can only see the domain name — not the full URL or request body.

To filter HTTP requests from a device:

1. [Install the Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on your device.
2. [Install the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on your device.
3. In the Cloudflare One Client Settings, log in to your organization's <GlossaryTooltip term="team name">Cloudflare One instance</GlossaryTooltip>.
4. [Enable the Gateway proxy](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy) for TCP. Optionally, enable the UDP proxy to also inspect QUIC traffic on port 443 — this covers HTTP/3, a newer protocol some browsers use by default.
5. To inspect HTTPS traffic, [enable TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/#turn-on-tls-decryption). TLS decryption allows Gateway to read encrypted requests. Without it, Gateway can see that a user visited `example.com` but not which specific page or what they uploaded.
6. (Optional) To scan file uploads and downloads for malware, [enable anti-virus scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/).

## 2. Verify device connectivity

To verify your device is connected to Cloudflare One and traffic is flowing through Gateway:

<Render
	file="gateway/verify-connectivity"
	product="cloudflare-one"
	params={{ one: "HTTP", two: "requests" }}
/>

After creating your first HTTP policy in the next step, you can test it by visiting a URL that your policy should block and confirming the request is denied.

## 3. Create your first HTTP policy

An HTTP policy defines which requests to match (for example, uploads to file-sharing sites) and the action to take (for example, block).

<Render
	file="gateway/get-started/create-http-policy"
	product="cloudflare-one"
/>

## 4. Add optional policies

Refer to our list of [common HTTP policies](/cloudflare-one/traffic-controls/http-policies/common-policies/) for other policies you may want to create. Common additions include blocking file downloads by type, isolating risky websites in a [remote browser](/cloudflare-one/remote-browser-isolation/), and adding Do Not Inspect rules for applications that break under TLS decryption (for example, apps that use certificate pinning to enforce their own certificates). Do Not Inspect rules tell Gateway to skip decryption for specific destinations so those applications continue to work.

# Get started

import { DirectoryListing } from "~/components";

This section covers best practices for setting up the following Gateway policy types:

<DirectoryListing />

For each type of policy, we recommend the following workflow:

1. Connect the devices and/or networks that you want to apply policies to.
2. Verify that Gateway is successfully proxying traffic from your devices.
3. Set up basic security and compatibility policies (recommended for most use cases).
4. Customize your configuration to the unique needs of your organization.

## Recommended deployment phases

Most organizations roll out Gateway in phases, starting with the lowest-effort, highest-impact policy type and adding deeper inspection over time.

### Phase 1: DNS filtering

DNS filtering requires the least deployment effort and provides immediate protection.

- Point your network DNS to Gateway's resolver addresses, or deploy the Cloudflare One Client in DNS-only mode.
- Block all [security threat categories](/cloudflare-one/traffic-controls/domain-categories/#security-categories) (malware, phishing, command and control).
- Block [content categories](/cloudflare-one/traffic-controls/domain-categories/#content-categories) that violate your acceptable use policy.
- Review [DNS logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) to gain visibility into Internet usage across your organization.

For setup instructions, refer to [Set up DNS filtering](/cloudflare-one/traffic-controls/get-started/dns/).

### Phase 2: Network policies

After DNS filtering is in place, add network-level controls for non-HTTP traffic.

- Deploy the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) and enable the [Gateway proxy](/cloudflare-one/traffic-controls/proxy/) for TCP.
- Block traffic to high-risk IP ranges or restrict which ports and protocols users can access.
- Use [protocol detection](/cloudflare-one/traffic-controls/network-policies/protocol-detection/) to identify applications by traffic pattern rather than port number.
- Enable network session logging for audit trails.

For setup instructions, refer to [Set up network filtering](/cloudflare-one/traffic-controls/get-started/network/).

### Phase 3: HTTP inspection

HTTP inspection provides the deepest visibility and the most granular controls, but it requires additional setup.

- Install the [Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on user devices.
- Enable [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) to inspect HTTPS traffic.
- Create [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policies for applications that use certificate pinning.
- Block risky file types, enable [anti-virus scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/), and configure [DLP profiles](/cloudflare-one/data-loss-prevention/) to detect sensitive data.
- Use [Browser Isolation](/cloudflare-one/remote-browser-isolation/) to render high-risk sites in a remote browser.

For setup instructions, refer to [Set up HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/).

### Phase 4: Egress control and full integration

With all policy layers active, extend Gateway to cover your full network and integrate with other Cloudflare One services.

- Connect branch offices and data centers with [network tunnels](/cloudflare-one/networks/) (IPsec/GRE via Magic WAN).
- Configure [dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/) so third-party services can identify your organization's traffic.
- Set up [resolver policies](/cloudflare-one/traffic-controls/resolver-policies/) to route internal DNS queries to your private DNS servers.
- Monitor SaaS application usage with [CASB](/cloudflare-one/cloud-and-saas-findings/).

:::note
You do not need to complete every phase. Choose the phases that match your organization's security requirements and deployment timeline.
:::

# Network filtering

import { GlossaryTooltip, Render, Tabs, TabItem } from "~/components";

Secure Web Gateway allows you to apply policies at the network level to control which websites and non-HTTP applications users can access. This is useful when you need to control traffic that is not web browsing — for example, blocking remote desktop connections or restricting file-transfer tools across your organization.

Network policies inspect individual TCP and UDP packets (the low-level data units that carry all Internet traffic), which means you can filter traffic that [DNS](/cloudflare-one/traffic-controls/get-started/dns/) and [HTTP](/cloudflare-one/traffic-controls/get-started/http/) policies cannot reach. DNS policies only see domain lookups, and HTTP policies only see web requests — network policies go deeper and can catch protocols like SSH (remote terminal access), RDP (remote desktop), and custom applications running on non-standard ports.

:::note
For a more detailed guide to filtering network traffic and more for your organization, refer to the [Secure your Internet traffic and SaaS apps](/learning-paths/secure-internet-traffic/concepts/) implementation guide.
:::

## 1. Connect to Gateway

### Connect devices

To filter network traffic from a device such as a laptop or phone:

1. [Install the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on your device.
2. In the Cloudflare One Client Settings, log in to your organization's <GlossaryTooltip term="team name">Cloudflare One instance</GlossaryTooltip>.
3. (Optional) If you want to display a [custom block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) when users are blocked, [install the Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on your device. Without the certificate, blocked users will see a generic browser connection error instead of an informative page.
4. [Enable the Gateway proxy](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy) for TCP. The Gateway proxy is what routes your device's traffic through Cloudflare so network policies can inspect it — without it enabled, your policies will have no effect. Optionally, enable the UDP proxy to also inspect QUIC traffic (a newer protocol used by HTTP/3 connections) on port 443.

### Connect private networks

To filter traffic from private networks (internal corporate networks not exposed to the public Internet), refer to the [Cloudflare Tunnel guide](/cloudflare-one/networks/connectors/cloudflare-tunnel/).

## 2. Verify device connectivity

Verifying connectivity ensures that traffic from your device is actually flowing through Cloudflare before you build policies against it.

To verify your device is connected to Cloudflare One:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. Under **Log traffic activity**, enable activity logging for all Network logs. This tells Cloudflare to record network-level traffic so you can confirm your device appears in the logs.
3. On your Cloudflare One Client device, open a browser and visit any website. This generates traffic that should appear in the logs.
4. Determine the **Source IP** for your device (the public-facing address Cloudflare sees for your connection):

  <Tabs>
	<TabItem label="Version 2026.2+">

   1. Open the Cloudflare One Client.
   2. Go to **Profile**.
   3. Note the **Client Interface IP**. This is the same address that will appear as the Source IP in your network logs.
  </TabItem>
	<TabItem label="Version 2026.1 and earlier">
   1. Open the Cloudflare One Client.
   2. Go to **Settings** (gear icon) **Preferences** > **General**.
   3. Note the **Public IP**. This is the same address that will appear as the Source IP in your network logs.

  </TabItem>
  </Tabs>

5. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Insights** > **Logs** > **Network logs**. Before building network policies, make sure you see network logs from the Source IP assigned to your device.

If no logs appear after a few minutes, check two things: first, verify that the [Gateway proxy is turned on](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy). Second, confirm that the device is enrolled in your Zero Trust organization by checking the Cloudflare One Client connection status.

## 3. Create your first network policy

A network policy has two parts: a matcher that selects which traffic to act on (for example, all packets destined for port 22, the default port for SSH) and an action that decides what to do with it (for example, block the connection).

<Render
	file="gateway/get-started/create-network-policy"
	product="cloudflare-one"
/>

## 4. Add optional policies

Refer to our list of [common network policies](/cloudflare-one/traffic-controls/network-policies/common-policies) for policies you may want to create. Common additions include blocking traffic to specific IP ranges, restricting access to non-standard ports (ports other than well-known ones like 80 for HTTP and 443 for HTTPS), and using protocol detection to identify applications like BitTorrent based on their traffic patterns rather than port numbers alone.

# AV scanning

import { Render, Details } from "~/components";

Cloudflare Gateway can scan files for malware as users upload or download them. Anti-virus (AV) scanning runs inline — Gateway inspects files as they pass through the proxy and blocks any file that contains a known malicious payload.

In addition to AV scanning, Gateway can quarantine previously unseen files into a sandbox to detect zero-day threats not yet in anti-virus databases. For more information, refer to [File sandboxing](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/).

## Get started

To turn on AV scanning:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Policy settings**, turn on **Scan files for malware**.
3. Choose whether to scan files for malicious payloads during uploads, downloads, or both. You can also block requests containing [non-scannable files](#non-scannable-files).
4. (Optional) Turn on **Display AV block notification for Cloudflare One Client** to send [block notifications](#cloudflare-one-client-block-notifications) to users connected to Gateway with the Cloudflare One Client when AV inspection blocks a file.

When a request is blocked due to the presence of malware, Gateway will log the match as a Block decision in your [HTTP logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/#http-logs).

### Cloudflare One Client block notifications

<Render
	file="gateway/client-notifications"
	product="cloudflare-one"
	params={{ toggleName: "**Display AV block notification for Cloudflare One Client**" }}
/>

## File scan criteria

If AV scanning is turned on, Gateway uses the following criteria (in order) to detect and scan files. The first match triggers a scan:

1. The `Content-Disposition` HTTP header is set to `Attachment`.
2. The byte signature of the request or response body matches a known file type:
   - **Executable** (for example, `.exe`, `.bat`, `.dll`, and `.wasm`)
   - **Documents** (for example, `.doc`, `.docx`, `.pdf`, `.ppt`, and `.xls`)
   - **Compressed** (for example, `.7z`, `.gz`, `.zip`, and `.rar`)
3. The file name in the `Content-Disposition` header contains a file extension matching one of the above categories.

If none of these conditions match, Gateway falls back to the origin's `Content-Type` header. Gateway will not scan files it determines to be image, video, or audio files. All other files default to being scanned.

## Opt content out from scanning

When an admin turns on AV scanning for uploads and/or downloads, Gateway will scan every supported file. Admins can selectively choose to disable scanning using HTTP policies. All [HTTP selectors](/cloudflare-one/traffic-controls/http-policies/#selectors) can opt HTTP traffic out from AV scanning using the **Do Not Scan** action. When traffic matches a Do Not Scan policy, nothing is scanned, regardless of file size or whether the file type is supported or not. For example, to prevent AV scanning of files uploaded to or downloaded from `example.com`, you can create the following policy:

| Selector | Operator      | Value         | Action      |
| -------- | ------------- | ------------- | ----------- |
| Hostname | matches regex | `example.com` | Do Not Scan |

Opting out of AV scanning applies to uploads and/or downloads of files, matching your account's global AV scanning setting. For example, if you have configured Gateway to globally scan uploads only, then opting out of AV scanning will only apply to uploads.

## Compatibility

### Supported compressed file types

In addition to standard object files like PDFs, Zero Trust supports AV scanning for the following archive types:

<Details header="Supported compressed file types">

- 7-Zip
- 7-Zip SFX
- ACE
- ACE SFX
- AutoHotkey
- AutoIt
- BASE64
- BZ2
- CHM Help Files
- CPIO SVR4
- Chrome Extension (CRX) Package Format
- eXtensible ARchive format (XAR)
- GZIP compressed files
- ISO 9660
- Inno Setup
- Indigo Rose Setup Factory
- Java ARchive
- LZH/LHA
- MacBinary
- MIME base64
- MSCOMPRESS
- Microsoft CAB
- Microsoft TNEF
- NSIS Nullsoft Installer
- Office Legacy XML
- PGP signed message, document, etc.
- RPM
- RAR
- SAPCar
- Self-extracting ARJ
- Self-extracting CA
- Self-extracting LZH/LHA
- Self-extracting RAR
- Self-extracting ZIP
- Smart Install Maker
- TAR
- UUE and XXE compressed files
- Windows Imaging File (WIM)
- XE compressed files (UUE and XXE)
- XZ file format
- ZIP
- ZOO

</Details>

Gateway cannot scan [certain archive files](#non-scannable-files) regardless of file type, such as large or encrypted files.

### Non-scannable files

Gateway cannot scan all files for malware. When Gateway encounters a non-scannable file, you can configure AV scanning to either fail open (allow the file to pass through unscanned) or fail closed (deny the file transfer).

Gateway cannot scan requests containing the following files:

- Files larger than:
  - 15 MB on Free plans
  - 25 MB on Pay-as-you-go plans
  - 100 MB on Enterprise plans
- PGP encrypted files
- Password protected archives
- Archives with more than three recursion levels
- Archives with more than 300 files

# Common policies

import { Render, Tabs, TabItem, APIRequest } from "~/components";

The following policies are commonly used to secure HTTP traffic. HTTP policies are evaluated in order from top to bottom, and the first matching policy applies — except for [Do Not Inspect](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) policies, which are always evaluated first.

For a baseline set of recommended policies, refer to [Secure your Internet traffic and SaaS apps](/learning-paths/secure-internet-traffic/build-http-policies/recommended-http-policies/).

For custom transformations, notifications, and capture, refer to [Worker templates](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/) using the [Forward action](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) (beta).

Refer to the [HTTP policies page](/cloudflare-one/traffic-controls/http-policies/) for a comprehensive list of other selectors, operators, and actions.

## Block sites

Block attempts to reach sites by hostname or URL paths. Different approaches may be required based on how a site is organized.

### Block sites by hostname

Block all subdomains that use a host.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator      | Value            | Action |
| -------- | ------------- | ---------------- | ------ |
| Host     | matches regex | `.*example\.com` | Block  |

</TabItem>

<TabItem label="API">

In the following API examples, `filters: ["http"]` indicates that this is an HTTP (Layer 7) policy.

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block sites by hostname",
		description: "Block all subdomains that use a specific hostname",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic: 'http.request.host matches ".*example\.com"',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Block sites by URL

Block a section of a site without blocking the entire site. For example, you can block a specific subreddit, such as `reddit.com/r/gaming`, without blocking `reddit.com`.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator      | Value       | Action |
| -------- | ------------- | ----------- | ------ |
| URL      | matches regex | `/r/gaming` | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block sites by URL",
		description: "Block specific parts of a site without blocking the hostname",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic: 'http.request.uri matches "/r/gaming"',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Block content categories

Block content categories which go against your organization's acceptable use policy.

<Render
	file="gateway/policies/dash-plus-api/http/block-content-categories"
	product="cloudflare-one"
/>

## Block unauthorized applications

<Render file="gateway/policies/block-applications" product="cloudflare-one" />

<Render
	file="gateway/policies/dash-plus-api/http/block-applications"
	product="cloudflare-one"
/>

## Check user identity

<Render file="gateway/policies/check-user-identity" product="cloudflare-one" />

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator | Value         | Logic | Action |
| ---------------- | -------- | ------------- | ----- | ------ |
| Application      | in       | _Salesforce_  | And   | Block  |
| User Group Names | in       | _Contractors_ |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Check user identity",
		description:
			"Block access to Salesforce by temporary employees and contractors",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic: "any(app.ids[] in {606})",
		identity: 'any(identity.groups.name[] in {"Contractors"})',
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Skip inspection for groups of applications

Certain client applications, such as Zoom or Apple services, rely on certificate pinning. These applications verify they are connecting directly to their own servers and will reject Gateway's TLS inspection certificate. To avoid connection errors, you must add a Do Not Inspect HTTP policy for these applications.

Gateway [evaluates Do Not Inspect policies first](/cloudflare-one/traffic-controls/order-of-enforcement/#http-policies), regardless of their position in the policy list. Cloudflare recommends moving your Do Not Inspect policies to the top of the list to reduce confusion.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector    | Operator | Value            | Action         |
| ----------- | -------- | ---------------- | -------------- |
| Application | in       | _Do Not Inspect_ | Do Not Inspect |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Bypass incompatible applications",
		description:
			"Skip TLS decryption for applications that are incompatible with Gateway",
		enabled: true,
		action: "off",
		filters: ["http"],
		traffic: "any(app.type.ids[*] in {16})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

:::note
You can select either individual applications or the entire Do Not Inspect set, which will update as new applications are added.
:::

## Check device posture

Require devices to have certain software installed or other configuration attributes. For instructions on setting up a device posture check, refer to [Enforce device posture](/cloudflare-one/reusable-components/posture-checks/).

### Enforce a minimum OS version

Perform an [OS version check](/cloudflare-one/reusable-components/posture-checks/client-checks/os-version/) to ensure users are running at least a minimum version.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector                     | Operator | Value                | Action |
| ---------------------------- | -------- | -------------------- | ------ |
| Passed Device Posture Checks | in       | _Minimum OS version_ | Allow  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Require OS version",
		description: "Perform an OS version check for minimum version",
		enabled: true,
		action: "allow",
		filters: ["http"],
		traffic: "",
		identity: "",
		device_posture:
			'any(device_posture.checks.passed[*] in {"<POSTURE_CHECK_UUID>"})',
	}}
/>

To get the UUIDs of your device posture checks, use the [List device posture rules](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/list/) endpoint.

</TabItem> </Tabs>

### Check for a specific file

Perform a [file check](/cloudflare-one/reusable-components/posture-checks/client-checks/file-check/) to ensure users have a certain file on their device.

Since the file path will be different for each operating system, you can configure a file check for each system and use the **Or** logical operator to only require one of the checks to pass.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector                     | Operator | Value              | Logic | Action |
| ---------------------------- | -------- | ------------------ | ----- | ------ |
| Passed Device Posture Checks | in       | _macOS File Check_ | Or    | Allow  |
| Passed Device Posture Checks | in       | _Linux File Check_ |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Check for specific file",
		description:
			"Ensure users have a specific file on their device regardless of operating system",
		enabled: true,
		action: "allow",
		filters: ["http"],
		traffic: "",
		identity: "",
		device_posture:
			'any(device_posture.checks.passed[] in {"<POSTURE_CHECK_1_UUID>"}) or any(device_posture.checks.passed[] in {"<POSTURE_CHECK_2_UUID>"})',
	}}
/>

To get the UUIDs of your device posture checks, use the [List device posture rules](/api/resources/zero_trust/subresources/devices/subresources/posture/methods/list/) endpoint.

</TabItem> </Tabs>

## Enforce session duration

[Require users to re-authenticate](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/) after a certain amount of time has elapsed.

## Isolate high risk sites in remote browser

If you are using the [Browser Isolation add-on](/cloudflare-one/remote-browser-isolation/), refer to our list of [common Isolate policies](/cloudflare-one/remote-browser-isolation/isolation-policies/#common-policies).

## Bypass inspection for self-signed certificates

When accessing origin servers with certificates not signed by a public certificate authority, you must bypass TLS decryption.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator | Value                  | Action         |
| -------- | -------- | ---------------------- | -------------- |
| Domain   | in       | `internal.example.com` | Do Not Inspect |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Bypass internal site inspection",
		description:
			"Bypass TLS decryption for internal sites with self-signed certificates",
		enabled: true,
		action: "off",
		filters: ["http"],
		traffic: 'any(http.request.domains[*] in {"internal.example.com"})',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Block file types

<Render file="gateway/policies/block-file-types" product="cloudflare-one" />

For more information on supported file types, refer to [Download and Upload File Types](/cloudflare-one/traffic-controls/http-policies/#download-and-upload-file-types).

## Isolate or block shadow IT applications

Isolate shadow IT applications discovered by the [Application Library](/cloudflare-one/team-and-resources/app-library/) that have not been reviewed yet or are currently under review, and block applications that are not approved by your organization.

For more information on reviewing shadow IT applications, refer to [Review applications](/cloudflare-one/team-and-resources/app-library/#review-applications).

### 1. Isolate unreviewed or in review applications

Isolate applications if their approval status is _Unreviewed_ or _In review_.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value        | Logic | Action  |
| ------------------ | -------- | ------------ | ----- | ------- |
| Application Status | is       | _Unreviewed_ | Or    | Isolate |
| Application Status | is       | _In review_  |       |         |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Isolate unreviewed or in review application status",
		description:
			"Isolate Shadow IT applications that have not been reviewed or are in review in the Application Library",
		enabled: true,
		action: "isolate",
		filters: ["http"],
		traffic:
			'any(app.statuses[*] == "unreviewed") or any(app.statuses[*] == "in review")',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### 2. Block unapproved applications

Block applications if their approval status is _Unapproved_.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector           | Operator | Value        | Action |
| ------------------ | -------- | ------------ | ------ |
| Application Status | is       | _Unapproved_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block unapproved application status",
		description:
			"Block Shadow IT applications that have been marked as unapproved in the Application Library",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic: 'any(app.statuses[*] == "unapproved")',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Block Google services

To enable Gateway inspection for Google Drive traffic, you must [add a Cloudflare certificate to Google Drive](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/#google-drive).

### Block Google Drive downloads

Block file downloads from Google Drive.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator      | Value                      | Logic | Action |
| ---------------- | ------------- | -------------------------- | ----- | ------ |
| Application      | in            | _Google Drive_             | And   | Block  |
| URL Path & Query | matches regex | `.*(e=download\|export).*` |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block Google Drive downloads",
		description: "Block file downloads from Google Drive",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic:
			'any(app.ids[] in {554}) and http.request.uri.path_and_query matches ".(e=download\|export).*"',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Block Google Drive uploads

Block file uploads from Google Drive.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator      | Value                                  | Logic | Action |
| ---------------- | ------------- | -------------------------------------- | ----- | ------ |
| Application      | in            | _Google Drive_                         | And   | Block  |
| Upload Mime Type | matches regex | `.*`                                   | And   |        |
| Host             | is not        | `drivefrontend-pa.clients6.google.com` |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block Google Drive uploads",
		description: "Block file uploads to Google Drive",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic:
			'any(app.ids[] in {554}) and http.upload.mime matches "." and not(http.request.host == "drivefrontend-pa.clients6.google.com")',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Block Gmail downloads

Block file downloads from Gmail.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator | Value                                   | Logic | Action |
| ---------------- | -------- | --------------------------------------- | ----- | ------ |
| Host             | is       | `mail-attachment.googleusercontent.com` | And   | Block  |
| URL Path & Query | is       | `/attachment/u/0`                       |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block Gmail downloads",
		description: "Block file downloads from Gmail",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic:
			'http.request.host == "mail-attachment.googleusercontent.com" and http.request.uri.path_and_query matches "/attachment/u/0"',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### Block Google Translate proxy

Block use of Google Translate to translate entire webpages.

When translating a website, Google Translate proxies webpages with the `translate.goog` domain. Your users may be able to use this service to bypass other Gateway policies. If you block `translate.goog`, users will still be able to access other Google Translate features.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator      | Value                      | Action |
| -------- | ------------- | -------------------------- | ------ |
| Domain   | matches regex | `^(.+\.)?translate\.goog$` | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block Google Translate for websites",
		description: "Block use of Google Translate to translate entire webpages",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic:
			'any(http.request.domains[*] matches "^(.+\\.)?translate\\.goog$")',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Filter WebSocket traffic

Gateway does not inspect or log [WebSocket](https://datatracker.ietf.org/doc/html/rfc6455) traffic. Instead, Gateway will only log the HTTP details used to make the WebSocket connection, as well as [network session information](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/). To filter your WebSocket traffic, create a policy with the `101` HTTP response code.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector      | Operator | Value                     | Action |
| ------------- | -------- | ------------------------- | ------ |
| HTTP Response | is       | _101 SWITCHING_PROTOCOLS_ | Allow  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Filter WebSocket",
		description: "Filter WebSocket traffic with HTTP response code 101",
		enabled: true,
		action: "allow",
		filters: ["http"],
		traffic: "http.response.status_code == 101",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

# File sandboxing

import { Render, Details } from "~/components";

:::note
Available as an add-on to Zero Trust Enterprise plans. For more information, contact your account team.
:::

In addition to [anti-virus (AV) scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/), Gateway can quarantine previously unseen files downloaded by your users into a sandbox and scan them for malware.

When a file download passes AV scanning without a malware detection, Gateway quarantines the file in the [sandbox](#sandbox-environment). If the file has not been downloaded before, Gateway monitors the file's behavior and compares it to known malware patterns. During this process, Gateway displays an interstitial page in the user's browser. If the sandbox does not detect malicious activity, Gateway releases the file and downloads it to the user's device. If the sandbox detects malicious activity, Gateway blocks the download. For any subsequent downloads of the same file, Gateway remembers and applies its previous allow/block decision.

Gateway will log any file sandbox decisions in your [HTTP logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/#http-logs).

```mermaid
flowchart TD
    A(["User starts file download"]) --> B["File sent to AV scanner"]
    B --> C["Malicious file detected?"]
    C -- Yes --> D["Download blocked"]
    C -- No --> G["File sent to sandbox"]
    G --> n1["First time file downloaded?"]
    K["Malicious activity detected?"] -- Yes --> N["Download blocked"]
    K -- No --> n3["Download allowed"]
    n2["Interstitial page displayed for user during scan"] --> n4["File activity monitored"]
    n1 -- Yes --> n2
    n4 --> K
    n1 -- No --> K

    B@{ shape: subproc}
    C@{ shape: hex}
    D@{ shape: terminal}
    n1@{ shape: hex}
    K@{ shape: hex}
    N@{ shape: terminal}
    n3@{ shape: terminal}
    n2@{ shape: display}
    n4@{ shape: rect}
    style D stroke:#D50000
    style N stroke:#D50000
    style n3 stroke:#00C853
```

## Get started

To begin quarantining downloaded files, turn on file sandboxing:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Policy settings**, turn on **Open previously unseen files in a sandbox environment**.
3. (Optional) To block requests containing [non-scannable files](#non-scannable-files), select **Block requests for files that cannot be scanned**.

You can now create [Quarantine HTTP policies](/cloudflare-one/traffic-controls/http-policies/#quarantine) to determine what files to scan in the sandbox.

## Create test policy

To test if file sandboxing is working, you can create a Quarantine policy that matches the [Cloudflare Sandbox Test](https://sandbox.cloudflaredemos.com/):

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies** > **HTTP**.
2. Select **Add a policy**.
3. Add the following expression:

   | Selector | Operator | Value                         | Action     |
   | -------- | -------- | ----------------------------- | ---------- |
   | Host     | is       | `sandbox.cloudflaredemos.com` | Quarantine |

4. In **Sandbox file types**, select _ZIP Archive (zip)_.
5. From a device [connected to your Zero Trust organization](/cloudflare-one/team-and-resources/devices/), open a browser and go to the [Cloudflare Sandbox Test](https://sandbox.cloudflaredemos.com/).
6. Select **Download Test File**.

Gateway will quarantine and scan the file, display an interstitial status page in the browser, then release the file for download.

## Sandbox environment

Gateway executes quarantined files in a sandboxed Windows operating system environment. Using machine learning, the sandbox compares how files of a certain type behave compared to how these files should behave. The sandbox detects file actions down to the kernel level and compares them against a real-time malware database. In addition, Gateway checks the sandbox's network activity for malicious behavior and data exfiltration.

## Compatibility

### Supported file types

<Render file="gateway/sandbox-file-types" product="cloudflare-one" />

### Non-scannable files

Gateway cannot scan requests containing the following files:

- Files larger than 100 MB
- PGP encrypted files

# Application Granular Controls

import { Tabs, TabItem, APIRequest } from "~/components";
import GranularControlApplicationsList from "~/components/cf/GranularControlApplicationsList.astro";

With Application Granular Controls, you can create [Gateway HTTP policies](/cloudflare-one/traffic-controls/http-policies/) to control specific user actions within supported SaaS applications. This allows you to give users access to an application while restricting the actions that they can take within the application.

## Prerequisites

To use Application Granular Controls, you must:

- Install a [Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) or a [custom certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/) on your users' devices.
- Turn on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/).
- Turn on the [Gateway proxy](/cloudflare-one/traffic-controls/proxy/#turn-on-the-gateway-proxy).
- (Optional) If an application uses HTTP/3, turn on the [Gateway proxy for UDP traffic](/cloudflare-one/traffic-controls/http-policies/http3/#enable-http3-inspection).
- (Optional) To turn on [AI prompt logging](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#log-generative-ai-prompt-content), create a [DLP payload encryption public key](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#set-a-dlp-payload-encryption-public-key).

## Create a policy with Application Granular Controls

To create a Gateway HTTP policy with Application Granular Controls:

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies** > **HTTP**.
2. Select **Add a policy**.
3. Name the policy.
4. Under **Traffic**, build a logical expression that defines the traffic you want to allow or block. Because granular controls are specific to each application, you must use the _Application_ selector with the _is_ operator.
5. In **Value**, select your desired application.
6. In **Controls**, choose one or more Application Controls or individual Operations. For example, you can create a policy to block file uploads to ChatGPT:

   | Selector    | Operator | Value     | Controls | Action |
   | ----------- | -------- | --------- | -------- | ------ |
   | Application | is       | _ChatGPT_ | _Upload_ | Block  |

7. Select **Create policy**.

</TabItem>

<TabItem label="API">

Use the [Create a Zero Trust Gateway rule](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/create/) endpoint to create a policy. For example, you can create a policy to block file uploads to ChatGPT:

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block ChatGPT uploads",
		description: "Block file uploads to ChatGPT while allowing other usage",
		enabled: true,
		action: "block",
		filters: ["http"],
		traffic:
			"any(app.ids[*] == 1199) and any(app_control.controls[*] in {1653})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

For more information, refer to [HTTP policies](/cloudflare-one/traffic-controls/http-policies/).

## Control definitions

Gateway defines Application Granular Controls at different levels of granularity, including Application Controls and Operations.

### Application Controls

Application Controls are pre-defined controls that represent user intent, such as uploads or downloads. Cloudflare organizes sets of related operations into Application Controls for each supported application. Use Application Controls when a pre-defined grouping matches your intent.

### Operations

Operations are the individual API-level actions that an application uses. Use Operations for more fine-grained control than Application Controls provide — for example, blocking only certain types of downloads or blocking comments where no Application Control exists. Because each SaaS application uses a unique set of operations with its own scope and behaviors, operation-level controls may require analysis for each use case.

Cloudflare provides Operations based on the [available APIs for an application](#application-apis). For more information on how Operations map to [Application Controls](#application-controls), refer to [Compatible applications](#compatible-applications).

#### Operation Groups

Operation Groups are groupings of operations defined by the application vendor. Operation Groups are typically based on a categorization of the different functional areas of the application, such as signature requests, or the entities that the application defines, such as files or folders. These definitions vary by application. Gateway groups operations into these operation groups to match the operations with the corresponding vendor API documentation.

### DLP payloads

You can use Application Granular Controls with [Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/) for operations that contain scannable content. This includes operations that contain the content of uploaded or downloaded files or AI prompts. For example, when a user performs a file upload, a sequence of API operations may result, such as setting up the file metadata, uploading the file content, and finalizing the upload. When applying DLP to your Zero Trust traffic, it can be helpful to specifically target an operation that contains file content.

## Application APIs

SaaS applications typically provide multiple APIs to interact with. For each application, Application Granular Controls may support the following API types:

- Web Application API: These APIs are consumed by the web application that users interact with through their browser.
- Platform API: These APIs are exposed to users to allow for programmatic interaction with the SaaS application. These are typically used by automations, scripts, or other applications.

[Application Controls](#application-controls) include Operations of both API types. If both API types are available when creating HTTP policies using [Operations](#operations), you should select the Operations that align to the API being used, or include both for wider coverage.

## Compatible applications

Application Granular Controls supports the following applications:

<GranularControlApplicationsList />

# HTTP/3 inspection

import { Details } from "~/components";

HTTP/3 uses the QUIC protocol over UDP instead of TCP. Because Gateway's default proxy only handles TCP traffic, HTTP/3 inspection requires turning on the UDP proxy. Without it, HTTP/3 traffic bypasses HTTP inspection. [Network policies](/cloudflare-one/traffic-controls/network-policies/) still apply to the underlying UDP traffic.

Gateway applies HTTP policies to HTTP/3 traffic last. For more information, refer to the [order of enforcement](/cloudflare-one/traffic-controls/order-of-enforcement/#http3-traffic).

## Turn on HTTP/3 inspection

Before you can inspect any HTTPS traffic, you must deploy a [user-side certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) to your devices and turn on [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/). To inspect HTTP/3 traffic, you must also turn on the [Gateway proxy](/cloudflare-one/traffic-controls/proxy/) for UDP.

To turn on the Gateway proxy for UDP and TLS decryption:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Proxy and inspection**, turn on **Allow Secure Web Gateway to proxy traffic**.
3. Select **TCP** and **UDP**.
4. Turn on **TLS decryption**.

### Application limitations

Gateway can inspect HTTP/3 traffic from Mozilla Firefox and Microsoft Edge by establishing an HTTP/3 proxy connection. Gateway will then terminate the HTTP/3 connection, decrypt and inspect the traffic, and connect to the destination server over HTTP/2. Gateway can also inspect other HTTP applications, such as cURL.

If both the UDP proxy and TLS decryption are turned on, Google Chrome will automatically cancel HTTP/3 connections and retry them over HTTP/2, which Gateway can inspect. If either the UDP proxy or TLS decryption is turned off, HTTP/3 traffic from Chrome bypasses inspection entirely.

:::caution
If you do not turn on the UDP proxy, HTTP/3 traffic from browsers other than Chrome will bypass HTTP policy enforcement. Network policies still apply.
:::

## Exempt HTTP/3 traffic from inspection

If you require HTTP/3 traffic with end-to-end encryption from the client to the origin while still using the Gateway proxy, you can create a [Do Not Inspect HTTP policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) to match the desired traffic. Using a Do Not Inspect policy allows HTTP/3 traffic to preserve proxy performance and end-to-end encryption by bypassing Gateway's TLS decryption and inspection.

## Force HTTP/2 traffic

To apply Gateway policies to HTTP traffic without turning on the UDP proxy, you must turn off QUIC in your users' browsers to ensure only HTTP/2 traffic reaches Gateway.

<Details header="Google Chrome">

1. Go to `chrome://flags`
2. Set **Experimental QUIC protocol** to _Disabled_.
3. Relaunch Chrome.

</Details>

<Details header="Safari">

You cannot turn off QUIC in Safari. All traffic will be sent over HTTP/3.

</Details>

<Details header="Firefox">

1. Go to `about:config`.
2. If you receive a warning, select **Accept the Risk and Continue**.
3. Set **network.http.http3.enable** to _false_.
4. Relaunch Firefox.

</Details>

<Details header="Microsoft Edge">

1. Go to `edge://flags`
2. Set **Experimental QUIC protocol** to _Disabled_.
3. Relaunch Edge.

</Details>

# HTTP policies

import { Badge, Details, InlineBadge, Render } from "~/components";

:::note
To use HTTP policies, install a [Cloudflare root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) or a [custom certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/).
:::

HTTP policies allow you to filter all HTTP and HTTPS requests based on URLs, hostnames, HTTP methods, file types, and other request attributes. Unlike [network policies](/cloudflare-one/traffic-controls/network-policies/) which operate at Layer 4 (TCP/UDP), HTTP policies operate at Layer 7 and can inspect the full content of web traffic.

By default, Gateway inspects HTTP traffic on port `80` and, with [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) turned on, HTTPS traffic on port `443`. You can also configure Gateway to [inspect HTTP/HTTPS traffic on all ports](/cloudflare-one/traffic-controls/network-policies/protocol-detection/#inspect-on-all-ports). Gateway supports HTTP/3 inspection with the [UDP proxy](/cloudflare-one/traffic-controls/http-policies/http3/) turned on.

An HTTP policy consists of an **Action** and a logical expression that determines the scope of the policy. To build an expression, choose a **Selector** and an **Operator**, then enter a value or range of values in the **Value** field. You can use **And** and **Or** logical operators to evaluate multiple conditions.

- [Actions](#actions)
- [Selectors](#selectors)
- [Comparison operators](#comparison-operators)
- [Value](#value)
- [Logical operators](#logical-operators)

<Render
	file="gateway/response"
	product="cloudflare-one"
	params={{ type: "query", example: "Source IP", example2: "Resolved IP" }}
/>

<Render file="gateway/terraform-precedence-warning" product="cloudflare-one" />

## Actions

Actions in HTTP policies allow you to choose what to do with a given set of elements (domains, IP addresses, file types, and so on). You can assign one action per policy.

### Allow

API value: `allow`

<Details header="Available selectors">

<Render file="gateway/http-allow-block-selectors" product="cloudflare-one" />

</Details>

The Allow action allows outbound traffic to reach destinations you specify within the [Selectors](#selectors) and [Value](#value) fields. For example, the following configuration allows traffic to reach all websites we categorize as belonging to the Education content category:

| Selector           | Operator | Value       | Action |
| ------------------ | -------- | ----------- | ------ |
| Content Categories | in       | _Education_ | Allow  |

#### Untrusted certificates

The **Untrusted certificate action** determines how to handle insecure requests.

| Option       | Action                                                                                                                                                                                                                                                   |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Error        | Display Gateway error page. Matches the default behavior when no action is configured.                                                                                                                                                                   |
| Block        | Display [block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) as set in the Cloudflare dashboard.                                                                                                                           |
| Pass through | Bypass insecure connection warnings and seamlessly connect to the upstream. For more information on what statuses are bypassed, refer to [Troubleshooting Gateway](/cloudflare-one/traffic-controls/troubleshooting/#error-526-invalid-ssl-certificate). |

#### Custom headers

Allow policies can modify HTTP request headers before forwarding traffic to the destination. You can add, overwrite, or delete headers, and use dynamic variables to inject identity, device, and network context into header values.

For more information, refer to [Custom headers](/cloudflare-one/traffic-controls/http-policies/tenant-control/).

### Block

API value: `block`

<Details header="Available selectors">

<Render file="gateway/http-allow-block-selectors" product="cloudflare-one" />

</Details>

The Block action blocks outbound traffic from reaching destinations you specify within the [Selectors](#selectors) and [Value](#value) fields. For example, the following configuration blocks users from being able to upload any file type to Google Drive:

| Selector         | Operator      | Value          | Logic | Action |
| ---------------- | ------------- | -------------- | ----- | ------ |
| Application      | in            | `Google Drive` | And   | Block  |
| Upload Mime Type | matches regex | `.*`           |       |        |

#### Cloudflare One Client block notifications

<Render
	file="gateway/client-notifications"
	product="cloudflare-one"
	params={{
		toggleName: "**Display block notification for Cloudflare One Client**",
	}}
/>

### Redirect

API value: `redirect`

<Details header="Available selectors">

**Traffic**

- [Access Infrastructure Target](#access-infrastructure-target)
- [Application](#application)
- [Content Categories](#content-categories)
- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Domain](#domain)
- [Host](#host)
- [HTTP Method](#http-method)
- [Proxy Endpoint](#proxy-endpoint)
- [Security Risks](#security-risks)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [URL](#url)
- [URL Path](#url-path)
- [URL Path & Query](#url-path-and-query)
- [URL Query](#url-query)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

The Redirect action allows you to redirect matched HTTP requests to a different URL you specify. For example, if your users browse to the public web page of a SaaS app, you can redirect them to your own self-hosted instance, a single sign-on page, or an internal policy page.

To redirect URLs with a Block action and the block page, refer to [Redirect to a block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/#redirect-to-a-block-page).

#### Policy settings

In **Policy URL redirect**, you can define what URL to redirect matched requests to. The redirect URL can contain paths and queries. For example, you can redirect `example.com` to `cloudflare.com/path/to/page?querystring=x`.

<Render file="gateway/policy-context" product="cloudflare-one" />

When you turn on **Preserve original path and query string**, Gateway will append the original path and query string to the redirected URL. Paths and queries in the redirect URL take precedence over the original URL. For example, if the original URL is `example.com/path/to/page?querystring=X` and the redirect URL is `cloudflare.com/redirect-path?querystring=Y`, Gateway will redirect requests to:

```txt "cloudflare.com/redirect-path" "?querystring=Y"
cloudflare.com/redirect-path/path/to/page?querystring=Y
```

When you turn on both options, Gateway will preserve the original path and query string, then append policy context to the end of the redirect URL. For example, if the original URL is `example.com/path/to/page?querystring=X&k=1` and the redirect URL is `cloudflare.com/redirect-path?querystring=Y`, Gateway will redirect requests to:

```txt "cloudflare.com/redirect-path" "?querystring=Y" ins="&cf_user_email=user@example.com"
cloudflare.com/redirect-path/path/to/page?querystring=Y&k=1&cf_user_email=user@example.com
```

### Forward <Badge text="Beta" variant="caution" />

API value: `forward`

<Render file="gateway/forward-availability" product="cloudflare-one" />

Forward invokes a Worker on permitted HTTP traffic. The Worker can modify a request before sending it to its original destination, transform the response, return a synthetic response, or run background work.

Forward policies run in a separate phase after HTTP request policies and before body policies and inspection. Gateway invokes the Worker selected by the first matching Forward policy in order of precedence.

Gateway inspects the original body before delivering it to the Worker. A body-inspection Block decision can interrupt the Worker.

An Allow decision does not skip the Forward phase. A Forward policy does not override a Block decision.

Use request metadata selectors, identity, and device posture conditions to select traffic. Body inspection results, DLP matches, and response selectors cannot select a Worker.

HTTPS traffic requires TLS decryption. Do Not Inspect traffic bypasses Forward. Gateway does not invoke a Worker for CONNECT tunnels, protocol upgrades, or trailers. WebSocket traffic connects directly to the original upstream service and bypasses the Worker. Refer to [Known limitations](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#known-limitations) for protocol restrictions and other constraints.

Refer to [Forward traffic](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) for setup, the Worker entrypoint, and inspection order. Start with a [Worker template](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/) for notifications, capture, or transformation.

### Isolate

API value: `isolate`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [Domain](#domain)
- [Host](#host)
- [HTTP Method](#http-method)
- [Security Risks](#security-risks)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [URL](#url)
- [URL Path](#url-path)
- [URL Path & Query](#url-path-and-query)
- [URL Query](#url-query)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

The Isolate action serves matched traffic to users via [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/). For more information on this action, refer to [Isolation policies](/cloudflare-one/remote-browser-isolation/isolation-policies/#isolate).

### Do Not Inspect

API value: `off`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Domain](#domain)
- [Host](#host)
- [Proxy Endpoint](#proxy-endpoint)
- [Security Risks](#security-risks)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

:::caution[Visibility limitation]

When you create a Do Not Inspect policy for a given hostname, application, or app type, you will lose the ability to log or block HTTP requests, apply DLP policies, and perform AV scanning.

Information contained within HTTPS encryption, such as the full requested URL, will not be visible if it bypasses Gateway inspection. However, you can still apply [network policies](/cloudflare-one/traffic-controls/network-policies/) to this traffic. For more information, refer to [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/).

:::

Do Not Inspect lets you bypass certain elements from inspection. To prevent Gateway from decrypting and inspecting HTTPS traffic, your policy must match against the Server Name Indication (SNI) in the TLS header. When accessing a Do Not Inspect site in the browser, your browser may display a **Your connection is not private** warning, which you can proceed through to connect. For more information about applications which may require a Do Not Inspect policy, refer to [TLS decryption limitations](/cloudflare-one/traffic-controls/http-policies/tls-decryption/#inspection-limitations).

:::note
All Do Not Inspect policies are evaluated before any Allow or Block policies, regardless of their position in the policy list. For more information, refer to [Order of enforcement](/cloudflare-one/traffic-controls/order-of-enforcement/#http-policies).
:::

### Do Not Isolate

API value: `noisolate`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [Domain](#domain)
- [Host](#host)
- [HTTP Method](#http-method)
- [Security Risks](#security-risks)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [URL](#url)
- [URL Path](#url-path)
- [URL Path & Query](#url-path-and-query)
- [URL Query](#url-query)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

The Do Not Isolate action turns off browser isolation for matched traffic. For more information on this action, refer to [Isolation policies](/cloudflare-one/remote-browser-isolation/isolation-policies/#do-not-isolate).

### Do Not Scan

API value: `noscan`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Domain](#domain)
- [Host](#host)
- [HTTP Method](#http-method)
- [Proxy Endpoint](#proxy-endpoint)
- [Security Risks](#security-risks)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [URL](#url)
- [URL Path](#url-path)
- [URL Path & Query](#url-path-and-query)
- [URL Query](#url-query)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

When an admin enables AV scanning for uploads and/or downloads, Gateway will scan every supported file. Admins can selectively choose to disable scanning by leveraging the HTTP rules. For example, to prevent AV scanning of files uploaded to or downloaded from `example.com`, an admin would configure the following rule:

| Selector | Operator      | Value           | Action      |
| -------- | ------------- | --------------- | ----------- |
| Hostname | matches regex | `.*example.com` | Do Not Scan |

When a Do Not Scan rule matches, nothing is scanned, regardless of file size or whether the file type is supported or not.

### Quarantine

API value: `quarantine`

<Details header="Available selectors">

**Traffic**

- [Application](#application)
- [Content Categories](#content-categories)
- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Domain](#domain)
- [Host](#host)
- [HTTP Method](#http-method)
- [Proxy Endpoint](#proxy-endpoint)
- [Security Risks](#security-risks)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [URL](#url)
- [URL Path](#url-path)
- [URL Path & Query](#url-path-and-query)
- [URL Query](#url-query)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

The Quarantine action sends files in matching requests to a file sandbox to scan for malware. Gateway will only quarantine files not previously seen in the file sandbox. For more information on this action, refer to [File sandboxing](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/).

#### Sandbox file types

In **Sandbox file types**, you can select which file types to quarantine with your policy. You must select at least one file type.

<Render file="gateway/sandbox-file-types" product="cloudflare-one" />

## Selectors

:::note
Policies created using the URL selector are case-sensitive.
:::

Gateway matches HTTP traffic against the following selectors, or criteria:

### Access Infrastructure Target

<Render
	file="gateway/selectors/all-access-app-targets"
	product="cloudflare-one"
/>

### Access Private App

<Render
	file="gateway/selectors/all-access-private-app-destinations"
	product="cloudflare-one"
/>

### Application Approval Status

The review approval status of an application from [Shadow IT Discovery](/cloudflare-one/insights/analytics/shadow-it-discovery/) or the [Application Library](/cloudflare-one/team-and-resources/app-library/). For more information, refer to [Review applications](/cloudflare-one/team-and-resources/app-library/#review-applications).

| UI name            | API example                          |
| ------------------ | ------------------------------------ |
| Application Status | `any(app.statuses[*] == "approved")` |

### Application

<Render
	file="gateway/selectors/application-http"
	product="cloudflare-one"
	params={{ policyType: "HTTP" }}
/>

:::caution[Multiple API selectors required for Terraform]
When using Terraform to create a policy with the [Do Not Inspect](#do-not-inspect) action, you must use the `app.hosts_ids` and `app.supports_ids` selectors. For example, to create a Do Not Inspect policy for Google Cloud Platform traffic, create a policy with both `any(app.hosts_ids[*] in {1245})` and `any(app.supports_ids[*] in {1245})`.
:::

#### Granular controls

When using the _is_ operator with the _Application_ selector, you can use Application Granular Controls to choose specific actions and operations to match application traffic. For example, you can block file uploads to ChatGPT without blocking all ChatGPT traffic:

| Selector    | Operator | Value     | Controls | Action |
| ----------- | -------- | --------- | -------- | ------ |
| Application | is       | _ChatGPT_ | _Upload_ | Block  |

You can match traffic based on **Application Controls**, which group multiple user actions together, or **Operations**, which allow for granular control of supported API-level actions for an application.

For more information, refer to [Application Granular Controls](/cloudflare-one/traffic-controls/http-policies/granular-controls/).

### Body Phase

The phase of an HTTP request. You can use this selector to specify whether to scan either the data sent in an HTTP request to your user's device or from your user's device to a destination. Policies without this selector will scan both the HTTP request and response bodies.

| UI name    | API example                       |
| ---------- | --------------------------------- |
| Body Phase | `http.body_phase == \"download\"` |

:::caution[Body phase mismatch]
When combining this selector with the [Download and Upload File Types selectors](#download-and-upload-file-types), ensure you use the matching phase together. For example, use the `download` body phase with the Download File Types selector. If body phase and file type selector logic do not match, the policy may not filter traffic as intended.
:::

### Browser Isolation <Badge text="Beta" variant="caution"/>

Whether the current session is running inside [Remote Browser Isolation](/cloudflare-one/remote-browser-isolation/). Use this selector to apply different policy behavior to isolated and non-isolated traffic.

| UI name           | API example               |
| ----------------- | ------------------------- |
| Browser Isolation | `net.is_isolated == true` |

### Content Categories

<Render
	file="gateway/selectors/net-http-content-categories"
	product="cloudflare-one"
	params={{ APIendpoint: "http.request.uri.content_category" }}
/>

### Destination Continent

:::note
Only applies to traffic sent through the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/set-up/#traffic-and-dns-mode-default).
:::

<Render
	file="gateway/selectors/destination-continent"
	product="cloudflare-one"
	params={{ one: "http.dst_ip" }}
/>

### Destination Country

:::note
Only applies to traffic sent through the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/set-up/#traffic-and-dns-mode-default).
:::

<Render
	file="gateway/selectors/destination-country"
	product="cloudflare-one"
	params={{ one: "http.dst_ip" }}
/>

### Destination IP

:::note
Only applies to traffic sent through the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/set-up/#traffic-and-dns-mode-default).
:::

<Render
	file="gateway/selectors/destination-ip"
	product="cloudflare-one"
	params={{ APIendpoint: "http.conn.dst_ip" }}
/>

### Device Posture

<Render file="gateway/selectors/device-posture" product="cloudflare-one" />

### Domain

<Render
	file="gateway/selectors/domain-http"
	product="cloudflare-one"
	params={{ APIendpoint: "http.request.domains" }}
/>

### Download and Upload File Size

Use these selectors to limit the file size of upload or download transactions. File sizes are measured in mebibytes (MiB).

| UI name                  | API example                     |
| ------------------------ | ------------------------------- |
| Download File Size (MiB) | `http.download.file.size >= 10` |

| UI name                | API example                  |
| ---------------------- | ---------------------------- |
| Upload File Size (MiB) | `http.upload.file.size < 10` |

### Download and Upload File Types

:::caution[Deprecated selectors]
The _Download File Types_ and _Upload File Types_ selectors supersede the _Download File Type_ and _Upload File Type_ selectors. Gateway will still evaluate policies with the previous selectors. However, Cloudflare recommends migrating any policies with deprecated selectors to the new corresponding selectors.
:::

These selectors will scan file signatures in the HTTP body. You can select from file categories or [specific file types](#supported-file-types), such as executables, archives and compressed files, unscannable files, Microsoft 365/Office documents, and Adobe files.

| UI name             | API example                                         |
| ------------------- | --------------------------------------------------- |
| Download File Types | `any(http.download.file.types[*] in {"docx" "7z"})` |

| UI name           | API example                                        |
| ----------------- | -------------------------------------------------- |
| Upload File Types | `any(http.upload.file.types[*] in {"compressed"})` |

#### Supported file types

Gateway supports the following file types for use with the _Download File Types_ and _Upload File Types_ selectors:

<Details header="Compressed">

- 7-Zip archive (`.7z`)
- `bzip2` archive (`.bz2`)
- GNU Gzip archive (`.gz`)
- Microsoft Cabinet file (`.cab`)
- Microsoft Compiled HTML Help file (`.chm`)
- RAR archive (`.rar`)
- `xz` archive (`.xz`)
- ZIP archive (`.zip`)

</Details>

<Details header="Documents">

- Microsoft Office/365 files
  - Word document (`.doc`, `.docx`, `.docm`)
  - Excel spreadsheet (`.xls`, `.xlsx`, `.xlsm`)
  - PowerPoint presentation (`.ppt`, `.pptx`, `.pptm`)
- PDF document (`.pdf`)

</Details>

<Details header="Executable">

- Apple Software Package (`.pkg`)
- Dynamic-link library (DLL) file (`.dll`)
- Executable and Linkable Format (ELF) file (`.elf`)
- Java archive (JAR) package (`.jar`)
- Java class file (`.class`)
- Mach object (Mach-O) file (`.macho`)
- Microsoft Windows installer (`.msi`)
- Microsoft Software Installer (`.msix`, `.appx`)
- Microsoft Windows executable (`.exe`)

</Details>

<Details header="Image">

- Adobe Photoshop document (`.psd`)
- Bitmap image (`.bmp`)
- GIF image (`.gif`)
- Icon file (`.ico`)
- JPEG image (`.jpg`, `.jpeg`)
- PNG image (`.png`)
- WebP image (`.webp`)

</Details>

<Details header="Other">

- BitTorrent file (`.torrent`)

</Details>

<Details header="System">

- Apple Disk Image (`.dmg`)

</Details>

<Details header="Unscannable">

<Render file="gateway/policies/unscannable-files" product="cloudflare-one" />

</Details>

### Download and Upload Mime Type

These selectors depend on the `Content-Type` header being present in the request (for uploads) or response (for downloads). The MIME type value must match the format used in the `Content-Type` header (for example, `image/png`, `application/pdf`).

| UI name            | API example                         |
| ------------------ | ----------------------------------- |
| Download Mime Type | `http.download.mime == "image/png"` |

| UI name          | API example                       |
| ---------------- | --------------------------------- |
| Upload Mime Type | `http.upload.mime == "image/png"` |

### DLP Profile

Use [Cloudflare Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/) to scan HTTP traffic for the presence of sensitive data such as personally identifiable information (PII) or source code. You must configure a [DLP profile](/cloudflare-one/data-loss-prevention/dlp-profiles/) before you can use this selector in a policy.

| UI name     | API example                                                          |
| ----------- | -------------------------------------------------------------------- |
| DLP Profile | `any(dlp.profiles[*] in {\"a0cabf16-7491-4c9a-ac02-f64cabc66394\"})` |

### Is MCP <Badge text="Beta" variant="caution"/>

Whether the HTTP request was identified as [Model Context Protocol (MCP)](https://www.cloudflare.com/learning/ai/what-is-model-context-protocol-mcp/) traffic. Gateway detects MCP traffic by inspecting protocol-specific headers and payload characteristics. Use this selector to build policies that allow, block, or isolate MCP traffic across your network.

| UI name | API example                   |
| ------- | ----------------------------- |
| Is MCP  | `experimental.is_mcp == true` |

For example, the following policy blocks MCP traffic that does not arrive through an [MCP portal](/cloudflare-one/access-controls/ai-controls/mcp-portals/):

| Selector       | Operator | Value        | Logic | Action |
| -------------- | -------- | ------------ | ----- | ------ |
| Is MCP         | is       | _True_       | And   | Block  |
| Traffic Source | is not   | _MCP portal_ |       |        |

### Host

<Render
	file="gateway/selectors/host-http"
	product="cloudflare-one"
	params={{ APIendpoint: "http.request.host" }}
/>

### HTTP Method

The HTTP request method used in the traffic.

| UI name     | API example                    |
| ----------- | ------------------------------ |
| HTTP Method | `http.request.method == "GET"` |

### HTTP Response

The HTTP response status code received by the traffic.

| UI name | API example                          |
| ------- | ------------------------------------ |
| URL     | `http.response.status_code == "200"` |

### Package Ecosystem

The package registry ecosystem detected from the HTTP request URL. For more information, refer to [Package registry security](/cloudflare-one/traffic-controls/http-policies/package-registry-security/).

| UI name           | API example              |
| ----------------- | ------------------------ |
| Package Ecosystem | `pkg.ecosystem == "npm"` |

### Package Name

The name of the package detected from the HTTP request URL.

| UI name      | API example            |
| ------------ | ---------------------- |
| Package Name | `pkg.name == "lodash"` |

### Package Namespace

The namespace of the package, when the ecosystem supports one. For npm, this is the scope. For Maven, this is the group ID. For Go, this is the module path.

| UI name           | API example                 |
| ----------------- | --------------------------- |
| Package Namespace | `pkg.namespace == "@babel"` |

### Package URL (PURL)

The [Package URL](https://github.com/package-url/purl-spec) derived from the detected package coordinates.

| UI name     | API example                            |
| ----------- | -------------------------------------- |
| Package URL | `pkg.purl == "pkg:npm/lodash@4.17.21"` |

### Package Version

The version of the package detected from the HTTP request URL. Supports exact match and ecosystem-aware version comparison operators. For more information on version comparison semantics, refer to [Package registry security](/cloudflare-one/traffic-controls/http-policies/package-registry-security/#version-comparison-operators).

| UI name         | API example                |
| --------------- | -------------------------- |
| Package Version | `pkg.version == "4.17.21"` |

### Proxy Endpoint

<Render file="gateway/selectors/proxy-endpoint" product="cloudflare-one" />

### Security Risks

<Render
	file="gateway/selectors/security-risks"
	product="cloudflare-one"
	params={{ APIendpoint: "http.request.uri.security_category" }}
/>

### Source Continent

The continent of the user making the request. <Render file="gateway/selectors/source-continent-http" product="cloudflare-one" params={{ one: "http.src_ip" }} />

### Source Country

The country of the user making the request. <Render file="gateway/selectors/source-country-http" product="cloudflare-one" params={{ one: "http.src_ip" }} />

### Source Internal IP

<Render
	file="gateway/selectors/source-internal-ip"
	product="cloudflare-one"
	params={{ one: "HTTP", two: "http.conn.internal_src_ip" }}
/>

### Source IP

<Render
	file="gateway/selectors/source-ip"
	product="cloudflare-one"
	params={{ APIendpoint: "http.conn.src_ip" }}
/>

### Traffic Destination <Badge text="Beta" variant="caution"/>

The method used to off-ramp traffic from Cloudflare. Use this selector to apply policies based on how traffic leaves Gateway.

| UI name             | API example                      |
| ------------------- | -------------------------------- |
| Traffic Destination | `net.offramp.type == "internet"` |

Available values: `internet` (Internet), `cloudflare_wan` (Cloudflare WAN), `cloudflare_tunnel` (Cloudflare Tunnel), `device_client` (Cloudflare One Client), `mesh` (Mesh).

### Traffic Source <Badge text="Beta" variant="caution"/>

The method used to on-ramp traffic to Cloudflare. Use this selector to apply policies based on how traffic reaches Gateway.

| UI name        | API example                          |
| -------------- | ------------------------------------ |
| Traffic Source | `net.onramp.type == "device_client"` |

Available values: `device_client` (Device client), `mesh` (Mesh), `cloudflare_wan` (Cloudflare WAN), `clientless_rdp` (Clientless RDP), `proxy_endpoint` (Proxy endpoint), `agentless_biso` (Clientless Browser Isolation), `mcp_portal` (MCP portal).

### URL

<Render file="gateway/url-slash" product="cloudflare-one" />

| UI name | API example                            |
| ------- | -------------------------------------- |
| URL     | `http.request.uri matches "/r/gaming"` |

### URL Path

The pathname of a webpage's URL.

| UI name  | API example                             |
| -------- | --------------------------------------- |
| URL Path | `http.request.uri.path == \"/foo/bar\"` |

### URL Path and Query

The pathname and query of a webpage's URL.

| UI name            | API example                                                     |
| ------------------ | --------------------------------------------------------------- |
| URL Path and Query | `http.request.uri.path_and_query == \"/foo/bar?ab%242=%2A342\"` |

### URL Query

The query of a webpage's URL.

| UI name   | API example                                 |
| --------- | ------------------------------------------- |
| URL Query | `http.request.uri.query == "ab%242=%2A342"` |

### Users

<Render file="gateway/selectors/users-http" product="cloudflare-one" />

### Virtual Network

<Render
	file="gateway/selectors/virtual-network"
	product="cloudflare-one"
	params={{ one: "http.conn.vnet_id" }}
/>

## Comparison operators

<Render file="gateway/comparison-operators" product="cloudflare-one" />

## Value

<Render
	file="gateway/value"
	product="cloudflare-one"
	params={{
		selector: "Host",
		selectordescription: "hostname",
		operator: "matches regex",
		valueOne: ".*whispersystems.org",
		valueTwo: ".*signal.org",
	}}
/>

## Logical operators

<Render
	file="gateway/logical-operators"
	product="cloudflare-one"
	params={{ one: "**Identity** or **Device Posture**" }}
/>

<Render
	file="gateway/response"
	product="cloudflare-one"
	params={{ type: "request", example: "Source IP", example2: "a DLP Profile" }}
/>

# Package registry security

:::note
Package registry security requires [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) to be turned on for the traffic you want to inspect.
:::

Gateway can detect software package downloads across seven major package ecosystems and give you policy control over supply chain traffic. When a developer or CI/CD pipeline downloads a package through Gateway, the proxy identifies the registry protocol from the request URL and extracts the package ecosystem, name, version, and namespace. You can then write HTTP policies using `pkg.*` selectors to allow or block package downloads at whatever granularity you need, from blocking an entire ecosystem to restricting a single package version.

## How it works

Package detection operates entirely on the HTTP request URL structure. Gateway recognizes the API contract of each supported registry protocol, not the hostname serving the traffic. This means detection works the same way whether traffic goes to the official public registry, a self-hosted mirror, a corporate proxy registry such as Artifactory or Nexus, or any other compatible endpoint. If the URL path matches the shape of a known registry download, Gateway classifies it.

Detection is fail-open. If the URL cannot be classified as a package download, the request proceeds as normal non-package traffic. A classification failure will never block or break a package install.

### Supported ecosystems

Gateway detects package downloads for the following ecosystems:

| Ecosystem | Example artifact pattern | Namespace |
| --------- | ------------------------ | --------- |
| npm | `/{package}/-/{package}-{version}.tgz` | Scope (for example, `@babel`) |
| PyPI | `/packages/{hash}/{hash}/{hash}/{package}-{version}.whl` | — |
| RubyGems | `/gems/{package}-{version}.gem` | — |
| Cargo | `/crates/{package}/{package}-{version}.crate` | — |
| Go | `/{module}/@v/{version}.zip` | Module path |
| Maven | `/maven2/{group}/{artifact}/{version}/{artifact}-{version}.jar` | Group ID |
| NuGet | `/{package}/{version}/{package}.{version}.nupkg` | — |

### Detected operation

The initial release detects **download** operations only. Download is the one operation where the package name, version, and ecosystem are all derivable from the URL path across every supported registry. Other operations such as resolve (metadata lookups) and publish use different endpoints, hosts, or HTTP methods that require additional signal beyond the URL path.

## Selectors

The following selectors are available for [HTTP policies](/cloudflare-one/traffic-controls/http-policies/) with the Allow and Block actions:

| Selector | UI name | API example | Description |
| --- | --- | --- | --- |
| `pkg.ecosystem` | Package Ecosystem | `pkg.ecosystem == "npm"` | The package ecosystem detected from the request URL. |
| `pkg.name` | Package Name | `pkg.name == "lodash"` | The package name extracted from the download URL. |
| `pkg.version` | Package Version | `pkg.version == "4.17.21"` | The package version extracted from the download URL. Supports exact match and ecosystem-aware comparison operators. |
| `pkg.namespace` | Package Namespace | `pkg.namespace == "@babel"` | The package namespace, when the ecosystem supports one. For npm this is the scope, for Maven this is the group ID, and for Go this is the module path. |
| `pkg.purl` | Package URL | `pkg.purl == "pkg:npm/lodash@4.17.21"` | The [Package URL (PURL)](https://github.com/package-url/purl-spec) derived from the detected coordinates. Available in the API only. |

### Build expressions in the dashboard

In the dashboard, **Package Ecosystem** is the primary selector. Selecting it reveals nested fields for specifying package name, version, and namespace. The following rules apply:

- You must select an ecosystem before any other package fields become available.
- You must select exactly one ecosystem to enable the nested fields. If you use the `in` or `not in` operator to match multiple ecosystems, the nested package name, version, and namespace fields are disabled.
- You must add a package name before you can add a version constraint.
- The namespace field is only available for ecosystems that support one. The label changes based on the selected ecosystem: **Scope** for npm, **Group ID** for Maven, and **Module namespace** for Go.
- The `pkg.purl` (Package URL) selector is not available in the dashboard. Use the API to write expressions that match on PURL.

When using the API directly, these selectors can be combined freely in wirefilter expressions without these constraints.

### Version comparison operators

The `pkg.version` selector supports ecosystem-aware comparison operators in addition to exact string matching. Each ecosystem uses its own native versioning semantics:

| Ecosystem | Versioning standard |
| --------- | ------------------- |
| npm | SemVer |
| Cargo | SemVer |
| PyPI | PEP 440 |
| RubyGems | Gem::Version |
| Go | Go module versions |
| Maven | Maven version ordering |
| NuGet | NuGet normalization and ordering |

The following comparison operators are supported:

| Operator | API syntax | Description |
| -------- | ---------- | ----------- |
| equals | `==` | Normalized equality using ecosystem-specific identity rules. |
| not equals | `!=` | Negation of normalized equality. |
| greater than | `>` | Version is greater than the specified value using native ordering. |
| greater than or equal | `>=` | Version is greater than or equal to the specified value. |
| less than | `<` | Version is less than the specified value. |
| less than or equal | `<=` | Version is less than or equal to the specified value. |

When a version string cannot be parsed by the ecosystem's versioning rules, or when the detected ecosystem does not match the comparison context, the comparison returns no match. This includes `!=`, meaning an unparseable version does not match anything.

:::note
Ecosystem-specific range syntax such as npm `^1.2.3`, PyPI `~=1.4`, or Maven interval notation is not supported. Use the comparison operators listed in the table to express version constraints.
:::

## Example policies

### Block all downloads from a specific ecosystem

To block all PyPI package downloads across your organization:

| Selector | Operator | Value | Action |
| --- | --- | --- | --- |
| Package Ecosystem | is | `pypi` | Block |

Wirefilter expression:

```txt
pkg.ecosystem == "pypi"
```

### Block a specific package

To block a known malicious or unwanted npm package regardless of version:

| Selector | Operator | Value | Logic | Action |
| --- | --- | --- | --- | --- |
| Package Ecosystem | is | `npm` | And | Block |
| Package Name | is | `event-stream` | | |

Wirefilter expression:

```txt
pkg.ecosystem == "npm" and pkg.name == "event-stream"
```

### Block vulnerable versions of a package

To block all versions of `lodash` below `4.17.21`, which is the version that patched CVE-2021-23337:

| Selector | Operator | Value | Logic | Action |
| --- | --- | --- | --- | --- |
| Package Ecosystem | is | `npm` | And | Block |
| Package Name | is | `lodash` | And | |
| Package Version | less than | `4.17.21` | | |

Wirefilter expression:

```txt
pkg.ecosystem == "npm" and pkg.name == "lodash" and pkg.version < "4.17.21"
```

### Restrict packages to a sanctioned registry mirror

To allow npm package downloads only through your corporate Artifactory instance and block all other npm downloads, create two policies:

**Policy 1 - Allow sanctioned mirror (higher priority):**

| Selector | Operator | Value | Logic | Action |
| --- | --- | --- | --- | --- |
| Package Ecosystem | is | `npm` | And | Allow |
| Host | is | `npm.internal.example.com` | | |

Wirefilter expression:

```txt
pkg.ecosystem == "npm" and http.request.host == "npm.internal.example.com"
```

**Policy 2 - Block all other npm downloads (lower priority):**

| Selector | Operator | Value | Action |
| --- | --- | --- | --- |
| Package Ecosystem | is | `npm` | Block |

Wirefilter expression:

```txt
pkg.ecosystem == "npm"
```

Because detection is based on the registry protocol rather than the hostname, both the public `registry.npmjs.org` and your internal mirror at `npm.internal.example.com` are detected as npm traffic. Policy 1 (at higher priority) allows the sanctioned mirror, and Policy 2 blocks everything else.

### Block a specific package unless from a sanctioned host

To allow downloads of a sensitive internal package only through your corporate registry, blocking it from all other sources:

**Policy 1 - Allow from sanctioned host (higher priority):**

| Selector | Operator | Value | Logic | Action |
| --- | --- | --- | --- | --- |
| Package Ecosystem | is | `npm` | And | Allow |
| Package Namespace | is | `@acme` | And | |
| Package Name | is | `internal-sdk` | And | |
| Host | is | `npm.internal.example.com` | | |

Wirefilter expression:

```txt
pkg.ecosystem == "npm" and pkg.namespace == "@acme" and pkg.name == "internal-sdk" and http.request.host == "npm.internal.example.com"
```

**Policy 2 - Block from all other hosts (lower priority):**

| Selector | Operator | Value | Logic | Action |
| --- | --- | --- | --- | --- |
| Package Ecosystem | is | `npm` | And | Block |
| Package Namespace | is | `@acme` | And | |
| Package Name | is | `internal-sdk` | | |

Wirefilter expression:

```txt
pkg.ecosystem == "npm" and pkg.namespace == "@acme" and pkg.name == "internal-sdk"
```

## Detection and mirrors

Package detection classifies traffic based on the URL path structure of each registry's download API. It does not rely on matching against a list of known registry hostnames. This design means that any server serving packages using a compatible URL layout is detected the same way, whether it is:

- The official public registry (for example, `registry.npmjs.org` or `pypi.org`)
- A corporate proxy registry such as JFrog Artifactory, Sonatype Nexus, or AWS CodeArtifact
- A self-hosted mirror
- A CDN-fronted registry endpoint

The `http.request.host` selector remains available for policies that need to distinguish between specific registry hosts. By combining `pkg.*` selectors with `http.request.host`, you can write rules that apply different actions depending on where the package is being fetched from.

## Logging

When Gateway detects a package download, the package metadata is included in the Gateway HTTP log. This data is available in [Gateway activity logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) and through [Logpush](/logs/logpush/logpush-job/datasets/account/gateway_http/).

### Activity Log fields

The following package fields are available in the Gateway Activity Log:

| Field | Description |
| ----- | ----------- |
| Package Ecosystem | The detected registry type (for example, `npm`, `pypi`, `cargo`). |
| Package URL (PURL) | The Package URL string derived from the detected coordinates (for example, `pkg:npm/lodash@4.17.21`). |

### Logpush fields

Package metadata is available in the `PackageInfo` object in the `gateway_http` Logpush dataset:

| Field | Type | Description |
| --- | --- | --- |
| `PackageInfo.Ecosystem` | `string` | The detected package ecosystem. |
| `PackageInfo.Namespace` | `string` | The package namespace, if applicable. |
| `PackageInfo.Name` | `string` | The package name. |
| `PackageInfo.Version` | `string` | The package version string. |
| `PackageInfo.Purl` | `string` | The Package URL string. |

## Limitations

- Package detection requires [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) to be turned on. Packages downloaded over connections that bypass TLS inspection (due to Do Not Inspect policies or applications on the [inspection limitations list](/cloudflare-one/traffic-controls/http-policies/tls-decryption/#inspection-limitations)) are not detected.
- Some language runtimes and HTTP clients maintain their own certificate trust stores separate from the operating system. If the certificate used for inspection (such as the Cloudflare managed certificate) is only installed in the OS trust store, package downloads from these clients may fail with certificate verification errors. Refer to [Install certificate manually](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/) for instructions on adding the certificate to application-specific trust stores, including Python, Node.js, Ruby, Rust, Java, and Go.
- Only **download** operations are detected. Metadata lookups (resolve), package publishing, and other registry operations are not classified.
- Version comparison uses each ecosystem's native ordering rules. Cross-ecosystem version comparisons are not supported.
- Ecosystem-specific range syntax (such as npm `^1.2.3`, PyPI `~=1.4`, or Maven interval notation) is not supported. Use the individual comparison operators (`>`, `<`, `>=`, `<=`) instead.
- Detecting dependency packages within package archives (such as dependencies in shaded or fat JARs) is not supported.

# Modify HTTP request headers in Gateway

import { Details, Render } from "~/components";

Gateway HTTP policies with Allow actions can modify the headers of matching requests before they reach their destination. You can add dynamic values to set headers to forward information like user identity, source IP, and other inputs to upstream services, enforce SaaS tenant control, strip internal headers, override header content.

Header manipulation requires [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/), because HTTP headers are only visible on traffic which Gateway can decrypt.

For header changes that require custom code or response transformations, use [Forward](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) (beta) to invoke a Worker after HTTP filtering. Built-in Allow-policy header operations remain available for changes expressed directly in the policy.

## Header operations

Gateway supports three header operations on HTTP policies. When a request matches an Allow policy with header operations configured, Gateway applies them in the following order:

1. **Delete** - Remove headers from the request.
2. **Overwrite** - Overwrite headers on the request. Headers with matched names will have their values overwritten. If the header does not exist, it is created.
3. **Add** - Append headers to the request. If the header already exists, the added value is appended to the existing value.

You can configure up to 20 header operations per policy. Header names are limited to 256 bytes, and header values are limited to 4 KB.

### Add headers

Adding a header appends a value to the request. If the header already exists, the value is added alongside the existing value rather than replacing it.

### Overwrite headers

Overwriting a header overwrites any existing value. If the header does not already exist on the request, it is created. Use this operation when you need to guarantee a specific header value regardless of what the client sent.

### Delete headers

Deleting a header removes it from the request entirely. If the header does not exist, the operation has no effect.

## Dynamic header values

Header values can include dynamic variables that Gateway resolves at request time using identity, device, and network context from the current session. Dynamic variables use the `@{...}` syntax and can be mixed with static text in the same value.

For example, a header value of `user-@{identity.email}` resolves to `user-jdoe@example.com` at request time.

The following dynamic variables are available:

| Variable              | Description                                                |
| --------------------- | ---------------------------------------------------------- |
| `@{identity.email}`   | User's email address from the identity provider.           |
| `@{identity.name}`    | User's display name from the identity provider.            |
| `@{identity.id}`      | User's Cloudflare identity UUID.                           |
| `@{identity.groups}`  | User's identity provider group memberships.                |
| `@{identity.SAML}`    | User's SAML attributes from the identity provider if configured.         |
| `@{identity.OIDC}`    | User's OIDC claims from the identity provider if configured.             |
| `@{source.ip}`        | Source IP address of the user's connection as seen in Gateway.                 |
| `@{destination.ip}`   | Destination IP address of the request.                     |
| `@{device.id}`        | Cloudflare One Client device UUID.                                          |
| `@{device.posture}`   | Device posture check results (serialized as a JSON string).|

Dynamic variables require an active identity session. If Gateway cannot resolve a variable (for example, the user is not authenticated), the variable is replaced with a warning string such as `cf-unresolved` or `cf-invalid`, and a warning is added to the HTTP log.

## Configure header operations

### Dashboard

To create an HTTP policy with header operations:

1. In the [Cloudflare One dashboard](https://dash.cloudflare.com/one), go to **Traffic policies** > **Firewall policies** > **HTTP**.
2. Select **Add a policy**.
3. Build an expression to match the traffic you want to modify.
4. In **Action**, select _Allow_.
5. Under **Modify request headers**, select **Add** or **Overwrite** to add or overwrite headers, or select **Remove** to delete a header.
6. For Add and Overwrite operations, enter the header name and value. To use a dynamic variable, enter the `@{...}` syntax in the value field, or select the `{}` button to see the list of available values. For Remove operations, enter only the header name.
7. Save your policy.

### API

To create an HTTP policy with header operations via the API, include `add_headers`, `set_headers`, and `delete_headers` in the `rule_settings` object.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/gateway/rules \
--header "Authorization: Bearer {api_token}" \
--header "Content-Type: application/json" \
--data '{
  "name": "Forward identity headers",
  "action": "allow",
  "enabled": true,
  "filters": ["http"],
  "traffic": "any(http.request.domains[*] in {\"app.example.com\"})",
  "rule_settings": {
    "add_headers": {
      "X-User-Email": ["@{identity.email}"],
      "X-User-Groups": ["@{identity.groups}"]
    },
    "set_headers": {
      "X-Forwarded-User": ["@{identity.email}"]
    },
    "delete_headers": ["X-Debug-Token", "X-Internal-Only"]
  }
}'
```

The `rule_settings` fields for header manipulation are:

| Field            | Type                         | Description                                                           |
| ---------------- | ---------------------------- | --------------------------------------------------------------------- |
| `add_headers`    | `map<string, array<string>>` | Headers to append. Each key is a header name, each value is a list of values to add.      |
| `set_headers`    | `map<string, array<string>>` | Headers to overwrite. Each key is a header name, each value is a list of values to set.    |
| `delete_headers` | `array<string>`              | Header names to remove from the request.                              |

A single header value can contain a mix of static text and dynamic variables. For example:

```json
{
  "add_headers": {
    "X-Request-Context": ["user=@{identity.email}, device=@{device.id}, src=@{source.ip}"]
  }
}
```

### Verify custom headers

If you save a HAR (HTTP Archive) file from a browser to analyze your web traffic, custom headers defined with Gateway will not appear in the file. This is because Gateway injects the header after the request leaves the browser.

To verify Gateway is applying a custom header:

1. In your policy with custom headers, add a selector to match traffic for [HTTPBin](https://httpbin.org/), an open-source site for testing HTTP requests. For example:

   | Selector    | Operator | Value              | Logic | Action | Untrusted certificate action |
   | ----------- | -------- | ------------------ | ----- | ------ | ---------------------------- |
   | Application | in       | _Google Workspace_ | Or   | Allow  | Block                        |
   | Domain      | in       | `httpbin.org`      |       |        |                              |

2. On your device, go to [`httpbin.org/anything`](https://httpbin.org/anything). Your custom header will appear in the list of headers.
3. (Optional) Remove the HTTPBin expression from your policy.

## Use cases

### SaaS tenant control

Tenant control allows your users to access corporate SaaS applications while blocking access to personal accounts on the same service. For example, you can allow access to your company's Google Workspace while blocking personal Gmail logins.

Gateway implements tenant control by injecting custom HTTP headers into matching requests. These headers tell the SaaS application which tenant (organization) is authorized. If the user attempts to authenticate with a personal account, the SaaS application reads the header and rejects the request.

<Details header="Microsoft 365">

Microsoft 365 tenant control requires two policies. When you order your policies, make sure they follow [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence).

| Precedence | Selector | Operator | Value            | Action | Untrusted certificate action |
| ---------- | -------- | -------- | ---------------- | ------ | ---------------------------- |
| 1          | Domain   | is       | `login.live.com` | Allow  | Block                        |

| Custom header name                  | Custom header value |
| ----------------------------------- | ------------------- |
| `Sec-Restrict-Tenant-Access-Policy` | `restrict-msa`      |

| Precedence | Selector    | Operator | Value                 | Action | Untrusted certificate action |
| ---------- | ----------- | -------- | --------------------- | ------ | ---------------------------- |
| 2          | Application | in       | _Microsoft Office365_ | Allow  | Block                        |

| Custom header name                                      | Custom header value        |
| ------------------------------------------------------- | -------------------------- |
| `Restrict-Access-To-Tenants`, `Restrict-Access-Context` | Your organization's domain |

For more information, refer to the [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity/enterprise-apps/tenant-restrictions).

</Details>

<Details header="Google Workspace">

| Selector    | Operator | Value              | Action | Untrusted certificate action |
| ----------- | -------- | ------------------ | ------ | ---------------------------- |
| Application | in       | _Google Workspace_ | Allow  | Block                        |

| Custom header name           | Custom header value        |
| ---------------------------- | -------------------------- |
| `X-GoogApps-Allowed-Domains` | Your organization's domain |

For more information, refer to the [Google Workspace documentation](https://support.google.com/a/answer/1668854).

</Details>

<Details header="Slack">

| Selector    | Operator | Value   | Action | Untrusted certificate action |
| ----------- | -------- | ------- | ------ | ---------------------------- |
| Application | in       | _Slack_ | Allow  | Block                        |

| Custom header name                                                   | Custom header value           |
| -------------------------------------------------------------------- | ----------------------------- |
| `X-Slack-Allowed-Workspaces-Requester`, `X-Slack-Allowed-Workspaces` | Your organization's workspace |

For more information, refer to the [Slack documentation](https://slack.com/help/articles/360024821873-Approve-Slack-workspaces-for-your-network).

</Details>

<Details header="Dropbox">

| Selector    | Operator | Value     | Action | Untrusted certificate action |
| ----------- | -------- | --------- | ------ | ---------------------------- |
| Application | in       | _Dropbox_ | Allow  | Block                        |

| Custom header name           | Custom header value    |
| ---------------------------- | ---------------------- |
| `X-Dropbox-allowed-Team-Ids` | Your organization's ID |

For more information, refer to the [Dropbox documentation](https://help.dropbox.com/security/network-control).

</Details>

<Details header="ChatGPT">

| Selector    | Operator | Value     | Action | Untrusted certificate action |
| ----------- | -------- | --------- | ------ | ---------------------------- |
| Application | in       | _ChatGPT_ | Allow  | Block                        |

| Custom header name             | Custom header value              |
| ------------------------------ | -------------------------------- |
| `Chatgpt-Allowed-Workspace-Id` | Your organization's workspace ID |

For more information, refer to the [OpenAI documentation](https://help.openai.com/articles/8798594-what-is-a-workspace-how-do-i-access-my-chatgpt-business-workspace).

</Details>

<Details header="Claude">

| Selector    | Operator | Value    | Action | Untrusted certificate action |
| ----------- | -------- | -------- | ------ | ---------------------------- |
| Application | in       | _Claude_ | Allow  | Block                        |

| Custom header name          | Custom header value      |
| --------------------------- | ------------------------ |
| `anthropic-allowed-org-ids` | Your organization's UUID |

To allow access from multiple organizations, enter a comma-separated list of UUIDs with no spaces (for example, `<org-uuid-1>,<org-uuid-2>`).

You can find your organization UUID in **Settings** > **Account** > **Organization ID** on [claude.ai](https://claude.ai/settings/account).

For more information, refer to the [Claude documentation](https://support.claude.com/en/articles/13198485-enforce-network-level-access-control-with-tenant-restrictions).

</Details>

### Forward user identity to upstream services

You can use dynamic header values to forward user identity information to your upstream applications without requiring those applications to integrate with Cloudflare Access directly.

| Header name        | Header value          |
| ------------------ | --------------------- |
| `X-User-Email`     | `@{identity.email}`  |
| `X-User-Name`      | `@{identity.name}`   |
| `X-User-Groups`    | `@{identity.groups}` |
| `X-Source-IP`       | `@{source.ip}`       |

Your upstream application can read these headers to identify the user, enforce authorization logic, or populate audit logs.

### Strip internal headers

To prevent clients from spoofing internal headers, use the delete operation to remove headers before forwarding the request, then use the add or set operation to re-inject them with verified values.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/gateway/rules \
--header "Authorization: Bearer {api_token}" \
--header "Content-Type: application/json" \
--data '{
  "name": "Replace internal headers",
  "action": "allow",
  "enabled": true,
  "filters": ["http"],
  "traffic": "any(http.request.domains[*] in {\"internal.example.com\"})",
  "rule_settings": {
    "delete_headers": ["X-Internal-User"],
    "set_headers": {
      "X-Internal-User": ["@{identity.email}"]
    }
  }
}'
```

### Exempt users in Cloudflare WAF

You can include custom headers in an HTTP policy to allow your users through [Cloudflare WAF](/waf/). This is useful for allowing only Cloudflare One Client users through your WAF.

1. Create an Allow policy for an internal domain behind your WAF with a custom header.

   | Selector | Operator | Value             | Action |
   | -------- | -------- | ----------------- | ------ |
   | Domain   | in       | `internalapp.com` | Allow  |

   | Custom header name | Custom header value |
   | ------------------ | ------------------- |
   | `X-Example-Header` | `example-value`     |

2. In Cloudflare WAF, [create a custom rule](/waf/custom-rules/) to [require the same HTTP header](/waf/custom-rules/use-cases/require-specific-headers/#example-2-require-http-header-with-a-specific-value).

### Use custom headers with Browser Isolation

You can configure [Browser Isolation](/cloudflare-one/remote-browser-isolation/) to send custom headers. This is useful for implementing tenant control for isolated SaaS applications or sending arbitrary custom request headers to isolated websites.

To use custom headers with Browser Isolation, create two HTTP policies targeting the same domain or application group. For example, you can create policies for [HTTPBin](https://httpbin.org/), an open-source site for testing HTTP requests:

1. Create an Isolate policy for `httpbin.org`.

   | Selector | Operator | Value         | Action  |
   | -------- | -------- | ------------- | ------- |
   | Domain   | in       | `httpbin.org` | Isolate |

2. Create an Allow policy for `httpbin.org` with a custom header.

   | Selector | Operator | Value         | Action |
   | -------- | -------- | ------------- | ------ |
   | Domain   | in       | `httpbin.org` | Allow  |

   | Custom header name | Custom header value |
   | ------------------ | ------------------- |
   | `Example-Header`   | `example-value`     |

3. Go to [`httpbin.org/anything`](https://httpbin.org/anything). Cloudflare will render the site in an isolated browser. Your custom header will appear in the list of headers.

# TLS decryption

import {
	GlossaryDefinition,
	GlossaryTooltip,
	Render,
	TabItem,
	Tabs,
} from "~/components";

Cloudflare Gateway can perform [SSL/TLS decryption](https://www.cloudflare.com/learning/security/what-is-https-inspection/) to inspect HTTPS traffic for malware and other security risks. TLS decryption is required for HTTP policies to inspect HTTPS traffic. Without it, information contained within HTTPS encryption, such as the full URL, headers, and request body, [will not be visible to Gateway](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect).

When you turn on TLS decryption, Gateway will decrypt all traffic sent over HTTPS, apply your HTTP policies, and then re-encrypt the request with a [user-side certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/).

Cloudflare prevents traffic interference by decrypting, inspecting, and re-encrypting HTTPS requests in its data centers in memory only. Gateway only stores eligible cache content at rest. All cache disks are encrypted at rest. You can configure where TLS decryption takes place with [Regional Services](/data-localization/regional-services/) in the [Cloudflare Data Localization Suite (DLS)](/data-localization/). To further control what data centers traffic egresses from, you can use [dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/).

Cloudflare supports connections from users to Gateway over TLS 1.1, 1.2, and 1.3.

The [Forward action](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/) (beta) also requires TLS decryption for HTTPS traffic. Gateway does not invoke a Worker for traffic matching Do Not Inspect. If you configure a Worker to archive decrypted content, that storage is controlled by your Worker code and bindings.

## Turn on TLS decryption

:::note[Prerequisite]
Before you turn on TLS decryption, ensure you have installed either a [Cloudflare-generated certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) or [custom certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/) on your users' devices.
:::

To turn on TLS decryption:

<Render file="gateway/enable-tls-decryption" product="cloudflare-one" />

## Inspection limitations

Gateway does not support TLS decryption for applications which use:

- [Certificate pinning](#incompatible-certificates)
- [Self-signed certificates](#incompatible-certificates)
- [Mutual TLS (mTLS) authentication](#incompatible-certificates)
- [ESNI and ECH handshake encryption](#esni-and-ech)
- [Automatic HTTPS upgrades](#google-chrome-automatic-https-upgrades)

### Inspect on all ports

<Render
	file="gateway/inspect-on-all-ports"
	product="cloudflare-one"
	params={{
		turnOnProcedure:
			"you can turn on [protocol detection](/cloudflare-one/traffic-controls/network-policies/protocol-detection/) and configure Gateway to [inspect traffic on all ports](/cloudflare-one/traffic-controls/network-policies/protocol-detection/#inspect-on-all-ports)",
	}}
/>

### Incompatible certificates

Applications that use certificate pinning and mTLS authentication do not trust Cloudflare certificates. For example, most mobile applications use <GlossaryTooltip term="certificate pinning" link="/ssl/reference/certificate-pinning/">certificate pinning</GlossaryTooltip>. Cloudflare does not trust applications that use self-signed certificates instead of certificates signed by a public CA.

If you try to perform TLS decryption on an application with an incompatible certificate configuration, the application may return an SSL or trust error and/or fail to load. To resolve this issue, you can:

- Add a [Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/manual-deployment/#5-add-the-certificate-to-applications) to supported applications.
- Create a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) to exempt applications from inspection. The [Application selector](/cloudflare-one/traffic-controls/http-policies/#application) provides a list of trusted applications that are known to use embedded certificates. Note that if you create a Do Not Inspect policy for an application or website, you will lose the ability to log or block HTTP requests, apply DLP policies, and perform AV scanning.
- Configure a [Split Tunnel](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/route-traffic/split-tunnels/) in Include mode to ensure Gateway will only inspect traffic destined for your IPs or domains. This is useful for organizations that deploy Zero Trust on users' personal devices or otherwise expect personal applications to be used.

Alternatively, to allow HTTP filtering while accessing a site with an insecure certificate, set your [Untrusted certificate action](/cloudflare-one/traffic-controls/http-policies/#untrusted-certificates) to _Pass through_.

### Google Chrome automatic HTTPS upgrades

Google Chrome can automatically upgrade HTTP requests to HTTPS requests, even when you select a link that explicitly declares `http://`. When you use Gateway to proxy and filter your traffic, this upgrade can interrupt the connection between your Zero Trust users and Gateway.

You can turn off automatic HTTPS upgrades via a Gateway pass through policy, a Chrome browser flag, or a Chrome Enterprise policy.

<Tabs> <TabItem label="Pass through policy">

To disable automatic HTTPS upgrades for a URL across your Zero Trust organization, create a Gateway pass through policy.

1. Deploy a [custom root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/custom-certificate/).

2. Create an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to match the domain of the URL being automatically upgraded. For example:

   | Selector | Operator | Value         | Action |
   | -------- | -------- | ------------- | ------ |
   | URL      | in       | `example.com` | Allow  |

3. In **Untrusted certificate action**, choose _Pass through_.

4. Select **Create policy**.

The pass through policy will bypass insecure connection upgrades for any device connected to your Zero Trust organization. For more information, refer to [Untrusted certificates](/cloudflare-one/traffic-controls/http-policies/#untrusted-certificates).

</TabItem> <TabItem label="Chrome browser flag">

To disable automatic HTTPS upgrades on a per-browser basis, go to [Chrome flags](chrome://flags/#https-upgrades) and turn off **HTTPS Upgrades**.

</TabItem> <TabItem label="Chrome enterprise policy">

Chrome Enterprise users can turn off automatic HTTPS upgrades for all URLs with a [`HttpsUpgradesEnabled` management policy](https://chromeenterprise.google/policies/#HttpsUpgradesEnabled).

</TabItem> </Tabs>

### Mutual TLS (mTLS)

In mutual TLS (mTLS), both the client and server present certificates to verify each other's identity. When Gateway decrypts TLS traffic, it terminates the connection from the client and creates a new connection to the origin server. Because Gateway cannot forward the client's certificate to the origin, the mTLS handshake fails. To prevent connection failures, create a [Do Not Inspect policy](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) for this traffic.

### ESNI and ECH

Websites that adhere to [ESNI or Encrypted Client Hello (ECH) standards](https://blog.cloudflare.com/encrypted-client-hello/) encrypt the Server Name Indication (SNI) during the TLS handshake and are therefore incompatible with HTTP inspection. Gateway relies on the SNI to match an HTTP request to a policy — if the SNI is encrypted, Gateway cannot determine which policy to apply. If the ECH fails, browsers will retry the TLS handshake using the unencrypted SNI from the initial request. To avoid this behavior, you can disable ECH in your users' browsers.

You can still apply all [network policy filters](/cloudflare-one/traffic-controls/network-policies/#selectors) except for SNI and SNI Domain. To restrict ESNI and ECH traffic, an option is to filter out all port `80` and `443` traffic that does not include an SNI header.

## Post-quantum support

Gateway supports post-quantum cryptography using a hybrid key exchange with X25519 and MLKEM768 over TLS 1.3. Once the key exchange is complete, Gateway uses AES-128-GCM to encrypt traffic.

Refer to [Post-quantum cryptography](/ssl/post-quantum-cryptography/) to learn more.

## FIPS compliance

By default, TLS decryption can use both TLS version 1.2 and 1.3. However, some environments such as FedRAMP may require cipher suites and TLS versions compliant with FIPS 140-3. FIPS compliance currently requires TLS version 1.2.

### Enable FIPS compliance

<Render file="gateway/enable-tls-decryption" product="cloudflare-one" />

3. Select **Enable only cipher suites and TLS versions compliant with FIPS 140-3**.

### Limitations

When FIPS compliance is enabled, Gateway will only choose [FIPS-compliant cipher suites](#cipher-suites) when connecting to the origin. If the origin does not support FIPS-compliant ciphers, the request will fail.

FIPS-compliant traffic defaults to [HTTP/3](/cloudflare-one/traffic-controls/http-policies/http3/). To enforce HTTP policies for UDP traffic, you must turn on the [Gateway proxy for UDP](/cloudflare-one/traffic-controls/http-policies/http3/#turn-on-http3-inspection).

## FedRAMP compliance

When you use [Cloudflare Regional Services](/data-localization/regional-services/) in the United States and the Cloudflare One Client to on-ramp TLS traffic to Gateway, traffic will egress from a Cloudflare data center within Cloudflare's FedRAMP boundary. If a user's closest data center is non-FedRAMP compliant, their traffic will still egress from a FedRAMP compliant data center, maintaining FedRAMP compliance for the traffic.

```mermaid
flowchart LR
 %% Accessibility
 accTitle: How Gateway routes FedRAMP compliant traffic with Regional Services
 accDescr: Flowchart describing how the Cloudflare One Client with Gateway routes traffic to egress from a FedRAMP compliant data center when used with Regional Services in the United States.

 %% Flowchart
 subgraph s1["Non-FedRAMP data center"]
        n2["WARP TLS encryption terminated"]
  end
 subgraph s2["FedRAMP data center"]
        n3["Gateway TLS encryption (FIPS) terminated"]
  end
 subgraph s3["Private internal network"]
        n5["FedRAMP compliant cloudflared"]
        n6(["Private server"])
  end
    n1(["User near non-FedRAMP compliant data center"]) -- Gateway TLS connection wrapped with WARP TLS (MASQUE) --> n2
    n2 -- Gateway TLS connection --> n3
    n3 <-- FIPS tunnel --> n5
    n5 --> n6

    n5@{ shape: rect}
```

## Cipher suites

<GlossaryDefinition term="cipher suite" prepend="A cipher suite is " />

The following table lists the default cipher suites Gateway uses for TLS decryption.

| Name (OpenSSL)                | Name (IANA)                             | FIPS-compliant |
| ----------------------------- | --------------------------------------- | -------------- |
| ECDHE-ECDSA-AES128-GCM-SHA256 | TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 | ✅             |
| ECDHE-ECDSA-AES256-GCM-SHA384 | TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 | ✅             |
| ECDHE-RSA-AES128-GCM-SHA256   | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   | ✅             |
| ECDHE-RSA-AES256-GCM-SHA384   | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   | ✅             |
| ECDHE-RSA-AES128-SHA          | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256   | ❌             |
| ECDHE-RSA-AES256-SHA384       | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384   | ✅             |
| AES128-GCM-SHA256             | TLS_RSA_WITH_AES_128_GCM_SHA256         | ✅             |
| AES256-GCM-SHA384             | TLS_RSA_WITH_AES_256_GCM_SHA384         | ✅             |
| AES128-SHA                    | TLS_RSA_WITH_AES_128_CBC_SHA            | ❌             |
| AES256-SHA                    | TLS_RSA_WITH_AES_256_CBC_SHA            | ❌             |

For more information on cipher suites, refer to [Cipher suites](/ssl/edge-certificates/additional-options/cipher-suites/).

# Available context

import { Render, PackageManagers, TypeScriptExample } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Gateway passes `context` as the second argument to `GatewayMiddleware.handle(request, context, next)`. Context describes the original request when Gateway selects a [Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/). Modifying the `Request` does not update context.

Field names use camelCase. Optional fields are omitted when unavailable, and empty metadata lists are omitted. Availability depends on the connection method, destination, and authenticated session. Check optional values before using them.

## TypeScript types

Install [`@cloudflare/zt-types`](https://www.npmjs.com/package/@cloudflare/zt-types) version `2026.10.1` or later in your Worker project:

<PackageManagers type="add" pkg="@cloudflare/zt-types" dev />

Use `import type` for Gateway types, as shown in the [example](#example) and each [Worker template](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/). These imports are removed when TypeScript compiles and add no runtime dependency. JavaScript examples omit the type imports and annotations and do not need this package. The package exports the following Gateway types:

| Type                         | Description                                                                            |
| ---------------------------- | -------------------------------------------------------------------------------------- |
| `GatewayMiddlewareContext`   | Request metadata, identity getter, and device posture getter.                          |
| `GatewayMiddlewareNext`      | Continuation that sends a `Request` through Gateway and returns a `Promise<Response>`. |
| `GatewayIdentity`            | User identity returned by `identity()`.                                                |
| `GatewayIdentityGroup`       | A group in the user's identity.                                                        |
| `GatewayDevicePosture`       | Device posture returned by `devicePosture()`.                                          |
| `GatewayDevicePostureChecks` | Passed posture check identifiers.                                                      |

## Request and connection fields

| Field               | Type                          | Description                                                                                  |
| ------------------- | ----------------------------- | -------------------------------------------------------------------------------------------- |
| `srcIp`             | `string`                      | Source IP address as seen by Gateway. Use this field instead of a client-supplied IP header. |
| `srcPort`           | `number`                      | Source port.                                                                                 |
| `dstIp`             | `string`, optional            | Destination IP address, when available.                                                      |
| `dstPort`           | `number`, optional            | Destination port, when available.                                                            |
| `networkProtocol`   | `string`                      | Network transport protocol, such as `tcp`.                                                   |
| `sni`               | `string`, optional            | Server Name Indication from the TLS connection.                                              |
| `onrampType`        | `string`, optional            | Connection method used to reach Gateway.                                                     |
| `vnetId`            | `string`, optional            | Virtual network identifier.                                                                  |
| `proxyEndpoint`     | `string`, optional            | Proxy endpoint associated with the connection.                                               |
| `internalSrcIp`     | `string`, optional            | Internal source IP address, when available.                                                  |
| `accountId`         | `string`                      | Cloudflare account identifier.                                                               |
| `policyId`          | `string`                      | UUID of the Forward policy that selected this Worker.                                        |
| `host`              | `string`                      | Original request host.                                                                       |
| `uri`               | `string`                      | Normalized original request URL.                                                             |
| `embeddedProtocols` | `readonly string[]`, optional | Detected protocols embedded in HTTP traffic.                                                 |
| `domainCategories`  | `readonly number[]`, optional | Destination domain category IDs.                                                             |

`context.uri` includes the scheme and authority. Normalization removes default ports and fragments, sorts query parameters, and normalizes encoding. It can differ from `request.url`. Use `request.url` when operating on the request itself and `context.uri` when comparing normalized metadata.

Use standard `Request` APIs to access the method, headers, cookies, and body. Context does not include custom function results, DLP match details, or raw internal device records.

## Identity

Call `await context.identity()` only when your script needs identity associated with the Gateway session. Its return type is `Promise<GatewayIdentity | null>`.

The awaited call behaves as follows:

- Returns an identity object when the user is identified.
- Returns `null` when there is no identified user.
- Throws an error if Gateway cannot retrieve identity.

| Field            | Type                                        | Description                                                                                   |
| ---------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `email`          | `string`, optional                          | User email address.                                                                           |
| `name`           | `string`, optional                          | User display name.                                                                            |
| `groups`         | `readonly GatewayIdentityGroup[]`, optional | Group memberships. Each group can have optional `id`, `email`, and `name` fields.             |
| `samlAttributes` | `readonly string[]`, optional               | SAML attributes associated with the identity.                                                 |
| `oidcClaims`     | `readonly string[]`, optional               | OIDC claims associated with the identity. This is a list, rather than a decoded token object. |

Use Gateway-provided identity for user-aware logic. Client-supplied identity headers are not an authoritative substitute.

Check for a missing identity (`!identity`) to explicitly allow or deny anonymous traffic.

## Device posture

Call `await context.devicePosture()` to retrieve passed posture check identifiers:

```json
{
	"checks": {
		"passed": ["<POSTURE_CHECK_UUID>"]
	}
}
```

`checks.passed` is a read-only list of posture check UUIDs. An empty array means no checks passed. When no device is registered, the getter resolves to `{ checks: { passed: [] } }`. A Worker that requires a specific passed check must deny this request.

If Gateway cannot retrieve device posture, awaiting `devicePosture()` throws an error. Handle this separately from a session with no passed checks.

The getter does not return raw test results or a list of failed checks.

## Handle identity and posture errors

Gateway still invokes the Worker if it cannot retrieve identity or device posture. If either awaited call throws and your Worker does not catch the error, Gateway returns HTTP `500`.

Use `try/catch` to choose how your Worker handles these errors. The [example](#example) returns HTTP `503` for retrieval errors and HTTP `403` when identity or a required posture check is missing.

If your application deliberately allows traffic when Gateway cannot retrieve identity or posture, return `next(request)` from the `catch` block. This continues the request without requiring the missing result.

Only use this approach before calling `next()`. [Call `next()` at most once per invocation](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#how-it-works).

## Example

This script requires an identity email and a specific passed posture check before continuing. Anonymous traffic and devices without the required check receive a synthetic `403` response.

Identity or posture retrieval errors receive `503`. Replace `<POSTURE_CHECK_UUID>` with a check in your account.

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

export class GatewayMiddleware extends WorkerEntrypoint {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		try {
			const identity = await context.identity();
			if (!identity?.email) {
				return new Response(null, { status: 403 });
			}
			const passed = (await context.devicePosture())?.checks.passed ?? [];
			if (!passed.includes("<POSTURE_CHECK_UUID>")) {
				return new Response(null, { status: 403 });
			}
		} catch {
			return new Response(null, { status: 503 });
		}
		return next(request);
	}
}
```

</TypeScriptExample>

When the requirement can be expressed using built-in [identity selectors](/cloudflare-one/traffic-controls/identity-selectors/) or [device posture selectors](/cloudflare-one/traffic-controls/http-policies/#device-posture), configure it in your Gateway filtering policy. Use Worker logic for requirements that need custom code.

## Header representation

Gateway combines repeated `Cookie` headers with semicolons and other repeated headers with commas. `Set-Cookie` values retain separate entries. Use `response.headers.getSetCookie()` to read the individual response cookies. When modifying headers, copy them with `new Headers(response.headers)` to preserve their representation.

# Forward traffic

import {
	APIRequest,
	DashButton,
	PackageManagers,
	Render,
	Steps,
	TypeScriptExample,
	WranglerConfig,
} from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

The **Forward** action invokes a Worker on HTTP traffic that Gateway permits. Use it to implement request and response transformations, application-specific controls, or background tasks without operating a separate HTTP proxy.

Write your own Worker or adapt a [Worker template](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/). The [gateway-workers skill](https://github.com/cloudflare/skills/blob/main/skills/gateway-workers/SKILL.md) provides guidance for coding agents working with this integration.

## How it works

Gateway evaluates Forward policies after HTTP request policies and before body policies and inspection. It invokes the Worker selected by the first matching Forward policy, in [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence).

An HTTP request policy with a Block action prevents Worker invocation. Traffic that matches no Forward policy continues without invoking a Worker.

Gateway inspects the original body before delivering it to the Worker. A Block decision from body policies or inspection can interrupt an already-running Worker.

Worker invocation does not confirm that body inspection succeeded.

```mermaid
flowchart LR
    accTitle: Gateway Forward request and response flow
    accDescr: Gateway evaluates HTTP request policies, then Forward policies, and invokes the first matching Worker before body policies and inspection. It inspects the original body before delivering it to the Worker. A Block decision from body policies or inspection can interrupt the Worker. Gateway inspects the final response before returning it to the user.
    A["Original request"] --> B["Gateway HTTP request policies"]
    B -->|Permitted, Forward matches| C["Invoke Worker"]
    B -->|Blocked| X["Block response"]
    B -->|Original body, Forward matches| I["Gateway body policies and inspection"]
    I -->|Inspected body stream| C
    I -->|Blocked| X
    I -.->|Body Block interrupts Worker| C
    C -->|next: at most once| D["Original destination"]
    D --> C
    C -->|Final or synthetic response| E["Gateway response inspection"]
    E --> F["User"]
```

Gateway calls the named `GatewayMiddleware` entrypoint's `handle(request, context, next)` method:

- `request` is the original HTTP `Request`, including its body stream.
- `context` contains [Gateway metadata](/cloudflare-one/traffic-controls/integrate-workers/available-context/). It describes the original request and does not change when your Worker modifies the request.
- `next(request)` sends a request to its original destination through Gateway and returns the upstream `Response`. Gateway establishes the upstream connection when the Worker calls `next()`.

The Worker can modify the method, headers, cookies, or body before calling `next()`. It can modify the upstream response or return a synthetic response without calling `next()`. Call `next()` at most once per invocation. You cannot use it to change the destination URL or host, retry an upstream request, or send requests to several destinations.

Gateway inspects the **original request body before delivering it to the Worker** and the **final response after the Worker**, including synthetic responses. Gateway does not inspect modified request bytes a second time before sending them upstream.

For example, a Worker cannot use redaction to bypass request-side Data Loss Prevention (DLP) inspection. Data the Worker adds to a permitted request does not receive another request-side DLP scan.

A normal Workers `fetch()` is an independent subrequest. Use `next()` to continue the user's request with Gateway's routing and egress configuration. Do not assume a `fetch()` call uses the user's Gateway policies or egress IP.

## Prerequisites

- Have a [Cloudflare account](/fundamentals/account/create-account/) with Workers set up. If you have not used Workers before, follow the [Workers getting started guide](/workers/get-started/guide/). Use the same account for Workers and Gateway.
- Set up [Gateway HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/) and route your test device's traffic through Gateway.
- Enable [TLS decryption](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) and install a trusted [root certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) for HTTPS traffic.
- Use a deployed Worker in the same Cloudflare account as your Gateway policy. Configure a [Workers subdomain](/workers/configuration/routing/workers-dev/) for the account. The Worker does not need a public route.

## Set up Forward

<Steps>

1. Create a Worker project.

   <PackageManagers type="exec" pkg="wrangler" args="init gateway-worker" />

   Use the following configuration. Disabling `workers_dev` and `preview_urls` keeps this Worker from being exposed through those public URLs.

   <WranglerConfig>

   ```toml
   name = "gateway-worker"
   main = "src/index.ts"
   compatibility_date = "$today"
   workers_dev = false
   preview_urls = false
   ```

   </WranglerConfig>

2. Replace `src/index.ts` with the required named Worker entrypoint.

   Gateway invokes `GatewayMiddleware.handle()` through [Workers RPC](/workers/runtime-apis/rpc/). Keep the exported class name `GatewayMiddleware` exactly as shown.

   Install [`@cloudflare/zt-types`](https://www.npmjs.com/package/@cloudflare/zt-types) version `2026.10.1` or later as a development dependency in this Worker project. It supplies the [Gateway context and continuation types](/cloudflare-one/traffic-controls/integrate-workers/available-context/#typescript-types):

   <PackageManagers type="add" pkg="@cloudflare/zt-types" dev />

   Keep the type-only imports in TypeScript examples. They are removed when TypeScript compiles. Import the runtime `WorkerEntrypoint` class from `cloudflare:workers`. The JavaScript version omits the type imports and annotations and does not need this package.

   This example passes the request through and adds a response header so you can verify the Worker ran. The [context reference](/cloudflare-one/traffic-controls/integrate-workers/available-context/) describes the fields you can use in other scripts.

   <TypeScriptExample filename="src/index.ts">

   ```ts
   import { WorkerEntrypoint } from "cloudflare:workers";
   import type {
     GatewayMiddlewareContext,
     GatewayMiddlewareNext,
   } from "@cloudflare/zt-types";

   export class GatewayMiddleware extends WorkerEntrypoint {
     async handle(
       request: Request,
       context: GatewayMiddlewareContext,
       next: GatewayMiddlewareNext,
     ): Promise<Response> {
       const response = await next(request);
       const headers = new Headers(response.headers);
       headers.set("x-gateway-worker", "gateway-worker");
       return new Response(response.body, {
         status: response.status,
         statusText: response.statusText,
         headers,
       });
     }
   }
   ```

   </TypeScriptExample>

3. Deploy the Worker to the account that contains your Gateway policies.

   <PackageManagers type="exec" pkg="wrangler" args="deploy" />

4. In Cloudflare One, go to **Traffic controls** > **Firewall policies** > **HTTP**.

   <DashButton url="/?to=/:account/gateway/policies" buttonName="Firewall policies" zeroTrust />

5. Select **Add a policy**, name the policy, and scope it to one test host and a test user or device group. Use request metadata selectors such as **Host**, **URL**, and **HTTP Method**. You can also add identity and device posture conditions.

6. Select **Forward** as the action, select your deployed Worker, and select **Create policy**. Gateway invokes its `GatewayMiddleware` entrypoint, rather than a default `fetch()` handler.

7. From the test device, request the selected host and check for the `x-gateway-worker: gateway-worker` response header. A matching filtering Block policy still takes precedence over Forward. Refer to [Troubleshooting](#troubleshooting) if the header is absent.

</Steps>

### Configure with the API

Use the [Create a Zero Trust Gateway rule](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/create/) endpoint with `action: "forward"` and the Worker's ID in `rule_settings.forward.worker_id`. The Worker ID is a 32-character lowercase hexadecimal identifier. Use its ID, rather than its name or a `workers.dev` URL.

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Forward example.com to a Worker",
		enabled: true,
		precedence: 1000,
		action: "forward",
		filters: ["http"],
		traffic: 'http.request.host == "example.com"',
		rule_settings: {
			forward: { worker_id: "<WORKER_ID>" },
		},
	}}
/>

For deployed Worker identifiers, refer to [List Workers](/api/resources/workers/subresources/beta/subresources/workers/methods/list/).

## Known limitations

Gateway resolves the selected Worker only when a Forward policy is saved. Deleting the Worker does not remove or disable the policy. Matching requests then fail with HTTP `500` and do not reach the origin. Before deleting a Worker, update all Forward policies that use it to select another Worker, or delete those policies.

Gateway drops HTTP traffic sent from the Cloudflare compute platform through [Workers VPC](/workers-vpc/) if it matches a Forward policy. Gateway does not invoke the selected Worker. Exclude this traffic from your Forward policies.

WebSocket traffic connects directly to the original upstream service and bypasses the selected Worker.
The Worker does not receive the WebSocket upgrade request or messages, so its logic does not apply to those connections.

Proxy HTTP `CONNECT` tunnels connect directly to the original upstream service and bypass the selected Worker.

| Area            | Behavior                                                                                                                                                                                            |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Policy matching | Forward uses request metadata, identity, and device posture conditions. Body inspection results, DLP matches, and response selectors cannot select a Worker.                                        |
| Decryption      | HTTPS traffic must be decrypted. Gateway does not invoke a Worker for traffic matching Do Not Inspect.                                                                                              |
| Protocols       | Regular HTTP requests are supported. Gateway does not invoke a Worker for CONNECT tunnels, WebSocket connections, protocol upgrades, or HTTP trailers.                                              |
| Destination     | `next()` preserves the original destination URL and host.                                                                                                                                           |
| Continuation    | Call `next()` zero or one time. Repeated or concurrent calls are unsupported.                                                                                                                       |
| Response        | Return a final status from `200` to `599`. Return no body for HEAD requests or statuses `204`, `205`, and `304`.                                                                                    |
| Streaming       | Request and response bodies stream through Gateway with backpressure and cancellation. Avoid buffering entire bodies.                                                                               |
| Failure         | Invocation errors, Worker errors, or failures in communication between Gateway and the Worker fail closed. Gateway does not bypass the Worker and forward the original request.                     |
| Limits          | Existing [Gateway limits](/cloudflare-one/account-limits/) and [Workers limits](/workers/platform/limits/) apply. Workers usage is subject to normal [Workers pricing](/workers/platform/pricing/). |

Use `this.ctx.waitUntil()` for background tasks that can run after returning the response. These tasks remain subject to [Workers execution limits](/workers/platform/limits/#duration) and are not a durable job queue. Use [Queues](/queues/) or [Workflows](/workflows/) for work that requires retries or more time.

## Troubleshooting

If your Worker does not run, confirm that the device uses Gateway HTTP filtering, HTTPS is decrypted, and the request is permitted by filtering policies. Check whether a Do Not Inspect policy or an earlier Forward policy matches the request.

If requests fail after enabling Forward, confirm that the Worker exports `GatewayMiddleware`, its `handle()` method returns a `Response`, and it calls `next()` at most once. Check for exceptions, unsupported status codes, and bodies on responses that must have no body. Use [Workers Logs](/workers/observability/logs/workers-logs/) to inspect invocation errors. Disable the test Forward policy while correcting a failing script.

For identity and device posture errors, refer to [Handle identity and posture errors](/cloudflare-one/traffic-controls/integrate-workers/available-context/#handle-identity-and-posture-errors).

If matching requests return HTTP `500` after a Worker was deleted, update the Forward policy to select an existing Worker, or delete the policy. Saving a policy that still references the deleted Worker fails with API error `2294`.

Start with a [Worker template](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/) once the pass-through example works.

# Integrate Workers

import { Render } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Use Cloudflare Workers to add your own logic to HTTP traffic that passes through Gateway. A Gateway HTTP policy with the **Forward** action sends matching, permitted traffic to a Worker in your account. The Worker can modify a request, transform a response, return its own response, or start background work such as a notification or an R2 write. You can write your own Worker for your use case or adapt a [Worker template](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/).

Gateway handles the proxy connection, TLS decryption, and upstream routing. Your Worker receives standard `Request` and `Response` objects and [Gateway context](/cloudflare-one/traffic-controls/integrate-workers/available-context/) about the connection, policy, and user.

Gateway evaluates HTTP request policies before Forward policies. Body policies and inspection run after Worker invocation and can interrupt the Worker if they block the request.

A Forward policy does not override a Block policy or replace Gateway inspection. Refer to [How Forward works](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#how-it-works) for the request and response inspection order.

WebSocket traffic connects directly to the original upstream service and bypasses the Worker. Refer to [Known limitations](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#known-limitations) for protocol restrictions and other constraints.

- [Forward traffic](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/): Deploy a Worker and select it in a Gateway HTTP policy.
- [Available context](/cloudflare-one/traffic-controls/integrate-workers/available-context/): Read connection metadata, user identity, and device posture.
- [Worker templates](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/): Start with a small script for notifications, capture, or content transformation.

The [gateway-workers skill](https://github.com/cloudflare/skills/blob/main/skills/gateway-workers/SKILL.md) provides guidance for coding agents building or reviewing Workers used in Forward policies.

# Display a coaching page

import { Render, TypeScriptExample } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Display an explanation when a user visits a selected page. The Worker returns a synthetic response without calling `next()`, so Gateway does not send the request to the original destination.

This template displays a notice with a link to an approved alternative. It does not implement a temporary exception, approval workflow, or an acknowledgement that unlocks the original site.

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

Use this script as `src/index.ts` in the [Worker project](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward). Replace the approved application URL and explanation with your organization's guidance. It needs no bindings.

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

export class GatewayMiddleware extends WorkerEntrypoint {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		if (
			request.method !== "GET" ||
			!request.headers.get("accept")?.includes("text/html")
		) {
			return next(request);
		}
		return new Response(
			`<!doctype html>
      <html lang="en"><meta charset="utf-8">
      <title>Use the approved application</title>
      <h1>Keep work data in the approved application</h1>
      <p>Use your company workspace when sharing work files.</p>
      <p><a href="https://approved.example.com/">Open your company workspace</a></p>
      </html>`,
			{
				status: 200,
				headers: {
					"content-type": "text/html; charset=utf-8",
					"cache-control": "no-store",
					"content-security-policy":
						"default-src 'none'; base-uri 'none'; frame-ancestors 'none'",
					"x-content-type-options": "nosniff",
				},
			},
		);
	}
}
```

</TypeScriptExample>

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward) for one test host, a selected page path, **HTTP Method** `GET`, and your test user group. Keep the approved alternative outside this policy's scope.

Visit the selected page in a browser and confirm that the coaching page appears. Check the test origin's logs to confirm that no upstream request was made for this visit. A request without an HTML `Accept` header passes through normally.

The method and `Accept` checks select when to show a page. They are not access controls because clients can change request headers.

Use Gateway Block policies when you need to prevent access. An HTTP request policy with a Block action prevents Worker invocation, while a Block decision from body policies or inspection can interrupt the Worker.

Synthetic responses still undergo Gateway response inspection.

# Capture a downloaded file

import { Render, TypeScriptExample, WranglerConfig } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Save a copy of a downloaded attachment to R2 while returning the original response to the user. This script captures complete files up to 25 MiB and skips larger files. It detects attachments using `Content-Disposition`.

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

[Create a private R2 bucket](/r2/buckets/create-buckets/) named `gateway-downloads`. Add this binding to the [Worker configuration](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward):

<WranglerConfig>

```toml
[[r2_buckets]]
binding = "CAPTURES"
bucket_name = "gateway-downloads"
```

</WranglerConfig>

Use this script as `src/index.ts`:

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

interface Env {
	CAPTURES: R2Bucket;
}
const LIMIT = 25 * 1024 * 1024;

async function readBounded(
	body: ReadableStream<Uint8Array>,
): Promise<ArrayBuffer | null> {
	const reader = body.getReader();
	const chunks: Uint8Array[] = [];
	let size = 0;
	try {
		while (true) {
			const { done, value } = await reader.read();
			if (done) break;
			size += value.byteLength;
			if (size > LIMIT) {
				void reader.cancel().catch(() => {});
				return null;
			}
			chunks.push(value);
		}
	} finally {
		reader.releaseLock();
	}
	const bytes = new Uint8Array(size);
	let offset = 0;
	for (const chunk of chunks) {
		bytes.set(chunk, offset);
		offset += chunk.byteLength;
	}
	return bytes.buffer;
}

export class GatewayMiddleware extends WorkerEntrypoint<Env> {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		const response = await next(request);
		const disposition = response.headers.get("content-disposition") ?? "";
		if (
			request.method === "GET" &&
			response.status === 200 &&
			!response.headers.has("content-encoding") &&
			/^attachment\b/i.test(disposition) &&
			response.body
		) {
			const copy = response.clone();
			this.ctx.waitUntil(
				this.capture(copy).catch(() =>
					console.error("Download capture failed"),
				),
			);
		}
		return response;
	}

	async capture(response: Response): Promise<void> {
		const bytes = await readBounded(response.body!);
		if (bytes === null) {
			console.log("Download capture skipped: file exceeds limit");
			return;
		}
		await this.env.CAPTURES.put(crypto.randomUUID(), bytes, {
			httpMetadata: { contentType: "application/octet-stream" },
		});
	}
}
```

</TypeScriptExample>

The reader counts actual streamed bytes instead of trusting `Content-Length`. It supplies R2 with an `ArrayBuffer` whose size is known, rather than an arbitrary stream. Random object keys avoid using untrusted filenames as storage paths.

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward) for a controlled download host and path, with **HTTP Method** `GET` and a test user group.

Download a small attachment through Gateway. Confirm that the browser download and the R2 object have identical contents. Download a file larger than 25 MiB and confirm that the user still receives it while the Worker logs that capture was skipped.

Cloning can buffer data when the two consumers run at different speeds. Capture also allocates memory for the bounded file. Keep the policy narrow, avoid high-volume parallel downloads, and test against [Workers memory limits](/workers/platform/limits/#memory). A slow transfer or failed background write can leave no captured object. Use a dedicated capture pipeline when retention must be guaranteed.

This example captures complete, uncompressed attachment responses with status `200`. It skips range responses and bodies with `Content-Encoding`. The copy is taken before Gateway's final response inspection, so a file later blocked by Gateway can still be stored in R2. Keep the bucket private, apply [retention rules](/r2/buckets/object-lifecycles/), and treat captured objects as untrusted files.

# Worker templates

import { Render } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

These templates are starting points for Workers invoked by Forward policies. You can adapt them or write your own Worker using the `GatewayMiddleware.handle(request, context, next)` entrypoint. Each script implements one task.

| Template                                                                                                              | What it does                                                                         |
| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| [Send a Slack notification](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/slack-notification/)  | Send a background notification when a selected request completes.                    |
| [Capture a screenshot](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/screenshot/)               | Capture a public page in an independent browser and save the image to R2.            |
| [Capture a downloaded file](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/download-capture/)    | Save a bounded copy of an attachment to R2 while returning the download to the user. |
| [Watermark HTML pages](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/watermark/)                | Add a visible user label to an HTML response.                                        |
| [Store headers and bodies as JSON](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/json-capture/) | Archive selected headers and bounded JSON request and response bodies in R2.         |
| [Display a coaching page](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/coaching-page/)         | Return an explanatory page without contacting the original destination.              |
| [Redact a JSON field](/cloudflare-one/traffic-controls/integrate-workers/worker-templates/json-redaction/)            | Replace a specific field in an application request before sending it upstream.       |

## Use a template

Follow [Set up Forward](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward), replace the pass-through script with a template, and configure its bindings. Deploy the Worker before selecting it in a Forward policy. Each template lists a test and the traffic scope it expects.

The [gateway-workers skill](https://github.com/cloudflare/skills/blob/main/skills/gateway-workers/SKILL.md) provides guidance for coding agents on the named entrypoint, Gateway context, and validation.

Every TypeScript template includes `import type` declarations for `GatewayMiddlewareContext` and `GatewayMiddlewareNext` from [`@cloudflare/zt-types`](https://www.npmjs.com/package/@cloudflare/zt-types). Install this package as a development dependency in your Worker project before using a template. Each template includes the installation command.

The package supplies [Gateway types](/cloudflare-one/traffic-controls/integrate-workers/available-context/#typescript-types). Continue to import the runtime `WorkerEntrypoint` class from `cloudflare:workers`. The `import type` declarations are removed when TypeScript compiles. The JavaScript version omits them and the type annotations, so it does not need `@cloudflare/zt-types`.

Begin with a test host and test user group. Page loads produce multiple HTTP requests, so scope notifications and screenshots to a specific method and path, rather than every request to a domain. Keep capture buckets private and configure [R2 lifecycle rules](/r2/buckets/object-lifecycles/) for retention. Capture only data your organization permits you to store.

Background examples use [`this.ctx.waitUntil()`](/workers/runtime-apis/context/#waituntil). They return the user's response without awaiting the background operation. A background failure is logged and does not change the returned response. Background work has [limited execution time](/workers/platform/limits/#duration). Use Queues or Workflows when you need durable processing.

Body capture uses cloned streams. A slow capture consumer can cause a clone to buffer data while the other consumer advances. The examples limit captured bytes, but cloning is not a guarantee of constant memory use or zero impact on traffic. Keep capture policies narrow and test representative body sizes and transfer speeds.

# Store headers and bodies as JSON

import { Render, TypeScriptExample, WranglerConfig } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Store a JSON record of an application request and response in R2. This template records selected headers and JSON body text up to 256 KiB per body, without changing the request or response.

Use a controlled test endpoint whose bodies are approved for capture. The script omits query parameters, cookies, and authorization headers, but bodies and URL paths can still contain sensitive data. Keep the bucket private and configure [retention rules](/r2/buckets/object-lifecycles/).

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

[Create an R2 bucket](/r2/buckets/create-buckets/) named `gateway-json-captures`. Add this binding to the [Worker configuration](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward):

<WranglerConfig>

```toml
[[r2_buckets]]
binding = "CAPTURES"
bucket_name = "gateway-json-captures"
```

</WranglerConfig>

Use this script as `src/index.ts`:

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

interface Env {
	CAPTURES: R2Bucket;
}
const LIMIT = 256 * 1024;

function headersToKeep(headers: Headers): Record<string, string> {
	return Object.fromEntries(
		["content-type", "accept"].flatMap((name) => {
			const value = headers.get(name);
			return value === null ? [] : [[name, value]];
		}),
	);
}

async function bodyToKeep(
	message: Request | Response,
): Promise<{ state: string; text?: string }> {
	if (!message.body) return { state: "empty" };
	const reader = message.body.getReader();
	const type = message.headers.get("content-type") ?? "";
	if (
		message.headers.has("content-encoding") ||
		!/^application\/(?:[\w.-]+\+)?json\b/i.test(type)
	) {
		void reader.cancel().catch(() => {});
		reader.releaseLock();
		return { state: "skipped-content-type-or-encoding" };
	}
	const decoder = new TextDecoder();
	let size = 0;
	let text = "";
	try {
		while (true) {
			const { done, value } = await reader.read();
			if (done) return { state: "captured", text: text + decoder.decode() };
			size += value.byteLength;
			if (size > LIMIT) {
				void reader.cancel().catch(() => {});
				return { state: "skipped-size-limit" };
			}
			text += decoder.decode(value, { stream: true });
		}
	} catch {
		return { state: "capture-failed" };
	} finally {
		reader.releaseLock();
	}
}

export class GatewayMiddleware extends WorkerEntrypoint<Env> {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		const upload = bodyToKeep(request.clone());
		const response = await next(request);
		const download = bodyToKeep(response.clone());
		const url = new URL(request.url);
		this.ctx.waitUntil(
			(async () => {
				const record = {
					timestamp: new Date().toISOString(),
					policyId: context.policyId,
					request: {
						method: request.method,
						url: url.origin + url.pathname,
						headers: headersToKeep(request.headers),
						body: await upload,
					},
					response: {
						status: response.status,
						headers: headersToKeep(response.headers),
						body: await download,
					},
				};
				await this.env.CAPTURES.put(
					`${crypto.randomUUID()}.json`,
					JSON.stringify(record),
					{
						httpMetadata: { contentType: "application/json" },
					},
				);
			})().catch(() => console.error("JSON capture failed")),
		);
		return response;
	}
}
```

</TypeScriptExample>

The Worker starts reading a cloned request before `next()` consumes the original request. It clones the upstream response for capture and returns the original response. Each reader counts streamed bytes and cancels its capture branch when the limit is exceeded. The record reports skipped or failed captures instead of storing a partial body as complete.

Body values are UTF-8 text inside the archive's JSON structure. Other content types and bodies with `Content-Encoding` are skipped. Configure the test endpoint to exchange uncompressed JSON. To add headers, extend the allowlist deliberately rather than copying every header.

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward) for a specific test API host and path, scoped to your test user group.

Send a small JSON request and verify that the application receives the original body and the client receives the original response. Inspect the R2 record for both bodies. Test a body larger than 256 KiB and confirm that its record says `skipped-size-limit`. Check that a test cookie, authorization header, and query parameter are absent from the archive.

Clone consumers can buffer data at different speeds, and background capture can time out. Keep the policy narrow and test transfer speeds as well as body sizes. This script records the upstream response before Gateway's final response inspection. A later Gateway Block decision does not remove data already captured in R2. Refer to [Forward inspection order](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#how-it-works).

# Redact a JSON field

import { Render, TypeScriptExample } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Replace the top-level `customerId` field in a JSON request with `[REDACTED]` before sending it to an application. Use this template for a specific endpoint with a known schema and uncompressed UTF-8 JSON bodies.

This is field-based transformation. It does not detect sensitive data throughout arbitrary text or files, and it does not use Data Loss Prevention (DLP) match results.

Gateway inspects the original request body before delivering it to the Worker. A DLP Block decision can interrupt the Worker, so this script cannot use redaction to bypass request-side DLP inspection.

Gateway does not scan the modified request again before forwarding it.

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

Use this script as `src/index.ts` in the [Worker project](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward). It needs no bindings.

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

const LIMIT = 256 * 1024;

export class GatewayMiddleware extends WorkerEntrypoint {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		if (request.method !== "POST") return next(request);
		const type = request.headers.get("content-type") ?? "";
		if (
			!/^application\/json\s*(?:;|$)/i.test(type) ||
			request.headers.has("content-encoding")
		) {
			return new Response("Expected uncompressed JSON", { status: 415 });
		}
		if (!request.body)
			return new Response("Expected a JSON object", { status: 400 });

		const reader = request.body.getReader();
		const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false });
		let size = 0;
		let text = "";
		try {
			while (true) {
				const { done, value } = await reader.read();
				if (done) break;
				size += value.byteLength;
				if (size > LIMIT) {
					void reader.cancel().catch(() => {});
					return new Response("Request body too large", { status: 413 });
				}
				text += decoder.decode(value, { stream: true });
			}
			text += decoder.decode();
		} catch {
			void reader.cancel().catch(() => {});
			return new Response("Invalid request body", { status: 400 });
		} finally {
			reader.releaseLock();
		}

		let object: Record<string, unknown>;
		try {
			object = JSON.parse(text);
			if (!object || typeof object !== "object" || Array.isArray(object))
				throw new Error();
		} catch {
			return new Response("Expected a JSON object", { status: 400 });
		}
		if (Object.hasOwn(object, "customerId")) object.customerId = "[REDACTED]";
		const headers = new Headers(request.headers);
		for (const name of [
			"content-length",
			"content-md5",
			"digest",
			"content-digest",
			"repr-digest",
		]) {
			headers.delete(name);
		}
		return next(
			new Request(request, { headers, body: JSON.stringify(object) }),
		);
	}
}
```

</TypeScriptExample>

The reader counts bytes while streaming and rejects bodies larger than 256 KiB. Invalid JSON, compressed bodies, and unsupported content types receive an explicit error. The script reconstructs the consumed body and preserves the original request URL.

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward) for a controlled API host and path, with **HTTP Method** `POST` and your test user group.

Send this JSON body through Gateway:

```json
{
	"customerId": "test-customer",
	"message": "Test request"
}
```

Confirm at the origin that `customerId` is `[REDACTED]` and `message` is unchanged. Verify that an invalid JSON body returns `400` and a body over the size limit returns `413`, with neither request reaching the origin.

The transformation changes JSON serialization and can invalidate signed payloads or application checksums. Use it only with an endpoint that accepts rewritten JSON. It replaces one top-level field and does not redact nested fields, query parameters, or other headers.

# Capture a screenshot

import { Render, TypeScriptExample, WranglerConfig } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Use [Browser Run](/browser-run/) to capture a screenshot after a selected request. Save the image in R2 while returning the original response to the user.

The browser makes an independent visit to a fixed public URL. It does not inherit the user's cookies, authorization, Gateway session, or egress configuration. The result is a screenshot of the public page, rather than an exact capture of what the user saw. Use this template for a public page you control.

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

[Create a private R2 bucket](/r2/buckets/create-buckets/) named `gateway-screenshots`, then add browser and R2 bindings to the [Worker configuration](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward):

<WranglerConfig>

```toml
[browser]
binding = "BROWSER"

[[r2_buckets]]
binding = "CAPTURES"
bucket_name = "gateway-screenshots"
```

</WranglerConfig>

The browser binding's `quickAction()` method requires a compatibility date of `2026-03-24` or later. Refer to [Browser Run configuration](/browser-run/reference/wrangler/).

Replace `https://example.com/` with a public test page you control. The local browser interface includes only the Quick Action used here.

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

const PAGE = "https://example.com/";

interface Env {
	CAPTURES: R2Bucket;
	BROWSER: {
		quickAction(
			action: "screenshot",
			options: {
				url: string;
				viewport: { width: number; height: number };
				gotoOptions: { timeout: number };
			},
		): Promise<Response>;
	};
}

export class GatewayMiddleware extends WorkerEntrypoint<Env> {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		const response = await next(request);
		const url = new URL(request.url);
		if (
			request.method === "GET" &&
			url.origin + url.pathname === PAGE &&
			response.ok
		) {
			this.ctx.waitUntil(
				this.capture().catch(() => console.error("Screenshot capture failed")),
			);
		}
		return response;
	}

	async capture(): Promise<void> {
		const image = await this.env.BROWSER.quickAction("screenshot", {
			url: PAGE,
			viewport: { width: 1280, height: 720 },
			gotoOptions: { timeout: 10_000 },
		});
		if (!image.ok) {
			await image.body?.cancel();
			throw new Error("Screenshot request failed");
		}
		await this.env.CAPTURES.put(
			`${crypto.randomUUID()}.png`,
			await image.arrayBuffer(),
			{
				httpMetadata: { contentType: "image/png" },
			},
		);
	}
}
```

</TypeScriptExample>

The script uses a fixed browser destination and does not copy query parameters or session credentials from the user's request. The viewport limits the image to a regular viewport screenshot.

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward) for the test page's **Host**, **URL Path**, and **HTTP Method** `GET`, scoped to your test user group.

Visit the page through Gateway. Confirm that the page loads normally and a PNG object appears in R2. Open the object to check the screenshot. Browser Run Quick Actions require a remote browser binding. They cannot be validated entirely with local emulation.

Screenshots consume [Browser Run usage](/browser-run/pricing/). Scope or sample captures to control volume. Browser queueing, navigation, and the R2 write must fit within [background execution limits](/workers/platform/limits/#duration). Use Queues or Workflows for reliable captures of slow pages.

# Send a Slack notification

import { Render, PackageManagers, TypeScriptExample } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Send a Slack notification when a request to a selected application endpoint completes. The Worker returns the upstream response and sends the notification in the background.

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

Create a [Slack incoming webhook](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/) for your chosen channel. Store its URL as a [Worker secret](/workers/configuration/secrets/) named `SLACK_WEBHOOK_URL`:

<PackageManagers
	type="exec"
	pkg="wrangler"
	args="secret put SLACK_WEBHOOK_URL"
/>

Use this script as `src/index.ts`:

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

interface Env {
	SLACK_WEBHOOK_URL: string;
}

export class GatewayMiddleware extends WorkerEntrypoint<Env> {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		const response = await next(request);
		this.ctx.waitUntil(
			this.notify(
				`${request.method} ${context.host}: HTTP ${response.status}`,
			).catch(() => console.error("Slack notification failed")),
		);
		return response;
	}

	async notify(text: string): Promise<void> {
		const result = await fetch(this.env.SLACK_WEBHOOK_URL, {
			method: "POST",
			headers: { "content-type": "application/json" },
			body: JSON.stringify({
				blocks: [{ type: "section", text: { type: "plain_text", text } }],
			}),
			signal: AbortSignal.timeout(10_000),
		});
		await result.body?.cancel();
		if (!result.ok) throw new Error("Webhook rejected notification");
	}
}
```

</TypeScriptExample>

The message uses Slack's plain text format to prevent request text from creating mentions. It includes the method, host, and response status. It does not send cookies, authorization headers, bodies, or URL query parameters.

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward). Match a test user group and a specific application event, such as **Host** is `app.example.com`, **URL Path** is `/signup`, and **HTTP Method** is `POST`.

Trigger one test request and confirm that its response is unchanged and one message appears in Slack. A filtering Block policy prevents both the upstream request and this notification.

This script sends a notification for every invocation, including application error responses. To notify only on success, guard `waitUntil()` with `if (response.ok)`. For higher-volume policies, add sampling or a queued notification pipeline and account for [Slack rate limits](https://docs.slack.dev/apis/web-api/rate-limits/). A broad host policy can generate a message for every page asset.

# Watermark HTML pages

import { Render, TypeScriptExample } from "~/components";

<Render file="gateway/forward-availability" product="cloudflare-one" />

Add a visible user label to HTML pages from an application you control. The Worker retrieves [Gateway identity](/cloudflare-one/traffic-controls/integrate-workers/available-context/#identity) and streams the response through [HTMLRewriter](/workers/runtime-apis/html-rewriter/).

## Configure the Worker

<Render file="gateway/worker-types" product="cloudflare-one" />

Use this script as `src/index.ts` in the [Worker project](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward). It needs no bindings.

<TypeScriptExample filename="src/index.ts">

```ts
import { WorkerEntrypoint } from "cloudflare:workers";
import type {
	GatewayMiddlewareContext,
	GatewayMiddlewareNext,
} from "@cloudflare/zt-types";

function escapeHtml(text: string): string {
	return text
		.replaceAll("&", "&amp;")
		.replaceAll("<", "&lt;")
		.replaceAll(">", "&gt;")
		.replaceAll('"', "&quot;")
		.replaceAll("'", "&#39;");
}

export class GatewayMiddleware extends WorkerEntrypoint {
	async handle(
		request: Request,
		context: GatewayMiddlewareContext,
		next: GatewayMiddlewareNext,
	): Promise<Response> {
		const response = await next(request);
		const contentType = response.headers.get("content-type") ?? "";
		if (
			request.method !== "GET" ||
			response.status !== 200 ||
			response.headers.has("content-encoding") ||
			!response.body ||
			!/^text\/html\b/i.test(contentType)
		) {
			return response;
		}
		const identity = await context.identity().catch(() => null);
		const label = escapeHtml(identity?.email ?? "Unidentified user");
		const headers = new Headers(response.headers);
		for (const name of [
			"content-length",
			"etag",
			"content-md5",
			"digest",
			"content-digest",
			"repr-digest",
		]) {
			headers.delete(name);
		}
		headers.set("cache-control", "private, no-store");
		const rewritten = new HTMLRewriter()
			.on("body", {
				element(element) {
					element.prepend(
						`<aside style="position:fixed;bottom:1rem;right:1rem;z-index:2147483647;pointer-events:none">Viewed by ${label}</aside>`,
						{ html: true },
					);
				},
			})
			.transform(
				new Response(response.body, { status: response.status, headers }),
			);
		return rewritten;
	}
}
```

</TypeScriptExample>

The script escapes the user label before inserting it into HTML. It removes stale length and integrity headers and marks the personalized response as non-cacheable. Other response headers, including Content Security Policy, are preserved.

This template deliberately allows anonymous traffic. It uses `Unidentified user` when identity is `null` or Gateway cannot retrieve identity.

## Forward and test

Deploy the Worker and [create a Forward policy](/cloudflare-one/traffic-controls/integrate-workers/forward-traffic/#set-up-forward) for your test application's HTML pages and a test user group. Visit the page and confirm that the label contains your Gateway identity email. Test an image or JSON endpoint and confirm its body is unchanged.

A page's Content Security Policy can prevent the inline style from being applied. The label will still be inserted, but its appearance depends on the page. Configure styling in your own application rather than weakening its security policy. Client-side rendering can also remove injected elements.

This template changes uncompressed HTML with a `200` response only. Configure your test origin to serve uncompressed HTML. It does not watermark PDF files, images, or other binary downloads, and a visible label can be removed by the user. Test authentication flows and application rendering before widening the policy scope.

# Common policies

import { Render, Tabs, TabItem, APIRequest } from "~/components";

The following policies are commonly used to secure network traffic. Network policies are evaluated in order from top to bottom, and the first matching policy applies. Place more specific Allow policies above broader Block policies.

For a baseline set of recommended policies, refer to [Secure your Internet traffic and SaaS apps](/learning-paths/secure-internet-traffic/build-network-policies/recommended-network-policies/).

Refer to the [network policies page](/cloudflare-one/traffic-controls/network-policies/) for a comprehensive list of other selectors, operators, and actions.

## Block unauthorized applications

<Render file="gateway/policies/block-applications" product="cloudflare-one" />

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector    | Operator | Value                     | Action |
| ----------- | -------- | ------------------------- | ------ |
| Application | in       | _Artificial Intelligence_ | Block  |

</TabItem>

<TabItem label="API">

In the following API examples, `filters: ["l4"]` indicates that this is a network (Layer 4) policy.

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block unauthorized applications",
		description: "Block access to unauthorized AI applications",
		enabled: true,
		action: "block",
		filters: ["l4"],
		traffic: "any(app.type.ids[*] in {25})",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Check user identity

<Render file="gateway/policies/check-user-identity" product="cloudflare-one" />

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator | Value         | Logic | Action |
| ---------------- | -------- | ------------- | ----- | ------ |
| Application      | in       | _Salesforce_  | And   | Block  |
| User Group Names | in       | _Contractors_ |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Check user identity",
		description:
			"Block access to Salesforce by temporary employees and contractors",
		enabled: true,
		action: "block",
		filters: ["l4"],
		traffic: "any(app.ids[*] in {606})",
		identity: 'any(identity.groups.name[*] in {"Contractors"})',
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Enforce device posture

Require devices to have certain software installed or other configuration attributes. For instructions on enabling a device posture check, refer to the [device posture section](/cloudflare-one/reusable-components/posture-checks/). For example, you can use a list of [device serial numbers](/cloudflare-one/reusable-components/posture-checks/client-checks/corp-device/) to ensure users can only access an application if they connect with the Cloudflare One Client from a company device:

<Render
	file="gateway/policies/dash-plus-api/network/enforce-device-posture"
	product="cloudflare-one"
/>

## Enforce session duration

To require users to re-authenticate after a certain amount of time has elapsed, configure [Cloudflare One Client sessions](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/client-sessions/).

## Allow only approved traffic

Restrict user access to only the specific sites or applications configured in your [HTTP policies](/cloudflare-one/traffic-controls/http-policies/). This pattern uses two policies: an Allow policy to permit HTTP/HTTPS traffic, followed by a Block policy to deny everything else. Place the Allow policy above the Block policy so that matching traffic is allowed before the catch-all block applies.

### 1. Allow HTTP and HTTPS traffic

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector          | Operator | Value       | Logic | Action |
| ----------------- | -------- | ----------- | ----- | ------ |
| Detected Protocol | is       | _TLS_       | And   | Allow  |
| Destination Port  | in       | `80`, `443` |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow HTTP and HTTPS traffic",
		description: "Restrict traffic to HTTP and HTTPS traffic",
		enabled: true,
		action: "allow",
		filters: ["l4"],
		traffic: 'net.detected_protocol == "tls" and net.dst.port in {80 443}',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### 2. Block all other traffic

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector | Operator | Value        | Action |
| -------- | -------- | ------------ | ------ |
| Protocol | in       | _TCP_, _UDP_ | Block  |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block all other traffic",
		description: "Block all other traffic that is not HTTP or HTTPS",
		enabled: true,
		action: "block",
		filters: ["l4"],
		traffic: 'net.protocol in {"tcp" "udp"}',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Filter HTTPS traffic when inspecting on all ports

If your organization blocks traffic by default with a Network policy and you want to [inspect HTTP traffic on all ports](/cloudflare-one/traffic-controls/network-policies/protocol-detection/#inspect-on-all-ports), you need to explicitly allow HTTP and TLS traffic to filter it.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector          | Operator | Value  | Logic | Action |
| ----------------- | -------- | ------ | ----- | ------ |
| Detected Protocol | is       | _TLS_  | Or    | Allow  |
| Detected Protocol | is       | _HTTP_ |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow on inspect all ports",
		description: "Filter HTTPS traffic when using inspect all ports",
		enabled: true,
		action: "allow",
		filters: ["l4"],
		traffic:
			'net.detected_protocol == "tls" or net.detected_protocol == "http"',
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Restrict private network access to proxy endpoint users

When using proxy endpoints, by default all devices added to the proxy endpoint can access your internal applications and services connected through [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/). To restrict access and add an additional layer of security, create the following policies.

### Source IP proxy endpoints

When using [source IP proxy endpoints](/cloudflare-one/traffic-controls/proxy-endpoints/#source-ip-endpoint), restrict access to only users connecting through the proxy endpoint from specific source IPs.

#### 1. Allow proxy endpoint traffic from specific source IPs

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector       | Operator | Value            | Logic | Action |
| -------------- | -------- | ---------------- | ----- | ------ |
| Proxy Endpoint | in       | _Proxy Endpoint_ | And   | Allow  |
| Source IP      | in       | `203.0.113.0/24` | And   |        |
| Destination IP | in       | `10.0.0.0/8`     |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow proxy endpoint traffic from specific source IPs",
		description:
			"Allow traffic from proxy endpoint users with specific source IPs to reach private network",
		enabled: true,
		action: "allow",
		filters: ["l4"],
		traffic:
			'net.proxy_endpoint.ids[*] in {"<PROXY_ENDPOINT_ID>"} and net.src.ip in {203.0.113.0/24} and net.dst.ip in {10.0.0.0/8}',
		identity: "",
		device_posture: "",
	}}
/>

Replace `<PROXY_ENDPOINT_ID>` with your proxy endpoint ID.

</TabItem> </Tabs>

#### 2. Block all other proxy endpoint traffic to private network

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector       | Operator | Value            | Logic | Action |
| -------------- | -------- | ---------------- | ----- | ------ |
| Proxy Endpoint | in       | _Proxy Endpoint_ | And   | Block  |
| Destination IP | in       | `10.0.0.0/8`     |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block all other proxy endpoint traffic",
		description:
			"Block any other proxy endpoint traffic from accessing the private network",
		enabled: true,
		action: "block",
		filters: ["l4"],
		traffic:
			'net.proxy_endpoint.ids[*] in {"<PROXY_ENDPOINT_ID>"} and net.dst.ip in {10.0.0.0/8}',
		identity: "",
		device_posture: "",
	}}
/>

Replace `<PROXY_ENDPOINT_ID>` with your proxy endpoint ID.

</TabItem> </Tabs>

### Authorization proxy endpoints

When using [authorization proxy endpoints](/cloudflare-one/traffic-controls/proxy-endpoints/#authorization-endpoint), add an additional layer of security by restricting access to only users connecting from specific source IPs. This prevents unauthorized access even if user credentials are compromised.

#### 1. Allow proxy endpoint traffic from specific source IPs

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector       | Operator | Value            | Logic | Action |
| -------------- | -------- | ---------------- | ----- | ------ |
| Proxy Endpoint | in       | _Proxy Endpoint_ | And   | Allow  |
| Source IP      | in       | `203.0.113.0/24` | And   |        |
| Destination IP | in       | `10.0.0.0/8`     |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow authorized proxy endpoint traffic from specific source IPs",
		description:
			"Allow traffic from authorization proxy endpoint users with specific source IPs to reach private network",
		enabled: true,
		action: "allow",
		filters: ["l4"],
		traffic:
			'net.proxy_endpoint.ids[*] in {"<PROXY_ENDPOINT_ID>"} and net.src.ip in {203.0.113.0/24} and net.dst.ip in {10.0.0.0/8}',
		identity: "",
		device_posture: "",
	}}
/>

Replace `<PROXY_ENDPOINT_ID>` with your proxy endpoint ID.

</TabItem> </Tabs>

#### 2. Block all other proxy endpoint traffic to private network

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector       | Operator | Value            | Logic | Action |
| -------------- | -------- | ---------------- | ----- | ------ |
| Proxy Endpoint | in       | _Proxy Endpoint_ | And   | Block  |
| Destination IP | in       | `10.0.0.0/8`     |       |        |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block all other authorized proxy endpoint traffic",
		description:
			"Block any other authorization proxy endpoint traffic from accessing the private network",
		enabled: true,
		action: "block",
		filters: ["l4"],
		traffic:
			'net.proxy_endpoint.ids[*] in {"<PROXY_ENDPOINT_ID>"} and net.dst.ip in {10.0.0.0/8}',
		identity: "",
		device_posture: "",
	}}
/>

Replace `<PROXY_ENDPOINT_ID>` with your proxy endpoint ID.

</TabItem> </Tabs>

## Restrict access to private networks

Restrict access to resources which you have connected through [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/).

The following example consists of two policies: the first allows specific users to reach your application, and the second blocks all other traffic.

### 1. Allow company employees

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render file="gateway/policies/restrict-access-to-private-networks-allow" product="cloudflare-one" params={{ selector: "Destination IP", value: "10.0.0.0/8" }} />
</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Allow company employees",
		description:
			"Allow any users with an organization email to reach the application",
		enabled: true,
		action: "allow",
		filters: ["l4"],
		traffic: "net.dst.ip in {10.0.0.0/8}",
		identity: 'identity.email matches ".*@example.com"',
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

### 2. Block everyone else

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

<Render
	file="gateway/policies/restrict-access-to-private-networks-block"
	product="cloudflare-one"
/>

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Block everyone else",
		description: "Block any other users from accessing the application",
		enabled: true,
		action: "block",
		filters: ["l4"],
		traffic: "net.dst.ip in {10.0.0.0/8}",
		identity: "",
		device_posture: "",
	}}
/>

</TabItem> </Tabs>

## Override IP address

Override traffic directed toward a specific IP address with a different IP address.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

| Selector         | Operator | Value          | Logic | Action           |
| ---------------- | -------- | -------------- | ----- | ---------------- |
| Destination IP   | in       | `203.0.113.17` | And   | Network Override |
| Destination Port | is       | `80`           |       |                  |

| Override IP | Override Port |
| ----------- | ------------- |
| `1.1.1.1`   | `80`          |

</TabItem>

<TabItem label="API">

<APIRequest
	path="/accounts/{account_id}/gateway/rules"
	method="POST"
	json={{
		name: "Override example.com with 1.1.1.1",
		description: "Override a site's IP address with another IP",
		enabled: true,
		action: "l4_override",
		filters: ["l4"],
		traffic: "net.dst.ip in {203.0.113.17} and net.dst.port == 80",
		identity: "",
		device_posture: "",
		rule_settings: {
			l4override: {
				ip: "1.1.1.1",
				port: 80,
			},
			override_host: "",
			override_ips: null,
		},
	}}
/>

</TabItem> </Tabs>

# Network policies

import { Details, InlineBadge, Render, Badge } from "~/components";

:::note
To enable this feature, download and deploy the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on your devices.
:::

Network policies control TCP and UDP traffic between your users and network destinations. Use them to allow or block non-HTTP traffic such as SSH, RDP, and database connections based on IP addresses, ports, and protocols.

Because Cloudflare One [integrates with your identity provider](/cloudflare-one/integrations/identity-providers/), you can also create identity-based network policies. This allows you to control access to non-HTTP resources on a per-user basis regardless of the user's location or device.

A network policy consists of an **Action** and a logical expression that determines the scope of the action. To build an expression, choose a **Selector** and an **Operator**, then enter a value or range of values in the **Value** field. You can use **And** and **Or** logical operators to evaluate multiple conditions.

- [Actions](#actions)
- [Selectors](#selectors)
- [Comparison operators](#comparison-operators)
- [Value](#value)
- [Logical operators](#logical-operators)

<Render
	file="gateway/response"
	product="cloudflare-one"
	params={{ type: "query", example: "Source IP", example2: "Resolved IP" }}
/>

<Render file="gateway/terraform-precedence-warning" product="cloudflare-one" />

## Actions

Like actions in DNS and HTTP policies, actions in network policies define which decision you want to apply to a given set of elements. You can assign one action per policy.

### Allow

API value: `allow`

<Details header="Available selectors">

**Traffic**

- [Access Infrastructure Target](#access-infrastructure-target)
- [Access Private App](#access-private-app)
- [Application](#application)
- [Browser Isolation](#browser-isolation)
- [Content Categories](#content-categories)
- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Destination Port](#destination-port)
- [Detected Protocol](#detected-protocol)
- [Protocol](#protocol)
- [Proxy Endpoint](#proxy-endpoint)
- [Security Risks](#security-risks)
- [SNI](#sni)
- [SNI Domain](#sni-domain)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [Source Port](#source-port)
- [Traffic Destination](#traffic-destination)
- [Traffic Source](#traffic-source)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

Policies with Allow actions allow network traffic to reach certain IPs or ports. In a default-block configuration, Allow policies define the exceptions — traffic that does not match an Allow policy will be blocked by a lower-priority catch-all Block policy. For example, the following configuration allows specific users to reach a given IP address:

| Selector       | Operator | Value           | Logic | Action |
| -------------- | -------- | --------------- | ----- | ------ |
| Destination IP | in       | `92.100.02.102` | And   | Allow  |
| Email          | in       | `*@example.com` |       |        |

### Block

API value: `block`

<Details header="Available selectors">

**Traffic**

- [Access Infrastructure Target](#access-infrastructure-target)
- [Access Private App](#access-private-app)
- [Application](#application)
- [Browser Isolation](#browser-isolation)
- [Content Categories](#content-categories)
- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Destination Port](#destination-port)
- [Detected Protocol](#detected-protocol)
- [Protocol](#protocol)
- [Proxy Endpoint](#proxy-endpoint)
- [Security Risks](#security-risks)
- [SNI](#sni)
- [SNI Domain](#sni-domain)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [Source Port](#source-port)
- [Traffic Destination](#traffic-destination)
- [Traffic Source](#traffic-source)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

Policies with Block actions block network traffic from reaching certain IPs or ports. For example, the following configuration blocks all traffic directed to port 443:

| Selector         | Operator | Value | Action |
| ---------------- | -------- | ----- | ------ |
| Destination Port | in       | `443` | Block  |

#### Cloudflare One Client block notifications

<Render
	file="gateway/client-notifications"
	product="cloudflare-one"
	params={{ toggleName: "**Display block notification for Cloudflare One Client**" }}
/>

### Network Override

API value: `l4_override`

<Details header="Available selectors">

**Traffic**

- [Destination Continent IP Geolocation](#destination-continent)
- [Destination Country IP Geolocation](#destination-country)
- [Destination IP](#destination-ip)
- [Destination Port](#destination-port)
- [Protocol](#protocol)
- [SNI](#sni)
- [SNI Domain](#sni-domain)
- [Source Continent IP Geolocation](#source-continent)
- [Source Country IP Geolocation](#source-country)
- [Source Internal IP](#source-internal-ip)
- [Source IP](#source-ip)
- [Source Port](#source-port)
- [Virtual Network](#virtual-network)

**Identity**

- [SAML Attributes](#users)
- [User Email](#users)
- [User Group Emails](#users)
- [User Group IDs](#users)
- [User Group Names](#users)
- [User Name](#users)

**Device Posture**

- [Passed Device Posture Checks](#device-posture)

</Details>

Policies with Network Override actions override traffic directed to or coming from certain IPv4/IPv6 addresses or ports. Destination IPs can be public IPs or private IPs connected to your Zero Trust network. For example, the following configuration overrides traffic sent to a public IP with a private IP based on a user's identity:

| Selector       | Operator | Value           | Logic | Action           |
| -------------- | -------- | --------------- | ----- | ---------------- |
| Destination IP | in       | `95.92.143.151` | And   | Network Override |
| User Email     | in       | `*@example.com` | And   |                  |
| Override IP    |          | `10.0.0.1`      |       |                  |

:::caution
If the override destination IP is unreachable, Gateway still rewrites the destination but does not log the connection. The traffic fails silently with no log entry. Verify that your override IP is reachable before deploying this policy.
:::

Gateway will only log successful override connections in your [network logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/#network-logs).

## Selectors

Gateway matches network traffic against the following selectors, or criteria.

### Access Infrastructure Target

<Render
	file="gateway/selectors/all-access-app-targets"
	product="cloudflare-one"
/>

### Access Private App

<Render
	file="gateway/selectors/all-access-private-app-destinations"
	product="cloudflare-one"
/>

### Application

<Render
	file="gateway/selectors/application-http"
	product="cloudflare-one"
	params={{ policyType: "network" }}
/>

### Browser Isolation <Badge text="Beta" variant="caution"/>

Whether the current session is running inside [Remote Browser Isolation](/cloudflare-one/remote-browser-isolation/). Use this selector to apply different policy behavior to isolated and non-isolated traffic.

| UI name           | API example               |
| ----------------- | ------------------------- |
| Browser Isolation | `net.is_isolated == true` |

### Content Categories

<Render
	file="gateway/selectors/net-http-content-categories"
	product="cloudflare-one"
	params={{ APIendpoint: "net.fqdn.content_category" }}
/>

### Destination Continent

<Render
	file="gateway/selectors/destination-continent"
	product="cloudflare-one"
	params={{ one: "net.dst" }}
/>

### Destination Country

<Render
	file="gateway/selectors/destination-country"
	product="cloudflare-one"
	params={{ one: "net.dst" }}
/>

### Destination IP

<Render
	file="gateway/selectors/destination-ip"
	product="cloudflare-one"
	params={{ APIendpoint: "net.dst.ip" }}
/>

### Destination Port

<Render file="gateway/selectors/destination-port" product="cloudflare-one" />

### Detected Protocol

The inferred network protocol based on Cloudflare's [protocol detection](/cloudflare-one/traffic-controls/network-policies/protocol-detection/).

| UI name           | API example                       |
| ----------------- | --------------------------------- |
| Detected Protocol | `net.protocol.detection == "ssh"` |

### Device Posture

<Render file="gateway/selectors/device-posture" product="cloudflare-one" />

### Protocol

<Render file="gateway/selectors/protocol" product="cloudflare-one" />

:::note
To enable Gateway filtering on TCP and UDP, go to **Traffic policies** > **Traffic settings** > **Allow Secure Web Gateway to proxy traffic**. Network policies apply to all enabled protocols unless you use the **Protocol** selector within a policy.
:::

### Proxy Endpoint

<Render file="gateway/selectors/proxy-endpoint" product="cloudflare-one" />

### Security Categories

<Render
	file="gateway/selectors/security-risks"
	product="cloudflare-one"
	params={{ APIendpoint: "net.fqdn.security_category" }}
/>

### SNI

Server Name Indication (SNI) is the hostname a client sends during the TLS handshake, before encryption begins. Gateway reads the SNI to identify the destination of encrypted traffic. The SNI selector matches the exact hostname.

<Render file="gateway/selectors/sni-443" product="cloudflare-one" />

| UI name | API example                         |
| ------- | ----------------------------------- |
| SNI     | `net.sni.host == "www.example.com"` |

### SNI Domain

The domain whose Server Name Indication (SNI) header Gateway will filter traffic against. For example, a rule for `example.com` will match `example.com`, `www.example.com`, and `my.test.example.com`.

<Render file="gateway/selectors/sni-443" product="cloudflare-one" />

| UI name    | API example                        |
| ---------- | ---------------------------------- |
| SNI Domain | `net.sni.domains == "example.com"` |

### Source Continent

The continent of the user making the request. <Render file="gateway/selectors/source-continent-http" product="cloudflare-one" params={{ one: "net.src" }} />

### Source Country

The country of the user making the request. <Render file="gateway/selectors/source-country-http" product="cloudflare-one" params={{ one: "net.src" }} />

### Source Internal IP

<Render
	file="gateway/selectors/source-internal-ip"
	product="cloudflare-one"
	params={{ one: "network", two: "net.src.internal_src_ip" }}
/>

### Source IP

<Render
	file="gateway/selectors/source-ip"
	product="cloudflare-one"
	params={{ APIendpoint: "net.src.ip" }}
/>

### Source Port

<Render file="gateway/selectors/source-port" product="cloudflare-one" />

### Traffic Destination <Badge text="Beta" variant="caution"/>

The method used to off-ramp traffic from Cloudflare. Use this selector to apply policies based on how traffic leaves Gateway.

| UI name             | API example                      |
| ------------------- | -------------------------------- |
| Traffic Destination | `net.offramp.type == "internet"` |

Available values: `internet` (Internet), `cloudflare_wan` (Cloudflare WAN), `cloudflare_tunnel` (Cloudflare Tunnel), `device_client` (Cloudflare One Client), `mesh` (Mesh).

### Traffic Source <Badge text="Beta" variant="caution"/>

The method used to on-ramp traffic to Cloudflare. Use this selector to apply policies based on how traffic reaches Gateway.

| UI name        | API example                          |
| -------------- | ------------------------------------ |
| Traffic Source  | `net.onramp.type == "device_client"` |

Available values: `device_client` (Device client), `mesh` (Mesh), `cloudflare_wan` (Cloudflare WAN), `clientless_rdp` (Clientless RDP), `proxy_endpoint` (Proxy endpoint), `agentless_biso` (Clientless Browser Isolation), `mcp_portal` (MCP portal).

### Users

<Render file="gateway/selectors/users-http" product="cloudflare-one" />

### Virtual Network

<Render
	file="gateway/selectors/virtual-network"
	product="cloudflare-one"
	params={{ one: "net.vnet_id" }}
/>

## Comparison operators

<Render file="gateway/comparison-operators" product="cloudflare-one" />

:::note
The _in_ operator allows you to specify IP addresses or networks using CIDR notation (for example, `10.0.0.0/8` matches all IPs from `10.0.0.0` to `10.255.255.255`).
:::

## Value

<Render
	file="gateway/value"
	product="cloudflare-one"
	params={{
		selector: "SNI",
		selectordescription: "SNI host",
		operator: "matches regex",
		valueOne: ".*whispersystems.org",
		valueTwo: ".*signal.org",
	}}
/>

## Logical operators

<Render
	file="gateway/logical-operators"
	product="cloudflare-one"
	params={{ one: "**Identity** or **Device Posture**" }}
/>

# Protocol detection

import { Render } from "~/components";

Gateway supports the detection, logging, and filtering of network protocols using packet attributes.

Protocol detection only applies to devices connected to Cloudflare One via the Cloudflare One Client in [Traffic and DNS mode](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/modes/#traffic-and-dns-mode-default) mode.

## Turn on protocol detection

To turn on protocol detection:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings** > **Proxy and inspection settings**.
2. Turn on **Allow protocol detection**.

You can now use _Detected Protocol_ as a selector in a [Network policy](/cloudflare-one/traffic-controls/network-policies/#detected-protocol).

### Inspect on all ports

<Render
	file="gateway/inspect-on-all-ports"
	product="cloudflare-one"
	params={{
		turnOnProcedure:
			"under **Manage HTTP inspection by port**, choose _Inspect on all ports_",
	}}
/>

#### Important considerations

**TLS interception on all ports**: When you turn on this setting, Gateway will attempt to intercept TLS traffic on every port, not just port `443`. This means all applications using TLS on non-standard ports will have their traffic intercepted by the Gateway proxy. If you only want to turn on SNI detection for Network policy filtering without full TLS interception, you will need to create [Do Not Inspect policies](/cloudflare-one/traffic-controls/http-policies/tls-decryption/#do-not-inspect) for the specific applications or domains that use TLS on non-standard ports.

:::caution[Non-HTTP protocols inside TLS bypass network policy filtering]
Once a Network policy allows a TLS connection at Layer 4, Gateway decrypts the TLS traffic. However, Gateway cannot filter non-HTTP protocols inside the TLS connection. All non-HTTPS traffic inside TLS (such as SSH over TLS, database protocols, or custom protocols) is allowed by default with no further filtering applied. If your organization uses a default-block Network policy, Gateway will still allow all non-HTTPS TLS traffic through.
:::

To use HTTP policies to filter all HTTPS traffic on all ports when using a default Block Network policy, [create a Network policy to explicitly allow HTTP and TLS traffic](/cloudflare-one/traffic-controls/network-policies/common-policies/#filter-https-traffic-when-inspecting-on-all-ports).

## Supported protocols

Gateway supports detection and filtering of the following protocols:

| Protocol                                                          | Notes                                                                                        |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| HTTP                                                              | Hypertext Transfer Protocol (HTTP/1.1).                                                      |
| HTTP2                                                             | Hypertext Transfer Protocol Version 2.                                                       |
| SSH                                                               | Secure Shell Protocol — remote login and command execution.                                  |
| TLS                                                               | Transport Layer Security. Gateway detects TLS versions 1.1 through 1.3 with the _TLS_ value. |
| DCERPC                                                            | Distributed Computing Environment / Remote Procedure Call.                                   |
| MQTT                                                              | Message Queuing Telemetry Transport — lightweight IoT messaging protocol.                    |
| TPKT                                                              | TPKT commonly initiates RDP sessions, so you can use it to identify and filter RDP traffic.  |
| IMAP         | Internet Message Access Protocol — email retrieval.                                          |
| POP3         | Post Office Protocol v3 — email retrieval.                                                   |
| SMTP         | Simple Mail Transfer Protocol — email sending.                                               |
| MYSQL        | MySQL database wire protocol.                                                                |
| RSYNC-DAEMON | rsync daemon protocol.                                                                       |
| LDAP         | Lightweight Directory Access Protocol.                                                       |
| NTP          | Network Time Protocol.                                                                       |

## Example network policy

You can create network policies that filter traffic based on protocol detections rather than common ports. For example, you can block all SSH traffic on your network without blocking port 22 or any other non-default ports:

| Selector          | Operator | Value | Action |
| ----------------- | -------- | ----- | ------ |
| Detected Protocol | in       | _SSH_ | Block  |

# Add policies

A root ruleset is the top-level container that holds all your firewall policies. You can check for an existing root ruleset from the dashboard or via the [Account rulesets API](/api/resources/rulesets/methods/list/). If you are a new Magic Transit customer, you may not have a root ruleset created for your account. To view examples for root rulesets, review the [Cloudflare Network Firewall Terraform documentation](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/magic_firewall_ruleset).

By default, you can create a maximum of 200 policies. Contact your account team to request a higher limit if needed. We recommend you create lists of IP addresses to reference within policies to streamline policy management.

## Add a policy

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Select **Add a policy**.
3. Fill out the information for your new policy. All existing policies apply to IPv4 traffic only. You can use a [Managed IP List](/waf/tools/lists/managed-lists/#managed-ip-lists) when populating the **Value**.
4. When you are done, select **Add new policy**.

## Create a disabled policy

When you add a new policy, the policy is **Enabled** by default.

To create a **Disabled** policy, follow the steps in [Add a policy](#add-a-policy) above and toggle **Enabled** to off. When a policy is in the disabled state, the policy will not perform the action until it is set to **Enabled**.

To disable an existing policy, from the **Custom policies** tab, set the **Enabled** toggle to off.

## Update a policy

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Locate the policy you want to edit and select the three dots > **Edit**.
3. Update the policy with your changes and select **Save**.

## Delete an existing policy

1. Locate the policy you want to delete in the list.
2. From the end of the row, select **Delete**.
3. Select **Delete** again to confirm the deletion.

## API

Below, you can find examples of how to use the API to perform certain actions.

:::caution[Warning]

The examples on this page all use the `https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets` endpoint. This endpoint creates policies from scratch and **will replace all existing policies in the ruleset**.

If you have a ruleset already deployed, consider using the `https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets/{ruleset_id}/rules` endpoint instead.

Refer to [Add a rule to a ruleset](/ruleset-engine/rulesets-api/add-rule/) and [Create an account ruleset](/api/resources/rulesets/methods/create/) for more information.

:::

### Skip action

A skip action tells the firewall to stop evaluating the current ruleset for matching traffic, effectively allowing it through. Rules in a ruleset evaluate in order from top to bottom. In the example below, the skip rule must appear before the block rule so that matching traffic (port `8080`) is allowed through before the catch-all block applies.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "name": "Example ruleset",
  "kind": "root",
  "phase": "magic_transit",
  "description": "Example ruleset description",
  "rules": [
    {
      "action": "skip",
      "action_parameters": { "ruleset": "current" },
      "expression": "tcp.dstport in { 8080 } ",
      "description": "Allow port 8080"
    },
    {
      "action": "block",
      "expression": "tcp.dstport in { 1..65535 }",
      "description": "Block all TCP ports"
    }
  ]
}'
```

### Block a country

The example below blocks all packets with a source or destination IP address coming from Brazil by using its 2-letter country code in [ISO 3166-1 Alpha 2](https://www.iso.org/obp/ui/#search/code/) format.

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "name": "Example ruleset",
  "kind": "root",
  "phase": "magic_transit",
  "description": "Example ruleset description",
  "rules": [
    {
      "action": "block",
      "expression": "ip.src.country == \"BR\"",
      "description": "Block traffic from Brazil"
    }
  ]
}'
```

### Use an IP list

Cloudflare Network Firewall supports [using lists in expressions](/waf/tools/lists/use-in-expressions/) for the `ip.src` and `ip.dst` fields. The supported lists are:

- `$cf.anonymizer` - Anonymizer proxies
- `$cf.botnetcc` - Botnet command and control channel
- `$cf.malware` - Sources of malware
- `$<IP_LIST_NAME>` - The name of an account-level IP list

```bash
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "name": "Example ruleset",
  "kind": "root",
  "phase": "magic_transit",
  "description": "Example ruleset description",
  "rules": [
    {
      "action": "block",
      "expression": "ip.src in $cf.anonymizer",
      "description": "Block traffic from anonymizer proxies"
    }
  ]
}'
```

## Next steps

Refer to [Form expressions](/cloudflare-one/traffic-controls/packet-filtering/form-expressions/) for more information on how to write rule expressions.

# Create Rate Limiting policies (beta)

Rate limiting policies (beta) allow you to set maximum traffic thresholds - measured in packets or bits per second — for incoming traffic destined for your network as it arrives at specific Cloudflare data centers. When traffic to a location exceeds your defined limit, the policy takes action.

This guide walks you through creating a policy that matches incoming packets and triggers when the traffic rate exceeds your configured threshold.

:::note
For Cloudflare Advanced Network Firewall customers, rate limiting (beta) is available by request through the account team.
:::

## Add a policy

To add a policy:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. In the **Rate limiting** tab, select **Add a policy**.
3. Fill out the information for your new policy:
   - Select the **Field**: At the moment, you can only choose a [data center name](/cloudflare-network-firewall/reference/network-firewall-fields/) (for example, `ORD` for Chicago).
   - Select the **Operator**: Choose among **equals** or **is in**.
   - Select the **Value**.
4. When you are done, select **Save policy**.

## Edit an existing policy

To edit a policy:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Select the **Rate limiting** tab.
3. Locate the policy you want to edit in the list and select **Edit**.
4. Edit the policy with your changes and select **Edit policy**.

## Delete an existing policy

To delete an existing policy:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Select the **Rate limiting** tab.
3. Locate the policy you want to delete from the list.
4. Select the three dots, then select **Remove**.

# Enable Managed Rulesets

With [managed rulesets](/ruleset-engine/managed-rulesets/), you can quickly deploy pre-built firewall rules maintained by Cloudflare. You use Cloudflare Network Firewall to control which managed rules are enabled.

In addition to enabling managed rulesets, you can also add and enable custom policies. Refer to [add policies](/cloudflare-one/traffic-controls/packet-filtering/add-policies/).

:::note
Before you can use managed rulesets with Cloudflare Network Firewall, your account must have managed rulesets enabled. Contact your account team to request access.
:::

To enable or disable a rule, you specify which properties should be overridden. Overrides are configured in the root ruleset of the Managed phase (the top-level ruleset that controls which managed rules are active). This root ruleset can contain only one rule, but that single rule can include multiple overrides for different managed rules.

Cloudflare recommends starting with the `action` set to `log` to evaluate impact before switching to block.

You have multiple options for enabling rules:

- Select an individual rule and enable it.
- Enable multiple rules by enabling by category in the `magic-transit-phase`.
- Enable an entire ruleset.

## API

### 1. Create a Managed phase Managed kind ruleset

To create a managed ruleset, you must first build a request with the following:

- `managed_ruleset_id`: The ID of the Managed phase Managed kind ruleset that contains the rule you want to enable. To find this ID, list available managed rulesets using `GET /accounts/{account_id}/rulesets?kind=managed&phase=magic_transit_managed`.
- `managed_rule_id`: The ID of the rule you want to enable.

Additionally, you need the properties you want to override. The properties you can override include:

- `enabled`: This value can be set to `true` or `false`. When set to `true`, the rule matches packets and applies the rule's default action if the action is not overridden. When set to `false`, the rule is disabled and does not match any packets.
- `action`: The value can be set to `log` so the rule only produces logs instead of applying the rule's default action.

The `enabled` and `action` properties for a rule are set in the Managed phase Managed kind ruleset. All rules in the Managed phase are currently disabled by default.

The example below contains a request for a Managed phase Managed Kind ruleset.

```bash title="Example request - Create a Managed phase Managed Kind ruleset"
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "name": "execute ruleset",
  "description": "Ruleset containing execute rules",
  "kind": "root",
  "phase": "magic_transit_managed",
  "rules": [
    {
      "expression": "true",
      "action": "execute",
      "description": "Enable one rule ",
      "action_parameters": {
        "id": "<MANAGED_RULESET_ID>",
        "version": "latest",
        "overrides": {
          "rules": [
            {
              "id": "<MANAGED_RULE_ID>",
              "enabled": true,
              "action": "log"
            }
          ]
        }
      }
    }
  ]
}'
```

### 2. Patch a Managed phase Managed kind ruleset

Because the root ruleset can only contain one rule, you must PATCH that existing rule (rather than adding new rules) when you want to enable additional managed rules.

Building off the example from the previous step, the example below enables a category to select multiple rules instead of a single rule. The category will be set to `log` mode, which means the rule can produce logs but will not accept or drop packets.

```bash title="Example request - Patch a Managed phase Managed kind ruleset"
curl --request PATCH \
https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets/{root_kind_ruleset}/rules/{root_kind_rule} \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "expression": "true",
  "action": "execute",
  "action_parameters": {
    "id": "<MANAGED_RULESET_ID>",
    "version": "latest",
    "overrides": {
      "rules": [
        {
          "id": "<MANAGED_RULE_ID>",
          "enabled": true
        }
      ],
      "categories": [
        {
          "category": "simple",
          "enabled": true,
          "action": "log"
        }
      ]
    }
  }
}'
```

### 3. Enable all rules

To enable the complete ruleset or enable all rules, send the request below.

```bash title="Example request to enable all rules"
curl --request PATCH \
https://api.cloudflare.com/client/v4/accounts/{account_id}/rulesets/{root_kind_ruleset}/rules/{root_kind_rule} \
--header "Authorization: Bearer <API_TOKEN>" \
--header "Content-Type: application/json" \
--data '{
  "expression": "true",
  "action": "execute",
  "action_parameters": {
    "id": "<MANAGED_RULESET_ID>",
    "version": "latest",
    "overrides": {
      "enabled": true
    }
  }
}'
```

### 4. Delete a ruleset

To delete a ruleset, refer to [Delete a rule in a ruleset](/ruleset-engine/rulesets-api/delete-rule/).

## Cloudflare dashboard

### Enable rules

You can also use the dashboard to enable managed rulesets:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Select **Managed rulesets**. This is where the dashboard lists all your managed rules.
3. To enable a rule, turn **Status** on.

### Edit rules

To edit a rule:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Select **Managed rulesets**. This is where the dashboard lists all your managed rules.
3. Select the three dots > **Edit**.
4. Make the necessary changes, then select **Save**.

### View rules

To view basic information about your rules:

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and go to **Networking** > **Firewall policies**.
2. Select **Managed rulesets**. This is where the dashboard lists all your managed rules.
3. Locate your managed rule, select the three dots > **View**.

# Form expressions

import { GlossaryTooltip } from "~/components";

Rules are written using the Cloudflare Rules language - a domain-specific language (DSL) intended to mimic Wireshark semantics. For more information, refer to the [Rules language](/ruleset-engine/rules-language/) documentation.

To start with a simple case, review below how you would match a source IP. In this expression, `ip.src` refers to the source IP address of the incoming packet, and `==` means "equals":

```txt
ip.src == 192.0.2.0
```

Expressions can be more complex by joining multiple clauses via a logical operator (`&&` means AND, `||` means OR). The following expression matches packets from `192.0.2.1` that also have the TCP push or reset flag set:

```txt
ip.src == 192.0.2.1 && (tcp.flags.push || tcp.flags.reset)
```

## Capabilities

You can use Cloudflare Network Firewall to skip or block <GlossaryTooltip term="data packet">packets</GlossaryTooltip> based on source or destination IP, source or destination port, protocol, packet length, or <GlossaryTooltip term="bit field matching">bit field match</GlossaryTooltip>.

## Restrictions

The expression engine supports CIDR notation (IP address ranges like `192.0.2.0/24`), but only inside curly-brace sets. A bare comparison will not work as expected:

```txt
ip.src == 192.0.2.0/24  # bad
ip.src in { 192.0.2.0/24 }  # good
```

Expressions have a complexity limit that is easily reached when many joined or nested clauses are in the expression. Here's an example:

```txt
(tcp.dstport == 1000 || tcp.dstport == 1001) && (tcp.dstport == 1002 || tcp.dstport == 1003) && (tcp.dstport == 1004 || tcp.dstport == 1005) && (tcp.dstport == 1006 || tcp.dstport == 1007) && (tcp.dstport == 1008 || tcp.dstport == 1009) && (tcp.dstport == 1010 || tcp.dstport == 1011) && (tcp.dstport == 1012 || tcp.dstport == 1013) && (tcp.dstport == 1014 || tcp.dstport == 1015) && (tcp.dstport == 1016 || tcp.dstport == 1017)
```

If the limit is reached, the response will have a `400` status code and an error message of `ruleset exceeds complexity constraints`. Split the expression across multiple rules and try again. Each rule can handle a subset of the conditions, and the firewall evaluates them in order.

# Packet filtering

Packet filtering lets you inspect individual pieces of network traffic (packets) and apply rules to allow or block them before they reach your network. Use the pages in this section to create and manage filtering policies.

# Overview

import {
	Description,
	Feature,
	GlossaryTooltip,
	Plan,
	RelatedProduct,
} from "~/components";

<Description>
	Protect your cloud infrastructure or network offices with advanced, scalable
	firewall-as-a-service protection.
</Description>

<Plan type="enterprise" />

Unwanted network traffic — from DDoS floods to unauthorized scans — can overwhelm your infrastructure. Cloudflare Network Firewall is a <GlossaryTooltip term="firewall-as-a-service">firewall-as-a-service (FWaaS)</GlossaryTooltip> delivered from the Cloudflare global network, meaning Cloudflare runs the firewall for you in the cloud instead of on your own hardware. You can apply filter rules on a variety of criteria, such as protocol (for example, TCP or UDP) and <GlossaryTooltip term="data packet">packet</GlossaryTooltip> length, to filter unwanted traffic before it reaches your network.

Cloudflare Network Firewall uses Wireshark display filter syntax — a rule language originally from the popular network analysis tool [Wireshark](https://www.wireshark.org/), widely used in networking and the same syntax used across other Cloudflare products. With this syntax, you can craft rules to precisely allow or deny any traffic in or out of your network.

Cloudflare Network Firewall is available with the purchase of [Magic Transit](/magic-transit/) or [Cloudflare WAN](/cloudflare-wan/).

---

## Features

<Feature
	header="Intrusion Detection System (IDS)"
	href="/cloudflare-one/traffic-controls/enable-ids/"
>

    Actively monitor for a wide range of known threat signatures in your
    traffic. IDS scans packets for patterns that match known attacks (such as malware
    signatures or exploit attempts) and alerts you when it finds a match.

</Feature>

---

## Related products

<RelatedProduct
	header="Cloudflare Magic Transit"
	href="/magic-transit/"
	product="cloudflare-one"
>
	Secure your network from incoming Internet traffic, and improve performance at
	Cloudflare scale.
</RelatedProduct>

<RelatedProduct
	header="Cloudflare WAN"
	href="/cloudflare-wan/"
	product="cloudflare-wan"
>
	Improve security and performance for your entire corporate networking,
	reducing cost and operation complexity.
</RelatedProduct>

# Protocol validation rules

Cloudflare Network Firewall can validate [Session Initiation Protocol (SIP)](https://datatracker.ietf.org/doc/html/rfc2543) traffic — the protocol used to set up voice and video calls over IP networks (VoIP). This lets you inspect whether SIP packets are properly formatted and enforce a positive security model (only allow well-formed SIP traffic, block everything else).

You can use the `sip` field when creating a rule to check whether packets contain valid SIP data, a Layer 7 (L7) protocol. The `sip` field evaluates to `true` for well-formed SIP packets. Refer to [Cloudflare Network Firewall fields](/cloudflare-network-firewall/reference/network-firewall-fields/), specifically the `sip` field, for more information on this topic.

Currently, SIP is the only protocol supported for deep validation. Contact your account manager if you need Cloudflare Network Firewall to support additional protocols.

# Ruleset logic

import { GlossaryTooltip } from "~/components";

Cloudflare Network Firewall rules are performed after Cloudflare's DDoS mitigations have been applied. The two systems are independent, and therefore, permitting traffic inside Cloudflare Network Firewall does not allow it within our DDoS mitigations. Traffic can still be blocked by DDoS mitigations that are applied first in the flow through Cloudflare's systems.

By default, Cloudflare Network Firewall policies allow all traffic until explicitly blocked by a rule. If no policy is configured, all traffic is permitted after DDoS mitigations have been applied.

## Security policy

You have two options for configuring a security policy:

- Enforce a positive security model, which blocks everything and creates allow rules for specific required traffic.
- Begin with a minimal ruleset to block specific traffic and, by default, everything else is permitted.

Traffic is matched in order of the configured rules. As soon as traffic is matched by an enabled rule, it is no longer validated against the later rules. Disabled rules are skipped entirely — traffic is not evaluated against them. In the dashboard under **Traffic policies** > **Firewall policies**, rule order begins from the top and flows down your list of rules.

For example, permitting all TCP traffic in a rule #4 would mean all TCP traffic is permitted. A rule #5 to block traffic for IP address `x.x.x.x` would not be checked.

For best practices when configuring your security policy, refer to [Best practices](/cloudflare-network-firewall/best-practices/).

## Packet filtering policies and Magic Transit endpoint health checks

Cloudflare-sourced traffic is also subject to the Cloudflare Network Firewall rules you configure. If you block all ICMP traffic, you will also block Cloudflare's <GlossaryTooltip term="tunnel health-check" link="/magic-transit/reference/tunnel-health-checks/#endpoint-health-checks">endpoint health checks</GlossaryTooltip>. When blocking ICMP traffic, ensure your rules first allow ICMP sourced from Cloudflare public IPs to your prefix endpoint IPs before applying a block ICMP rule.

For a list of Cloudflare's public IPs, refer to [IP Ranges](https://www.cloudflare.com/ips/).

## Cloudflare Network Firewall phases

Traffic is processed in two phases: first against your Custom rules, then against Cloudflare's Managed rules.

### Custom phase ruleset

The Custom phase is a set of rules you define and control. You can customize the expression, order, and actions of these rules.

Cloudflare Network Firewall evaluates custom policies before managed policies in the order of precedence. Therefore, if traffic meets the conditions from a custom policy first, that is the action Cloudflare Network Firewall will take.

The actions available for a custom rule are **Block** or **Skip** (allow).

### Managed phase ruleset

Managed phase rulesets are maintained by Cloudflare and contain rules based on best practices, known malicious patterns, and other threat intelligence.

Cloudflare maintains the expressions and order of execution for rules in the Managed phase. You can enable, disable, or set individual rules to log matching packets.

Refer to [Enable managed rulesets](/cloudflare-one/traffic-controls/packet-filtering/enable-managed-rulesets/) for more information.

# Traffic types

import { GlossaryTooltip } from "~/components";

Cloudflare Network Firewall enables you to allow or block traffic on a variety of packet characteristics, including:

- **Source and destination IP** — the sender's and receiver's IP addresses
- **Source and destination port** — the numeric port identifying the specific service (for example, port 80 for HTTP)
- **Protocol** — the communication method, such as TCP or UDP
- **Packet length** — the size of the packet in bytes
- **<GlossaryTooltip term="bit field matching">Bit field match</GlossaryTooltip>** — inspect individual flags within packet headers

Cloudflare Network Firewall operates at OSI layers 3 and 4 — the network layer (IP addressing and routing) and transport layer (port-based connections). It supports protocols such as TCP (reliable, ordered connections), UDP (fast, connectionless messages), and ICMP (network diagnostic messages like ping). You can write rules against any layer 3 or 4 protocol, not only TCP and UDP.

To see the full list of fields you can use when writing filter expressions, refer to [Cloudflare Network Firewall fields](/cloudflare-network-firewall/reference/network-firewall-fields/).

# Extended ruleset

The extended ruleset builds on the [Minimal ruleset](/cloudflare-one/traffic-controls/packet-filtering/best-practices/minimal-ruleset/) by creating targeted rules for different types of systems on your network. Before creating these rules, you must [create IP lists](/waf/tools/lists/custom-lists/#ip-lists) for each category.

If you are unable to export your current perimeter firewall rules, consider identifying categories of systems or user groups that reside on your Magic Transit prefixes. For example:

- [Endpoints (user devices)](#endpoints-user-devices)
- [Internal routers](#internal-routerfirewall-ip-addresses)
- [Web servers](#web-servers)
- [Non-web servers](#non-web-servers)

For each item above, consider the requirements in terms of their permitted Internet access. For example, permit what is required for legitimate traffic and block the rest.

## Create lists for using Cloudflare Network Firewall rules

For more information on lists, refer to [Use rule lists](/cloudflare-one/reusable-components/use-rules-list/).

You can also create a list from the dashboard from **Configurations** > **Lists** on your **Account Home**.

## Endpoints (User devices)

Endpoint devices do not operate as servers, which means:

- They receive traffic from standard common ports — for example `80` or `443` — towards their ephemeral ports (temporary ports assigned by the OS for outbound connections, typically above `32768` in modern operating systems).
- Connections flow outwards, not inwards, and therefore do not receive unsolicited inbound TCP connections.
- They typically only need client TCP and UDP, with no requirement for ingress ICMP.

For example, you can create a list for the combination of generic client TCP and client UDP that allows external pings or traceroutes and a catchall rule for all other protocols and traffic.

Create a list named **Endpoints** and specify the list of endpoints or user IP addresses to reference within the rules.

:::caution
Rule 10 in the example ruleset below is a catch-all (a final rule that matches any remaining traffic) that blocks all traffic not permitted in rules 1-3 towards your list of Endpoint IP addresses. If you want to permit other traffic to these destination IP addresses, the new rule must be added before rule 10.
:::

### Suggested rules

**Rule ID**: 1
**Description**: Allows return traffic (responses to outbound requests) to ephemeral ports while blocking unsolicited inbound connections. Blocks inbound SYN-only traffic (meaning SYN-ACKs are permitted).
**Match**: `ip.proto eq "tcp" and ip.dst in $endpoints and tcp.dstport in {32768..60999} and not (tcp.flags.syn and not tcp.flags.ack)`
**Action**: Allow

**Rule ID**: 2
**Description**: Endpoints (clients) will receive traffic destined for ephemeral ports
**Match**: `ip.proto eq "udp" and ip.dst in $endpoints and udp.dstport in {32768..60999}`
**Action**: Allow

**Rule ID**: 3
**Description**: Permits ICMP traffic to destination IP addresses in `$endpoints` list with ICMP Types:

- Type 0 = Echo Reply
- Type 3 = Destination Unreachable
- Type 11 = Time Exceeded

**Match**: `ip.proto eq "icmp" and ip.dst in $endpoints and (icmp.type eq 0 or icmp.type eq 3 or icmp.type eq 11)`
**Action**: Allow

**Rule ID**: 10
**Description**: Otherwise deny all traffic to IP's in `$endpoints` list
**Match**: `ip.dst in $endpoints`
**Action**: Block

## Internal router/Firewall IP addresses

Follow the best practices for internal routers or firewall interface IP addresses on your MT prefixes below.

1. Create [an IP list](/waf/tools/lists/custom-lists/#ip-lists), **Internal routers** for example, with your IP addresses.
2. Block ICMP if it is not needed.
3. Permit GRE/ESP as needed if the devices have GRE/IPsec tunnels via the Internet.

### Suggested rules

**Rule ID**: 1
**Description**: Permit limited ICMP traffic inbound, including:

- Type 0 - Echo Reply
- Type 3 - Destination Unreachable
- Type 8 - Echo
- Type 11 - Time Exceeded

**Match**: `ip.proto eq "icmp" and ip.dst in $internal_routers and ( (icmp.type eq 0 or icmp.type eq 3) or (icmp.type eq 11) or (icmp.type eq 8) )`
**Action**: Allow

**Rule ID**: 2
**Description**: Block all other traffic destined to these IP addresses
**Match**: `ip.dst in $internal_routers`
**Action**: Block

## Web Servers

Web servers require careful consideration of necessary traffic flows. Traffic for the **web server** functionality is required in addition to traffic flows where the web server is acting as a client.

Where possible, permit the required destination IP addresses and ports for web servers and block everything else. Additional services, for example NTP/DNS, may be required along with the ports for the web traffic.

The following is an example of suggested rules, but you should only make changes based on your specific requirements. For example, if you are not proxied by Cloudflare Layer 7 protection and you expect traffic sourced from the web towards your web servers:

1. Create [an IP list](/waf/tools/lists/custom-lists/#ip-lists), **web servers** for example, to list IP addresses for your web servers.
2. Permit traffic for the web server traffic inbound from the Internet.
3. Permit traffic for the infrastructure or client traffic flows from the Internet, for example DNS and NTP.
4. Block all other traffic destined for the web server IP addresses.

### Suggested rules

**Rule ID**: 1
**Description**: Allows inbound HTTP/S traffic from the Internet with SYN-only or ACK-only flag (not SYN/ACKs)
**Match**: `ip.proto eq "tcp" and tcp.srcport in {32768..60999} and ip.dst in $web_servers and tcp.dstport in {80 443} and not (tcp.flags.syn and tcp.flags.ack)`
**Action**: Allow

**Rule ID**: 2
**Description**: Allows UDP replies for DNS and NTP to web servers
**Match**: `ip.dst in $web_servers and ip.proto eq "udp" and udp.srcport in {53 123} and udp.dstport in {1024..65535}`
**Action**: Allow if necessary but Disable if under attack

**Rule ID**: 3
**Description**: Catch-all to block all other traffic destined for web server IP addresses
**Match**: `ip.dst in $web_servers`
**Action**: Block

Alternatively, if you have Cloudflare Layer 7 protection, the Cloudflare public IP addresses can be permitted as the source IP addresses to the destination IP addresses for the HTTP/HTTPS inbound traffic. This recommendation effectively replaces Rule 1 in the example above.

:::caution
Cloudflare's IP ranges may change. Refer to [Cloudflare's IP addresses](https://www.cloudflare.com/ips/) for the current list, or use an [IP list](/waf/tools/lists/custom-lists/#ip-lists) that you update periodically rather than hardcoding ranges in your rules.
:::

### Suggested rules for Cloudflare proxied traffic

**Description**: Allow inbound HTTP/S traffic from Cloudflare with SYN or ACK
**Match**: `ip.proto eq "tcp" and ip.dst in $web_servers and tcp.dstport in {80 443} and not (tcp.flags.syn and tcp.flags.ack) and ip.src in {173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22}`
**Action**: Allow

## Non-web servers

Restrict the source based on whether the server is expecting traffic from the general Internet or from only specific users.

1. Apply rules based on source IP or ports if possible.
2. Restrict permitted destination ports to only those that are required.
3. Block incoming SYN to the closed ports.

### Suggested rules

- `IP Destination Address { non-web server } and TCP dst port in \<valid ports> — Permit`
- `IP Destination Address { non-web server } and UDP dst port in \<valid ports> — Permit`
- `IP Destination Address { web server } — Block`

# Best practices

import { DirectoryListing } from "~/components";

By default, Cloudflare Network Firewall allows all incoming (ingress) traffic that has passed through Cloudflare's core DDoS mitigations. To reduce your exposure to attacks and prevent unwanted traffic from reaching your network, configure rules using the following guidelines.

If you are setting up firewall rules for the first time, start with the [Minimal ruleset](/cloudflare-one/traffic-controls/packet-filtering/best-practices/minimal-ruleset/). If you have existing on-premises or edge firewall rules, the best approach is to replicate those rules in Network Firewall. If you are unable to export your current firewall rules, contact your Cloudflare Implementation Manager for help translating the rules into Network Firewall rules.

<DirectoryListing />

# Magic Transit egress

The suggestions in the [Minimal ruleset](/cloudflare-one/traffic-controls/packet-filtering/best-practices/minimal-ruleset) and [Extended ruleset](/cloudflare-one/traffic-controls/packet-filtering/best-practices/extended-ruleset) are recommendations for ingress (incoming) traffic. This page covers the additional consideration needed for egress (outgoing) traffic.

Cloudflare Network Firewall does not track connection state (it is not "stateful"). A stateful firewall automatically allows return traffic for active connections — for example, if you send a request outbound, the response is allowed back in. Because Network Firewall is not stateful, each packet — whether ingress or egress — is evaluated independently against your rules. This means ingress block rules can inadvertently block egress traffic.

For Magic Transit egress traffic, consider the following:

- Network Firewall rules apply to both Magic Transit ingress and egress traffic passing through Cloudflare.
- If you have a "default drop" catchall rule (a final rule that blocks all traffic not matched by earlier rules) for ingress traffic, you must add an earlier rule to permit traffic sourced from your Magic Transit prefix with the destination as **any** to allow outbound egress traffic.

   For example, place the following allow rule before any default-drop catchall rule:

   **Match**: `ip.src in {<YOUR_MAGIC_TRANSIT_PREFIX>}` <br/>
   **Action**: Allow

# Minimal ruleset

The suggested minimal ruleset blocks some known common vectors for DDoS attacks and permits all other ESP (Encapsulating Security Payload, used in IPsec VPNs), TCP, UDP, GRE (Generic Routing Encapsulation, used for tunnels), and ICMP traffic.

This is a suggested list and not an exhaustive list. Check which ports and protocols your infrastructure uses (for example, VPN, NTP, or database services) and ensure they are not blocked by these rules.

## Recommended rules

**Rule ID**: 1 <br/>
**Description**: Single rule that blocks all traffic with UDP source ports which are used in attacks or invalid in Magic Transit ingress. <br/>
**Match**: `(udp.srcport in {1900 11211 389 111 19 1194 3702 10001 20800 161 162 137 27005 520 0})` <br/>
**Action**: Block <br/>

**Rule ID**: 2 <br/>
**Description**: Blocks TCP traffic with source port `0` and common ports used in TCP SYN/ACK reflection attacks (attacks that exploit TCP handshake responses to flood a target). <br/>
**Match**: `(tcp.srcport in {21 0 3306})` <br/>
**Action**: Block <br/>

**Rule ID**: 3 <br/>
**Description**: Blocks HOPOPT (Hop-by-Hop Options, IP protocol 0), which has no legitimate use in most environments, and blocks any protocol that is not ESP, TCP, UDP, GRE, or ICMP. Permit the relevant protocols for your environment.<br/>
**Match**: `(ip.proto eq "hopopt") or (not ip.proto in {"esp" "tcp" "udp" "gre" "icmp"})` <br/>
**Action**: Block <br/>

These rules are also available as [managed rules](/cloudflare-one/traffic-controls/packet-filtering/enable-managed-rulesets/) that you can enable without manual configuration. The rules above are provided for reference and customization.

## Traffic and port types

The information below covers traffic type, how the port is used, and reasons for blocking the port.

| Traffic                          | Port use                                                                                                          | Reason to block                                                                                                            |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| UDP source port `0`              | Reserved port. Should not be used by applications.                                                                | Invalid as a legitimate traffic source port. Commonly used in DDoS attacks.                                                |
| UDP source port `1900`           | Simple Service Discovery Protocol (SSDP). Allows universal plug and play devices to send and receive information. | [SSDP DDoS attacks](https://www.cloudflare.com/learning/ddos/ssdp-ddos-attack/) exploit Universal Plug and Play protocols. |
| UDP source port `11211`          | Memcached. A database caching system designed to speed up websites and networks.                                  | [Memcached DDoS Attacks](https://www.cloudflare.com/learning/ddos/memcached-ddos-attack/).                                 |
| UDP source port `389`            | Connection-less Lightweight Directory Access Protocol (CLDAP).                                                    | [Used in reflection attacks](https://blog.cloudflare.com/reflections-on-reflections/).                                     |
| UDP source port `111`            | SunRPC                                                                                                            | Common attack vector. [Used in reflection attacks](https://blog.cloudflare.com/reflections-on-reflections/).               |
| UDP source port `19`             | CHARGEN                                                                                                           | [Amplification attack vector](https://blog.cloudflare.com/memcrashed-major-amplification-attacks-from-port-11211/).        |
| UDP source port `1194`           | OpenVPN                                                                                                           | Unless this is an authorized VPN in your environment, this common VPN should be blocked.                                   |
| UDP source port `3702`           | Web Services Dynamic Discovery Multicast discovery protocol (WS-Discovery)                                        | Vulnerable to exploiting for DDoS attacks.                                                                                 |
| UDP source port `10001`          | Ubiquiti UniFi discovery protocol                                                                                 | Ubiquiti devices were exploited and used to conduct DDoS attacks on this port.                                             |
| UDP source port `20800`          | Call of Duty                                                                                                      | [Commonly used in attacks](https://blog.cloudflare.com/reflections-on-reflections/).                                       |
| UDP source ports `161` and `162` | SNMP                                                                                                              | Vulnerable to exploiting for DDoS attacks.                                                                                 |
| UDP source port `137`            | NetBIOS                                                                                                           | NetBIOS allows file sharing over networks. If configured improperly, can expose file systems.                              |
| UDP source port `27005`          | SRCDS                                                                                                             | Used in [amplication attacks](https://blog.cloudflare.com/reflections-on-reflections/).                                    |
| UDP source port `520`            | Routing Information Protocol (RIP)                                                                                | Internal routing protocol. Not required on Internet WAN access.                                                            |
| TCP source port `0`              | Reserved port. Should not be used by applications.                                                                | Commonly used in DDoS attacks. Invalid as a legitimate traffic source port.                                                |
| TCP source port `21`             | FTP                                                                                                               | Commonly used for attacks.                                                                                                 |
| TCP source port `3306`           | MYSQL open source database                                                                                        | Used as attack vector in DDoS attacks.                                                                                     |

## Other common traffic to consider

The list below is a common list of traffic types you should also consider blocking or restricting inbound.

- SFTP, TFTP
- SSH, Telnet
- RDP
- RCP
- SMCP
- NTP
  - Common vector for reflection attacks. Consider using [Cloudflare One traffic policies](/cloudflare-one/traffic-controls/), [1.1.1.1's DNS over HTTPS (DoH)](/1.1.1.1/), or an internal DNS service if possible. Consider restricting your firewall rules to only allow the source and destination of DNS traffic.
- MS-SQL
  - Common vector and [increasingly used as vector for DDoS attacks](https://blog.cloudflare.com/ddos-attack-trends-for-2021-q4/). Block if unused or consider restricting only to the required source IP addresses.
- HTTP and HTTPS
  - If you only have servers on your Magic Transit prefixes, consider blocking ingress traffic on TCP source ports 80 and 443 from outside. If you have endpoints on your Magic Transit prefixes, you can allow traffic on the source ports but consider creating a disabled rule you can activate to respond to reflection attacks as needed.

If relevant to your environment, consider blocking based on geolocation data, which blocks traffic based on the country or user when an end user's IP address is registered in the geolocation database.

If you are interested in participating in the beta for [Session Initiation Protocol (SIP) Validation](https://blog.cloudflare.com/programmable-packet-filtering-with-magic-firewall/), contact your Implementation Manager.

# PAC file best practices

import { Details } from "~/components";

A PAC file is a text file that specifies which traffic should redirect to the proxy server. When a browser makes a web request, it consults the PAC file's `FindProxyForURL()` function, which evaluates the request and returns routing instructions, such as a direct connection, proxy server, or failover sequence.

## PAC file format

The default Cloudflare PAC file follows a standard format:

```js title="default-pac.js"
function FindProxyForURL(url, host) {
	// No proxy for private (RFC 1918) IP addresses (intranet sites)
	if (
		isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") ||
		isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") ||
		isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0")
	) {
		return "DIRECT";
	}

	// No proxy for localhost
	if (isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) {
		return "DIRECT";
	}

	// Proxy all
	return "HTTPS 3ele0ss56t.proxy.cloudflare-gateway.com:443";
}
```

You can [customize the PAC file](https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file) and host it somewhere your browser can access.

### Formatting considerations

- Make sure the directive used for the endpoint is `HTTPS` and not `PROXY`. For example:
  - Correct: `return "HTTPS your-subdomain.proxy.cloudflare-gateway.com:443";`
  - Incorrect: `return "PROXY your-subdomain.proxy.cloudflare-gateway.com:443";`
- You must use a PAC file instead of configuring the endpoint directly in the proxy configuration of the browser. Modern browsers do not support HTTPS proxies without PAC files.
- Use a plain text editor such as VS Code to avoid extra characters.
- If you are using PAC files for public Internet browsing (instead of only internal services), refer to [Common bypass rules](#common-bypass-rules) for domains you may need to exclude from the proxy to prevent website functionality issues.

## PAC file template with identity provider bypass

When using [authorization endpoints](/cloudflare-one/traffic-controls/proxy-endpoints/#authorization-endpoint), you must bypass your identity provider (IdP) domains in the PAC file. This prevents authentication loops where the browser tries to authenticate with the proxy before it can reach the IdP to authenticate.

The following example PAC file is a comprehensive template that includes common IdP bypass rules. Replace the placeholder values with your configuration:

```js title="pac-idp-template.js"
function FindProxyForURL(url, host) {
	// *** Identity Provider Bypass ***
	// CRITICAL: Bypass your IdP to prevent authentication loops
	// Uncomment and configure the section for your IdP:

	// Okta
	// if (host === "your-domain.okta.com" || shExpMatch(host, "*.oktacdn.com")) {
	// 	return "DIRECT";
	// }

	// Microsoft Entra ID (Azure AD)
	// if (
	// 	host === "login.microsoftonline.com" ||
	// 	host === "aadcdn.msauth.net" ||
	// 	host === "aadcdn.msftauth.net"
	// ) {
	// 	return "DIRECT";
	// }

	// Google Workspace
	// if (
	// 	host === "accounts.google.com" ||
	// 	shExpMatch(host, "*.gstatic.com")
	// ) {
	// 	return "DIRECT";
	// }

	// GitHub
	// if (shExpMatch(host, "*.github.com")) {
	// 	return "DIRECT";
	// }

	// *** Private Networks ***
	// Bypass private RFC 1918 IP addresses
	if (
		isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") ||
		isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") ||
		isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0")
	) {
		return "DIRECT";
	}

	// Bypass localhost
	if (isInNet(dnsResolve(host), "127.0.0.0", "255.0.0.0")) {
		return "DIRECT";
	}

	// Bypass plain hostnames (no dots)
	if (isPlainHostName(host)) {
		return "DIRECT";
	}

	// Bypass .local domains
	if (shExpMatch(host, "*.local")) {
		return "DIRECT";
	}

	// *** Cloudflare Access Logout ***
	// Optional: Redirect logout requests to your Access logout page
	// if (shExpMatch(url, "*logout*")) {
	// 	return "HTTPS your-team-name.cloudflareaccess.com/cdn-cgi/access/logout";
	// }

	// *** Proxy all other traffic ***
	return "HTTPS your-subdomain.proxy.cloudflare-gateway.com:443";
}
```

:::caution[IdP bypass requirement]
When using authorization endpoints, you must configure IdP bypass for your identity provider. Without this, your users will be unable to authenticate and encounter errors when trying to use the proxy.
:::

## Performance optimization

Browsers evaluate PAC files for every request. Optimizing PAC file performance is critical to avoid delays and issues in web browsing for your users.

### Cache DNS results in variables

When performing DNS resolution with `dnsResolve()`, store the result in a variable to reuse it across multiple checks. This avoids redundant DNS lookups:

```js
function FindProxyForURL(url, host) {
	// Resolve once and reuse
	var hostIP = dnsResolve(host);

	if (isInNet(hostIP, "10.0.0.0", "255.0.0.0")) {
		return "DIRECT";
	}

	// Reuse hostIP for additional checks
	if (isInNet(hostIP, "172.16.0.0", "255.240.0.0")) {
		return "DIRECT";
	}

	return "HTTPS proxy.example.com:443";
}
```

### Check for plain hostnames first

NetBIOS names (hostnames without periods) are typically internal and should bypass the proxy. Check for these first:

```js
if (isPlainHostName(host)) return "DIRECT";
```

## Advanced techniques

### Case sensitivity handling

JavaScript is case-sensitive. Convert hostnames to lowercase for consistent matching:

```js
function FindProxyForURL(url, host) {
	// Normalize to lowercase
	host = host.toLowerCase();
	url = url.toLowerCase();

	if (shExpMatch(host, "*.example.com")) {
		return "DIRECT";
	}

	return "HTTPS proxy.cloudflare-gateway.com:443";
}
```

## Common bypass rules

When using PAC files for public Internet browsing (not just internal services), you may need to bypass the proxy for certain domains to prevent website functionality issues. The following are common scenarios where your proxy may interfere with traffic.

:::note[Optional rules]
These bypass rules are optional and depend on your organization's security requirements. Evaluate each bypass rule against your security policies before implementation. You can also use [HTTP policies](/cloudflare-one/traffic-controls/http-policies/) to selectively filter traffic from these domains while still routing them through the proxy.
:::

### Font and static asset providers

Font APIs and static asset providers should typically bypass the proxy to prevent rendering issues:

```js
// Bypass font providers
if (
	shExpMatch(host, "*.googleapis.com") ||
	shExpMatch(host, "*.gstatic.com") ||
	shExpMatch(host, "fonts.adobe.com")
) {
	return "DIRECT";
}
```

### Streaming and media services

Video streaming and large media downloads may perform better with direct connections:

```js
// Bypass streaming services
if (
	shExpMatch(host, "*.netflix.com") ||
	shExpMatch(host, "*.youtube.com") ||
	shExpMatch(host, "*.googlevideo.com")
) {
	return "DIRECT";
}
```

### Apps with certificate pinning

When HTTPS inspection is enabled, applications and services that use certificate pinning reject the Cloudflare-injected certificate and fail to load when routed through the proxy. Bypass these domains in your PAC file:

```js
// Bypass certificate-pinned apps
if (
	shExpMatch(host, "*.example-bank.com") ||
	shExpMatch(host, "*.example-pinned-app.com")
) {
	return "DIRECT";
}
```

[Do Not Inspect (DNI) policies](/cloudflare-one/traffic-controls/http-policies/#do-not-inspect) will not prevent certificate pinning errors on these connections — bypassing certificate-pinned apps in the PAC file is required.

## Test PAC files

### Test with expected websites

Before deploying your PAC file to all users in your organization, test it with the websites and applications your users commonly access. This helps ensure:

- Internal resources are accessible and not incorrectly routed through the proxy
- External websites are properly filtered through Gateway
- Performance is acceptable for typical usage patterns

:::tip
Create a test list of representative domains and verify the PAC file routes them correctly.
:::

### Validate syntax

PAC files use JavaScript syntax. A single syntax error (such as a missing closing parenthesis `)` or bracket `]`) will cause the entire PAC file to fail. Use a JavaScript-aware text editor to find and fix syntax errors before deployment.

## Troubleshoot configurations

### Debug PAC file routing decisions

If you have an issue with proxy routing, most browsers provide debugging tools to verify PAC file behavior:

<Details header="Chromium-based browsers (Chrome, Edge, Brave)">

1. In your browser, go to `chrome://net-export/` (or `edge://net-export/`).
2. Select **Start Logging to Disk**.
3. Go to the website you want to test with the affected browser.
4. Select **Stop Logging**.
5. Open the downloaded file with [netlog-viewer](https://netlog-viewer.appspot.com/).
6. Search for your domain to see proxy resolution decisions.

</Details>

<Details header="Firefox">

1. In Firefox, go to **Tools** > **Browser Tools** > **Browser Console**.
2. Go to the website you want to test with the affected browser.
3. Look for messages related to proxy resolution.

</Details>

<Details header="Safari">

1. In Safari, go to **Safari** > **Settings**, then select **Advanced**.
2. Turn on **Show Develop menu in menu bar**.
3. Select **Develop** > **Show Web Inspector**.
4. Go to the **Network** tab.
5. Look at the request details to verify proxy usage.

</Details>

### Browsing on a device using a PAC file is slow

Excessive DNS lookups in the PAC file can cause delays. Review your PAC file and minimize the use of `dnsResolve()`, `isInNet()`, and `isResolvable()` functions.

### Browser caches PAC files incorrectly

When you update a PAC file, browsers may continue to use a cached version, causing unexpected behavior. Clear your browser cache and restart the browser after updating the PAC file to ensure the browser uses the latest version.

# Configure a PAC file on your device

import { Details, Tabs, TabItem } from "~/components";

After you [create a proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/) and [create a PAC file](/cloudflare-one/traffic-controls/proxy-endpoints/#2-create-a-pac-file), configure your devices to use the PAC file URL. You can configure system-level proxy settings (which apply to most browsers) or configure individual browsers separately.

Chromium-based browsers (Google Chrome, Microsoft Edge, Brave) use the operating system proxy settings. Firefox uses its own proxy settings by default and must be configured separately. Safari and iOS/iPadOS do not support the HTTPS proxy type required by proxy endpoints.

## Prerequisites

Before you configure a PAC file on your device, make sure you have:

- A [Cloudflare Gateway proxy endpoint](/cloudflare-one/traffic-controls/proxy-endpoints/#1-create-a-proxy-endpoint)
- A PAC file URL (either [hosted by Cloudflare](/cloudflare-one/traffic-controls/proxy-endpoints/#create-a-hosted-pac-file) or [self-hosted](/cloudflare-one/traffic-controls/proxy-endpoints/#self-hosting-pac-files))
- The [Cloudflare certificate installed](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on your device (required for HTTPS inspection)

## Configure system proxy settings

Configure your operating system to use the PAC file. This applies the proxy to all browsers that use system proxy settings (Chrome, Edge, Brave).

<Tabs>

<TabItem label="Windows">

For more information, refer to [Use a proxy server in Windows](https://support.microsoft.com/windows/use-a-proxy-server-in-windows-03096c53-0554-4ffe-b6ab-8b1deee8dae1).

1. Open the **Settings** app and select **Network & internet** > **Proxy**.
2. Next to **Use setup script**, select **Set up**.
3. In the **Edit setup script** dialog, turn on **Use setup script**.
4. In the **Script address** field, enter your PAC file URL.
5. Select **Save**.

:::note
On Windows 10, the **Use setup script** toggle and script address field are on the same page under **Automatic proxy setup**. On Windows 11, you must select **Set up** to open the **Edit setup script** dialog.
:::

</TabItem>

<TabItem label="macOS">

For more information, refer to [Change proxy settings on Mac](https://support.apple.com/guide/mac-help/mchlp2591/mac).

1. Open the Apple menu and select **System Settings**.
2. Select **Network** in the sidebar.
3. Select your active network service (for example, **Wi-Fi**), then select **Details**.
4. Select **Proxies**.
5. Turn on **Automatic proxy configuration**.
6. In the **URL** field, enter your PAC file URL.

The setting saves automatically. Chromium-based browsers will now route traffic through your proxy endpoint.

:::note
Safari does not support the HTTPS proxy type required by Cloudflare proxy endpoints. Use a Chromium-based browser (Chrome, Edge, Brave) or [Firefox](/cloudflare-one/traffic-controls/proxy-endpoints/configure-pac-file-on-device/#configure-firefox-separately) on macOS instead.
:::

</TabItem>

<TabItem label="Linux">

Steps vary depending on your desktop environment.

<Details header="GNOME (Ubuntu, Fedora)">

1. Open **Settings** > **Network**.
2. Select the gear icon next to your active connection.
3. Select the **Proxy** tab.
4. Set the method to **Automatic**.
5. In the **Configuration URL** field, enter your PAC file URL.

</Details>

<Details header="KDE Plasma">

1. Open **System Settings** > **Network Settings** > **Proxy**.
2. Select **Use proxy auto configuration URL**.
3. In the URL field, enter your PAC file URL.
4. Select **Apply**.

</Details>

:::note
Most Linux command-line tools (such as `curl` and `wget`) do not natively support PAC files. The system proxy settings apply to GUI browsers only. For command-line tools, configure the `http_proxy` and `https_proxy` environment variables with your proxy endpoint address directly.
:::

</TabItem>

<TabItem label="iOS / iPadOS">

:::caution
iOS and iPadOS do not support the HTTPS proxy settings which are used by Gateway proxy endpoints. iOS and iPadOS are not supported at this time. Cloudflare recommends installing the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) where possible.
:::

</TabItem>

<TabItem label="Android">

Android does not have a global proxy setting. You must configure the proxy for each Wi-Fi network. Steps vary by device manufacturer and Android version.

On stock Android (Pixel) and most Android devices:

1. Open **Settings** > **Network & internet** > **Internet** (or **Wi-Fi**).
2. Tap the gear icon next to your connected network.
3. Select **Advanced options** (or tap the edit icon).
4. Under **Proxy**, select **Proxy Auto-Config**.
5. In the **PAC URL** field, enter your PAC file URL.
6. Tap **Save**.

For more information, refer to [Manage advanced network settings on your Android phone](https://support.google.com/android/answer/9654714).

:::note
The exact menu names and paths differ across manufacturers (Samsung, OnePlus, Xiaomi) and Android versions. If you cannot find the proxy settings, search for "proxy" in your device Settings.
:::

</TabItem>

<TabItem label="ChromeOS">

ChromeOS uses system-level proxy settings that apply to the Chrome browser.

1. Select the time in the status area, then select **Settings**.
2. Select **Network**, then select **Wi-Fi** (or **Ethernet**).
3. Select your active connection.
4. Expand the **Proxy** section.
5. Select **Automatic proxy configuration**.
6. Enter your PAC file URL.
7. Close the settings window. The configuration saves automatically.

For managed ChromeOS devices, refer to [Deploy PAC files at scale](#deploy-pac-files-at-scale) for Google Admin console instructions.

</TabItem>

</Tabs>

## Configure Firefox separately

Firefox uses its own proxy settings and does not inherit the operating system proxy configuration by default. To configure Firefox to use your PAC file:

1. In Firefox, go to **Settings** and scroll to **Network Settings**.
2. Select **Settings**.
3. Select **Automatic proxy configuration URL**.
4. Enter your PAC file URL (for example, `https://pac.cloudflare-gateway.com/<account-id>/<slug>`).
5. Select **OK**.

HTTP traffic from Firefox is now filtered by Gateway.

:::note
To make Firefox use the system proxy settings instead, select **Use system proxy settings** in the Network Settings dialog. This is useful when you have already configured a PAC file at the operating system level.
:::

## Deploy PAC files at scale

For enterprise environments, you can deploy PAC file configurations to managed devices using Group Policy, MDM, or browser management tools.

### Windows Group Policy (GPO)

You can deploy the PAC file URL through Group Policy by configuring the Internet Settings preference:

1. Open **Group Policy Management** and create or edit a Group Policy Object.
2. Go to **User Configuration** > **Preferences** > **Windows Settings** > **Registry**.
3. Add a registry item with the following values:
   - **Hive**: `HKEY_CURRENT_USER`
   - **Key path**: `Software\Microsoft\Windows\CurrentVersion\Internet Settings`
   - **Value name**: `AutoConfigURL`
   - **Value type**: `REG_SZ`
   - **Value data**: Your PAC file URL

### Microsoft Intune

Use the Settings Catalog to deploy proxy auto-configuration:

1. In the [Microsoft Intune admin center](https://intune.microsoft.com/), create a new **Configuration profile**.
2. Select **Settings catalog** as the profile type.
3. Search for **Proxy** and configure the auto-config URL setting for your target platform (Windows or macOS).
4. Assign the profile to your device groups.

### Apple MDM (Jamf Pro, Jamf School, other MDM)

Deploy a configuration profile with the proxy payload:

1. Create a new configuration profile in your MDM solution.
2. Add a **Global HTTP Proxy** or **Network** payload.
3. Set the proxy type to **Auto** and enter your PAC file URL.

For detailed payload settings, refer to the [Network Proxy Configuration settings](https://support.apple.com/guide/deployment/network-proxy-configuration-settings-depb27492e34/web) in the Apple Platform Deployment guide.

### Google Admin console (ChromeOS)

For managed ChromeOS devices and Chrome browsers:

1. In the [Google Admin console](https://admin.google.com/), go to **Devices** > **Networks**.
2. Select the organizational unit for your managed devices.
3. Add or edit a network configuration (Wi-Fi or Ethernet).
4. Under **Proxy settings**, select **Automatic proxy configuration**.
5. Enter your PAC file URL.
6. Select **Save**.

For more information, refer to [Set up networks for managed devices](https://support.google.com/chrome/a/answer/2634553).

### Chrome Browser Cloud Management

To deploy proxy settings to managed Chrome browsers on any operating system:

1. In the [Google Admin console](https://admin.google.com/), go to **Devices** > **Chrome** > **Settings**.
2. Select the organizational unit for your managed browsers.
3. Search for **Proxy** and configure the **Proxy mode** to **Use a .pac proxy auto-config file**.
4. Enter your PAC file URL.
5. Select **Save**.

## Verify your configuration

After you configure a PAC file on your device, verify that traffic routes through Gateway:

1. Open a browser on the configured device.
2. Create an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to block a test domain (for example, `example.com`).
3. Visit the blocked domain in your browser.
4. Verify that the Gateway block page appears.

If the block page does not appear, refer to the [PAC file troubleshooting section](/cloudflare-one/traffic-controls/proxy-endpoints/best-practices/#troubleshoot-configurations) for debugging steps.

## Next steps

- [Create HTTP policies](/cloudflare-one/traffic-controls/http-policies/) to filter proxy endpoint traffic.
- Review [PAC file best practices](/cloudflare-one/traffic-controls/proxy-endpoints/best-practices/) for formatting, performance optimization, and bypass rules.
- Use the [Proxy Endpoint selector](/cloudflare-one/traffic-controls/http-policies/#proxy-endpoint) in HTTP and network policies to apply rules to proxy traffic.

# Proxy endpoints

import {
	Details,
	GlossaryDefinition,
	TabItem,
	Tabs,
	APIRequest,
} from "~/components";

:::note
[Source IP proxy endpoints](#source-ip-endpoint) are only available on Enterprise plans.
:::

Proxy endpoints allow you to apply Gateway policies without installing a client on your devices. By configuring a [Proxy Auto-Configuration (PAC) file](#what-is-a-pac-file) at the browser level, you can route traffic through Gateway for filtering and policy enforcement. Cloudflare supports configuring two types of proxy endpoints: identity-based [authorization endpoints](#authorization-endpoint) and [source IP proxy endpoints](#source-ip-endpoint).

:::note
For the best experience and deepest visibility, Cloudflare recommends using the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/). Use proxy endpoints when installing a device client is not feasible for your environment.
:::

### When to use proxy endpoints

Proxy endpoints are designed for environments where deploying the Cloudflare One Client is not an option. Common use cases include:

- **Virtual desktops (VDI)**: Users log into a virtual machine and use a browser to reach the Internet.
- **Compliance-restricted endpoints**: Environments where you are legally or technically prohibited from installing software on the endpoint.
- **Legacy SWG migration**: Organizations transitioning from legacy Secure Web Gateways that use PAC files.

### Logging

Traffic sent through proxy endpoints generates [Zero Trust Network Session Logs](/logs/logpush/logpush-job/datasets/account/zero_trust_network_sessions/), which are available via [Logpush](/cloudflare-one/insights/logs/logpush/) and [Log Explorer](/observability/logs/log-explorer/).

### What is a PAC file

<GlossaryDefinition term="PAC file" prepend="A PAC file is " />

When end users visit a website, their browser sends the request to a Cloudflare proxy server associated with your account to be filtered by Gateway. PAC files are evaluated by the browser for every request, determining whether traffic should go through the proxy or connect directly. Note that Gateway [cannot filter every type of HTTP traffic](#traffic-limitations) proxied using PAC files.

PAC files offer several advantages:

- **Centralized management**: Update routing rules in one location without reconfiguring individual devices
- **Flexible routing**: Route different traffic types to different proxies or direct connections based on domain, IP range, or protocol
- **Load balancing**: Distribute traffic across multiple proxy servers with automatic failover

:::note
PAC files require user interaction. Authorization endpoints require users to log in through [Cloudflare Access](/cloudflare-one/access-controls/policies/). PAC files do not currently support username/password, mTLS, or Kerberos authorization. Support for additional authentication methods is planned for future releases.
:::

### Types of proxy endpoints

Cloudflare One offers two types of proxy endpoints, each with different authorization methods.

Once you create a proxy endpoint, you cannot change its type. If you need a different authorization method, you must create a new proxy endpoint.

#### Authorization endpoint

Authorization endpoints use [Cloudflare Access](/cloudflare-one/access-controls/policies/) to provide Zero Trust authorization. Users must authenticate through an identity provider and pass Access policies before they can use the proxy endpoint.

Use authorization endpoints when:

- You need user-level authentication and identity-based policies
- You want to associate specific users with their proxy traffic
- Your organization requires login through identity providers (such as Okta, Microsoft Entra ID, or Google Workspace)
- You need granular control over who can access the proxy

#### Source IP endpoint

Source IP endpoints authorize traffic based on the originating IP address. Only traffic from pre-configured IP addresses can use the proxy endpoint.

Use source IP endpoints when:

- You have a fixed set of office or network locations
- You want simpler setup without user authentication
- Your devices share a common egress IP address
- You do not need to identify individual users

## 1. Create a proxy endpoint

:::caution
All devices you add to the proxy endpoint can access your Cloudflare Tunnel applications and services. If you only want to proxy web traffic, [create a Network policy](/cloudflare-one/traffic-controls/network-policies/common-policies/#restrict-private-network-access-to-proxy-endpoint-users) that restricts proxy endpoint traffic from connecting to your internal resources.
:::

<Tabs syncKey="dashPlusAPI">

<TabItem label="Dashboard">

<Details header="Authorization endpoint" open={true}>

To add an [authorization endpoint](#authorization-endpoint):

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Proxy endpoints**.
2. In **Proxy endpoints**, select **Add an endpoint**.
3. Choose **Add an authorization endpoint**.
4. Name your endpoint.
5. Add [Access policies](/cloudflare-one/access-controls/policies/) to control who can access your applications. You can select existing policies or create new policies.
6. Choose which login methods to support. To accept all login methods, turn on **Accept all available identity providers**.
7. (Optional) If only one identity provider is configured, turn on **Apply instant authentication** to skip identity provider selection when your users reach the proxy endpoint.
8. Select **Create**.

</Details>

<Details header="Source IP endpoint" open={true}>

To add a [source IP endpoint](#source-ip-endpoint):

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Proxy endpoints**.
2. In **Proxy endpoints**, select **Add an endpoint**.
3. Choose **Add a source IP endpoint**.
4. Name your endpoint.
5. Add the source IP addresses of your devices in CIDR notation. For example:
   - **IPv4**: `192.0.2.0/8`
   - **IPv6**: `2001:0db8:0000:0000:0000:1234:5678:0000/32`
6. Select **Save endpoint**.

</Details>

</TabItem>

<TabItem label="API">

<Details header="Authorization endpoint" open={true}>

To create an authorization endpoint:

1. Use [Create a Proxy Endpoint](/api/resources/zero_trust/subresources/gateway/subresources/proxy_endpoints/methods/create/) with the following call:

   <APIRequest
   	path="/accounts/{account_id}/gateway/proxy_endpoints"
   	method="POST"
   	json={{
   		kind: "identity",
   		name: "any_name",
   	}}
   />

2. The response returns output similar to the following:

   ```json title="Example output" {8}
   {
   	"result": {
   		"kind": "identity",
   		"id": "d969d7bf-ec28-4291-9af0-86825f472c21",
   		"name": "Identity Proxy Endpoint",
   		"created_at": "2014-01-01T05:20:00.12345Z",
   		"updated_at": "2014-01-01T05:20:00.12345Z",
   		"subdomain": "3ele0ss56t"
   	},
   	"success": true,
   	"errors": [],
   	"messages": []
   }
   ```

   Note the `subdomain` value returned by the API. You will use this to create the Access application.

3. Use [Add An Access Application](/api/resources/zero_trust/subresources/access/subresources/applications/methods/create/) to associate the proxy endpoint with Access policies:

   <APIRequest
   	path="/accounts/{account_id}/access/apps"
   	method="POST"
   	json={{
   		domain: "<SUBDOMAIN>.proxy.cloudflare-gateway.com",
   		name: "Proxy Endpoint App",
   		session_duration: "12h",
   		type: "proxy_endpoint",
   		policies: [
   			{
   				id: "<ACCESS_POLICY_ID>",
   			},
   		],
   	}}
   />

   Replace `<SUBDOMAIN>` with the subdomain from step 2 and `<ACCESS_POLICY_ID>` with the ID of an existing [Access policy](/cloudflare-one/access-controls/policies/).

</Details>

<Details header="Source IP endpoint" open={true}>

To create a source IP endpoint:

1. Use [Create A Proxy Endpoint](/api/resources/zero_trust/subresources/gateway/subresources/proxy_endpoints/methods/create/) with the following call:

   <APIRequest
   	path="/accounts/{account_id}/gateway/proxy_endpoints"
   	method="POST"
   	json={{
   		name: "any_name",
   		ips: ["<PUBLIC_IP>", "<PUBLIC_IP2>", "<PUBLIC_IP3>"],
   	}}
   />

   Replace `<PUBLIC_IP>` with the source IP address of your device in CIDR notation. For example:
   - **IPv4**: `192.0.2.0/8`
   - **IPv6**: `2001:0db8:0000:0000:0000:1234:5678:0000/32`

   :::note
   Gateway limits the prefix length of source networks for proxy endpoints to `/8` for IPv4 networks and `/32` for IPv6 networks.
   :::

2. The response returns output similar to the following:

   ```json title="Example output" {10}
   {
   	"result": {
   		"id": "d969d7bf-ec28-4291-9af0-86825f472c21",
   		"name": "test",
   		"created_at": "2022-03-02T10:57:18.094789Z",
   		"updated_at": "2022-03-02T10:57:18.094789Z",
   		"ips": ["90.90.241.229/8"],
   		"subdomain": "3ele0ss56t"
   	},
   	"success": true,
   	"errors": [],
   	"messages": []
   }
   ```

   Note the `subdomain` value returned by the API. Your Cloudflare proxy server domain is of the form:

   ```txt
   <SUBDOMAIN>.proxy.cloudflare-gateway.com
   ```

   In the example above, the subdomain is `3ele0ss56t` and the proxy server domain is `3ele0ss56t.proxy.cloudflare-gateway.com`.

</Details>

</TabItem>

</Tabs>

## 2. Create a PAC file

A PAC file is a text file written in JavaScript that specifies which traffic should redirect to the proxy server. You can create a PAC file in the Cloudflare dashboard or write your own custom PAC file.

:::tip
For detailed instructions and examples for creating a PAC file, refer to [PAC file best practices](/cloudflare-one/traffic-controls/proxy-endpoints/best-practices/).
:::

### Create a hosted PAC file

When you create a PAC file in Cloudflare One, Cloudflare will host it in a publicly accessible Worker. Hosted PAC files are automatically distributed through Cloudflare's global network.

To create a hosted PAC file:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Proxy endpoints**.
2. Select **Proxy endpoints**.
3. [Create a proxy endpoint](#1-create-a-proxy-endpoint) or select an existing one, then select **Edit**.
4. Select **Add PAC files**.
5. Configure your PAC file:

   In **PAC file details**:
   1. Enter the **Basic Information**, including a name and optional description.
   2. (Optional) Customize the **URL slug** to create a memorable URL path. The slug cannot be changed after creation.
   3. In **PAC file configuration**, select **Browse PAC file configuration templates** and choose a pre-configured template to customize. The available templates are Okta and Azure. After you select a template, **PAC file JavaScript** will populate with the selected template.
   4. Modify the JavaScript as needed to match your network requirements.

   In **Setup instructions**:
   1. Choose a browser.
   2. Follow the instructions in Cloudflare One to configure devices.

6. Select **Create**.

Your hosted PAC file URL will be:

```txt
https://pac.cloudflare-gateway.com/<account-id>/<slug>
```

Where:

- `<account-id>` is your [Cloudflare account ID](/fundamentals/account/find-account-and-zone-ids/)
- `<slug>` is the customizable path you specified (or an auto-generated value if not customized)

#### Hosted PAC file limits

Cloudflare-hosted PAC files have the following limits:

- **Maximum file size**: 256 KB per PAC file
- **Maximum PAC files per account**: 50 (non-Enterprise plans) or 1,000 (Enterprise plans)
- **Update propagation**: Changes to PAC files propagate within seconds to minutes across the global network

#### Caching behavior

Hosted PAC files are cached globally for performance and reliability:

- Browsers and operating systems may cache PAC files locally based on their own policies
- Updates to hosted PAC files automatically invalidate the cache
- If you need to force clients to fetch a new version, you may need to clear browser caches or restart browsers depending on the client configuration

### Self-hosting PAC files

You can also host PAC files on your own infrastructure, such as an internal web server or [Cloudflare Workers](/workers/). Self-hosting gives you complete control over the hosting environment but requires you to manage availability and distribution.

### Proxy endpoint limits

Each account has a maximum number of proxy endpoints:

- **Non-Enterprise plans**: 50 proxy endpoints
- **Enterprise plans**: 500 proxy endpoints

## 3. Configure your devices

### 3a. Install Cloudflare certificate

You must [install a Cloudflare certificate](/cloudflare-one/team-and-resources/devices/user-side-certificates/) on your devices. Authorization endpoints use the certificate to inspect TLS traffic and read the authorization cookie. Source IP endpoints use the certificate to apply Gateway HTTP policies such as blocking specific domains or displaying the Gateway block page.

### 3b. Configure browser to use PAC file

All major browsers support PAC files. You can configure individual browsers, or you can configure system-level proxy settings that apply to all browsers on the device. Multiple devices can call the same PAC file as long as their source IP addresses were included in the proxy endpoint configuration.

For detailed, OS-specific instructions (including Windows, macOS, Linux, iOS, Android, ChromeOS, and enterprise deployment), refer to [Configure a PAC file on your device](/cloudflare-one/traffic-controls/proxy-endpoints/configure-pac-file-on-device/).

<Details header="Chromium-based browsers">

Chromium-based browsers (such as Google Chrome, Microsoft Edge, and Brave) rely on your operating system's proxy server settings. Configure the PAC file URL in your [operating system proxy settings](/cloudflare-one/traffic-controls/proxy-endpoints/configure-pac-file-on-device/#configure-system-proxy-settings).

</Details>

<Details header="Mozilla Firefox">

Firefox uses its own proxy settings and does not inherit the operating system proxy configuration by default. You must configure Firefox separately. For step-by-step instructions, refer to [Configure Firefox separately](/cloudflare-one/traffic-controls/proxy-endpoints/configure-pac-file-on-device/#configure-firefox-separately).

</Details>

<Details header="Safari">

:::caution
Safari does not support the HTTPS proxy type required by Cloudflare proxy endpoints. Apple has confirmed that Safari (on both macOS and iOS/iPadOS) does not support HTTPS proxies. Use a [Chromium-based browser](#chromium-based-browsers) or [Firefox](#mozilla-firefox) instead.
:::

</Details>

## 4. Test your HTTP policy

To test your configuration, create an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to block a test domain. When you visit the blocked domain in your browser, you should see the Gateway block page.

You can now use the Proxy Endpoint selector in [network](/cloudflare-one/traffic-controls/network-policies/#proxy-endpoint) and [HTTP](/cloudflare-one/traffic-controls/http-policies/#proxy-endpoint) policies to filter traffic proxied via PAC files.

## 5. (Optional) Configure firewall

You may need to configure your organization's firewall to allow your users to connect to a proxy endpoint. Depending on your firewall, you will need to create a rule using either your proxy endpoint's domain or IP addresses.

To get the domain of a proxy endpoint:

<Tabs syncKey="dashPlusAPI">

<TabItem label="Dashboard">

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Proxy endpoints**.
2. Choose the proxy endpoint. Select **Edit**.
3. In **Proxy Endpoint**, copy the domain.

</TabItem>

<TabItem label="API">

1. Use the [List proxy endpoints](/api/resources/zero_trust/subresources/gateway/subresources/proxy_endpoints/methods/list/) operation to get a list of your proxy endpoints and their details. For example:

   <APIRequest
   	path="/accounts/{account_id}/gateway/proxy_endpoints"
   	method="GET"
   />

   ```json {8} output
   {
   	"success": true,
   	"result": {
   		"id": "ed35569b41ce4d1facfe683550f54086",
   		"created_at": "2014-01-01T05:20:00.12345Z",
   		"ips": ["192.0.2.1/32"],
   		"name": "DevOps team",
   		"subdomain": "oli3n9zkz5.proxy.cloudflare-gateway.com",
   		"updated_at": "2014-01-01T05:20:00.12345Z"
   	}
   }
   ```

2. Find the proxy endpoint you want to use.
3. Copy the value of the `subdomain` key.

</TabItem>

</Tabs>

Using your proxy endpoint's domain, you can get the IP addresses assigned to the proxy endpoint:

<Tabs>

<TabItem label="macOS and Linux">

1. Open a terminal.
2. Run `dig` on your proxy endpoint's A records to get its IPv4 addresses. For example:

   ```bash
   dig A example.cloudflare-gateway.com +short
   ```

   ```txt output
   162.159.36.5
   162.159.36.20
   ```

3. Run `dig` on your proxy endpoint's AAAA records to get its IPv6 addresses. For example:

   ```bash
   dig AAAA example.cloudflare-gateway.com +short
   ```

   ```txt output
   2606:4700:54::a29f:2407
   2606:4700:5c::a29f:2e07
   ```

</TabItem>

<TabItem label="Windows">

1. Open a PowerShell terminal.
2. Run `Resolve-DnsName` on your proxy endpoint's A records. Your proxy endpoint's IPv4 addresses will appear under `IPAddress`. For example:

   ```powershell
   Resolve-DnsName -Name example.cloudflare-gateway.com -Type A
   ```

   ```txt output
   Name                                           Type   TTL   Section    IPAddress
   ----                                           ----   ---   -------    ---------
   example.cloudflare-gateway.com                 A      300   Answer     162.159.36.5
   example.cloudflare-gateway.com                 A      300   Answer     162.159.36.20
   ```

3. Run `Resolve-DnsName` on your proxy endpoint's AAAA records. Your proxy endpoint's IPv6 addresses will appear under `IPAddress`. For example:

   ```powershell
   Resolve-DnsName -Name example.cloudflare-gateway.com -Type AAAA
   ```

   ```txt output
   Name                                           Type   TTL   Section    IPAddress
   ----                                           ----   ---   -------    ---------
   example.cloudflare-gateway.com                 AAAA   300   Answer     2606:4700:5c::a29f:2e07
   example.cloudflare-gateway.com                 AAAA   300   Answer     2606:4700:54::a29f:2407
   ```

</TabItem>

</Tabs>

To ensure responses are allowed through your firewall, add an inbound rule to allow the static IPv4 address for Cloudflare proxy endpoints, `162.159.193.21`.

## Edit proxy endpoints

You can modify proxy endpoint settings after creation.

### Edit authorization endpoint

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Proxy endpoints**.
2. Locate your authorization endpoint (indicated by **Authorization** under **Type**).
3. Select the three dots, then select **Configure**.
4. Choose what to edit:
   - **Basic info**: Update the endpoint name and description.
   - **Access policies**: Add, remove, or modify Access policies that control who can use the endpoint.
   - **Login methods**: Select which [identity providers](/cloudflare-one/integrations/identity-providers/) users can authenticate with.
5. Select **Save**.

### Edit source IP endpoint

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Proxy endpoints**.
2. Locate your source IP endpoint (indicated by **Source IP** under **Type**).
3. Select the three dots, then select **Configure**.
4. Update the endpoint name or modify the allowed source IP addresses.
5. Select **Save**.

## Logs

Proxy endpoint traffic is logged in the following locations:

- **Authentication logs**: When users authenticate through an authorization endpoint, login events appear in your [Access logs](/cloudflare-one/insights/logs/).
- **Traffic logs**: HTTP and network traffic proxied through the endpoint appears in [Gateway logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/), with the specific proxy endpoint indicated.

## Billing

Each user who authenticates through an authorization proxy endpoint occupies a [Gateway seat](/cloudflare-one/team-and-resources/users/seat-management/), the same as a user connected through the Cloudflare One Client.

## Limitations

### Authorization endpoint limitations

When using [authorization endpoints](#authorization-endpoint), be aware of the following limitations. For configuration guidance on apps with certificate pinning, refer to [PAC file best practices](/cloudflare-one/traffic-controls/proxy-endpoints/best-practices/#apps-with-certificate-pinning).

#### TLS inspection required

Authorization endpoints require [TLS inspection](/cloudflare-one/traffic-controls/http-policies/tls-decryption/) on all proxied traffic. Gateway must decrypt HTTPS requests to read the authorization cookie that identifies each user session. Gateway always performs TLS decryption for traffic routed through an authorization endpoint, even if you turn off TLS decryption at the account level. You cannot selectively bypass TLS inspection for specific destinations when using an authorization endpoint.

#### Plaintext HTTP traffic

Authorization endpoints do not support plaintext HTTP traffic unless the traffic is configured through an [Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) or bypassed with the PAC file.

#### Referer header traffic

Traffic with a referer HTTP header matching the domain of a recently logged in user from the same source IP will be allowed through and logged with the following non-identity email address:

```txt
auth-proxy-non-identity@<your-team-name>.cloudflareaccess.com
```

Where `<your-team-name>` is your [team name](/cloudflare-one/faq/getting-started-faq/#what-is-a-team-domainteam-name).

This occurs because browsers do not tag HTTP sub-requests with the identity cookie used to verify user authentication. This is an industry-standard behavior for proxy-based Secure Web Gateways.

To filter this traffic, you have two options:

- Set up an [HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to block or allow all traffic matching the `auth-proxy-non-identity@<your-team-name>.cloudflareaccess.com` email address.
- To restrict non-identity traffic to specific source IPs, create a [network policy](/cloudflare-one/traffic-controls/network-policies/) that matches both the source IP and the proxy endpoint.

### Safari and iOS not supported

Safari (on macOS) and all browsers on iOS/iPadOS do not support the HTTPS proxy type that Cloudflare proxy endpoints require. This is a platform-level limitation confirmed by Apple. On macOS, use a Chromium-based browser or Firefox instead. On iOS/iPadOS, proxy endpoints cannot be used.

### Traffic limitations

Each type of proxy endpoint supports the following features:

| Feature                                                                             | Source IP endpoint | Authorization endpoint |
| ----------------------------------------------------------------------------------- | ------------------ | ---------------------- |
| **HTTP/HTTPS traffic**                                                              | ✅[^1]             | ✅[^2]                 |
| **Non-HTTP TCP traffic**                                                            | ✅                 | —                      |
| **UDP traffic**                                                                     | —                  | —                      |
| **[HTTP3](/cloudflare-one/traffic-controls/http-policies/http3/)**                  | —                  | —                      |
| **[Identity-based policies](/cloudflare-one/traffic-controls/identity-selectors/)** | —                  | ✅                     |
| **mTLS authentication**                                                             | —                  | —                      |
| **[Happy Eyeballs](https://datatracker.ietf.org/doc/html/rfc6555)**                 | —                  | —                      |
| **Browser HTTPS auto-upgrade**                                                      | —[^3]              | —[^3]                  |

[^1]: For [source IP endpoints](#source-ip-endpoint), to access plaintext HTTP (non-HTTPS) origins, configure them as [self-hosted Access applications](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/). This allows users to access HTTP resources while maintaining security through Access policies.

[^2]: To access plaintext HTTP (non-HTTPS) origins with [authorization endpoints](#authorization-endpoint), refer to [Plaintext HTTP traffic](#plaintext-http-traffic).

[^3]: Proxy endpoints do not support HTTPS when browsers automatically upgrade HTTP requests to HTTPS (such as Chrome's automatic HTTPS upgrades). If you encounter connection issues with sites that are being auto-upgraded, you may need to disable automatic HTTPS upgrades in your browser settings or configure the site as an exception.

### Session duration

All connections proxied through Cloudflare Gateway have a maximum guaranteed duration of 10 hours. For more information, refer to [Troubleshooting](/cloudflare-one/access-controls/troubleshooting/#long-lived-ssh-sessions-disconnect).

### Gateway DNS and resolver policies

Gateway [DNS](/cloudflare-one/traffic-controls/dns-policies/) and [resolver](/cloudflare-one/traffic-controls/resolver-policies/) policies will always apply to traffic proxied with PAC files, regardless of device configuration.

# DNS over HTTPS (DoH)

import { Details, GlossaryTooltip, Render } from "~/components";

With Cloudflare Gateway, you can filter DNS over HTTPS (DoH) requests by [DNS location](/cloudflare-one/traffic-controls/resolver-endpoints/) or by user without needing to install the Cloudflare One Client on your devices.

Location-based policies require that you send DNS requests to a [location-specific DoH endpoint](#filter-doh-requests-by-location), while identity-based policies require that requests include a [user-specific DoH token](#filter-doh-requests-by-user).

## Filter DoH requests by location

Location-based policies require that you send DNS queries to a unique <GlossaryTooltip term="DoH subdomain">DoH endpoint</GlossaryTooltip> assigned to the location:

```txt
https://<YOUR_DOH_SUBDOMAIN>.cloudflare-gateway.com/dns-query
```

### Prerequisites

Obtain your location's <GlossaryTooltip term="DoH subdomain">DoH subdomain</GlossaryTooltip>.

### Configure browser for DoH

Browsers can be configured to use any DNS over HTTPS (DoH) endpoint. If you choose to configure DoH directly in your browser, you must choose a Gateway DNS location as your DoH endpoint, otherwise DNS filtering will not occur in that browser.

<Render file="gateway/doh-instructions" product="cloudflare-one" />

Your DNS queries will now be sent to Gateway for filtering. To filter these requests, build a DNS policy using the [**DNS Location**](/cloudflare-one/traffic-controls/resolver-endpoints/) selector.

### Configure operating system for DoH

<Details header="Windows 11">

1. Obtain the `A` and `AAAA` record values associated with your location's DoH endpoint.
   1. Run the following command to obtain your `A` record values:

   ```powershell
   nslookup -type=A <your-subdomain>.cloudflare-gateway.com
   ```

   2. Obtain your `AAAA` record values.

   ```powershell
   nslookup -type=AAAA <your-subdomain>.cloudflare-gateway.com
   ```

   3. Copy the resulting IP addresses.

2. Add the addresses to your list of known DoH servers.
   1. Run the following command for each address:

   ```powershell
   Add-DnsClientDohServerAddress -ServerAddress <IP-address> -DohTemplate https://<your-subdomain>.cloudflare-gateway.com/dns-query -AllowFallbackToUdp $False -AutoUpgrade $False
   ```

   2. Confirm the addresses were added.

   ```powershell
   Get-DnsClientDohServerAddress
   ```

3. In Windows, go to **Settings** > **Network & internet** > your active Internet connection. This option may be either **Ethernet** or **Wi-Fi**.

4. Under **DNS server assignment**, select **Edit**.

5. In the drop-down menu, choose _Manual_.

6. Enable **IPv4**.

7. In **Preferred DNS** and **Alternate DNS**, enter the IPv4 addresses from your `A` record command. Set **DNS over HTTPS** to _On (automatic template)_.

8. Enable **IPv6**.

9. In **Preferred DNS** and **Alternate DNS**, enter the IPv6 addresses from your `AAAA` record command. Set **DNS over HTTPS** to _On (automatic template)_.

</Details>

<Details header="Windows Server 2022">

Obtain the `A` and `AAAA` record values associated with your location's DoH endpoint.

1. Run the following command to obtain your `A` record values:

```powershell
nslookup -type=A <your-subdomain>.cloudflare-gateway.com
```

2. Obtain your `AAAA` record values.

```powershell
nslookup -type=AAAA <your-subdomain>.cloudflare-gateway.com
```

3. Copy the resulting IP addresses.
4. [Add the addresses](https://learn.microsoft.com/en-us/windows-server/networking/dns/doh-client-support#add-a-new-doh-server-to-the-list-of-known-servers) to your list of known DoH servers.
5. [Configure the Windows Server client](https://learn.microsoft.com/en-us/windows-server/networking/dns/doh-client-support#configure-the-dns-client-to-support-doh) or [set up a Group Policy](https://learn.microsoft.com/en-us/windows-server/networking/dns/doh-client-support#configuring-doh-through-group-policy) to use DoH.

For more information, refer to [Microsoft's DoH guide](https://learn.microsoft.com/en-us/windows-server/networking/dns/doh-client-support) for Windows Server 2022 and newer.

</Details>

### Use generic DoH endpoint

You can send DoH requests to the generic Cloudflare DoH endpoint, `dns.cloudflare-gateway.com`. To specify a location in your request, include a header named `cf-dns-location` with a value of your location's DoH subdomain. For example:

```http
GET /dns-query?name=example.com&type=A HTTP/2
Host: dns.cloudflare-gateway.com
cf-dns-location: 9y65g5srsm
Accept: application/dns-message
```

## Request DNS64

To request [DNS64](/cloudflare-one/traffic-controls/resolver-endpoints/dns64/) for a DoH query, include the `cf-dns-options` header with the value `dns64`. DNS64 synthesizes `AAAA` records from `A` records for IPv6-only networks. These networks require network address translation from IPv6 to IPv4 (NAT64).

To run this example, replace `<YOUR_DOH_SUBDOMAIN>` with the DoH subdomain for your location:

```bash
curl --get "https://<YOUR_DOH_SUBDOMAIN>.cloudflare-gateway.com/dns-query" \
  --header "accept: application/dns-json" \
  --header "cf-dns-options: dns64" \
  --data-urlencode "name=ipv4only.arpa" \
  --data-urlencode "type=AAAA"
```

The header requests synthesis independently of the **Enable DNS64** location setting. It supports DoH requests in DNS wire format or JSON.

Use the same endpoint authentication as your other DoH requests.

You can also turn on DNS64 for a location. DoH requests in DNS wire format also support Extension Mechanisms for DNS (EDNS). Send an empty option `65051` to request DNS64.

For configuration and expected answers, refer to [DNS64](/cloudflare-one/traffic-controls/resolver-endpoints/dns64/).

## Filter DoH requests by user

In order to filter DoH queries based on user identity, each query must include a user-specific authentication token. If you have several devices per user and want to apply device-specific policies, you will need to map each device to a different email.

Currently, authentication tokens can only be generated through the API. You can run this [interactive Python script](/cloudflare-one/static/authenticated-doh.py) which automates the setup procedure, or follow the steps described below.

### 1. Create a service token for the account

Each Cloudflare account can only have one active Access [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) authorized for DNS over HTTPS (DoH) at a time.

```bash
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/service_tokens" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{"name":"ACME Corporation service token"}'
```

Save the service token's `client_id`, `client_secret`, and `id`.

<Details header="Example response">

```json null {3,4,8}
{
	"result": {
		"client_id": "88bf3b6d86161464f6509f7219099e57.access",
		"client_secret": "cfast_EqN8f9vx3sKOSY4mwCMCbZYb02L4OvfAkacLAqTZ63a435a7",
		"created_at": "2022-06-09T01:59:17Z",
		"enabled": true,
		"expires_at": "2023-06-09T01:59:17Z",
		"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
		"name": "ACME Corporation service token",
		"updated_at": "2022-06-09T01:59:17Z"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

</Details>

### 2. Enable DoH functionality for the service token

```bash
curl --request PUT \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/organizations/doh/$SERVICE_TOKEN_ID" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

If you get an `access.api.error.service_token_not_found` error, check that `$SERVICE_TOKEN_ID` is the value of `id` and not `client_id`.

:::note

Although you can create multiple valid service tokens, only one service token can be designated for issuing DoH tokens. Calling the API to enable DoH on a new service token replaces the previously active service token. If a new token overrides an active service token, the API call will fail.

:::

<Details header="Example response">

```json
{
	"result": {
		"client_id": "88bf3b6d86161464f6509f7219099e57.access",
		"created_at": "2022-06-09T01:59:17Z",
		"expires_at": "2023-06-09T01:59:17Z",
		"id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
		"name": "ACME Corporation service token",
		"updated_at": "2022-06-09T01:59:17Z",
		"duration": "8760h"
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

</Details>

### 3. Create a user

Create a new user and optionally add them to a group.

```bash
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/users" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
  "name": "John Doe",
  "email": "jdoe@acme.com",
  "custom": {"groups":[{"id": "02fk6b3p3majl10", "email": "finance@acme.com", "name": "Finance"}]}
}'
```

Save the user's `id` returned in the response.

<Details header="Example response">

```json null {3}
{
	"result": {
		"id": "54d425de-7a78-4186-9975-d43c88ee7899",
		"created_at": "2022-03-16T21:18:39.93598Z",
		"updated_at": "2022-05-17T23:50:39.598345Z",
		"uid": "54d425de-7a78-4186-9975-d43c88ee7899",
		"name": "John Doe",
		"email": "jdoe@acme.com",
		"custom": {
			"groups": [
				{
					"email": "finance@acme.com",
					"id": "02fk6b3p3majl10",
					"name": "Finance"
				}
			]
		}
	},
	"success": true,
	"errors": [],
	"messages": []
}
```

</Details>

:::note

Steps 1-3 above only need to be completed once, while Steps 4-5 below would occur during normal operation.

:::

### 4. Generate a DoH token for the user

Request a DoH token for the user, using your service token to authenticate into your <GlossaryTooltip term="team domain">team domain</GlossaryTooltip>.

```bash
curl "https://<TEAM_NAME>.cloudflareaccess.com/cdn-cgi/access/doh-token?account-id=<ACCOUNT_ID>&user-id=<USER_ID>&auth-domain=<TEAM_NAME>.cloudflareaccess.com" \
--header "Cf-Access-Client-Id: <CLIENT_ID>" \
--header "Cf-Access-Client-Secret: <CLIENT_SECRET>"
```

The response contains a unique DoH token associated with the user. This token expires in 24 hours. We recommend setting up a refresh flow for the DoH token instead of generating a new one for every DoH query.

<Details header="Example response">

```json
{
	"token": "y2khbGciOiJSUzI1NiIsImtpZCI6ImJlZjVkYjg4ZTEwMjk3ZDEwNzhkMmEyYjE0MjMxZTljYTQwMjQ2NjAwOTQzNmJhOTQwOGJkODY3ZmI4OWFiOGQifQ.eyJ0eXBlIjoiZG9oIiwiYXVkIjoiY2xvdWRmbGFyZS1nYXRld2F5LmNvbSIsImlhdCI6MTY1NDc1MTg3NSwiZXhwIjoxNjU0ODM4Mjc1LCJhY2NvdW50LWlkIjoiMTA4MDM0OGIyZGYzYmQwN2QxZmI1MjM3Y2Q1ZDU5M2EiLCJ1c2VyLWlkIjoiNTRkNDI1ZGUtN2E3OC00MTg2LTk5NzUtZDQzYzg4ZWU3ODk5In0.I5p4WsH2dPhQ8vwy84zF05PsoBHCsUSXAaMpNhEH36oFZ3tXcs9ksLz7OzpZ_x3HxUfO3n57LlpAF1VehaBt2i94XCkvSgtHpYcwd_qZydLp-BGtcyfU1LbdXQC3m6zxKcIWu5VySi8I-J25UYlpyJhYgZ4DQUZIpqbSSt6WcVRKvA7OBa7xjkTux4OcqWAViO_ZS-GLwl-fqhvolmiwk37seBD3YuV1zG06VeWXfrMkZ5MbhooHD1DZDBHOZpTtmN8MbeKeI4tlY1mb_O3-jE-um6F9Hrl4NQm89MKFzsum-_Rywi5m4PTSlDza7fjdJs7RzFgJd3VWgzG-jgyQKw"
}
```

</Details>

### 5. Send an authenticated DoH query

Send DoH queries to the resolver at `https://<ACCOUNT_ID>.cloudflare-gateway.com/dns-query`, making sure to include the user's DoH token in the `CF-Authorization` header.

```bash
curl --silent "https://<ACCOUNT_ID>.cloudflare-gateway.com/dns-query?name=example.com" \
--header "accept: application/dns-json" \
--header "CF-Authorization: <USER_DOH_TOKEN>" | jq
```

If the site is blocked and you have turned on the [block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/#configure-policy-block-behavior) for the policy, the query will return `162.159.36.12` (the IP address of the Gateway block page). If the block page is disabled, the response will be `0.0.0.0`.

<Details header="Example response">

```json
{
	"Status": 0,
	"TC": false,
	"RD": true,
	"RA": true,
	"AD": false,
	"CD": false,
	"Question": [
		{
			"name": "example.com",
			"type": 1
		}
	],
	"Answer": [
		{
			"name": "example.com",
			"type": 1,
			"TTL": 60,
			"data": "162.159.36.12"
		}
	]
}
```

</Details>

You can verify that the request was associated with the correct user email by checking your [Gateway DNS logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/). To filter these requests, build a DNS policy using any of the Gateway [identity-based selectors](/cloudflare-one/traffic-controls/identity-selectors/).

# DNS over TLS (DoT)

By default, DNS is sent over a plaintext connection. DNS over TLS (DoT) is a standard for encrypting DNS queries to keep them secure and private. DoT uses the same security protocol, TLS, that HTTPS websites use to encrypt and authenticate communications.

Cloudflare supports DoT on standard port `853` over TLS 1.2 and TLS 1.3 in compliance with [RFC7858](https://tools.ietf.org/html/rfc7858).

## Configure DoT queries

### 1. Obtain your DoT hostname

Each Gateway DNS location has a unique DoT hostname. DNS locations and corresponding DoT hostnames have policies associated with them.

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Resolver endpoints**.
2. Under **DNS locations**, [add a new location](/cloudflare-one/traffic-controls/resolver-endpoints/) or select an existing location from the list.
3. Under **DoT endpoint**, copy the value in **DoT addresses**.

The DoT hostname contains your unique location name. For example, if the DoT hostname is `9y65g5srsm.cloudflare-gateway.com`, the location name is `9y65g5srsm`.

### 2. Configure your DoT client

To configure a DoT client such as `dig`, specify the IP address and the DoT hostname for your location in your query. For example:

```txt
Hostname: 9y65g5srsm.cloudflare-gateway.com
IP address: 162.159.36.5
```

Alternatively, you can use the generic DoT endpoint (`dns.cloudflare-gateway.com`) and include an `OPT` record with code `65011`. You can select a specific location for the value of the `OPT` record. For example:

```txt
Hostname: dns.cloudflare-gateway.com
IP address: 162.159.36.5
OPT Record:
  - Code: 65011
  - Value: 9y65g5srsm
```

Some stub resolvers support DoT natively. For example, you can configure Unbound to send a DoT query:

```txt
# Unbound TLS Config
tls-cert-bundle: "/etc/ssl/cert.pem"
# Forwarding Config
forward-zone:
 name: "."
 forward-tls-upstream: yes
 forward-addr: 162.159.36.5@853#9y65g5srsm.cloudflare-gateway.com
 forward-addr: 2001:db8:abcd::1234#9y65g5srsm.cloudflare-gateway.com
```

# DNS resolver IPs and hostnames

When you create a DNS location, Gateway assigns IPv4/IPv6 addresses and DoT/DoH hostnames to that location. These are the IP addresses and hostnames you send your DNS queries to for Gateway to resolve.

To view the resolver endpoint IP addresses and hostnames for a DNS location:

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic controls** > **Resolver endpoints**.
2. Select the DNS location, then select **Edit**.
3. Go to **Setup instructions**. The addresses and hostnames will appear in **Your configuration**.

## DNS query location matching

Gateway uses different methods to match a DNS query to DNS locations depending on the type of request and network:

```mermaid
flowchart TB
    %% Accessibility
    accTitle: How Gateway matches queries to DNS locations
    accDescr: Flowchart describing the order of checks Cloudflare Gateway performs to determine the DNS location of a DNS query.

    %% Flowchart
    router(["Router"])-->gateway["Cloudflare Gateway"]

    gateway-->query{{"Is the DNS query sent over HTTPS?"}}

    query--"Yes"-->hostname["Look up location by<br />unique hostname"]
    query--"No"-->ipv4{{"Is it over IPv4?"}}

    ipv4--"Yes"-->source["Look up location by<br />source IPv4 address"]
    ipv4--"No"-->destination["Look up location by<br />destination IPv6 address"]
```

1. First, Gateway checks whether the query was sent using DNS over HTTPS. If yes, Gateway looks up the DNS location by its unique hostname.
2. Next, if the query was not sent with DNS over HTTPS, Gateway checks whether it was sent over IPv4. If yes, it looks up the DNS location by the source IPv4 address.
3. Last, if the query was not sent over IPv4, it means it was sent over IPv6. Gateway will look up the DNS location associated with the query based on the unique DNS resolver IPv6 address.

## IPv4/IPv6 address

### Source IP

Gateway uses the public source IPv4 address of your network to identify your DNS location, apply policies, and log DNS requests. Unless you have purchased a [dedicated IPv4 resolver IP](#dedicated-dns-resolver-ip), you must provide source IP addresses for the IPv4 traffic you want to filter with DNS policies. Otherwise, Gateway will not be able to attribute the traffic to your account.

If you are on an Enterprise plan, you have the option of manually entering one or more source IP addresses of your choice. This enables you to create Gateway DNS locations even if you are not connecting from any of those networks' IP addresses.

### DNS resolver IP

When you create a DNS location, Gateway will resolve queries over IPv4 with the default DNS resolver IP addresses. These addresses are anycast IP addresses shared across every Cloudflare Zero Trust account. To resolve queries over IPv6, your location will receive and use a unique DNS resolver IPv6 address. These IP addresses are how Gateway will match DNS queries to locations and apply the appropriate filtering rules.

#### Dedicated DNS resolver IP

Enterprise users can request a dedicated DNS resolver IPv4 address to be provisioned for a DNS location instead of the default anycast addresses. Queries forwarded to that address will be identified using the dedicated DNS resolver IPv4 address.

Cloudflare will only assign resolver IP addresses to the Zero Trust account you request. For more information on requesting dedicated DNS resolver IPv4 addresses, contact your account team.

#### Bring your own DNS resolver IP

Enterprise users can use their own authority-provided IPv4 and IPv6 addresses as DNS endpoints for a location. Gateway can resolve UDP, TCP, DoT, and DoH queries through the IPv4 addresses provided, as well as UDP and TCP queries through the IPv6 addresses provided.

After you onboard your IP addresses, the IP addresses will appear under the associated endpoint when you create a new DNS location. If you did not provide IP addresses for a specific endpoint type, you can use the default Cloudflare resolver IPs or dedicated resolver IPs alongside your own resolver IPs. For example, if you want to use the IPv6 endpoint but only provided IPv4 addresses, you can use your own resolver IPs for IPv4 and the default Cloudflare IPs for IPv6.

For more information, refer to [Cloudflare BYOIP](/byoip/) or contact your account team.

## DNS over TLS (DoT)

Each DNS location is assigned a unique hostname for DNS over TLS (DoT). Gateway will identify your location based on its DoT hostname.

## DNS over HTTPS (DoH)

Each DNS location is assigned a unique hostname for DNS over HTTPS (DoH). Gateway will identify your location based on its DoH hostname.

### DoH subdomain

Each DNS location in Cloudflare Zero Trust has a unique DoH subdomain (previously known as unique ID). If your organization uses DNS policies, you can enter your location's DoH subdomain as part of the Cloudflare One Client settings.

For example, for the DoH hostname `https://65y9p2vm1u.cloudflare-gateway.com/dns-query`, the DoH subdomain is `65y9p2vm1u`.

## Send specific queries to Gateway

By default, all queries from a configured DNS location will be sent to its DNS resolver IP address to be inspected by Gateway. You can configure Gateway to only filter queries originating from specific networks within a location:

1. [Create an IP list](/cloudflare-one/reusable-components/lists/) with the IPv4 and/or IPv6 addresses that your organization will source queries from.
2. Add a [Source IP](/cloudflare-one/traffic-controls/dns-policies/#source-ip) condition to your DNS policies.

For example, to block security threats for specific networks, you could create the following policy:

| Selector            | Operator | Value                                                           | Logic | Action |
| ------------------- | -------- | --------------------------------------------------------------- | ----- | ------ |
| Security Categories | in       | Select all categories that apply                                | And   | Block  |
| Source IP           | in list  | The name of the IP list containing your organization's networks |       |        |

DNS queries made from IP addresses that are not in your IP list will not be filtered or populate your organization's [Gateway activity logs](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/).

# DNS64

Cloudflare Gateway supports DNS64 for IPv6-only networks. DNS64 synthesizes `AAAA` records from `A` records when no `AAAA` records are available.

Gateway uses the well-known prefix `64:ff9b::/96` for DNS64. Your network must provide network address translation from IPv6 to IPv4 (NAT64) for this prefix.

DNS64 synthesizes DNS answers. NAT64 translates network traffic.

## Request DNS64

DNS64 is off by default. You can request synthesis for a DNS location or an individual query.

Extension Mechanisms for DNS (EDNS) adds options to DNS queries. The location setting and EDNS option support UDP, TCP, DNS over TLS (DoT), and DNS over HTTPS (DoH).

Gateway supports these opt-in mechanisms:

| Mechanism    | Configuration                                              | Scope                           |
| ------------ | ---------------------------------------------------------- | ------------------------------- |
| DNS location | **Enable DNS64**, or `dns64_enabled: true` through the API | Queries that match the location |
| EDNS option  | Option code `65051` with an empty payload                  | Individual DNS query            |
| DoH header   | `cf-dns-options: dns64`                                    | Individual DoH request          |

Each mechanism independently requests DNS64. Turning off the location setting does not prevent per-query opt-in.

### DNS location setting

To request DNS64 for a location, turn on **Enable DNS64**. You can change this setting when you add or edit a [DNS location](/cloudflare-one/traffic-controls/resolver-endpoints/).

The setting applies to queries that match the location. For matching criteria, refer to [DNS query location matching](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#dns-query-location-matching).

To configure DNS64 through the API, set `dns64_enabled` to `true`. Include this field when you [create a location](/api/resources/zero_trust/subresources/gateway/subresources/locations/methods/create/) or [update a location](/api/resources/zero_trust/subresources/gateway/subresources/locations/methods/update/). The field has type `boolean` and defaults to `false`.

:::caution[Preserve DNS64 in location updates]
Location updates use `PUT`. Include `dns64_enabled` explicitly in every update. Omitting the field resets it to `false`, even if it was previously `true`. Preserve the other location settings in the update request.
:::

### EDNS option

To request DNS64 for an individual query, include a Cloudflare-specific EDNS option. Set the following fields in the `OPT` record:

| Field         | Value   |
| ------------- | ------- |
| Option code   | `65051` |
| Option length | `0`     |
| Option data   | Empty   |

An option with a nonempty payload does not request DNS64. It does not turn off DNS64 requested by the location setting or DoH header.

If a query contains multiple instances of option `65051`, any instance with an empty payload requests DNS64.

To send an empty option with `dig`, replace `<DNS_RESOLVER_IP>` with the resolver IP address for your location:

```bash
dig @"<DNS_RESOLVER_IP>" ipv4only.arpa AAAA +ednsopt=65051 +short
```

To send the same query over TCP, add `+tcp`:

```bash
dig @"<DNS_RESOLVER_IP>" ipv4only.arpa AAAA +ednsopt=65051 +tcp +short
```

To send an EDNS option over DoH, use a request in DNS wire format. The JSON DoH query format cannot carry this option.

### DoH header

To request DNS64 over DoH, include the `cf-dns-options` header with the value `dns64`. The header works independently of the location setting. For a request example, refer to [Request DNS64](/cloudflare-one/traffic-controls/resolver-endpoints/dns-over-https/#request-dns64).

## Verify DNS64

When you request DNS64, an eligible `AAAA` query for `ipv4only.arpa` returns these synthesized addresses:

```txt output
64:ff9b::c000:aa
64:ff9b::c000:ab
```

These addresses correspond to the `A` records for `ipv4only.arpa`: `192.0.0.170` and `192.0.0.171`.

To verify the location setting independently, omit the EDNS option and DoH header. With the location setting off and no per-query opt-in, this domain returns no `AAAA` answers.

DNS64 does not change existing `AAAA` answers, `A` queries, or DNS error responses. It does not apply to DNS policy block responses or DNS override answers.

Clients that independently validate DNS Security Extensions (DNSSEC) may reject synthesized answers. DNS64 cannot create valid DNSSEC signatures for the synthesized `AAAA` records.

# Resolver endpoints

import { GlossaryDefinition, Render } from "~/components";

<Render file="gateway/add-locations" product="cloudflare-one" />

10. Change the DNS resolvers on your router, browser, or OS by following the setup instructions in the UI.
11. Select **Go to DNS Location**. Your location will appear in your list of locations.

You can now apply [DNS policies](/cloudflare-one/traffic-controls/dns-policies/) to your location using the [Location selector](/cloudflare-one/traffic-controls/dns-policies/#location).

## DNS endpoints

### IPv4 and IPv6 DNS

Cloudflare will prefill the [**Source IPv4 Address**](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#source-ip) based on the network you are on. Additionally, Enterprise users can use [dedicated DNS resolver IP addresses](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#dns-resolver-ip) assigned to their account or [resolver IP addresses they provide (BYOIP)](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#bring-your-own-dns-resolver-ip).

You do not need to configure the IPv4 DNS endpoint if:

- Your network only uses IPv6.
- Your users will send all DNS requests from this location using [DNS over HTTPS](#dns-over-https-doh) via a browser.
- You will deploy the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/).

:::note[Your IPv4 address is taken error]
When you try to configure a DNS location over IPv4, Gateway may display a **Your source IPv4 address is taken** error. This may mean someone else in the same network configured Gateway before you did. If your network supports IPv6, you can still use Gateway's DNS filtering by sending DNS queries over IPv6. You can also use the DNS over HTTPS hostname to send queries using a DNS over HTTPS client.

If you think someone else is wrongfully using this IPv4 address, [contact Cloudflare support](/support/contacting-cloudflare-support/#getting-help-with-an-issue).
:::

### DNS over TLS (DoT)

<GlossaryDefinition term="DNS over TLS" />

For more information, refer to [DNS over TLS](/cloudflare-one/traffic-controls/resolver-endpoints/dns-over-tls/).

### DNS over HTTPS (DoH)

<GlossaryDefinition term="DNS over HTTPS" />

Gateway requires a DoH endpoint for default DNS locations. For more information, refer to [DNS over HTTPS](/cloudflare-one/traffic-controls/resolver-endpoints/dns-over-https/).

## DNS64

Gateway can synthesize `AAAA` records from `A` records for IPv6-only networks. These networks require network address translation from IPv6 to IPv4 (NAT64).

Turn on **Enable DNS64** for a location, or request synthesis for an individual query.

For configuration and request examples, refer to [DNS64](/cloudflare-one/traffic-controls/resolver-endpoints/dns64/).

## Secure DNS locations

Secure DNS locations provide additional protection against malicious domains for use in services such as [protective DNS (PDNS)](/reference-architecture/diagrams/sase/gateway-for-protective-dns/). For a DNS location to be considered secure, Gateway requires that:

- Your IPv4 and IPv6 endpoints use your [BYOIP addresses](/cloudflare-one/traffic-controls/resolver-endpoints/dns-resolver-ips/#bring-your-own-dns-resolver-ip) (if any).
- [Source network filtering](/cloudflare-one/traffic-controls/network-policies/) is configured for your IPv4, IPv6, and DoT endpoints.
- Source network filtering or token authentication are configured for your DoH endpoints.
- Any enabled endpoints for a DNS location meet security permissions.

You can assign users the [**Cloudflare Zero Trust DNS Locations Write** role](/cloudflare-one/roles-permissions/#zero-trust-roles) to grant them the permission to create and edit secure DNS locations. To allow users to view locations, you must also assign the **Cloudflare Zero Trust Read Only** role. Users with these roles can view any DNS location, but can only create or edit secure locations.

Roles that supersede **Cloudflare Zero Trust DNS Locations Write** include:

- Cloudflare Gateway
- Cloudflare Zero Trust
- Super Administrator

## Limitations

### Captive portals

<Render
	file="gateway/add-locations-static-ip-warning"
	product="cloudflare-one"
/>

### Third-party filtering

<Render file="gateway/third-party-warning" product="cloudflare-one" />

# Tiered policies

:::note
Only available on Enterprise plans.
:::

Gateway tiered policies allow you to share and enforce Gateway policies across multiple Zero Trust accounts. This enables centralized policy management for organizations that manage multiple accounts.

There are two approaches for setting up tiered policies, depending on your deployment model and policy requirements:

- **[Cloudflare Organizations](/cloudflare-one/traffic-controls/tiered-policies/organizations/)** — Share DNS, network, HTTP, and resolver policies across accounts in a Cloudflare Organization using the dashboard.
- **[Tenant API](/cloudflare-one/traffic-controls/tiered-policies/tenant-api/)** — Manage DNS policies across parent and child accounts for Managed Service Provider (MSP) deployments.

## Organizations vs. Tenant API

| Feature                    | [Cloudflare Organizations](/cloudflare-one/traffic-controls/tiered-policies/organizations/) | [Tenant API](/cloudflare-one/traffic-controls/tiered-policies/tenant-api/) |
| -------------------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **Supported policy types** | DNS, Network, HTTP, Resolver                                                                | DNS only                                                                         |
| **Account model**          | Source / Recipient accounts                                                                 | Parent / Child accounts                                                          |
| **Shareable settings**     | Block pages, extended email matching                                                        | Block pages                                                                      |
| **Setup**                  | Dashboard (self-serve)                                                                      | API-only                                                                         |
| **Availability**           | Enterprise                                                                                  | Enterprise (GA)                                                                  |

# Cloudflare Organizations

:::note
Only available on Enterprise plans.
:::

Gateway supports using [Cloudflare Organizations](/fundamentals/organizations/) to share configurations between and apply specific policies to accounts within an Organization. Tiered Gateway policies with Organizations support [DNS](/cloudflare-one/traffic-controls/dns-policies/), [network](/cloudflare-one/traffic-controls/network-policies/), [HTTP](/cloudflare-one/traffic-controls/http-policies/), and [resolver](/cloudflare-one/traffic-controls/resolver-policies/) policies.

For a DNS-only deployment using the Tenant API, refer to [Tenant API](/cloudflare-one/traffic-controls/tiered-policies/tenant-api/).

## Get started

To set up Cloudflare Organizations, refer to [Create an Organization](/fundamentals/organizations/#create-an-organization). Once you have provisioned and configured your Organization's accounts, you can create [Gateway policies](/cloudflare-one/traffic-controls/).

## Account types

Zero Trust accounts in Cloudflare Organizations include source accounts and recipient accounts.

In a tiered policy configuration, a top-level source account can share Gateway policies with its recipient accounts. Recipient accounts can add policies as needed while still being managed by the source account. Organization owners can also configure a [custom block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) for recipient accounts independently from the source account. Gateway will automatically [generate a unique root CA](/cloudflare-one/team-and-resources/devices/user-side-certificates/#generate-a-cloudflare-root-certificate) for each recipient account in an Organization.

Each recipient account is subject to the default Zero Trust [account limits](/cloudflare-one/account-limits/).

Gateway evaluates source account policies before any recipient account policies. Shared policies always take priority in recipient accounts — recipient accounts cannot bypass, modify, or reorder shared policies, and cannot move any of their own policies above shared ones. If you update the relative priority of shared policies in the source account, the change will be reflected in recipient accounts within approximately two minutes.

All traffic and corresponding policies, logs, and configurations for a recipient account will be contained to that recipient account. Organization owners can view logs for recipient accounts on a per-account basis, and [Logpush jobs](/logs/logpush/) must be configured separately. When using DLP policies with [payload logging](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#log-the-payload-of-matched-rules), each recipient account must configure its own [encryption public key](/cloudflare-one/data-loss-prevention/dlp-policies/logging-options/#set-a-dlp-payload-encryption-public-key).

```mermaid
flowchart TD
%% Accessibility
 accTitle: How Gateway policies work in a tiered account configuration
 accDescr: Flowchart describing the order of precedence Gateway applies policies in a tiered account configuration using Cloudflare Organizations.

%% Flowchart
 subgraph s1["Source account"]
        n1["Block malware"]
        n2["Block spyware"]
        n3["Block DNS tunnel"]
  end
 subgraph s2["Recipient account A"]
        n5["Block malware"]
        n6["Block spyware"]
        n4["Block social media"]
  end
 subgraph s3["Recipient account B"]
        n8["Block malware"]
        n9["Block spyware"]
        n10["Block DNS tunnel"]
        n7["Block instant messaging"]
  end
    n1 ~~~ n2
    n2 ~~~ n3
    s1 -- Share policies with --> s2 & s3

    n1@{ shape: rect}
    n2@{ shape: rect}
    n3@{ shape: rect}
    n4@{ shape: rect}
    n5@{ shape: rect}
    n6@{ shape: rect}
    n7@{ shape: rect}
    n8@{ shape: rect}
    n9@{ shape: rect}
    n10@{ shape: rect}
     n1:::Sky
     n2:::Sky
     n3:::Peach
     n4:::Forest
     n5:::Sky
     n6:::Sky
     n7:::Forest
     n8:::Sky
     n9:::Sky
     n10:::Peach
    classDef Sky stroke-width:1px, stroke-dasharray:none, stroke:#374D7C, fill:#E2EBFF, color:#374D7C
    classDef Peach stroke-width:1px, stroke-dasharray:none, stroke:#FBB35A, fill:#FFEFDB, color:#8F632D
    classDef Forest stroke-width:1px, stroke-dasharray:none, stroke:#2D6A4F, fill:#D8F3DC, color:#2D6A4F
```

In the diagram above:

- Blue policies (**Block malware** and **Block spyware**) are shared from the source account.
- Orange policies (**Block DNS tunnel**) are not shared.
- Green policies (**Block social media** and **Block instant messaging**) are created locally in recipient accounts.

## Limitations

Tiered policies with Organizations have the following limitations:

- [Egress policies](/cloudflare-one/traffic-controls/egress-policies/) cannot be shared between accounts.
- Source accounts cannot share policies that use [device posture](/cloudflare-one/reusable-components/posture-checks/) selectors, the [Detected protocol](/cloudflare-one/traffic-controls/network-policies/#detected-protocol) selector, or the [Quarantine](/cloudflare-one/traffic-controls/http-policies/#quarantine) action. Source and recipient accounts can still create and apply policies with these selectors and actions separately from the Organization share.
- Policies can only be shared within an Organization. Sharing to sub-organizations is not supported.

:::caution
If a shared policy contains identity-based selectors, ensure that both the source account and recipient accounts have matching identity provider (IdP) configurations. If there is a mismatch in IdPs between the source account and a recipient account, the shared policy will never apply to traffic in that recipient account.
:::

## Manage policies

You can create, configure, and share your tiered policies in the source account for your Cloudflare Organization.

### Share policy

To share a Gateway policy from a source account to a recipient account:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**.
2. Choose the policy type you want to share. If you want to share a resolver policy, go to **Traffic policies** > **Resolver policies**.
3. Find the policy you want to share from the list. In the three-dot menu, select **Share**. Alternatively, to bulk share multiple policies, you can select each policy you want to share, then select **Actions** > **Share**.
4. In **Select account**, choose the accounts you want to share the policy with. To share the policy with all existing and future recipient accounts in your Organization, choose _Select all accounts in org_.
5. Select **Continue**, then select **Share**.

A sharing icon will appear next to the policy's name. When sharing is complete, the policy will appear in and apply to the recipient accounts. Shared policies will appear grayed out in the recipient account's list of Gateway policies.

:::note
After sharing a policy, it may take up to two minutes before the policy appears in recipient accounts.
:::

If a policy fails to share to recipient accounts, Gateway will retry deploying the policy automatically unless the error is unrecoverable.

### Edit share recipients

To change or remove recipients for a Gateway policy:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**.
2. Choose the policy type you want to edit. If you want to edit a resolver policy, go to **Traffic policies** > **Resolver policies**.
3. Find the policy you want to edit from the list.
4. In the three-dot menu, select **Edit shared configuration recipients**.
5. In **Select account**, choose the accounts you want to share the policy with. To remove a recipient, select **Remove** next to the recipient account's name.
6. Select **Continue**, then select **Save**.

When sharing is complete, the policy sharing will update across the configured recipient accounts.

:::note
If you selected _Select all accounts in org_ when sharing the policy, you will need to [unshare the policy](#unshare-policy) before you can edit its recipient accounts.
:::

### Unshare policy

To stop sharing a policy with all recipient accounts:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies**.
2. Choose the policy type you want to remove. If you want to remove a resolver policy, go to **Traffic policies** > **Resolver policies**.
3. Find the policy you want to remove from the list. In the three-dot menu, select **Unshare**. Alternatively, to bulk remove multiple policies, you can select each policy you want to remove, then select **Actions** > **Unshare**.
4. Select **Unshare**.

When sharing is complete, Gateway will stop sharing the policy with all recipient accounts and only apply the policy to the source account.

### Edit shared policy

Changes made to shared policies will apply to all recipient accounts. Deleting a shared policy will delete the policy from both the source account and all recipient accounts.

## Manage Gateway settings

You can share certain Gateway settings - the Gateway block page and extended email address matching - from your source account to recipient accounts in your Cloudflare Organization. Other Gateway settings configured in a source account, such as [AV scanning](/cloudflare-one/traffic-controls/http-policies/antivirus-scanning/) and [file sandboxing](/cloudflare-one/traffic-controls/http-policies/file-sandboxing/), will not affect recipient account configurations.

### Share Gateway block page

To share your [Gateway block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/) settings from a source account to a recipient account:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Reusable components** > **Custom pages**.
2. In **Account Gateway block page**, select the three-dot menu and choose **Share**.
3. In **Select account**, choose the accounts you want to share the settings with. To share the settings with all existing and future recipient accounts in your Organization, choose _Select all accounts in org_.
4. Select **Continue**, then select **Share**.

A sharing icon will appear next to the setting. When sharing is complete, the setting will appear in and apply to the recipient accounts.

To modify share recipients or unshare the setting, select the three-dot menu and choose **Edit shared configuration recipients** or **Unshare**.

### Share extended email address matching

To share your [extended email address matching](/cloudflare-one/traffic-controls/identity-selectors/#extended-email-addresses) settings from a source account to a recipient account:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Traffic settings**.
2. In **Firewall** > **Matched extended email address**, select the three-dot menu and choose **Share**.
3. In **Select account**, choose the accounts you want to share the settings with. To share the settings with all existing and future recipient accounts in your Organization, choose _Select all accounts in org_.
4. Select **Continue**, then select **Share**.

A sharing icon will appear next to the setting. When sharing is complete, the setting will appear in and apply to the recipient accounts.

To modify share recipients or unshare the setting, select the three-dot menu and choose **Edit shared configuration recipients** or **Unshare**.

# Tenant API

:::note
Only available for [Cloudflare Partners](https://www.cloudflare.com/partners/) on Enterprise plans. To gain access, contact your account team.
:::

Gateway supports the [Cloudflare Tenant API](/tenant/), which allows Cloudflare-partnered managed service providers (MSPs) to set up and manage Cloudflare accounts and services for their customers. With the Tenant API, MSPs can create Zero Trust deployments with global Gateway policy control. Policies can be customized or overridden at a group or individual account level.

:::caution
The Tenant API platform only supports [DNS policies](/cloudflare-one/traffic-controls/dns-policies/). To apply HTTP, network, and resolver policies, use [Cloudflare Organizations](/cloudflare-one/traffic-controls/tiered-policies/organizations/) instead.
:::

For more information, refer to the [Cloudflare Zero Trust for managed service providers](https://blog.cloudflare.com/gateway-managed-service-provider/) blog post.


## Get started

To set up the Tenant API, refer to [Get started](/tenant/get-started/). Once you have provisioned and configured your customer's Cloudflare accounts, you can create [DNS policies](/cloudflare-one/traffic-controls/dns-policies/).

## Account types

The Gateway Tenant platform supports tiered and siloed account configurations.

### Tiered accounts

In a tiered account configuration, a top-level parent account enforces global security policies that apply to all of its child accounts. Child accounts can override or add policies as needed while still being managed by the parent account. MSPs can also configure child accounts independently from the parent account for the following Gateway features:

- **[Custom block page](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/)**: Child accounts will use the block page setting used by the parent account unless you configure separate block settings for the child account. This applies to both redirects and custom block pages. The block page uses the account certificate for each child account.
- **[Root certificates](/cloudflare-one/team-and-resources/devices/user-side-certificates/)**: If Gateway cannot attribute an incoming DNS query to a child account, it will use the parent account's certificate. This happens when the source IP address of the DNS query does not match a child account or if a custom DNS resolver endpoint is not configured.
- **[DNS locations](/cloudflare-one/traffic-controls/resolver-endpoints/)**
- **[Lists](/cloudflare-one/reusable-components/lists/)**

Each child account is subject to the default Zero Trust [account limits](/cloudflare-one/account-limits/).

Gateway evaluates parent account policies before any child account policies. To allow a child account to override a specific parent account policy, you can use the [Update a Zero Trust Gateway rule](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/update/) endpoint to set the policy's `allow_child_bypass` rule setting to `true`.

```mermaid
flowchart TD
%% Accessibility
 accTitle: How Gateway policies work in a tiered account configuration
 accDescr: Flowchart describing the order of precedence Gateway applies policies in a tiered account configuration.

%% Flowchart
 subgraph s1["Parent account"]
        n1["Block malware"]
        n2["Block DNS tunnel"]
        n3["Block spyware"]
  end
 subgraph s2["Child account A"]
        n4["Block social media"]
  end
 subgraph s3["Child account B"]
        n5["Block instant messaging"]
  end
    n1 ~~~ n2
    n2 ~~~ n3
    A["Tenant"] --Administers--> s1
    s1 -- "Applies policies to" --> s2 & s3

    n1@{ shape: lean-l}
    n2@{ shape: lean-l}
    n3@{ shape: lean-l}
    n4@{ shape: lean-l}
    n5@{ shape: lean-l}
```

### Siloed accounts

In a siloed account configuration, each account operates independently within the same tenant. MSPs manage each account's own security policies, resources, and configurations separately.

```mermaid
flowchart TD
%% Accessibility
 accTitle: How Gateway policies work in a siloed account configuration
 accDescr: Flowchart describing the order of precedence Gateway applies policies in a siloed account configuration.

%% Flowchart
 subgraph s1["Siloed account A"]
        n1["Block social media"]
  end
 subgraph s2["Siloed account C"]
        n2["Block instant messaging"]
  end
 subgraph s3["Siloed account B"]
        n3["Block news"]
  end
    A["Tenant"] -- Administers --> s1 & s3 & s2

    n1@{ shape: lean-l}
    n2@{ shape: lean-l}
    n3@{ shape: lean-l}
```

# Access

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/access" product="cloudflare-one" />

---

## More Access resources

For more information, refer to the full Access troubleshooting guide.

<LinkButton href="/cloudflare-one/access-controls/troubleshooting/">
  Full Access troubleshooting guide ❯
</LinkButton>

# Browser Isolation

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/browser-isolation" product="cloudflare-one" />

---

## More Browser Isolation resources

For more information, refer to the full Browser Isolation documentation.

<LinkButton href="/cloudflare-one/remote-browser-isolation/troubleshooting/">
  Browser Isolation troubleshooting ❯
</LinkButton>

# CASB

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/casb" product="cloudflare-one" />

---

## More CASB resources

For more information, refer to the full CASB documentation.

<LinkButton href="/cloudflare-one/integrations/cloud-and-saas/troubleshooting/">
  CASB troubleshooting ❯
</LinkButton>

# Contact Cloudflare Support

import { Render } from "~/components";

If you cannot resolve an issue using our troubleshooting guides, you can [open a support case](/support/contacting-cloudflare-support/).

To help us investigate your issue quickly, please collect and provide the following information when you contact Cloudflare Support.

## 1. Gather general information

For all issues, please include:

- **Timestamp (UTC)**: The exact time the issue occurred.
- **Detailed description**: A clear description of the problem and the steps to reproduce it.
- **Actual vs. Expected**: What happened versus what you expected to happen.
- **Problem frequency**: How often does the issue occur?
- **Screenshots**: Any relevant screenshots or videos of the error.
- **Example URLs**: Specific URLs where the issue is occurring.

## 2. Collect product diagnostics

Depending on the product, providing diagnostic files is critical for a technical investigation.

### Cloudflare One Client (WARP)
If the issue involves the Cloudflare One Client, run the `warp-diag` command on the affected device and attach the resulting `.zip` file to your case. For more information, refer to [Diagnostic logs](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/diagnostic-logs/).

### Cloudflare Tunnel
If the issue involves Cloudflare Tunnel, run the `cloudflared tunnel diag` command and provide the generated report. For more information, refer to [Tunnel diagnostic logs](/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/diag-logs/).

### Access and Gateway
For issues related to authentication loops, blocked websites, or policy enforcement:

- **HAR file**: Provide a [HAR file](/support/troubleshooting/general-troubleshooting/gathering-information-for-troubleshooting-sites/#generate-a-har-file) captured while reproducing the issue.
- **Ray ID**: If you see a Cloudflare error page, provide the **Ray ID** displayed at the bottom of the page.
- **Identity Provider logs**: Relevant logs from your identity provider (IdP) if the issue involves login failures.
- **Request ID**: For Gateway issues, you can find the `request_id` (HTTP logs) or `query_id` (DNS logs) in your [Gateway logs](/cloudflare-one/traffic-controls/troubleshooting/).

### Digital Experience Monitoring (DEX)
For issues with DEX tests or device monitoring, provide a [remote capture](/cloudflare-one/insights/dex/diagnostics/client-packet-capture/) from the Zero Trust dashboard.

---

For more information, refer to [Contacting Cloudflare Support](/support/contacting-cloudflare-support/).

# DEX

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/dex" product="cloudflare-one" />

---

## More DEX resources

For more information, refer to the full DEX documentation.

<LinkButton href="/cloudflare-one/insights/dex/troubleshooting/">
  DEX troubleshooting ❯
</LinkButton>

# DLP

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/dlp" product="cloudflare-one" />

---

## More DLP resources

For more information, refer to the full DLP documentation.

<LinkButton href="/cloudflare-one/data-loss-prevention/troubleshoot-dlp/">
	DLP troubleshooting ❯
</LinkButton>

# Email Security

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/email-security" product="cloudflare-one" />

---

## More Email Security resources

For more information, refer to the full Email Security documentation.

<LinkButton href="/cloudflare-one/email-security/troubleshooting/">
  Email Security troubleshooting ❯
</LinkButton>

# Gateway

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/gateway" product="cloudflare-one" />

---

## More Gateway resources

For more information, refer to the full Gateway troubleshooting guide.

<LinkButton href="/cloudflare-one/traffic-controls/troubleshooting/">
  Full Gateway troubleshooting guide ❯
</LinkButton>

# Troubleshooting

import { LinkButton, DirectoryListing } from "~/components";

Cloudflare One provides troubleshooting guides to help you diagnose and resolve common connectivity, configuration, and security issues across your Zero Trust organization.

If you cannot resolve an issue using these guides, you can collect diagnostic information and [contact Cloudflare Support](/cloudflare-one/troubleshooting/contact-support/).

<DirectoryListing />

# Tunnel

import { Render, LinkButton } from "~/components";

Explore common issues and solutions for Cloudflare Tunnel.

<Render 
  file="tunnel/common-errors" 
  product="cloudflare-one" 
  params={{
    logsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/logs/",
    protocolURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/protocols/",
    configFileURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/",
    originServerNameURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/",
    caPoolURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/",
    noTLSVerifyURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/origin-parameters/",
    httpsOriginURL: "/tunnel/troubleshooting/https-origins/",
    ulimitsURL: "/cloudflare-one/networks/connectors/cloudflare-tunnel/availability/ulimits/"
  }} 
/>

---

## More Tunnel resources

For more information, refer to the full Tunnel troubleshooting guide.

<LinkButton href="/cloudflare-one/networks/connectors/cloudflare-tunnel/troubleshoot-tunnels/">
  Full Tunnel troubleshooting guide ❯
</LinkButton>

# Cloudflare One Client

import { Render, LinkButton } from "~/components";

<Render file="troubleshooting/warp-client" product="cloudflare-one" />

---

## More Cloudflare One Client resources

For more information, refer to the full Cloudflare One Client documentation.

<LinkButton href="/cloudflare-one/team-and-resources/devices/cloudflare-one-client/troubleshooting/">
  Cloudflare One Client troubleshooting ❯
</LinkButton>

# Connectivity

import { Render, LinkButton } from "~/components";

<Render
	file="troubleshoot-connectivity"
	product="networking-services"
	params={{
		productName: "Cloudflare WAN",
		tunnelHealthLink: "/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/",
		routingBgpLink: "/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/routing-and-bgp/",
		checkHealthDashLink: "/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/",
		tunnelHealthChecksLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/",
		networkAnalyticsLink: "/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/"
	}}
/>

---

## More WAN resources

For more information, refer to the full Cloudflare WAN documentation.

<LinkButton href="/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/connectivity/">
  Full connectivity troubleshooting guide ❯
</LinkButton>

# Cloudflare WAN

import { DirectoryListing } from "~/components"

Explore resources to help you resolve issues with Cloudflare WAN (formerly Magic WAN) connectivity, routing, and tunnel health.

<DirectoryListing />

# IPsec

import { Render, LinkButton } from "~/components"

<Render file="ipsec-logs-troubleshooting"
	product="networking-services"
	params={{
		supportedIpsecConfigLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters",
		antiReplayLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/",
		tunnelHealthLink: "/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/",
		mtuMssLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/mtu-mss/"
	}}
/>

---

## More WAN resources

For more information, refer to the full Cloudflare WAN documentation.

<LinkButton href="/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/ipsec-troubleshoot/">
  Full IPsec troubleshooting guide ❯
</LinkButton>

# Routing and BGP

import { LinkButton } from "~/components";

This guide helps you diagnose and resolve common routing and BGP issues with Cloudflare WAN. These issues can affect traffic delivery, cause unexpected latency, or result in connectivity loss.

## Quick diagnostic checklist

If you are experiencing routing or BGP issues, check these items first:

1. **BGP session state**: Verify session is **Established**, not stuck in **Connect** or **Active**.
2. **Firewall rules**: Ensure TCP port `179` is permitted bidirectionally between your router and Cloudflare.
5. **Tunnel or CNI health**: Check that underlying connectivity is healthy. Degraded tunnels affect route priority.
6. **Static route conflicts**: Static routes take precedence over BGP routes at equal priority.

## Resolve common issues

### BGP session not establishing

This section covers BGP peering sessions between your network and Cloudflare, established over [CNI](/network-interconnect/) or tunnels.

#### Symptoms

- BGP session never reaches **Established** state
- No routes being advertised or received
- Router logs show repeated connection attempts

#### BGP session states

| State | Meaning | Action |
| :--- | :--- | :--- |
| **Established** | Session up, exchanging routes | Normal operation |
| **Active** | Attempting to initiate connection | Check firewall rules, verify neighbor IP |
| **Connect** | TCP connection in progress | Check port `179` access, verify peering IP |
| **Idle** | Session down, no connection attempts | Check configuration, verify BGP is enabled |

#### Solution

1. Verify your firewall permits TCP port `179` bidirectionally between your router and the Cloudflare peering address.
2. Confirm the neighbor IP matches the Cloudflare-provided peering address exactly.
3. Verify your ASN configuration matches the dashboard settings. Only eBGP is supported, so your ASN must differ from the Cloudflare account ASN.
4. If using MD5 authentication, verify the password matches on both sides.

### Unexpected traffic routing or latency

#### Symptoms

- Traffic from specific regions routed through distant data centers
- Higher than expected latency for regional users
- Traffic not using the closest tunnel or CNI

#### Causes

- Tunnel health degradation causing route deprioritization
- Regional route scoping misconfiguration
- BGP route priorities not set as expected
- Static routes overriding BGP routes

#### Solution

1. **Check tunnel health**: Degraded tunnels have 500,000 added to their route priority. Down tunnels have 1,000,000 added. Traffic shifts to healthier paths, which may be in different regions. Refer to <a href="/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/">Troubleshoot tunnel health</a> for diagnostic steps.

2. **Review route priorities**: Lower priority values indicate higher preference. Verify your routes have the expected priority configuration.

   - Default BGP route priority: `100`
   - Static routes at priority `100` take precedence over BGP routes at `100`

3. **Check regional scoping**: If you use region-scoped routes, ensure all regions have route coverage. Traffic arriving at a region without a matching route is dropped.

4. **Use Network Analytics**: Review traffic patterns to identify where traffic is landing and which paths it follows. Refer to <a href="/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/">Network Analytics</a> for usage instructions.

### CNI link failures

#### Symptoms

- CNI shows down in dashboard
- BGP session over CNI drops
- Traffic fails over to tunnels or alternate CNIs

#### CNI issue layers

CNI issues can occur at multiple layers:

| Issue type | Impact | What to check |
| :--- | :--- | :--- |
| Physical link down | All traffic over that CNI affected | Light levels, cross-connect status |
| BGP session down | Dynamic routes withdrawn | BGP neighbor state on your router |
| Prefixes withdrawn | Specific routes unavailable | BGP advertised and received routes |

A healthy physical link can still have BGP issues. A healthy BGP session can exist while specific prefixes are withdrawn.

#### Solution

**Check physical layer (your side):**

:::note
In the case of interconnects provisioned by third parties, you may need to request that your provider carry these steps out.
:::

1. Verify the interface is administratively up on your router.
2. Check optical light levels (Tx/Rx dBm). Abnormal readings indicate fiber or transceiver issues.
3. If light levels are low or absent on your receive side, contact your data center to verify cross-connect status.

**Check BGP session:**

1. Verify BGP neighbor state on your router shows **Established**.
2. Check for MD5 authentication mismatches if authentication is configured.
3. Review BGP logs for error messages indicating why the session may have dropped.

**Check for maintenance:**

1. Review [Cloudflare Status](https://www.cloudflarestatus.com/) for scheduled maintenance affecting your CNI location.
2. Some maintenance events may temporarily affect CNI connectivity even when marked as non-disruptive.

Refer to [Network Interconnect](/network-interconnect/) for CNI configuration and setup information.

### Static and BGP route conflicts

#### Symptoms

- BGP routes not being used despite being learned
- Traffic not following expected BGP path
- Route changes not taking effect as expected

#### Cause

Cloudflare prefers static routes when static and BGP routes share the same prefix and priority. This ensures manually configured routes take precedence unless explicitly deprioritized.

#### Solution

Adjust route priorities based on your preference:

- **To prefer BGP routes**: Set static route priority to a higher number (for example, `150` or `200`). Higher numbers indicate lower preference.
- **To prefer static routes**: Keep static route priority at or below `100`. BGP routes default to priority `100`.

| Route type | Prefix | Priority | Selected |
| :--- | :--- | :--- | :--- |
| Static | `10.0.0.0/24` | `100` | Yes (static wins ties) |
| BGP | `10.0.0.0/24` | `100` | No |

To make the BGP route preferred in this example, change the static route priority to `150` or higher, or remove the static route entirely.

Refer to <a href="/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#route-prioritization">Route prioritization</a> for detailed information on how priorities work.

## CNI, tunnel, and BGP health

Understanding the relationship between these components helps diagnose routing issues:

| Component | What it monitors | Impact when unhealthy |
| :--- | :--- | :--- |
| **CNI health** | Physical or virtual interconnect link status | BGP session may drop. All traffic over that CNI is affected. |
| **Tunnel health** | Logical GRE or IPsec tunnel through health check probes | Route priority penalized. Traffic steers to healthier tunnels. |
| **BGP session** | Control plane connectivity for dynamic routing | Dynamic routes withdrawn. Static routes remain unaffected. |

A healthy CNI can have an unhealthy tunnel if health check probes are blocked or misconfigured. BGP routes can be withdrawn even when the underlying physical link is operational.

## Gather information for support

If you have worked through this guide and still experience routing issues, gather the following information before contacting Cloudflare support.

### Required information

1. **Account ID** and affected prefix(es), tunnel name(s), or CNI identifier(s)
2. **Timestamps** (in UTC) when the issue occurred
3. **BGP configuration details:**
   - Your ASN and Cloudflare peering ASN
   - Neighbor IP addresses
   - Sanitized router configuration (remove passwords and keys)
4. **Current state information:**
   - BGP session state from your router
   - Dashboard screenshots showing prefix, route, or tunnel status

### Helpful diagnostic data

- **Router logs**: BGP neighbor logs covering the incident timeframe
- **Traceroute results**: From affected source networks to your prefix
- **For CNI issues**: Optical light level readings from your equipment

### Router diagnostic commands

Collect output from these commands (syntax varies by vendor):

```sh
# Show BGP neighbor status
show bgp neighbors

# Show BGP summary
show bgp ipv4 unicast summary

# Show specific prefix in BGP table
show bgp ipv4 unicast <YOUR_PREFIX>

# Show interface status (for CNI)
show interface <YOUR_INTERFACE_NAME>

# Show received and advertised routes
show bgp ipv4 unicast neighbors <YOUR_NEIGHBOR_IP> routes
show bgp ipv4 unicast neighbors <YOUR_NEIGHBOR_IP> advertised-routes
```

## Resources

- <a href="/cloudflare-one/networks/connectors/cloudflare-wan/reference/traffic-steering/#route-prioritization">Traffic steering</a>: Route prioritization, BGP communities, and ECMP behavior
- <a href="/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-routes/">Configure routes</a>: Static route configuration
- [Network Interconnect](/network-interconnect/): CNI setup and BGP peering
- <a href="/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/">Troubleshoot tunnel health</a>: Tunnel-specific diagnostic steps
- <a href="/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/">Network Analytics</a>: Traffic analysis and monitoring
- [Cloudflare Status](https://www.cloudflarestatus.com/): Maintenance and incident notifications

---

## More WAN resources

For more information, refer to the full Cloudflare WAN documentation.

<LinkButton href="/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/routing-and-bgp/">
  Full routing and BGP guide ❯
</LinkButton>

# Tunnel health

import { Render, LinkButton } from "~/components";

<Render
  file="tunnel-health/troubleshoot-tunnel-health"
  product="networking-services"
  params={{
    productName: "Cloudflare WAN",
    healthCheckDefault: "_Bidirectional_",
    tunnelsConfigLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters",
    checkHealthDashLink: "/cloudflare-one/networks/connectors/cloudflare-wan/configuration/common-settings/check-tunnel-health-dashboard/",
    antiReplayLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/anti-replay-protection/",
    tunnelHealthChecksLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/tunnel-health-checks/",
    configureTunnelsLink: "/cloudflare-one/networks/connectors/cloudflare-wan/configuration/how-to/configure-tunnel-endpoints/",
    networkAnalyticsLink: "/cloudflare-one/networks/connectors/cloudflare-wan/analytics/network-analytics/",
    supportedIpsecConfigLink: "/cloudflare-one/networks/connectors/cloudflare-wan/reference/gre-ipsec-tunnels/#supported-configuration-parameters"
  }}
/>

---

## More WAN resources

For more information, refer to the full Cloudflare WAN documentation.

<LinkButton href="/cloudflare-one/networks/connectors/cloudflare-wan/troubleshooting/tunnel-health/">
  Full tunnel health guide ❯
</LinkButton>

# Create custom headers for Cloudflare Access-protected origins with Workers

import { TypeScriptExample, DashButton } from "~/components";

This tutorial covers how to use a [Cloudflare Worker](/workers/) to add custom HTTP headers to traffic, and how to send those custom headers to your origin services protected by [Cloudflare Access](/cloudflare-one/access-controls/policies/).

Some applications and networking implementations require specific custom headers to be passed to the origin, which can be difficult to implement for traffic moving through a Zero Trust proxy. You can configure a Worker to send the [user authorization headers](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/) required by Access.

---

## Before you begin

- Secure your origin server with Cloudflare Access

## Before you begin

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to the **Workers & Pages** page.

   <DashButton url="/?to=/:account/workers-and-pages" />

2. If this is your first Worker, select **Create Worker**. Otherwise, select **Create application**, then select **Create Worker**.

3. Enter an identifiable name for the Worker, then select **Deploy**.

4. Select **Edit code**.

5. Input the following Worker:

<TypeScriptExample>

```ts title="Worker with custom HTTP headers"
export default {
	async fetch(request, env, ctx): Promise<Response> {
		const { headers } = request;
		const cfaccessemail = headers.get("cf-access-authenticated-user-email");

		const requestWithID = new Request(request);
		requestWithID.headers.set("company-user-id", cfaccessemail);

		return fetch(requestWithID);
	},
} satisfies ExportedHandler<Env>;
```

</TypeScriptExample>

6. Select **Save and deploy**.

Your Worker is now ready to send custom headers to your Access-protected origin services.

## Apply the Worker to your hostname

1. Select the Worker you created, then go to **Triggers**.
2. In **Routes**, select **Add route**.
3. Enter the hostname and zone for your origin, then select **Add route**.

The Worker will now insert a custom header into requests that match the defined route. For example:

```http title="Example custom header" {4,5}
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "Accept-Encoding": "gzip",
    "Accept-Language": "en-US,en;q=0.9",
    "Cf-Access-Authenticated-User-Email": "user@example.com",
    "Company-User-Id": "user@example.com",
    "Connection": "keep-alive"
```

# Create and secure an AI agent wrapper using AI Gateway and Zero Trust

import { TabItem, Tabs, Details, Render, DashButton } from "~/components";

This tutorial explains how to use [Cloudflare AI Gateway](/ai-gateway/) and Zero Trust to create a functional and secure website wrapper for an AI agent. Cloudflare Zero Trust administrators can protect access to the wrapper with [Cloudflare Access](/cloudflare-one/access-controls/policies/). Additionally, you can enforce [Gateway policies](/cloudflare-one/traffic-controls/) to control how your users interact with AI agents, including executing AI agents in an isolated browser with [Browser Isolation](/cloudflare-one/remote-browser-isolation/), enforcing [Data Loss Prevention](/cloudflare-one/data-loss-prevention/) profiles to prevent your users from sharing sensitive data, and scanning content to avoid answers from AI agents that violate internal corporate guidelines. Creating an AI agent wrapper is also an effective way to enforce tenant control if you have an enterprise plan for a specific AI provider, such as ChatGPT Enterprise.

This tutorial uses ChatGPT as an example AI agent.

## Before you begin

Make sure you have:

- A [Cloudflare Zero Trust organization](/cloudflare-one/setup/).
- An API key for your desired AI provider, such as an [OpenAI API key](https://platform.openai.com/api-keys) for ChatGPT.

## 1. Create an AI gateway

First, create an AI gateway to control your AI app.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to the **AI Gateway** page.

   <DashButton url="/?to=/:account/ai/ai-gateway" />

2. Select **Create Gateway**.
3. Name your gateway.
4. Select **Create**.
5. Configure your desired options for the gateway.
6. [Connect your AI provider](/ai-gateway/get-started/#connect-application) to proxy queries to your AI agent of choice using your AI gateway.
7. (Optional) Turn on [Authenticated Gateway](/ai-gateway/configuration/authentication/). The Authenticated Gateway feature ensures your AI gateway can only be called securely by enforcing a token in the form of a request header `cf-aig-authorization`.
   1. Go to **AI** > **AI Gateway**.
   2. Select your AI gateway, then go to **Settings**.
   3. Turn on **Authenticated Gateway**, then choose **Confirm**.
   4. Select **Create authentication token**, then select **Create an AI Gateway authentication token**.
   5. Configure your token and copy the token value. When creating your Worker, you will need to pass this token when calling your AI gateway.

For more information, refer to [Getting started with AI Gateway](/ai-gateway/get-started/).

## 2. (Optional) Use Guardrails to block unsafe or inappropriate content

[Guardrails](/ai-gateway/features/guardrails/) is an built-in AI Gateway security feature that allows Cloudflare to identify unsafe or inappropriate content in prompts and responses based on selected categories.

1. In the Cloudflare dashboard, go to the **AI Gateway** page.

   <DashButton url="/?to=/:account/ai/ai-gateway" />

2. Select your AI gateway.
3. Go to **Guardrails**.
4. Turn on Guardrails.
5. Select **Change** to configure the categories you would like to filter for both prompts and responses.

## 3. Build a Worker to serve the wrapper

### 1. Create the Worker

In order to build the Worker, you will need to choose if you want to build it locally using [Wrangler](/workers/wrangler/install-and-update/) or remotely using the [dashboard](https://dash.cloudflare.com/).

<Tabs> <TabItem label="Wrangler">

1. In a terminal, log in to your Cloudflare account:

   ```bash
   wrangler login
   ```

2. Initiate the project locally:

   ```bash
   mkdir ai-agent-wrapper
   cd ai-agent-wrapper
   wrangler init
   ```

3. Create a Wrangler configuration file:

   ```toml
   name = "ai-agent-wrapper"
   main = "src/index.js"
   upload_source_maps = true
   compatibility_date = "2023-10-30"

   [vars]
   # Add any environment variables here
   ```

4. Add your AI provider's API key as a [secret](/workers/configuration/secrets/):

   ```bash
   wrangler secret put <OPENAI_API_KEY>
   ```

You can now build the Worker using the `index.js` file created by Wrangler.

</TabItem> <TabItem label="Dashboard">

1. In the Cloudflare dashboard, go to the **Workers & Pages** page.

   <DashButton url="/?to=/:account/workers-and-pages" />

2. Select **Create**.
3. In **Workers**, choose the **Hello world** template.
4. Name your worker, then select **Deploy**.
5. Select your Worker, then go to the **Settings** tab.
6. Go to **Variables and Secrets**, then select **Add**.
7. Choose _Secret_ as the type, name your secret (for example, `OPENAI_API_KEY`), and enter the value of your AI provider's API key in **Value**.

You can now build the Worker using the online code editor by selecting **Edit code** on your Worker page.

</TabItem> </Tabs>

### 2. Build the Worker

The following is an example starter Worker that serves a simple front-end to allow a user to interact with an AI provider behind AI Gateway. This example uses OpenAI as its AI provider:

```javascript {9} collapse={54-230}
export default {
	async fetch(request, env) {
		if (request.url.endsWith("/api/chat")) {
			if (request.method === "POST") {
				try {
					const { messages } = await request.json();

					const response = await fetch(
						"https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/openai/chat/completions",
						{
							method: "POST",
							headers: {
								"Content-Type": "application/json",
								Authorization: `Bearer ${env.OPENAI_API_KEY}`,
							},
							body: JSON.stringify({
								model: "gpt-4o-mini",
								messages: messages,
							}),
						},
					);

					if (!response.ok) {
						throw new Error(`AI Gateway Error: ${response.status}`);
					}

					const result = await response.json();
					return new Response(
						JSON.stringify({
							response: result.choices[0].message.content,
						}),
						{
							headers: { "Content-Type": "application/json" },
						},
					);
				} catch (error) {
					return new Response(JSON.stringify({ error: error.message }), {
						status: 500,
						headers: { "Content-Type": "application/json" },
					});
				}
			}
			return new Response("Method not allowed", { status: 405 });
		}

		return new Response(HTML, {
			headers: { "Content-Type": "text/html" },
		});
	},
};

const HTML = `<!DOCTYPE html>
  <html lang="en" data-theme="dark">
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>ChatGPT Wrapper</title>
      <style>
          :root {
              --background-color: #1a1a1a;
              --chat-background: #2d2d2d;
              --text-color: #ffffff;
              --input-border: #404040;
              --message-ai-background: #404040;
              --message-ai-text: #ffffff;
          }

          body {
              font-family: system-ui, sans-serif;
              margin: 0;
              padding: 20px;
              background: var(--background-color);
              display: flex;
              flex-direction: column;
              align-items: center;
              gap: 20px;
              color: var(--text-color);
          }

          .chat-container {
              width: 100%;
              max-width: 800px;
              background: var(--chat-background);
              border-radius: 10px;
              box-shadow: 0 2px 10px rgba(0,0,0,0.1);
              height: 80vh;
              display: flex;
              flex-direction: column;
          }

          .chat-header {
              padding: 15px 20px;
              border-bottom: 1px solid var(--input-border);
              background: var(--chat-background);
              border-radius: 10px 10px 0 0;
              text-align: center;
          }

          .chat-messages {
              flex-grow: 1;
              overflow-y: auto;
              padding: 20px;
          }

          .message {
              margin-bottom: 20px;
              padding: 10px 15px;
              border-radius: 10px;
              max-width: 80%;
          }

          .user-message {
              background: #007AFF;
              color: white;
              margin-left: auto;
          }

          .ai-message {
              background: var(--message-ai-background);
              color: var(--message-ai-text);
          }

          .input-container {
              padding: 20px;
              border-top: 1px solid var(--input-border);
              display: flex;
              gap: 10px;
          }

          input {
              flex-grow: 1;
              padding: 10px;
              border: 1px solid var(--input-border);
              border-radius: 5px;
              font-size: 16px;
              background: var(--chat-background);
              color: var(--text-color);
          }

          button {
              padding: 10px 20px;
              background: #007AFF;
              color: white;
              border: none;
              border-radius: 5px;
              cursor: pointer;
              font-size: 16px;
          }

          button:disabled {
              background: #ccc;
          }

          .error {
              color: red;
              padding: 10px;
              text-align: center;
          }
      </style>
  </head>
  <body>
      <div class="chat-container">
          <div class="chat-header">
              <h2>AI Assistant</h2>
          </div>
          <div class="chat-messages" id="messages"></div>
          <div class="input-container">
              <input type="text" id="userInput" placeholder="Type your message..." />
              <button onclick="sendMessage()" id="sendButton">Send</button>
          </div>
      </div>

      <script>
          let messages = [];
          const messagesDiv = document.getElementById('messages');
          const userInput = document.getElementById('userInput');
          const sendButton = document.getElementById('sendButton');

          userInput.addEventListener('keypress', (e) => {
              if (e.key === 'Enter') sendMessage();
          });

          async function sendMessage() {
              const content = userInput.value.trim();
              if (!content) return;

              userInput.disabled = true;
              sendButton.disabled = true;

              messages.push({ role: 'user', content });
              appendMessage('user', content);
              userInput.value = '';

              try {
                  const response = await fetch('/api/chat', {
                      method: 'POST',
                      headers: { 'Content-Type': 'application/json' },
                      body: JSON.stringify({
                          messages
                      })
                  });

                  if (!response.ok) {
                      throw new Error('API request failed');
                  }

                  const result = await response.json();
                  const aiMessage = result.response;

                  messages.push({ role: 'assistant', content: aiMessage });
                  appendMessage('ai', aiMessage);
              } catch (error) {
                  appendMessage('ai', 'Sorry, there was an error processing your request.');
                  console.error('Error:', error);
              }

              userInput.disabled = false;
              sendButton.disabled = false;
              userInput.focus();
          }

          function appendMessage(role, content) {
              const messageDiv = document.createElement('div');
              messageDiv.className = 'message ' + role + '-message';
              messageDiv.textContent = content;
              messagesDiv.appendChild(messageDiv);
              messagesDiv.scrollTop = messagesDiv.scrollHeight;
          }
      </script>
  </body>
  </html>`;
```

Note that the account ID and gateway ID need to be replaced in the AI Gateway endpoint. You can add these as [environment variables](/workers/configuration/environment-variables/) or [secrets](/workers/configuration/secrets/) in Workers. If you chose to use Authenticated Gateway when creating your AI gateway, make sure to also add your token as a secret and pass its value to the AI gateway in the `cf-aig-authorization` header.

### 3. Publish the Worker

Once the Worker code is complete, you need to make the Worker addressable using a hostname controllable by Cloudflare Access.

<Tabs> <TabItem label="Wrangler">

Edit the Wrangler configuration file and add the following information to ensure that the Worker is only accessible using the custom hostname:

```diff lang="toml"
name = "ai-agent-wrapper"
main = "src/index.js"
compatibility_date = "2023-10-30"
workers_dev = false

+# Replace with your custom domain
+routes = [
+  { pattern = "<YOUR_CUSTOM_DOMAIN>", custom_domain = true }
+]

[vars]
# Add any environment variables here
```

To publish the worker, run `wrangler deploy`.

</TabItem> <TabItem label="Dashboard">

If you built your Worker remotely using the [code editor](/workers/get-started/dashboard/) available in the Cloudflare dashboard, you can deploy it by selecting **Deploy**.

To ensure that the Worker is only accessible from the custom hostname:

1. In the Cloudflare dashboard, go to the **Workers & Pages** page.

   <DashButton url="/?to=/:account/workers-and-pages" />

2. Select your Worker.
3. Go to **Settings**.
4. Within **Domains & Routes**, select **Add**.
5. Choose **Custom domain**.
6. Enter your desired custom domain name.
7. Select **Add domain**.

The Worker is now behind an addressable public hostname. Make sure to turn off both **workers.dev** and **Preview URLs** so that the Worker can only be accessed with its custom domain.

</TabItem> </Tabs>

## 4. Secure the wrapper with Access

To secure the AI agent wrapper to ensure that only trusted users can access it:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application**.
3. Select **Self-hosted and private**.
4. Select **Add public hostname** and enter the custom domain you set for your Worker.
5. [Configure your Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for your Worker.
6. Add [Access policies](/cloudflare-one/access-controls/policies/policy-management/) to control who can connect to your application.

Now your AI wrapper can only be accessed by your users that successfully match your Access policies.

## 5. Block access to public AI agents with Gateway

You can now block access to all unauthorized public AI agents with a Gateway [HTTP policy](/cloudflare-one/traffic-controls/http-policies/).

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Firewall policies** > **HTTP**.
2. Select **Add a policy**.
3. Add the following policy:

   | Selector           | Operator | Value                     | Action |
   | ------------------ | -------- | ------------------------- | ------ |
   | Content Categories | in       | _Artificial Intelligence_ | Block  |

4. Select **Create policy**.

This ensures that public AI agents are not accessible using a managed endpoint.

Alternatively, you can prevent users from using public AI agents by displaying a [custom block message](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/#customize-the-block-page), [redirect](/cloudflare-one/reusable-components/custom-pages/gateway-block-page/#redirect-to-a-block-page), or a [user notification](/cloudflare-one/traffic-controls/http-policies/#cloudflare-one-client-block-notifications) directing users to the AI agent wrapper.

## 6. Enforce Data Loss Prevention and Clientless Browser Isolation

Now that you have full control over access to your AI agent wrapper, you can enforce extra security methods such as Data Loss Prevention (DLP) and Clientless Web Isolation to protect and control data shared with the AI agent.

### Apply Data Loss Prevention profiles

You can use [Data Loss Prevention (DLP)](/cloudflare-one/data-loss-prevention/) to prevent your users from sending sensitive data to the AI agent.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Data loss prevention** > **Profiles**.
2. Ensure that the [DLP profiles](/cloudflare-one/data-loss-prevention/dlp-profiles/) you want to enforce are properly configured.
3. Add an HTTP policy to enforce the DLP profile for the hostname for your wrapper. For example:

   | Selector    | Operator | Value                    | Logic | Action |
   | ----------- | -------- | ------------------------ | ----- | ------ |
   | Host        | is       | `ai-wrapper.example.com` | And   | Block  |
   | DLP Profile | in       | _AI DLP profile_         |       |        |

4. Select **Create policy**.

For more information on creating DLP policies, refer to [Scan HTTP traffic](/cloudflare-one/data-loss-prevention/dlp-policies/).

### Execute in a clientless isolated browser

Because you published your wrapper as a self-hosted Access application, you can execute it in an [isolated session](/cloudflare-one/remote-browser-isolation/setup/clientless-browser-isolation/) for your users by creating an [Access policy](/cloudflare-one/access-controls/policies/) and configuring it for your application.

<Render file="clientless-browser-isolation" product="cloudflare-one" />

3. Go to **Access controls** > **Policies**.
4. Select **Add a policy**.
5. Set the **Action** to _Allow_.
6. In **Add rules**, add identity rules to define who the application should be isolated for.
7. In **Additional settings (optional)**, turn on **Isolate application**.

Once the Access policy has been created, you can attach it to your wrapper.

1. Go to **Access controls** > **Applications**.
2. Choose your wrapper application, then select **Configure**.
3. In **Policies**, select **Select existing policies**.
4. Choose the Access policy you previously created.
5. Select **Confirm**, then select **Save**.

Because Clientless Web Isolation traffic applies your Gateway HTTP policies, your configured DLP profiles will apply to isolated sessions.

For more information on isolating an Access application, refer to [Isolate self-hosted application](/cloudflare-one/access-controls/policies/isolate-application/).

## Additional benefits

Organizations that adopt Cloudflare to secure access to AI agents will benefit from improved visibility and configurability.

### Visibility

Zero Trust will log all [Access events](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/) and [DLP detections](/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/#http-logs). In addition, AI Gateway provides [visibility](/ai-gateway/observability/logging/) into user prompts, model response, token usage, and costs.

Logs can be exported to external providers with [Logpush](/logs/logpush/).

### Configurability

You can configure your wrapper to use a [different AI provider](/ai-gateway/usage/providers/) or give your users the option to choose between multiple AI providers, including AI models running directly on Cloudflare's global network with [Workers AI](/workers-ai/). With this, you can control costs related to AI usage or adopt newer models without impacting your users or the access controls already put in place.

# Connect through Cloudflare Access using a CLI

Cloudflare's `cloudflared` command-line tool allows you to interact with endpoints protected by Cloudflare Access. You can use `cloudflared` to interact with a protected application's API.

These instructions are not meant for configuring a service to run against an API. The token in this example is tailored to user identity and intended only for an end user interacting with an API via a command-line tool.

**This walkthrough covers how to:**

- Connect to resources secured by Cloudflare Access from a CLI

**Time to complete:**

30 minutes

---

## Authenticate a session from the command line

Once you have [installed `cloudflared`](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/), you can use it to retrieve a Cloudflare Access [application token](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/). This walkthrough uses the domain `example.com` as a stand-in for a protected API.

1. To generate a token, run the following command:

   ```sh
   cloudflared access login https://example.com
   ```

   With this command, `cloudflared` launches a browser window containing the same Access login page found when attempting to access a web application.

2. Select your identity provider and log in.

If the browser window does not launch, you can use the unique URL that is automatically printed to the command line.

1. Once you have successfully authenticated, the browser returns the token to `cloudflared` in a cryptographic transfer and stores it.

The token is valid for the [session duration](/cloudflare-one/access-controls/access-settings/session-management/) configured by the Access administrator.

## Access your API

Once you have retrieved a token, you can access the protected API. The `cloudflared` command-line tool includes a wrapper for transferring data via `curl`, which uses URL syntax (for more, see the [curl](https://github.com/curl/curl) GitHub project). The wrapper injects the token into the `curl` request as a query argument named _token_. You can invoke the wrapper as follows:

```sh
cloudflared access curl http://example.com
```

It is possible also to use the `put` command with `cloudflared` for any Unix tool to include the token in the request.

Read on for other available commands.

## Available commands

### login

The `login` command initiates the login flow for an application behind Access.

```sh
cloudflared access login http://example.com
```

### curl

The `curl` command invokes the client wrapper and includes the token in the request automatically.

```sh
cloudflared access curl http://example.com
```

### token

The `token` command retrieves the token scoped to that specific application for use in other command-line tools.

```sh
cloudflared access token -app=http://example.com
```

## Using the token as an environment variable

It is possible to save the token as an environment variable for convenience and concision in scripts that access a protected application.

Set up a token as an environment variable as follows:

1. Run the following command to export the token to the shell environment:

   ```sh
   export TOKEN=$(cloudflared access token -app=http://example.com)
   ```

2. Confirm the token was saved with the following:

   ```sh
   echo $TOKEN
   ```

Once you have exported the token to your environment, use the variable with the Cloudflare Access request header in the script to access a protected endpoint, as in the following example:

```sh
curl -H "cf-access-token: $TOKEN" https://example.com/rest/api/2/item/foo-123
```

# Access a web application via its private hostname without the Cloudflare One Client

import { DashButton, Render } from "~/components";

With Cloudflare Browser Isolation and resolver policies, users can connect to private web-based applications via their private hostnames without needing to install the Cloudflare One Client. By the end of this tutorial, users who pass your Gateway DNS and network policies will be able to access your private application at `https://<your-team-name>.cloudflareaccess.com/browser/https://internalrecord.com`.

## Before you begin

Make sure you have:

- [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/) enabled on your account
- [Resolver policies](/cloudflare-one/traffic-controls/resolver-policies/) enabled on your account
- An HTTP or HTTPS application that users access through a browser

## Create a Cloudflare Tunnel

First, install `cloudflared` on a server in your private network:

<Render file="tunnel/create-tunnel" product="cloudflare-one" />

## Add private network routes

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. Select **Create route** > **Tunnel CIDR**. Select the tunnel you just created, enter the private IP/CIDR of your application server (for example, `10.128.0.175/32`), and select **Create route**.

3. Repeat to create a second route for the private IP/CIDR of your DNS server.

The application and DNS server are now connected to Cloudflare.

## Enable Clientless Web Isolation

<Render file="clientless-browser-isolation" product="cloudflare-one" />

3. For **Permissions**, select **Manage**.

4. Select **Add a rule**.

5. Create an expression that defines who can open the Clientless Web Isolation browser. For example,

   | Rule action | Rule type | Selector         | Value          | Action           |
   | ----------- | --------- | ---------------- | -------------- | ---------------- |
   | Allow       | Include   | Emails ending in | `@example.com` | Select **Save**. |

To test, open a browser and go to `https://<team-name>.cloudflareaccess.com/browser/https://<private-IP-of-application>`.

## Create a Gateway resolver policy

1. Go to **Traffic policies** > **Resolver policies**.

2. Select **Add a policy**.

3. Create an expression to match against the private [domain](/cloudflare-one/traffic-controls/resolver-policies/#domain) or [hostname](/cloudflare-one/traffic-controls/resolver-policies/#host) of the application:

   | Selector | Operator | Value                |
   | -------- | -------- | -------------------- |
   | Domain   | in       | `internalrecord.com` |

4. In **Select DNS resolver**, select _Configure custom DNS resolvers_.

5. Enter the private IP address of your DNS server.

6. In the dropdown menu, select _`<IP-address> - Private`_.

7. (Optional) Enter a custom port.

8. Select **Create policy**.

To test, open a browser and go to `https://<team-name>.cloudflareaccess.com/browser/https://internalrecord.com`.

## Create a Gateway network policy (recommended)

1. Go to **Traffic policies** > **Firewall policies** > **Network**.

2. Add a [network policy](/cloudflare-one/traffic-controls/network-policies/) that targets the private IP address of your application. You can optionally include any ports or protocols relevant for application access. For example,

   | Selector         | Operator      | Value           | Logic | Action |
   | ---------------- | ------------- | --------------- | ----- | ------ |
   | Destination IP   | in            | `10.128.0.175`  | And   | Allow  |
   | Destination Port | in            | `80`            | Or    |        |
   | User Email       | matches regex | `.*example.com` |       |        |

:::note

Device posture checks are not supported because they require the Cloudflare One Client.

:::

For best practices on securing private applications, refer to [Build secure access policies](/learning-paths/replace-vpn/build-policies/).

## Connect as a user

Users can now access the application at the following URL:

`https://<team-name>.cloudflareaccess.com/browser/https://internalrecord.com`

The application will load in an isolated browser. You can optionally [configure remote browser controls](/cloudflare-one/remote-browser-isolation/isolation-policies/#policy-settings) such as disabling copy/paste, printing, or keyboard input.

# Deploy the Cloudflare One Client on headless Linux machines

import { GlossaryTooltip, Render, Steps } from "~/components";

This tutorial explains how to deploy the [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) on Linux devices using a service token and an installation script. This deployment workflow is designed for headless servers - that is, servers which do not have access to a browser for identity provider logins - and for situations where you want to fully automate the onboarding process. Because devices will not register through an identity provider, [identity-based policies](/cloudflare-one/traffic-controls/identity-selectors/) and logging will be unavailable.

:::note
This tutorial focuses on deploying the Cloudflare One Client as an endpoint device agent. If you are looking to deploy the Cloudflare One Client as a gateway to a private network, refer to the [Cloudflare Mesh documentation](/mesh/).
:::

## Prerequisites

- [Cloudflare Zero Trust account](/cloudflare-one/setup/#2-create-a-zero-trust-organization)
- Root or `sudo` access on a supported Linux device
- Zero Trust team name
- Service token Client ID and Client Secret

## 1. Create a service token

Fully automated deployments rely on a service token to enroll the Cloudflare One Client in your Zero Trust organization. You can use the same token to enroll multiple devices, or generate a unique token per device if they require different [device profile settings](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/configure/device-profiles/).

To create a service token:

<Render file="access/create-service-token" product="cloudflare-one" />

## 2. Configure device enrollment permissions

Device enrollment permissions determine the users and devices that can register WARP with your Zero Trust organization.

To allow devices to enroll using a service token:

<Steps>

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Team & Resources** > **Devices**. Select the **Management** tab.
2. In **Device enrollment permissions**, select **Manage**.
3. In the **Policies** tab, select **Create new policy**. A new tab will open with the policy creation page.
4. For **Action**, select _Service Auth_.
5. For the **Selector** field, you have two options: you can either allow all service tokens (`Any Access Service Token`) or specific service tokens (`Service Token`). For example:

   | Rule Action  | Rule type | Selector      | Value          |
   | ------------ | --------- | ------------- | -------------- |
   | Service Auth | Include   | Service Token | `<TOKEN-NAME>` |

6. Save the policy.
7. Go back to **Device enrollment permissions** and add the newly created policy to your permissions.
8. Select **Save**.

</Steps>

To configure service-token enrollment with Terraform, refer to [Check for a service token](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/device-enrollment/#check-for-service-token).

## 3. Create an installation script

You can use a shell script to automate WARP installation and registration. The following example shows how to deploy the Cloudflare One Client on Ubuntu 24.04.

<Steps>

1.  In a terminal, create a new `.sh` file using a text editor. For example:
    ```sh
    umask 077
    vim install_warp.sh
    ```
2.  Press `i` to enter insert mode and add the following lines:

        ```bash
        #!/bin/bash
        set -e

        # Download and install the Cloudflare One Client
        function warp() {
        		curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
        		echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
        		sudo apt-get update --assume-yes
        		sudo apt-get install --assume-yes cloudflare-warp
        }

        # Create an MDM file with your Cloudflare One Client deployment parameters
        function mdm() {
                sudo install -m 600 /dev/null /var/lib/cloudflare-warp/mdm.xml
                sudo tee /var/lib/cloudflare-warp/mdm.xml > /dev/null << "EOF"
        <dict>
                <key>auth_client_id</key>
                <string>YOUR_CLIENT_ID</string>
                <key>auth_client_secret</key>
                <string>YOUR_CLIENT_SECRET</string>
        		<key>auto_connect</key>
        		<integer>1</integer>
        		<key>onboarding</key>
        		<false/>
        		<key>organization</key>
        		<string>your-team-name</string>
        		<key>service_mode</key>
        		<string>warp</string>
        </dict>
        EOF
        }

        #main program
        warp
        mdm
        sudo systemctl restart warp-svc
        ```

3.  If you are using Debian or RHEL / CentOS, modify the `warp()` function so that it installs the correct [WARP package](https://pkg.cloudflareclient.com/) for your OS.
4.  Modify the values in the `mdm()` function:
    1. For `auth_client_id` and `auth_client_secret`, replace the string values with the Client ID and Client Secret of your [service token](/cloudflare-one/tutorials/deploy-client-headless-linux/#1-create-a-service-token).
    2. For `organization`, replace `your-team-name` with your Zero Trust <GlossaryTooltip term="team name">team name</GlossaryTooltip>.
    3. (Optional) Add or modify other [Cloudflare One Client deployment parameters](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/) according to your preferences.
5.  Press `esc`, then type `:x` and press `Enter` to save and exit.

</Steps>

## 4. Install WARP

To install the Cloudflare One Client using the example script:

<Steps>

1. Make the script executable:

   ```sh
   chmod 700 install_warp.sh
   ```

2. Run the script:
   ```sh
   sudo ./install_warp.sh
   ```
3. Delete the script because it contains the service token secret:

   ```sh
   rm install_warp.sh
   ```

</Steps>

The Cloudflare One Client is now deployed with the configuration parameters stored in the root-only `/var/lib/cloudflare-warp/mdm.xml`. Assuming [`auto_connect`](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/mdm-deployment/parameters/#auto_connect) is configured, the Cloudflare One Client will automatically connect to your Zero Trust organization.

Verify registration and connection:

```sh
sudo warp-cli --accept-tos registration show
sudo warp-cli --accept-tos status
```

Successful enrollment creates a device in **Zero Trust** > **Team & Resources** > **Devices** with the email `non_identity@<team-name>.cloudflareaccess.com`. Verify that `status` reports `Connected`. A command completion or HTTP redirect alone does not prove registration or connectivity.

The registration and status commands verify enrollment and the connection to Cloudflare. To verify end-to-end traffic, connect to an included destination using the protocol you intend to use. For Cloudflare Mesh, follow [Verify connectivity](/mesh/guides/connect-client-devices/#verify-connectivity).

If the client reports `Registration Missing due to: Does not exist in API`, or the `warp-svc` logs show an HTTP `400` enrollment response, enrollment failed. Confirm that the service token policy uses the _Service Auth_ action, the policy is attached to device enrollment permissions, and the MDM team name and token values are correct. After correcting or confirming the configuration, restart the service and repeat both verification commands:

```sh
sudo systemctl restart warp-svc
sudo warp-cli --accept-tos registration show
sudo warp-cli --accept-tos status
```

# Detect MCP traffic in Gateway logs

import { TypeScriptExample } from "~/components";

Organizations may lack visibility into Model Context Protocol (MCP) traffic, which can allow employees to connect to remote MCP servers outside of IT oversight. These connections risk the exfiltration of sensitive internal data and credentials, tool injection attacks or software supply chain risks.

As an IT administrator, you want to identify shadow MCP traffic to prevent unauthorized data exfiltration while still supporting governed use cases. In this tutorial, you will use the Cloudflare GraphQL Analytics API to scan Gateway HTTP logs for MCP traffic patterns, create DLP profiles that detect MCP JSON-RPC methods, and classify traffic to differentiate between authorized traffic sent to MCP server portals and traffic sent to "shadow" remote MCP servers.

## Prerequisites

- A Cloudflare account with a [Zero Trust organization](/cloudflare-one/setup/)
- [Gateway](/cloudflare-one/traffic-controls/) with HTTP filtering enabled and actively proxying user traffic
- An [API token](/fundamentals/api/get-started/create-token/) with the following permissions:
  - Account-level `Zero Trust: Read`
  - Account-level `DLP: Write`
  - Account-level `Gateway: Write`
- Your Cloudflare account ID (available in the [Cloudflare dashboard](https://dash.cloudflare.com/login) under **Account Home**)
- Familiarity with [GraphQL Analytics API](/analytics/graphql-api/) queries
- A working knowledge of TypeScript and REST APIs

## 1. Review the Gateway HTTP dataset

The `gatewayHttpRequestsAdaptiveGroups` dataset in the GraphQL Analytics API provides aggregated Gateway HTTP log data. Use this dataset to query for MCP-related traffic patterns:

- **Dimensions**: `httpHost`, `httpRequestURI`, `action`, `users`, `dlpProfiles`
- **Time range**: Up to 30 days of historical data
- **Grouping**: Aggregates results by dimension values
- **Filtering**: Supports `OR`, `AND`, and `like` operators

## 2. Build the MCP detection query

MCP traffic can be identified by three signals:

1. **Domain patterns**: Hostnames containing `mcp` (for example, `mcp.datadog.com`)
2. **URL paths**: Standard MCP endpoints such as `/mcp`, `/mcp/sse`, and `/sse`
3. **DLP matches**: JSON-RPC methods in request bodies (covered in a later step)

The following GraphQL query scans Gateway logs for the first two signals:

<TypeScriptExample>

```ts
const query = `
  query MCPTrafficScan($accountTag: string, $since: string, $until: string) {
    viewer {
      accounts(filter: { accountTag: $accountTag }) {
        gatewayHttpRequestsAdaptiveGroups(
          filter: {
            datetime_geq: $since
            datetime_leq: $until
            OR: [
              { httpHost_like: "%mcp%" }
              { httpRequestURI_like: "%/mcp%" }
              { httpRequestURI_like: "%/sse%" }
            ]
          }
          limit: 10000
        ) {
          dimensions {
            httpHost
            action
            users
          }
          count
        }
      }
    }
  }
`;

const variables = {
	accountTag: "<YOUR_ACCOUNT_ID>",
	since: "<START_DATE>", // ISO-8601 format, for example 2025-03-08T00:00:00Z
	until: "<END_DATE>", // Up to 30 days after start date
};

const response = await fetch("https://api.cloudflare.com/client/v4/graphql", {
	method: "POST",
	headers: {
		Authorization: `Bearer ${apiToken}`,
		"Content-Type": "application/json",
	},
	body: JSON.stringify({ query, variables }),
});

const data = await response.json();
const groups =
	data.data?.viewer?.accounts?.[0]?.gatewayHttpRequestsAdaptiveGroups || [];
```

</TypeScriptExample>

Replace `<YOUR_ACCOUNT_ID>` with your Cloudflare account ID. Replace `<START_DATE>` and `<END_DATE>` with ISO-8601 timestamps covering your desired time range (up to 30 days).

## 3. Process the query results

Each group in the response represents aggregated traffic for a specific `httpHost` and `action` combination. Parse the results to identify unblocked MCP connections:

<TypeScriptExample>

```ts
interface MCPTrafficHit {
	domain: string;
	requestCount: number;
	users: string[];
	actions: {
		allowed: number;
		blocked: number;
	};
}

const hits: MCPTrafficHit[] = groups.map((group: any) => ({
	domain: group.dimensions.httpHost,
	requestCount: group.count,
	users: group.dimensions.users || [],
	actions: {
		allowed: group.dimensions.action === "allow" ? group.count : 0,
		blocked: group.dimensions.action === "block" ? group.count : 0,
	},
}));

const totalMCPRequests = hits.reduce((sum, h) => sum + h.requestCount, 0);
const unblockedHits = hits.filter((h) => h.actions.allowed > 0);

console.log(`Found ${totalMCPRequests} MCP requests`);
console.log(`${unblockedHits.length} destinations are unblocked`);
```

</TypeScriptExample>

Key insights from the data:

- **Unblocked traffic** (`action` = `allow`) - Active MCP connections that need investigation or blocking
- **Blocked traffic** (`action` = `block`) - Your existing policies are working
- **User attribution** - This indicates which employees are connecting to MCP servers

## 4. Create DLP profiles for MCP JSON-RPC detection

Gateway HTTP policies can match domains and URL paths, but they cannot inspect request bodies. DLP profiles scan `POST` body content for patterns, which is useful for shadow MCP detection, since MCP uses JSON-RPC over HTTP and has several detectable hallmarks.

Every MCP request contains a `"method"` field:

```json
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "tools/call",
	"params": { "name": "read_file", "arguments": { "path": "/etc/passwd" } }
}
```

An attacker could run an MCP server on a non-standard domain (for example, `internal-tools.company.com/api/assistant`) without triggering domain-based or path-based rules. You can use DLP scans of the `POST` body for `"method": "tools/call"` and other MCP-specific patterns to provide more robust protection of MCP traffic.

### Review DLP constraints

Before building detection patterns, note the following DLP limitations:

- **Regex syntax** — Rust regex (differs slightly from JavaScript and PCRE)
- **Scan depth** — First 1,024 bytes of the request body only
- **POST only** — DLP only scans `POST` requests
- **Performance** — Regex patterns must be efficient to avoid catastrophic backtracking

### Build MCP detection patterns

MCP indicators can be found in JSON-RPC method fields. The following regex patterns cover the core MCP protocol methods:

<TypeScriptExample>

```ts
const DLP_REGEX_PATTERNS = [
	{
		name: "MCP Initialize Method",
		regex: '"method"\\s{0,5}:\\s{0,5}"initialize"',
	},
	{
		name: "MCP Tools Call",
		regex: '"method"\\s{0,5}:\\s{0,5}"tools/call"',
	},
	{
		name: "MCP Tools List",
		regex: '"method"\\s{0,5}:\\s{0,5}"tools/list"',
	},
	{
		name: "MCP Resources Read",
		regex: '"method"\\s{0,5}:\\s{0,5}"resources/read"',
	},
	{
		name: "MCP Resources List",
		regex: '"method"\\s{0,5}:\\s{0,5}"resources/list"',
	},
	{
		name: "MCP Prompts List",
		regex: '"method"\\s{0,5}:\\s{0,5}"prompts/(list|get)"',
	},
	{
		name: "MCP Sampling Create Message",
		regex: '"method"\\s{0,5}:\\s{0,5}"sampling/createMessage"',
	},
	{
		name: "MCP Protocol Version",
		regex: '"protocolVersion"\\s{0,5}:\\s{0,5}"202[4-9]',
	},
	{
		name: "MCP Notifications Initialized",
		regex: '"method"\\s{0,5}:\\s{0,5}"notifications/initialized"',
	},
	{
		name: "MCP Roots List",
		regex: '"method"\\s{0,5}:\\s{0,5}"roots/list"',
	},
];
```

</TypeScriptExample>

Pattern explanation:

- `\\s{0,5}` — Allows zero to five whitespace characters to handle both minified and pretty-printed JSON
- `"method"` — Double quotes are literal because JSON requires them
- `"tools/call"` — Matches the exact MCP method name
- `202[4-9]` — Matches MCP protocol versions 2024 through 2029

### Create the DLP profile via API

Send a `POST` request to create a custom DLP profile containing all detection patterns:

<TypeScriptExample>

```ts
const dlpProfile = {
	name: "MCP-Shield: MCP JSON-RPC Detection",
	description: "Detects MCP protocol JSON-RPC methods in HTTP request bodies.",
	type: "custom",
	entries: DLP_REGEX_PATTERNS.map((p) => ({
		name: p.name,
		enabled: true,
		pattern: {
			regex: p.regex,
			validation: "luhn",
		},
	})),
};

const response = await fetch(
	`https://api.cloudflare.com/client/v4/accounts/${accountId}/gateway/rules`,
	{
		method: "POST",
		headers: {
			Authorization: `Bearer ${apiToken}`,
			"Content-Type": "application/json",
		},
		body: JSON.stringify(dlpRule),
	},
);

const data = await response.json();
if (data.success) {
	console.log(`Created DLP profile: ${data.result.id}`);
}
```

</TypeScriptExample>

Replace `${accountId}` with your Cloudflare account ID and `${apiToken}` with your API token.

### Reference the DLP profile in a Gateway rule

After the DLP profile exists, create a Gateway HTTP policy that blocks requests matching the profile:

<TypeScriptExample>

```ts
const dlpRule = {
	name: "MCP-Shield: Block MCP JSON-RPC via DLP",
	description: "Blocks requests with MCP JSON-RPC patterns detected by DLP",
	precedence: 85,
	enabled: true,
	action: "block",
	filters: ["http"],
	traffic:
		'any(http.request.body.scan.dlp.profiles[*] == "MCP-Shield: MCP JSON-RPC Detection")',
};
```

</TypeScriptExample>

This rule triggers when the DLP profile matches any of the regex patterns in the request body.

## 5. Classify Portal traffic and shadow MCP traffic

Cloudflare [MCP Server Portals](/cloudflare-one/) provide governed infrastructure for approved MCP access within your organization, including:

- **Governed access** — Centralized MCP infrastructure managed by your IT team
- **Audit trails** — All MCP requests logged through Gateway with user attribution
- **Policy enforcement** — Zero Trust policies apply automatically, including authentication and DLP
- **Approved tools** — A curated set of MCP tools and resources vetted by security

When analyzing Gateway logs, it is helpful to differentiate between two types of MCP traffic:

| Traffic type       | Characteristics                                                                                      | Risk level  | Action                    |
| ------------------ | ---------------------------------------------------------------------------------------------------- | ----------- | ------------------------- |
| MCP Portal traffic | `httpHost` matches your portal domain (for example, `mcp.yourcompany.com`) | Authorized  | Monitor                   |
| Shadow MCP traffic | `httpHost` does not match any portal domain (for example, `mcp.datadog.com`, `api.stripe.com/mcp`)   | Investigate | Block, redirect or review |

Extend the query processing from [Process the query results](#3-process-the-query-results) to classify traffic by comparing hostnames against your list of approved portal domains:

<TypeScriptExample>

```ts
const portalDomains = [
	"mcp.yourcompany.com",
	"mcp-portal.example.com",
	"approved-mcp.example.com",
];

const results = groups.map((group) => {
	const isPortalTraffic = portalDomains.some((domain) =>
		group.dimensions.httpHost.includes(domain),
	);

	return {
		domain: group.dimensions.httpHost,
		requestCount: group.count,
		users: group.dimensions.users || [],
		trafficType: isPortalTraffic ? "portal" : "shadow",
		riskLevel: isPortalTraffic ? "low" : "high",
	};
});

const portalTraffic = results.filter((r) => r.trafficType === "portal");
const shadowTraffic = results.filter((r) => r.trafficType === "shadow");

console.log("Portal traffic:", portalTraffic);
console.log("Shadow MCP traffic:", shadowTraffic);
```

</TypeScriptExample>

Replace the `portalDomains` array with the actual domains of your approved MCP Server Portals.

## Related resources

- [Zero Trust documentation](/cloudflare-one/)
- [Gateway policies](/cloudflare-one/traffic-controls/)
- [DLP profiles](/cloudflare-one/data-loss-prevention/)
- [GraphQL Analytics API](/analytics/graphql-api/)
- [Rules language and wirefilter expressions](/ruleset-engine/rules-language/)
- [Pages Functions](/pages/functions/)
- [Logpush](/logs/logpush/)

# Use Microsoft Entra ID Conditional Access policies in Cloudflare Access

With [Conditional Access](https://learn.microsoft.com/entra/identity/conditional-access/overview) in Microsoft Entra ID (formerly Azure Active Directory), administrators can enforce policies on applications and users directly in Entra ID. Conditional Access has a set of checks that are specialized to Windows and are often preferred by organizations with Windows power users.

## Before you begin

Make sure you have:

- Global admin rights to Microsoft Entra ID account
- Configured users in the Microsoft Entra ID account

## Set up an identity provider for your application

Refer to [our IdP setup instructions](/cloudflare-one/integrations/identity-providers/entra-id/#set-up-entra-id-as-an-identity-provider) for Entra ID.

## Add API permission in Entra ID

Once the base IdP integration is tested and working, grant permission for Cloudflare to read Conditional Access policies from Entra ID.

1. In Microsoft Entra ID, go to **App registrations**.

2. Select the application you created for the IdP integration.

3. Go to **API permissions** and select **Add a permission**.

4. Select **Microsoft Graph**.

5. Select **Application permissions** and add `Policy.Read.ConditionalAccess`.

   :::note
   You must select **Application permissions**; delegated permissions will not work for this feature.
   :::

6. Select **Grant admin consent**.

## Configure Conditional Access in Entra ID

1. In Microsoft Entra ID, go to **Enterprise applications** > **Conditional Access**.
2. Go to **Authentication Contexts**.
3. [Create an authentication context](https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-cloud-apps#authentication-context) to reference in your Cloudflare Access policies. Give the authentication context a descriptive name (for example, `Require compliant devices`).
4. Next, go to **Policies**.
5. [Create a new Conditional Access policy](https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-policies) or select an existing policy.
6. Assign the conditional access policy to an authentication context:
   1. In the policy builder, select **Target resources**.
   2. In the **Select what this policy applies to** dropdown, select _Authentication context_.
   3. Select the authentication context that will use this policy.
   4. Save the policy.

## Sync Conditional Access with Zero Trust

To import your Conditional Access policies into Cloudflare Access:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. In **Manage your App Launcher**, select **Manage**.
3. Choose **Login methods**.
4. Find your Microsoft Entra ID integration and select **Edit**.
5. Enable **Azure AD Policy Sync**.
6. Select **Save**.

## Create an Access application

To enforce your Conditional Access policies on a Cloudflare Access application:

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **Self-hosted and private**.

4. Select **Add public hostname** and enter the target URL of the protected application.

5. Select **Create new policy** and build an [Access policy](/cloudflare-one/access-controls/policies/) using the _Azure AD - Auth context_ selector. For example:

   | Action | Rule type | Selector                | Value                       |
   | ------ | --------- | ----------------------- | --------------------------- |
   | Allow  | Include   | Emails ending in        | `@example.com`              |
   |        | Require   | Azure AD - Auth context | `Require compliant devices` |

6. Add this policy to your application configuration.

7. For **Identity providers**, select your Microsoft Entra ID integration.

8. Follow the remaining [self-hosted application creation steps](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) to publish the application.

Users will only be allowed access if they pass the Microsoft Entra ID Conditional Access policies associated with this authentication context.

# Isolate risky Entra ID users

import { WranglerConfig } from "~/components";

Microsoft Entra ID (formerly Azure Active Directory) calculates a user's [risk level](https://learn.microsoft.com/entra/id-protection/howto-identity-protection-investigate-risk) based on the probability that their account has been compromised. With Cloudflare Zero Trust, you can synchronize the Entra ID risky users list with Cloudflare Access and apply more stringent Zero Trust policies to users at higher risk.

This tutorial demonstrates how to automatically redirect users to a remote browser when they are deemed risky by Entra ID.

**Time to complete:**

1 hour

## Prerequisites

- Microsoft Entra ID Premium P2 license
- [Cloudflare Browser Isolation](/cloudflare-one/remote-browser-isolation/) add-on
- [Gateway HTTP filtering](/cloudflare-one/traffic-controls/get-started/http/) enabled on your devices
- [npm](https://docs.npmjs.com/getting-started) installation
- [Node.js](https://nodejs.org/en/) installation

## 1. Set up Entra ID as an identity provider

Refer to [our IdP setup instructions](/cloudflare-one/integrations/identity-providers/entra-id/#set-up-entra-id-as-an-identity-provider) for Entra ID.

:::note

- When you configure the IdP in Cloudflare One, be sure to select **Enable group membership change reauthentication**.
- Save the **Application (client) ID**, **Directory (tenant) ID**, and **Client secret** as you will need them again in a later step.
  :::

## 2. Add Entra ID API permissions

Once the base IdP integration is tested and working, enable additional permissions that will allow a script to create and update risky user groups in Entra ID:

1. In Microsoft Entra ID, go to **App registrations**.

2. Select the application you created for the IdP integration.

3. Go to **API permissions** and select **Add a permission**.

4. Select **Microsoft Graph**.

5. Select **Application permissions** and add the following [permissions](https://learn.microsoft.com/en-us/graph/permissions-reference):
   - `IdentityRiskyUser.ReadAll`
   - `Directory.ReadWriteAll`
   - `Group.Create`
   - `Group.ReadAll`
   - `GroupMember.ReadAll`
   - `GroupMember.ReadWriteAll`

6. Select **Grant admin consent**.

You will see the list of enabled permissions.

![API permissions in Entra ID](~/assets/images/cloudflare-one/identity/azure/risky-users-permissions.png)

## 3. Add risky users to Entra ID group

Next, configure an automated script that will populate an Entra ID security group with risky users.

To get started quickly, deploy our example Cloudflare Workers script by following the step-by-step instructions below. Alternatively, you can implement the script using [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview) or any other tool.

1. Open a terminal and clone our example project.

   ```sh
   npm create cloudflare@latest risky-users -- --template https://github.com/cloudflare/msft-risky-user-ad-sync
   ```

2. Go to the project directory.

   ```sh
   cd risky-users
   ```

3. Modify the [Wrangler configuration file](/workers/wrangler/configuration/) to include the following values:
   - `<ACCOUNT_ID>`: your Cloudflare [account ID](/fundamentals/account/find-account-and-zone-ids/).
   - `<TENANT_ID>`: your Entra ID **Directory (tenant) ID**, obtained when [setting up Entra ID as an identity provider](#1-set-up-entra-id-as-an-identity-provider).
   - `<CLIENT_ID>`: your Entra ID **Application (client) ID**, obtained when [setting up Entra ID as an identity provider](#1-set-up-entra-id-as-an-identity-provider).

   <WranglerConfig>

   ```jsonc
   {
   	"$schema": "./node_modules/wrangler/config-schema.json",
   	"name": "risky-users",
   	"compatibility_date": "$today",
   	"main": "src/index.js",
       "upload_source_maps": true,
   	"workers_dev": false,
   	"account_id": "<ACCOUNT-ID>",
   	"vars": {
   		"AZURE_AD_TENANT_ID": "<TENANT-ID>",
   		"AZURE_AD_CLIENT_ID": "<CLIENT-ID>",
   	},
   	"triggers": {
   		"crons": ["* * * * *"],
   	},
   }
   ```

   </WranglerConfig>

:::note

The [Cron Trigger](/workers/configuration/cron-triggers/) in this example schedules the script to run every minute. Learn more about [supported cron expressions](/workers/configuration/cron-triggers/#supported-cron-expressions).
:::

4. Deploy the Worker to Cloudflare's global network.

   ```sh
   npx wrangler deploy
   ```

5. Create a secret variable named `AZURE_AD_CLIENT_SECRET`.

   ```sh
   wrangler secret put AZURE_AD_CLIENT_SECRET
   ```

   You will be prompted to input the secret's value. Enter the **Client secret** obtained when [setting up Microsoft Entra ID as an identity provider](#1-set-up-azure-ad-as-an-identity-provider).

The Worker script will begin executing once per minute. To view realtime logs, run the following command and wait for the script to execute:

```sh
wrangler tail --format pretty
```

After the initial run, the auto-generated groups will appear in the Entra ID dashboard.

![Risky user groups in the Entra ID dashboard](~/assets/images/cloudflare-one/identity/azure/risky-users-groups.png)

## 4. Synchronize risky user groups

Next, synchronize Entra ID risky user groups with Cloudflare Access:

1. [Enable SCIM synchronization](/cloudflare-one/integrations/identity-providers/entra-id/#synchronize-users-and-groups).

2. In Entra ID, assign the following groups to your SCIM enterprise application:
   - `IdentityProtection-RiskyUser-RiskLevel-high`
   - `IdentityProtection-RiskyUser-RiskLevel-medium`
   - `IdentityProtection-RiskyUser-RiskLevel-low`

Cloudflare Access will now synchronize changes in group membership with Entra ID. You can verify the synchronization status on the SCIM application's **Provisioning** page.

## 5. Create a browser isolation policy

Finally, create a [Gateway HTTP policy](/cloudflare-one/traffic-controls/http-policies/) to isolate traffic for risky user groups.

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Traffic policies** > **Firewall policies** > **HTTP**.

2. Select **Add a policy**.

3. Build an [Isolate policy](/cloudflare-one/remote-browser-isolation/isolation-policies/) that contains a _User Group Names_ rule. For example, the following policy serves `app1.example.com` and `app2.example.com` in a remote browser for all members flagged as high risk:

   | Selector         | Operator | Value                                         | Logic | Action  |
   | ---------------- | -------- | --------------------------------------------- | ----- | ------- |
   | Domain           | in       | `app1.example.com`, `app2.example.com`        | And   | Isolate |
   | User Group Names | in       | `IdentityProtection-RiskyUser-RiskLevel-high` |       |         |

To test the policy, refer to the Microsoft documentation for [simulating risky detections](https://learn.microsoft.com/entra/id-protection/howto-identity-protection-simulate-risk).

# Send SSO attributes to Access-protected origins with Workers

import {
	Render,
	GlossaryTooltip,
	PackageManagers,
	WranglerConfig,
} from "~/components";

This tutorial will walk you through extending the single-sign-on (SSO) capabilities of [Cloudflare Access](/cloudflare-one/access-controls/policies/) with our serverless computing platform, [Cloudflare Workers](/workers/). Specifically, this guide will demonstrate how to modify requests sent to your secured origin to include additional information from the Cloudflare Access authentication event.

**Time to complete:** 45 minutes

## Authentication flow

[Cloudflare Access](/cloudflare-one/access-controls/policies/) is an authentication proxy in charge of validating a user's identity before they connect to your application. As shown in the diagram below, Access inserts a [JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/) into the request, which can then be [verified](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/#validate-jwts) by the origin server.

![Standard authentication flow for a request to an Access application](~/assets/images/cloudflare-one/applications/access-standard-flow.png)

You can extend this functionality by using a Cloudflare Worker to insert additional HTTP headers into the request. In this example, we will add the [device posture attributes](/cloudflare-one/reusable-components/posture-checks/#enforce-device-posture) `firewall_activated` and `disk_encrypted`, but you can include any attributes that Cloudflare Access collects from the authentication event.

![Extended authentication flow uses a Worker to pass additional request headers to the origin](~/assets/images/cloudflare-one/applications/access-extended-flow-serverless.png)

## Benefits

This approach allows you to:

- **Enhance security:** By incorporating additional information from the authentication event, you can implement more robust security measures. For example, you can use device posture data to enforce access based on device compliance.
- **Improve user experience:** You can personalize the user experience by tailoring content or functionality based on user attributes. For example, you can display different content based on the user's role or location.
- **Simplify development:** By using Cloudflare Workers, you can easily extend your Cloudflare Access configuration without modifying your origin application code.

## Before you begin

- Add a [self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) to Cloudflare Access.
- Enable the [Disk encryption](/cloudflare-one/reusable-components/posture-checks/client-checks/disk-encryption/) and [Firewall](/cloudflare-one/reusable-components/posture-checks/client-checks/firewall/) device posture checks.
- Install [Wrangler](/workers/wrangler/install-and-update/) on your local machine.

## 1. Create the Worker

1.  Create a new Workers project:

        <PackageManagers
        	type="create"
        	pkg="cloudflare@latest"
        	args={"device-posture-worker"}
        />

        <Render
        	file="c3-post-run-steps-hello-world"
        	product="workers"
        	params={{
        		type: "Worker only",
        		lang: "JavaScript",
        	}}
        />

2.  Change to the project directory:

    ```sh
    $ cd device-posture-worker
    ```

3.  Copy-paste the following code into `src/index.js`. Be sure to replace `<your-team-name>` with your Zero Trust <GlossaryTooltip term="team name">team name</GlossaryTooltip>.

    ```js title="index.js"
    import { parse } from "cookie";
    export default {
    	async fetch(request, env, ctx) {
    		// The name of the cookie
    		const COOKIE_NAME = "CF_Authorization";
    		const CF_GET_IDENTITY =
    			"https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/get-identity";
    		const cookie = parse(request.headers.get("Cookie") || "");
    		if (cookie[COOKIE_NAME] != null) {
    			try {
    				let id = await (await fetch(CF_GET_IDENTITY, request)).json();
    				let diskEncryptionStatus = false;
    				let firewallStatus = false;

    				for (const checkId in id.devicePosture) {
    					const check = id.devicePosture[checkId];
    					if (check.type === "disk_encryption") {
    						console.log(check.type);
    						diskEncryptionStatus = check.success;
    					}
    					if (check.type === "firewall") {
    						console.log(check.type);
    						firewallStatus = check.success;
    						break;
    					}
    				}
    				//clone request (immutable otherwise) and insert posture values in new header set
    				let newRequest = await new Request(request);
    				newRequest.headers.set(
    					"Cf-Access-Firewall-Activated",
    					firewallStatus,
    				);
    				newRequest.headers.set("Cf-Access-Disk-Encrypted", firewallStatus);

    				//sent modified request to origin
    				return await fetch(newRequest);
    			} catch (e) {
    				console.log(e);
    				return await fetch(request);
    			}
    		}
    		return await fetch(request);
    	},
    };
    ```

## 2. View the user's identity

The script in `index.js` uses the [`get-identity`](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/application-token/#user-identity) endpoint to fetch a user's complete identity from a Cloudflare Access authentication event. To view a list of available data fields, log in to your Access application and append `/cdn-cgi/access/get-identity` to the URL. For example, if `www.example.com` is behind Access, go to `https://www.example.com/cdn-cgi/access/get-identity`.

Below is an example of a user identity that includes the `disk_encryption` and `firewall` posture checks. The Worker inserts the posture check results into the request headers **Cf-Access-Firewall-Activated** and **Cf-Access-Disk-Encrypted**.

```json title="Example user identity" {33,52}
{
  "id": "P51Tuu01fWHMBjIBvrCK1lK-eUDWs2aQMv03WDqT5oY",
  "name": "John Doe",
  "email": "john.doe@cloudflare.com",
  "amr": [
    "pwd"
  ],
  "oidc_fields": {
    "principalName": "XXXXXX_cloudflare.com#EXT#@XXXXXXcloudflare.onmicrosoft.com"
  },
  "groups": [
    {
      "id": "fdaedb59-e9be-4ab7-8001-3e069da54185",
      "name": "XXXXX"
    }
  ],
  "idp": {
    "id": "b9f4d68e-dac1-48b0-b728-ae05a5f0d4b2",
    "type": "azureAD"
  },
  "geo": {
    "country": "FR"
  },
  "user_uuid": "ce40d564-c72f-475f-a9b8-f395f19ad986",
  "account_id": "121287a0c6e6260ec930655e6b39a3a8",
  "iat": 1724056537,
  "devicePosture": {
    "f6f9391e-6776-4878-9c60-0cc807dc7dc8": {
      "id": "f6f9391e-6776-4878-9c60-0cc807dc7dc8",
      "schedule": "5m",
      "timestamp": "2024-08-19T08:31:59.274Z",
      "description": "",
      "type": "disk_encryption",
      "check": {
        "drives": {
          "C": {
            "encrypted": true
          }
        }
      },
      "success": false,
      "rule_name": "Disk Encryption - Windows",
      "input": {
        "requireAll": true,
        "checkDisks": []
    },
    "a0a8e83d-be75-4aa6-bfa0-5791da6e9186": {
      "id": "a0a8e83d-be75-4aa6-bfa0-5791da6e9186",
      "schedule": "5m",
      "timestamp": "2024-08-19T08:31:59.274Z",
      "description": "",
      "type": "firewall",
      "check": {
        "firewall": false
      },
      "success": false,
      "rule_name": "Local Firewall Check - Windows",
      "input": {
        "enabled": true
      }
    }
    ...
  }
```

## 3. Route the Worker to your application

In the [Wrangler configuration file](/workers/wrangler/configuration/), [set up a route](/workers/configuration/routing/routes/) that maps the Worker to your Access application domain:

<WranglerConfig>

```jsonc
{
	"route": {
		"pattern": "app.example.com/*",
		"zone_name": "example.com"
	}
}
```

</WranglerConfig>

## 4. Deploy the Worker

```sh
npx wrangler deploy
```

The Worker will now insert the **Cf-Access-Firewall-Activated** and **Cf-Access-Disk-Encrypted** headers into requests that pass your application's Access policies.

```json title="Example request headers" {7,8}
{
	"headers": {
		"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
		"Accept-Encoding": "gzip",
		"Accept-Language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-GB;q=0.6",
		"Cf-Access-Authenticated-User-Email": "John.Doe@cloudflare.com",
		"Cf-Access-Disk-Encrypted": "false",
		"Cf-Access-Firewall-Activated": "false",
		"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
	}
}
```

You can verify that these headers are received by the origin server.

# Validate the Access token with FastAPI

This tutorial covers how to validate that the [Access JWT](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/) is on requests made to FastAPI apps.

**Time to complete:** 15 minutes

## Prerequisites

- A [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) for your FastAPI app
- The [AUD tag](/cloudflare-one/access-controls/applications/http-apps/authorization-cookie/validating-json/#get-your-aud-tag) for your Access application

## 1. Create a validation function

1. In your FastAPI project, create a new file called `cloudflare.py` that contains the following code:

```python
from fastapi import Request, HTTPException

# The Application Audience (AUD) tag for your application
POLICY_AUD = "XXXXX"

# Your CF Access team domain
TEAM_DOMAIN = "https://<your-team-name>.cloudflareaccess.com"
CERTS_URL = "{}/cdn-cgi/access/certs".format(TEAM_DOMAIN)

async def validate_cloudflare(request: Request):
    """
    Validate that the request is authenticated by Cloudflare Access.
    """
    if verify_token(request) != True:
        raise HTTPException(status_code=400, detail="Not authenticated properly!")


def _get_public_keys():
    """
    Returns:
        List of RSA public keys usable by PyJWT.
    """
    r = requests.get(CERTS_URL)
    public_keys = []
    jwk_set = r.json()
    for key_dict in jwk_set["keys"]:
        public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key_dict))
        public_keys.append(public_key)
    return public_keys


def verify_token(request):
    """
    Verify the token in the request.
    """
    token = ""

    if "CF_Authorization" in request.cookies:
        token = request.cookies["CF_Authorization"]
    else:
        raise HTTPException(status_code=400, detail="missing required cf authorization token")

    keys = _get_public_keys()

    # Loop through the keys since we can't pass the key set to the decoder
    valid_token = False
    for key in keys:
        try:
            # decode returns the claims that has the email when needed
            jwt.decode(token, key=key, audience=POLICY_AUD, algorithms=["RS256"])
            valid_token = True
            break
        except:
            raise HTTPException(status_code=400, detail="Error decoding token")
    if not valid_token:
        raise HTTPException(status_code=400, detail="Invalid token")

    return True

```

## 2. Use the validation function in your app

You can now add the validation function as a dependency in your FastAPI app. One way to do this is by creating an [`APIRouter` instance](https://fastapi.tiangolo.com/tutorial/bigger-applications/#another-module-with-apirouter). The following example executes the validation function on each request made to paths that start with `/admin`:

```python
from fastapi import APIRouter, Depends, HTTPException
from cloudflare import validate_cloudflare

router = APIRouter(
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(validate_cloudflare)]
    responses={404: {"description": "Not found"}},
)

@router.get("/")
async def root():
    return {"message": "Hello World"}
```

# Zero Trust GitLab SSH & HTTP

import { DashButton } from "~/components";

You can use Cloudflare Access to add Zero Trust rules to a self-hosted instance of GitLab. Combined with Cloudflare Tunnel, users can connect through HTTP and SSH and authenticate with your team's identity provider.

**This walkthrough covers how to:**

- Deploy an instance of GitLab
- Lock down all inbound connections to that instance and use Cloudflare Tunnel to set outbound connections to Cloudflare
- Build policies with Cloudflare Access to control who can reach GitLab
- Connect over HTTP and SSH through Cloudflare

**Time to complete:**

1 hour

---

## Deploying GitLab

This section walks through deploying GitLab in DigitalOcean. If you have already deployed GitLab, you can skip this section.

Create a Droplet that has 16 GB of RAM and 6 CPUs. This should make it possible to support 500 users, based on [GitLab's resource recommendations](https://docs.gitlab.com/ee/install/requirements.html).

![Create Droplet](~/assets/images/cloudflare-one/zero-trust-security/gitlab/create-droplet.png)

GitLab will provide an external IP that is exposed to the Internet (for now). You will need to connect to the deployed server using this external IP for the initial configuration. You can secure connections to the IP by [adding SSH keys](https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2) to your DigitalOcean account.

This example uses a macOS machine to configure the Droplet. Copy the IP address assigned to the machine from DigitalOcean.

![Machine IP](~/assets/images/cloudflare-one/zero-trust-security/gitlab/show-ip.png)

Open Terminal and run the following command, replacing the IP address with the IP assigned by DigitalOcean.

```sh
ssh root@134.209.124.123
```

Next, install GitLab. This example uses the [Ubuntu package](https://about.gitlab.com/install/#ubuntu) and the steps in the GitLab documentation, with a few exceptions called out below.

Run the following commands to begin.

```sh
sudo apt-get update

sudo apt-get install -y curl openssh-server ca-certificates
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
```

The commands above download the GitLab software to this machine. You must now install it. This is the first place this tutorial will diverge from the operations in the GitLab documentation. The next step in the GitLab-provided tutorial sets an external hostname. Instead, you can just install the software.

```sh
sudo apt-get install gitlab-ee
```

After a minute or so, GitLab will be installed.

![Install GitLab](~/assets/images/cloudflare-one/zero-trust-security/gitlab/install-gitlab.png)

However, the application is not running yet. You can check to see what ports are listening to confirm by using `ss`.

```sh
sudo ss -lntup
```

The result should be only the services currently active on the machine:

```bash
sudo ss -lntup
```

```bash output
Netid   State    Recv-Q   Send-Q     Local Address:Port     Peer Address:Port   Process
udp     UNCONN   0        0                      *:9094                *:*
tcp     LISTEN   0        128              0.0.0.0:22            0.0.0.0:*       users:(("sshd",pid=29,fd=3))
tcp     LISTEN   0        128                 [::]:22               [::]:*       users:(("sshd",pid=29,fd=4))
```

To start GitLab, run the software's reconfigure command.

```sh
sudo gitlab-ctl reconfigure
```

GitLab will launch its component services. Once complete, confirm that GitLab is running and listening on both ports 22 and 80.

![GitLab Services](~/assets/images/cloudflare-one/zero-trust-security/gitlab/gitlab-services.png)

```bash
sudo ss -lntup
```

```bash output
Netid   State    Recv-Q   Send-Q     Local Address:Port     Peer Address:Port   Process
udp     UNCONN   0        0                      *:9094                *:*
tcp     LISTEN   0        4096           127.0.0.1:9236          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:8150          0.0.0.0:*
tcp     LISTEN   0        128              0.0.0.0:22            0.0.0.0:*       users:(("sshd",pid=29,fd=3))
tcp     LISTEN   0        4096           127.0.0.1:8151          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:3000          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:8153          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:8154          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:8155          0.0.0.0:*
tcp     LISTEN   0        511              0.0.0.0:8060          0.0.0.0:*       users:(("nginx",pid=324,fd=8))
tcp     LISTEN   0        4096           127.0.0.1:9121          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:9090          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:9187          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:9093          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:9229          0.0.0.0:*
tcp     LISTEN   0        1024           127.0.0.1:8080          0.0.0.0:*
tcp     LISTEN   0        511              0.0.0.0:80            0.0.0.0:*       users:(("nginx",pid=324,fd=7))
tcp     LISTEN   0        4096           127.0.0.1:9168          0.0.0.0:*
tcp     LISTEN   0        4096           127.0.0.1:8082          0.0.0.0:*
tcp     LISTEN   0        128                 [::]:22               [::]:*       users:(("sshd",pid=29,fd=4))
tcp     LISTEN   0        4096                   *:9094                *:*
```

Users connect to GitLab over SSH (port 22 here) and HTTP for the web app (port 80). In the next step, you will make it possible for users to try both through Cloudflare Access. I'll leave this running and head over to the Cloudflare dashboard.

## Securing GitLab with Zero Trust rules

### Building Zero Trust policies

You can use Cloudflare Access to build Zero Trust rules to determine who can connect to both the web application of GitLab (HTTP) and who can connect over SSH.

When a user makes a request to a site protected by Access, that request hits Cloudflare's network first. Access can then check if the user is allowed to reach the application. When integrated with Cloudflare Tunnel, the Zero Trust architecture looks like this:

![GitLab Services](~/assets/images/cloudflare-one/zero-trust-security/gitlab/teams-diagram.png)

To determine who can reach the application, Cloudflare Access relies on integration with identity providers like Okta, Microsoft Entra ID, or Google to issue the identity cards that get checked at the door. While a VPN allows users free range on a private network unless someone builds an active rule to stop them, Access enforces that identity check on every request (and at any granularity configured).

For GitLab, start by building two policies. Users will connect to GitLab in a couple of methods: in the web app and over SSH. Create policies to secure a subdomain for each. First, the web app.

Before you build the rule, you'll need to follow [these instructions](/cloudflare-one/setup/) to set up Cloudflare Access in your account.

Once enabled, go to the **Applications** page in Zero Trust. Select **Create new application**.

Select **Self-hosted and private**.

![Self Hosted](~/assets/images/cloudflare-one/zero-trust-security/gitlab/policy.png)

You will be prompted to add a subdomain that will represent the resource. This must be a subdomain of a domain in your Cloudflare account. You will need separate subdomains for the web application and SSH flows.

This example uses `gitlab.widgetcorp.tech` for the web application and `gitlab-ssh.widgetcorp.tech` for SSH connectivity.

You can decide which identity providers will be allowed to authenticate. By default, all configured providers are allowed. Add rules to determine who can reach the site.

Select **Create** to publish the application. Repeat these steps for the second application, `gitlab-ssh.widgetcorp.tech`.

## Cloudflare Tunnel

Cloudflare Tunnel creates a secure, outbound-only, connection between this machine and Cloudflare's network. With an outbound-only model, you can prevent any direct access to this machine and lock down any externally exposed points of ingress. And with that, no open firewall ports.

Cloudflare Tunnel is made possible through a lightweight daemon from Cloudflare called `cloudflared`. Download and install `cloudflared` on the DigitalOcean machine by following the instructions listed on the [Downloads](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) page.

Once installed, authenticate the instance of `cloudflared` with the following command.

```sh
cloudflared login
```

The command will print a URL that you must visit to login with your Cloudflare account.

Choose a website that you have added into your account.

Once you select one of the sites in your account, Cloudflare will download a certificate file to authenticate this instance of `cloudflared`. You can now use `cloudflared` to control Cloudflare Tunnel connections in your Cloudflare account.

![Download Cert](~/assets/images/cloudflare-one/secure-origin-connections/share-new-site/cert-download.png)

### Connecting to Cloudflare

You can now connect GitLab to Cloudflare using Cloudflare Tunnel.

1. Create a new Tunnel by running the following command.

```sh
cloudflared tunnel create gitlab
```

`cloudflared` will generate a unique ID for this Tunnel, for example `6ff42ae2-765d-4adf-8112-31c55c1551ef`. You can use this Tunnel both for SSH and HTTP traffic.

2. You will need to configure Cloudflare Tunnel to proxy traffic to both destinations. The configuration below will take traffic bound for the DNS record that will be created for the web app and the DNS record to represent SSH traffic to the right port.

You use the text editor of your choice to edit the configuration file. The example relies on `Vi`.

```sh
vim ~/.cloudflared/config.yml
```

3. Configure the Tunnel to serve traffic.

```yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  - hostname: gitlab.widgetcorp.tech
    service: http://localhost:80
  - hostname: gitlab-ssh.widgetcorp.tech
    service: ssh://localhost:22
  # Catch-all rule, which just responds with 404 if traffic doesn't match any of
  # the earlier rules
  - service: http_status:404
```

![Self Hosted](~/assets/images/cloudflare-one/zero-trust-security/gitlab/config-file.png)

4. You can test that the configuration file is set correctly with the following command:

```sh
cloudflared tunnel ingress validate
```

`cloudflared` should indicate the Tunnel is okay. You can now begin running the Tunnel.

```sh
cloudflared tunnel run
```

![Tunnel Run](~/assets/images/cloudflare-one/zero-trust-security/gitlab/tunnel-run.png)

:::note

This command should be run as a `systemd` service for long-term use; if it terminates, GitLab will be unavailable.

:::

### Configure DNS records

You can now create DNS records for GitLab in the Cloudflare dashboard. Remember, you will still need two records - one for the web application and one for SSH traffic.

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to the **DNS Records** page for your domain.

   <DashButton url="/?to=/:account/:zone/dns/records" />

2. Select **Add record**. Choose `CNAME` as the record type.
3. In the **Name** field, input `gitlab`.
4. In the **Target** field, input the ID of the Tunnel created followed by `cfargotunnel.com`. In this example, that value is:

```txt
6ff42ae2-765d-4adf-8112-31c55c1551ef.cfargotunnel.com
```

5. Select **Save**.
6. Repeat the process again by creating a second `CNAME` record, with the same **Target**, but input `gitlab-ssh` for the **Name**. Both records should then appear, pointing to the same Tunnel. The ingress rules defined in the configuration file above will direct traffic to the appropriate port.

![View DNS](~/assets/images/cloudflare-one/zero-trust-security/gitlab/view-dns.png)

### Connecting to the web application

You can now test the end-to-end configuration for the web application. Visit the subdomain created for the web application. Cloudflare Access will prompt you to authenticate. Login with your provider.

Once authenticated, you should see the GitLab web application.

![GitLab Web](~/assets/images/cloudflare-one/zero-trust-security/gitlab/gitlab-web.png)

Register your own account and create a Blank project to test SSH in the next step.

![Blank Project](~/assets/images/cloudflare-one/zero-trust-security/gitlab/blank-project.png)

GitLab will create a new project and repository.

:::note

To pull or push code, you must also add an SSH key to your profile in GitLab.

:::

### Configuring SSH

To push and pull code over SSH, you will need to install `cloudflared` on the client machine as well. This example uses a macOS laptop. On macOS, you can install `cloudflared` with the following command.

```sh
brew install cloudflared
```

While you need to install `cloudflared`, you do not need to wrap your SSH commands in any unique way. Instead, you will need to make a one-time change to your SSH configuration file.

```sh
vim /Users/samrhea/.ssh/config
```

Input the following values; replacing `gitlab-ssh.widgetcorp.tech` with the hostname you created.

```txt
Host gitlab-ssh.widgetcorp.tech
  ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
```

You can now test the SSH flow by attempting to clone the project created earlier.

```sh
git clone git@gitlab-ssh.widgetcorp.tech:samrhea/demo
```

`cloudflared` will prompt you to login with my identity provider and, once successful, issue a token to your device to allow you to authenticate.

![GitLab Clone](~/assets/images/cloudflare-one/zero-trust-security/gitlab/git-clone.png)

### Lock down exposed ports

You can now configure your DigitalOcean firewall with a single rule, block any inbound traffic, to prevent direct access.

![Set Rules](~/assets/images/cloudflare-one/zero-trust-security/gitlab/disable-ingress.png)

Cloudflare Tunnel will continue to run outbound-only connections and I can avoid this machine getting caught up in a crypto mining operation, or something worse.

## View logs

You can also view logs of the events that are allowed and blocked. Open the `Access` page of the `Logs` section in Zero Trust.

## Troubleshooting

If you are using Git Large File Storage (LFS), note that Git LFS is not automatically supported by `cloudflared`. To access repositories protected by Cloudflare Access, you need to authenticate manually by running:

```sh
cloudflared access login <your-git-access-url>
```

Replace `<your-git-access-url>` with the Cloudflare Access-protected URL.

# Monitor Cloudflare Tunnel with Grafana

import { Render } from "~/components";

<Render
	file="tunnel/metrics/grafana-tutorial"
	product="cloudflare-one"
	params={{
		metricsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/",
		getStartedURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/",
		metricsRunParamURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#metrics",
		addRunParamsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/run-parameters/#add-run-parameters-to-tunnel-service",
		availableMetricsURL:
			"/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/metrics/#available-metrics",
	}}
/>

# GraphQL Analytics

import { DashButton } from "~/components";

Use the GraphQL Analytics API to review data for Cloudflare Network Firewall network traffic related to rules matching your traffic. This contains both rules you configured in the Cloudflare Network Firewall dashboard, and the rules managed by Cloudflare as a part of [Cloudflare Network Firewall Managed rules](/cloudflare-network-firewall/how-to/enable-managed-rulesets/) and [Cloudflare Network Firewall IDS](/cloudflare-network-firewall/about/ids/) features.

Before you begin, you must have an [API token](/analytics/graphql-api/getting-started/authentication/). For additional help getting started with GraphQL Analytics, refer to [GraphQL Analytics API](/analytics/graphql-api/).

## Obtain Cloudflare Account ID

To construct a Network Firewall GraphQL query for an object, you will need a Cloudflare Account ID

### Obtain your Cloudflare Account ID

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/), and select your account.
2. The URL in your browser's address bar should show `https://dash.cloudflare.com/` followed by a hex string. The hex string is your Cloudflare Account ID.

### Obtain the rule ID for a firewall rule

To construct queries to gather analytics for a particular rule, you need the rule ID for each firewall rule.

1. In the Cloudflare dashboard, go to the **Cloudflare Network Firewall** page.

   <DashButton url="/?to=/:account/network-security/magic_firewall" />

2. In the **Custom rules** tab, locate the rule you need the rule ID for from the list and select the three dots > **Edit**.
3. Locate the **Rule ID** and select the copy button.
4. Select **Cancel** to return to the **Cloudflare Network Firewall** page.

## Explore GraphQL schema with Cloudflare Network Firewall query example

In this section, you will run a test query to retrieve a five minute count of all configured Cloudflare Network Firewall rules within five minute intervals. You can copy and paste the code below into GraphiQL.

For additional information about the Analytics schema, refer to [Explore the Analytics schema with GraphiQL](/analytics/graphql-api/getting-started/explore-graphql-schema/).

```graphql graphql-api-explorer
query MagicFirewallExample($accountTag: string!, $start: Time, $end: Time) {
	viewer {
		accounts(filter: { accountTag: $accountTag }) {
			magicFirewallSamplesAdaptiveGroups(
				filter: { datetime_geq: $start, datetime_leq: $end }
				limit: 2
				orderBy: [datetimeFiveMinute_DESC]
			) {
				sum {
					bits
					packets
				}
				dimensions {
					datetimeFiveMinute
					ruleId
				}
			}
		}
	}
}
```

## Example queries for Cloudflare Network Firewall

### Obtain analytics for a specific rule

Use the example below to display the total number of packets and bits for the top ten suspected malicious traffic streams within the last hour. After receiving the results, you can sort by packet rates with a five minute average.

For each stream, display the:

- Source and destination IP addresses
- Ingress Cloudflare data centers that received it
- Total traffic volume in bits and packets received within the hour
- Actions taken by the firewall rule

```graphql graphql-api-explorer
query MagicFirewallObtainRules(
	$accountId: string!
	$ruleId: string
	$start: Time
	$end: Time
) {
	viewer {
		accounts(filter: { accountTag: $accountId }) {
			magicFirewallNetworkAnalyticsAdaptiveGroups(
				filter: { ruleId: $ruleId, datetime_geq: $start, datetime_leq: $end }
				limit: 10
				orderBy: [avg_packetRateFiveMinutes_DESC]
			) {
				sum {
					bits
					packets
				}
				dimensions {
					coloCity
					ipDestinationAddress
					ipSourceAddress
					outcome
				}
			}
		}
	}
}
```

### Obtain IDS analytics

Use the example below to display the total number of packets and bits for the top 10 traffic streams that Cloudflare Network Firewall IDS has detected in the last hour.

By setting `verdict` to `drop` and `outcome` as `pass`, we are filtering for traffic that was marked as a detection (i.e. verdict was drop) but was not dropped (for example, outcome was `pass`). This is because currently, Cloudflare Network Firewall IDS only detects malicious traffic but does not drop the traffic.

For each stream, display the:

- Source and destination IP addresses.
- Ingress Cloudflare data centers that received it.
- Total traffic volume in bits and packets received within the hour.

```graphql graphql-api-explorer
query MagicFirewallObtainIDS($accountTag: string!, $start: Time, $end: Time) {
	viewer {
		accounts(filter: { accountTag: $accountTag }) {
			magicIDPSNetworkAnalyticsAdaptiveGroups(
				filter: {
					datetime_geq: $start
					datetime_leq: $end
					verdict: drop
					outcome: pass
				}
				limit: 10
				orderBy: [avg_packetRateFiveMinutes_DESC]
			) {
				sum {
					bits
					packets
				}
				dimensions {
					coloCity
					ipDestinationAddress
					ipSourceAddress
				}
			}
		}
	}
}
```

Alternatively, to inspect all traffic that was analyzed, but grouped into malicious traffic and other traffic, the example below can be used. The response will contain two entries for each five minute timestamp. `verdict` will be set to `drop` for malicious traffic, and `verdict` will be set to `pass` for traffic that did not match any of the IDS rules.

```graphql graphql-api-explorer
query MagicFirewallTraffic($accountTag: string!, $start: Time, $end: Time) {
	viewer {
		accounts(filter: { accountTag: $accountTag }) {
			magicIDPSNetworkAnalyticsAdaptiveGroups(
				filter: { datetime_geq: $start, datetime_leq: $end }
				limit: 10
				orderBy: [avg_packetRateFiveMinutes_DESC]
			) {
				sum {
					bits
					packets
				}
				dimensions {
					coloCity
					ipDestinationAddress
					ipSourceAddress
					verdict
				}
			}
		}
	}
}
```

# Tutorials

import { ListTutorials, Render } from "~/components";

<ListTutorials />

# Integrate Microsoft MCAS with Cloudflare Zero Trust

Many security teams rely on Microsoft MCAS (Microsoft Cloud App Security), Microsoft's CASB solution, to identify and block threats on the Internet, as well as allow or block access to cloud applications. This tutorial covers how to integrate MCAS with Cloudflare Zero Trust, and create Gateway HTTP policies to ensure visibility and control over data.

Microsoft provides an MCAS API endpoint to allow queries to see which applications have been marked as blocked or allowed. With an MCAS API call, you can manage a URL category that contains the blocked URLs returned by the API query, and use the output to create a Hostname List that can be used by Gateway HTTP policies to block them.

**Time to complete:**

20 minutes

## Basic configuration

In your Microsoft account, you first need to create an API token and URL endpoint to use to query the URLs blocked by MCAS.
Follow the guide for [Managing API tokens for Microsoft Cloud App Security](https://learn.microsoft.com/defender-cloud-apps/api-authentication) to generate a new API token and a custom API URL for the API endpoint.

## Using the API to query banned applications

Once you have the API token and API URL, use curl to get the list of banned applications from Microsoft MCAS:

```sh
curl -v "https://<MCAS API URL>/api/discovery_block_scripts/?format=120&type=banned" -H "Authorization: Token <API token>"
```

This will return a list of banned hostnames. In this case, Angie's List is the banned application.

![Banned hostnames](~/assets/images/cloudflare-one/microsoft-mcas/mcas-domains.png)

### Processing the output

As you can see, the banned hostnames are preceded by a `.`. To use this output for a Zero Trust List, we need to do some text processing.

1. Run the curl API call and direct the output to a file, in this case `mcas.txt`:

   ```sh
   curl -v "https://<MCAS API URL>/api/discovery_block_scripts/?format=120&type=banned" -H "Authorization: Token <API token>" > mcas.txt
   ```

2. Remove the leading `.`, for example by running `sed` from the CLI:

   ```sh
   sed -i 's/^.//' mcas.txt
   ```

3. This will give you the list of hostnames without leading `.`.

4. Replace the file's `.txt` extension with `.csv`. The file can now be imported into Cloudflare Zero Trust as a Hostname list.

## Using the API to query allowed applications

If you would like to get a list of all of the MCAS allowed applications, you can use the same API query, but instead of using `type=banned`, use `type=allowed`. This will return a much larger list.

```sh
curl -v "https://<MCAS API URL>/api/discovery_block_scripts/?format=120&type=allowed" -H "Authorization: Token <API token>"
```

## Adding a hostname list in Cloudflare One

1. In [Cloudflare One](https://one.dash.cloudflare.com), go to **Reusable components** > **Lists**
2. Select **Upload CSV**. Even though the hostname list is not in CSV format, it will work with no issues.
3. Add a name for the list, specify _Hostnames_ as the list type, and give it a description.
4. Drag and drop your MCAS output file created via the API call, or you can select **Select a file**.
5. Select **Create**. You will see the list of hostnames that have been added to the list.
6. Save the list.

Your list is now ready to be referenced by Gateway HTTP policies.

## Creating an HTTP policy

1. Go to **Traffic policies** > **Traffic policies** > **HTTP**.
2. Select **Add a policy**.
3. Create the following policy.

   | Selector | Operator | Value                | Action |
   | -------- | -------- | -------------------- | ------ |
   | Host     | in list  | \<NEW_HOSTNAME_LIST> | Block  |

Now when trying to visit one of the MCAS defined sites, the user will be blocked.

![Access Restricted](~/assets/images/cloudflare-one/microsoft-mcas/mcas-block-page.png)

# Connect through Cloudflare Access using kubectl

import { DashButton } from "~/components";

You can connect to machines over `kubectl` using Cloudflare's Zero Trust platform.

**This walkthrough covers how to:**

- Build a policy in Cloudflare Access to secure the machine
- Connect a machine to Cloudflare's network using kubectl
- Connect from a client machine

**Before you start**

- [Add a website to Cloudflare](/fundamentals/manage-domains/add-site/)

**Time to complete:**

30 minutes

---

## Create an Access policy

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.
2. Select **Create new application**.
3. Select **Self-hosted and private**.
4. Select **Add public hostname** and input a subdomain. This will be the hostname where your application will be available to users.
5. [Create a new policy](/cloudflare-one/access-controls/policies/policy-management/) to control who can reach the application, or select existing policies.
6. Follow the remaining [self-hosted application creation steps](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) to publish the application.

## Install `cloudflared`

Cloudflare Tunnel creates a secure, outbound-only connection between this machine and Cloudflare's network. With an outbound-only model, you can prevent any direct access to this machine and lock down any externally exposed points of ingress. And with that, no open firewall ports.

Cloudflare Tunnel is made possible through a lightweight daemon from Cloudflare called `cloudflared`. Download and install `cloudflared` on the DigitalOcean machine by following the instructions listed on the [Downloads](/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/) page.

## Authenticate `cloudflared`

Run the following command to authenticate cloudflared into your Cloudflare account.

```sh
cloudflared tunnel login
```

`cloudflared` will open a browser window and prompt you to log in to your Cloudflare account. If you are working on a machine that does not have a browser, or a browser window does not launch, you can copy the URL from the command-line output and visit the URL in a browser on any machine.

Choose any hostname presented in the list. Cloudflare will issue a certificate scoped to your account. You do not need to pick the specific hostname where you will serve the Tunnel.

## Create a Tunnel

Next, create a tunnel with the command below.

```sh
cloudflared tunnel create <NAME>
```

Replacing `<NAME>` with a name for the Tunnel. This name can be any value. A single Tunnel can also serve traffic for multiple hostnames to multiple services in your environment, including a mix of connection types like SSH and HTTP.

The command will output an ID for the Tunnel and generate an associated credentials file. At any time you can list the Tunnels in your account with the following command.

```sh
cloudflared tunnel list
```

## Configure the Tunnel

You can now [configure the tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/#4-create-a-configuration-file) to serve traffic.

Create a `YAML` file that `cloudflared` can reach. By default, `cloudflared` will look for the file in the same folder where `cloudflared` has been installed.

```sh
vim ~/.cloudflared/config.yml
```

Next, configure the Tunnel, replacing the example ID below with the ID of the Tunnel created above. Additionally, replace the hostname in this example with the hostname of the application configured with Cloudflare Access.

```yaml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json

ingress:
  - hostname: azure.widgetcorp.tech
    service: tcp://kubernetes.docker.internal:6443
    originRequest:
      proxyType: socks
  - service: http_status:404
  # Catch-all rule, which responds with 404 if traffic doesn't match any of
  # the earlier rules
```

## Route to the Tunnel

You can now create a DNS record that will route traffic to this Tunnel. Multiple DNS records can point to a single Tunnel and will send traffic to the configured service as long as the hostname is defined with an [ingress rule](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/#file-structure-for-public-hostnames).

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to the **DNS Records** page for your domain.

   <DashButton url="/?to=/:account/:zone/dns/records" />

2. Select **Add record**. Choose `CNAME` as the record type. For **Name**, choose the hostname where you want to create a Tunnel. This should match the hostname of the Access policy.

3. For **Target**, input the ID of your Tunnel followed by `.cfargotunnel.com`. For example:

```txt
  6ff42ae2-765d-4adf-8112-31c55c1551ef.cfargotunnel.com
```

4. Select **Save**.

## Run the Tunnel

You can now run the Tunnel to connect the target service to Cloudflare. Use the following command to run the Tunnel, replacing `<NAME>` with the name created for your Tunnel.

```sh
cloudflared tunnel run <NAME>
```

We recommend that you run `cloudflared` [as a service](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/as-a-service/) that is configured to launch on start.

## Connect from a client machine

You can now connect from a client machine using `cloudflared`.

This example uses a macOS laptop. On macOS, you can install `cloudflared` with the following command using Homebrew.

```sh
brew install cloudflared
```

Run the following command to create a connection from the device to Cloudflare. Any available port can be specified.

```sh
cloudflared access tcp --hostname azure.widgetcorp.tech --url 127.0.0.1:1234
```

With this service running, you can run a `kubectl` command and `cloudflared` will launch a browser window and prompt the user to authenticate with your SSO provider. Once authenticated, `cloudflared` will expose the connection to the client machine at the local URL specified in the command.

`kubeconfig` does not support proxy command configurations at this time, though the community has submitted plans to do so. In the interim, users can alias the cluster's API server to save time.

```sh
alias kubeone="env HTTPS_PROXY=socks5://127.0.0.1:1234 kubectl"
```

# Protect access to Microsoft 365 with dedicated egress IPs

:::note

Only available on Zero Trust Enterprise plans.

:::

This tutorial covers how to secure access to your Microsoft 365 applications with Cloudflare Gateway dedicated egress IPs.

You can map a named location in Microsoft Entra ID to a location associated with your dedicated egress IPs. Traffic will egress from Cloudflare with these IP addresses. If users attempt to access your Microsoft applications without these IPs, Entra ID will block access.

## Before you begin

Make sure you have:

- In Cloudflare, a Zero Trust Enterprise plan with [dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/)
- In Microsoft 365, an organization managed with [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/)

## Create an egress policy in Cloudflare Gateway

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Traffic policies** > **Egress policies**.

2. Select **Add a policy**.

3. Name your policy, then add conditions to check users are configured in Microsoft Entra ID. For example, you can check for [identity conditions](/cloudflare-one/traffic-controls/identity-selectors/):

   | Selector         | Operator | Value                                         |
   | ---------------- | -------- | --------------------------------------------- |
   | User Group Names | in       | `Sales and Marketing`, `Retail`, `U.S. Sales` |

   Additionally, you can check for [device posture conditions](/cloudflare-one/reusable-components/posture-checks/):

   | Selector                    | Operator | Value                                             | Logic |
   | --------------------------- | -------- | ------------------------------------------------- | ----- |
   | Passed Device Posture Check | is       | `CrowdStrike Overall ZTA score (Crowdstrike s2s)` | And   |
   | Passed Device Posture Check | is       | `AppCheckMac - Required Software (Application)`   |       |

4. Enable **Use dedicated Cloudflare egress IPs**. Select your desired IPv4 and IPv6 addresses. For example:

   | Primary IPv4 address | IPv6 address    |
   | -------------------- | --------------- |
   | `203.0.113.0`        | `2001:db8::/32` |

## Create a named IP range location in Microsoft Entra ID

1. Log in to the [Microsoft Azure portal](https://aka.ms/azureportal).
2. In the sidebar, select **Microsoft Entra ID**.
3. Go to **Security** > **Named locations**.
4. Select **IP ranges location**.
5. Name your location, then add the IP addresses used in your Cloudflare dedicated egress IP policy.
6. Select **Upload**.

This named location corresponds with the locations of your dedicated egress IPs.

## Create a conditional access policy in Microsoft Entra ID

1. In **Protect**, go to **Conditional Access**.
2. Select **Create new policy**.
3. Configure which Entra ID users you want to limit access for, and which traffic, applications, or actions you want to protect.
4. In **Conditions**, select **Locations**. Enable **Configure**.
5. In **Include**, select _Any location_. In **Exclude**, select the named location you created.
6. In **Access controls**, go to **Grant**. Enable _Block access_.

Your policy will block access for your selected users from any location except those using your dedicated egress IPs.

## Test your policies

1. Using [Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/), sign in to your Zero Trust organization with a user's account.
2. Go to any Microsoft 365 app within your organization. Entra ID should allow access.
3. Disconnect the Cloudflare One Client from your Zero Trust organization. Entra ID should block access to any Microsoft 365 applications.

# MongoDB SSH

import { Details } from "~/components";

You can build Zero Trust rules to secure connections to MongoDB deployments using Cloudflare Access and Cloudflare Tunnel. Cloudflare Tunnel requires a lightweight daemon, `cloudflared`, running alongside the deployment and as on the client side.

In this tutorial, a client running `cloudflared` connects over SSH to a MongoDB deployment running on Kubernetes. The deployment example is structured to connect [Compass](https://www.mongodb.com/products/compass) to the MongoDB instance. The MongoDB Kubernetes deployment runs both the MongoDB database service and `cloudflared` as a ingress service that operates like a jump host.

**This tutorial covers how to:**

- Create a Cloudflare Access rule to secure a MongoDB deployment
- Configure a StatefulSet and service definition for the deployment
- Configure an Cloudflare Tunnel connection to Cloudflare's edge
- Create an SSH configuration file for the client

**Time to complete:**

50 minutes

---

## Configure Cloudflare Access

You can build a rule in Cloudflare Access to control who can connect to your MongoDB deployment. Cloudflare Access rules are built around a hostname; even though this deployment will be accessible over SSH, the resource will be represented in Cloudflare as a hostname. For example, if you have the website `app.com` in your Cloudflare account, you can build a rule to secure `mongodb.app.com`.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Applications**.

2. Select **Create new application**.

3. Select **Self-hosted and private**.

4. Select **Add public hostname** and enter the subdomain where users will connect to your deployment (for example, `mongodb.app.com`).

5. Add [Access policies](/cloudflare-one/access-controls/policies/) to control who can reach the deployment. You can build a policy that allows anyone in your organization to connect or you can build more granular policies based on signals like identity provider groups, [multifactor method](/cloudflare-one/tutorials/okta-u2f/), or [country](/cloudflare-one/access-controls/policies/groups/).

6. Follow the remaining [self-hosted application creation steps](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) to publish the application.

## Configure the Kubernetes deployment

To be accessible over SSH, the Kubernetes deployment should manage both the MongoDB standalone service and an SSH proxy service. The configuration below will deploy 1 replica of the database service, available at port 27017, as well as an SSH proxy available at port 22.

<Details header=" StatefulSet Configuration">

```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongodb-standalone
  namespace: mongodb
spec:
  serviceName: database
  replicas: 1
  selector:
    matchLabels:
      app: database
  template:
    metadata:
      labels:
        app: database
        selector: mongodb-standalone
    spec:
      containers:
        - name: mongodb-standalone
          image: mongo
          command: ["mongod"]
          args: ["--config=/config/mongod.conf"]
          ports:
            - containerPort: 27017
              protocol: TCP
              name: mongod
          volumeMounts:
            - name: mongodb-conf
              mountPath: /config
              readOnly: true
            - name: mongodb-data
              mountPath: /data/db
            - name: tls
              mountPath: /etc/tls
            - name: mongodb-socket
              mountPath: /socket
        - name: ssh-proxy
          image: ubuntu:20.04
          command: ["/scripts/entrypoint.sh"]
          ports:
            - containerPort: 22
              protocol: TCP
              name: ssh-port
          volumeMounts:
            - name: mongodb-socket
              mountPath: /socket
            - name: scripts
              mountPath: /scripts
              readOnly: true
            - name: ssh-authorized-keys
              mountPath: /config/ssh
              readOnly: true
          resources:
            requests:
              cpu: 20m
              memory: 32Mi
      volumes:
        - name: mongodb-socket
          emptyDir: {}
        - name: mongodb-conf
          configMap:
            name: mongodb-standalone
            items:
              - key: mongod.conf
                path: mongod.conf
        - name: tls
          secret:
            secretName: tls
        - name: mongodb-data
          persistentVolumeClaim:
            claimName: mongodb-standalone
        - name: scripts
          configMap:
            name: scripts
            items:
              - key: entrypoint.sh
                path: entrypoint.sh
                mode: 0744
        - name: ssh-authorized-keys
          configMap:
            name: ssh-proxy-config
            items:
              - key: authorized_keys
                path: authorized_keys
                mode: 0400
```

</Details>

The corresponding service definition should also specify the ports and target ports for the containers (in this case, the database service and the SSH proxy service).

<Details header="Service Definition">

```yaml
apiVersion: v1
kind: Service
metadata:
  name: database
  namespace: mongodb
  labels:
    app: database
spec:
  clusterIP: None
  selector:
    app: database
  ports:
    - protocol: TCP
      port: 27017
      targetPort: 27017
---
apiVersion: v1
kind: Service
metadata:
  name: ssh-proxy
  namespace: mongodb
  labels:
    app: database
spec:
  selector:
    app: database
  ports:
    - protocol: TCP
      port: 22
      targetPort: 22
```

</Details>

The MongoDB pod and the SSH jump host will share a Unix socket over an empty directory volume. The `entrypoint.sh` file run by the jump host, example below, will start an OpenSSH server.

```bash
#!/bin/sh
export TZ=America/Chicago
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
apt-get update -y && apt-get install -y openssh-server
mkdir /root/.ssh
cp /config/ssh/authorized_keys /root/.ssh/authorized_keys
chmod 400 /root/.ssh/authorized_keys
service ssh start
while true;
do sleep 30;
done;
```

## Configure Cloudflare Tunnel

Next, you can use `cloudflared` to connect to Cloudflare's Edge using Cloudflare Tunnel. Start by [downloading and installing](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/) the Cloudflare Tunnel daemon, `cloudflared`.

Once installed, run the following command to authenticate the instance of `cloudflared` into your Cloudflare account.

```sh
cloudflared login
```

The command will launch a browser window and prompt you to login with your Cloudflare account. Choose a website that you have added into your account.

Once you select one of the sites in your account, Cloudflare will download a certificate file, called `cert.pem` to authenticate this instance of `cloudflared`. The `cert.pem` file uses a certificate to authenticate your instance of `cloudflared` and includes an API key for your account to perform actions like DNS record changes.

You can now use `cloudflared` to control Cloudflare Tunnel connections in your Cloudflare account.

![Download Certificate](~/assets/images/cloudflare-one/secure-origin-connections/share-new-site/cert-download.png)

### Create a Tunnel

You can now [create a Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/) that will connect `cloudflared` to Cloudflare's edge. You'll configure the details of that Tunnel in the next step.

Run the following command to create a Tunnel. You can replace `mongodb` with any name that you choose. This command requires the `cert.pem` file.

`cloudflared tunnel create mongodb`

Cloudflare will create the Tunnel with that name and generate an ID and credentials file for that Tunnel.

![New Tunnel](~/assets/images/cloudflare-one/secure-origin-connections/share-new-site/create.png)

### Delete the `cert.pem` file

The credentials file is separate from the `cert.pem` file. Unlike the `cert.pem` file, the credentials file consists of a token that authenticates only the Named Tunnel you just created. Formatted as `JSON`, the file cannot make changes to your Cloudflare account or create additional Tunnels.

If you are done creating Tunnels, you can delete the `cert.pem` file, leave only the credentials file, and continue to manage DNS records directly in the Cloudflare dashboard or API. For additional information on the different functions of the two files, refer to the list of [useful terms](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/#certpem).

Store the `JSON` file as a Kubernetes secret.

### Configure Cloudflare Tunnel

The previous setps used `cloudflared` to generate a credentials file for your Cloudflare account. When run as a service alongside the MongoDB Kubernetes deployment you will need to use a Docker image of `cloudflared`. Cloudflare makes an [official image available](https://hub.docker.com/r/cloudflare/cloudflared) in DockerHub.

The configuration below will run a single replica of `cloudflared` as an ingress point alongside the MongoDB and SSH proxy services. `cloudflared` will proxy traffic to the SSH proxy service. The `cloudflared` instance will run as its own deployment in a different namespace and, if network policy allows, ingress to any service in the Kubernetes node.

<Details header="`cloudflared` Configuration">

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dashboard-tunnel
  namespace: argotunnel
  labels:
    app: dashboard-tunnel
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dashboard-tunnel
  template:
    metadata:
      labels:
        app: dashboard-tunnel
    spec:
      containers:
        - name: dashboard-tunnel
          # Image from https://hub.docker.com/r/cloudflare/cloudflared
          image: cloudflare/cloudflared:2020.11.11
          command: ["cloudflared", "tunnel"]
          args: ["--config", "/etc/tunnel/config.yaml", "run"]
          ports:
            - containerPort: 5000
          livenessProbe:
            tcpSocket:
              port: 5000
            initialDelaySeconds: 60
            periodSeconds: 60
          volumeMounts:
            - name: dashboard-tunnel-config
              mountPath: /etc/tunnel
            - name: tunnel-credentials
              mountPath: /etc/credentials
      volumes:
        - name: dashboard-tunnel-config
          configMap:
            name: dashboard-tunnel-config
        - name: tunnel-credentials
          secret:
            secretName: tunnel-credentials
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: dashboard-tunnel-config
  namespace: argotunnel
data:
  config.yaml: |
    tunnel: 9a00ef26-4997-4de2-83db-631efc74245c
    credentials-file: /etc/credentials/k8s-dashboard.json
    metrics: :5000
    protocol: http2
    no-autoupdate: true
    ingress:
    - hostname: mongodb.widgetcorp.tech
      originRequest:
        bastionMode: true
    - service: http_status:404
```

</Details>

## Connect from a client

Once deployed, you can run `cloudflared` on the client side to connect to the MongoDB deployment. Add the following lines to your SSH configuration file, replacing the examples with your hostname and details. The `--destination` value should match the URL of the SSH Proxy service configured previously.

```bash
Host mongodb
  ProxyCommand /usr/local/bin/cloudflared access ssh --hostname mongodb.widgetcorp.tech --destination ssh-proxy.mongodb.svc.cluster.local:22
  LocalForward 27000 /socket/mongodb-27017.sock
  User root
  IdentityFile /Users/username/.ssh/id_rsa
```

This is a one-time step. When you next attempt to make an SSH connection to the deployment, `cloudflared` will launch a browser window and prompt you to authenticate. Once authenticated, you will be connected if you have a valid session. Once the tunnel is established, all requests to `localhost:27000` on your machine will be forwarded to `/socket/mongodb-27017.sock` on the SSH proxy container.

You can then set MongoDB Compass to connect to `localhost:27000`.

# Access and secure a MySQL database using Cloudflare Tunnel and network policies

import { DashButton, Render } from "~/components";

Using Cloudflare Tunnel's private networks, users can connect to arbitrary non-browser based TCP/UDP applications, like databases. You can set up network policies that implement zero trust controls to define who and what can access those applications using the Cloudflare One Client.

By the end of this tutorial, users that pass network policies will be able to access a remote MySQL database available through a Cloudflare Tunnel on TCP port 3306.

## Before you begin

Make sure you have:

- A MySQL database listening for remote connections and configured with users that can connect remotely
- (Optional)[Resolver policies](/cloudflare-one/traffic-controls/resolver-policies/) enabled on your account

## Create a Cloudflare Tunnel

Install `cloudflared` on a server in your private network. This server should have connectivity to the MySQL database.

<Render file="tunnel/create-tunnel" product="cloudflare-one" />

## Add private network routes

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. Select **Create route** > **Tunnel CIDR**. Select the tunnel you just created, enter the private IP/CIDR of your MySQL server (for example, `10.128.0.175/32`), and select **Create route**.

3. (Optional) Repeat to create a second route for the private IP/CIDR of your internal DNS server.

The application and (optional) DNS server are now connected to Cloudflare.

## Create a Gateway network policy

1. Go to **Traffic policies** > **Network policies**.
2. Add a [network policy](/cloudflare-one/traffic-controls/network-policies/) that targets the private IP address and the port of the MySQL database (port 3306 by default). The following example allows access to the database to the users that enrolled into the Cloudflare One Client using an `@example.com` email address. The network policies can also take into consideration [device posture checks](/cloudflare-one/reusable-components/posture-checks/).

| Selector         | Operator      | Value           | Logic | Action |
| ---------------- | ------------- | --------------- | ----- | ------ |
| Destination IP   | in            | `10.128.0.175`  | And   | Allow  |
| Destination Port | in            | `3306`          | And   |        |
| User Email       | matches regex | `.*example.com` |       |        |

In addition to the Allow rule above, Cloudflare recommends adding a [catch-all block policy](/learning-paths/replace-vpn/build-policies/) to the bottom of your network policy list to enforce a default-deny model.

Allowed Cloudflare One Client users can now connect to the MySQL server at `10.128.0.175` using the MySQL client of their choice.

## (Optional) Create a Gateway resolver policy

To allow users to access the MySQL database using an internal hostname instead of the private IP address, configure a Gateway resolver policy.

1. Go to **Traffic policies** > **Resolver policies**.

2. Select **Add a policy**.

3. Create an expression to match against the private [domain](/cloudflare-one/traffic-controls/resolver-policies/#domain) or [hostname](/cloudflare-one/traffic-controls/resolver-policies/#host) of the application, like in the following example:

   | Selector | Operator | Value                |
   | -------- | -------- | -------------------- |
   | Domain   | in       | `internalrecord.com` |

4. In **Select DNS resolver**, select _Configure custom DNS resolvers_.

5. Enter the private IP address of your DNS server.

6. In the dropdown menu, select _`<IP-address> - Private`_.

7. (Optional) Enter a custom port.

8. Select **Create policy**.

If your internal DNS server has an `A` record for the MySQL database, users can connect to the server using this record. For example, assuming a BIND server that includes the entry:

`mysql IN  A  10.128.0.175`

Allowed Cloudflare One Client users can connect to the MySQL database at `mysql.internalrecord.com` using the MySQL client of their choice.

# Require U2F with Okta

Many identity providers, like Okta, support multiple multifactor authentication (MFA) options simultaneously. For example, Okta will allow you to login with your password and a temporary code generated in an app or a U2F hard key like a Yubikey.

Some second factor methods are more resistant to phishing. U2F options require you to have access to a physical device, also known as a hardware key. Without that key, a user cannot impersonate you even if they have your password. You can build rules in Cloudflare Access to require that users authenticate with a hardware key - even if your provider supports multiple options. When users login with a less secure option, like an app-based code, Access will block them.

**This tutorial covers how to:**

- Integrate Cloudflare Access with Okta
- Configure Okta for U2F enrollment
- Build an [Access policy](/cloudflare-one/access-controls/policies/) that require users login with a hardware key
- Specify that policy to apply to certain Access applications

The first two sections of this tutorial link to guides to set up Cloudflare Access and integrate Okta. If you already use Cloudflare Access with Okta, you can skip ahead to the fourth section.

**Time to complete:**

20 minutes

---

## Configure Cloudflare Access

Before you begin, you'll need to follow [these instructions](/cloudflare-one/setup/) to set up Cloudflare Access in your account. The hardware key feature is available on any plan, including the free plan.

## Integrate Okta

Follow [these instructions](/cloudflare-one/integrations/identity-providers/okta/) to integrate Okta with your Cloudflare Access account. Once integrated, Access will be able to apply rules using identity, group membership, and multifactor method from Okta.

## Configure Okta for U2F

An Okta administrator in your organization must first [enable U2F support](https://help.okta.com/en/prod/Content/Topics/Security/MFA.htm) in your Okta account **and** [configure users](https://help.okta.com/en/prod/Content/Topics/Security/healthinsight/required-factors.htm) to be prompted for it. This is a global setting; if your account has already configured U2F, you do not need to do anything unique to use it with Cloudflare Access.

## Test U2F in Access

You can begin building U2F policies by testing your Okta integration.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Access controls** > **Access settings**.
2. In **Manage your App Launcher**, select **Manage**.
3. Choose **Login methods**.
4. Choose the row for Okta and select **Test**.

Cloudflare Access will prompt you to login with your Okta account. For the purposes of the test, use a second factor option like an app-based code. Okta will return `amr` values to Cloudflare Access - these are standard indicators of multifactor methods shared between identity control systems.

The `mfa` value is sent by Okta to tell Cloudflare Access that you used a multifactor authentication option. The `pwd` value indicates you used a password. In this example, the `otp` value is sent because the user authenticatd with an app-based code.

You can test with a hardkey by logging out of Okta and returning to the list of providers in Access. Select **Test** again, but this time use your hardware key as a second factor. Cloudflare Access will now see Okta share `hwk` in the `amr` fields.

![Test MFA](~/assets/images/cloudflare-one/zero-trust-security/require-yubikey/with-hwk.png)

## Build a Zero Trust policy to require U2F

You can use this information to build a rule in Access. Go to the `Applications` list in the Cloudflare Access section of the dashboard. Choose an application that you have already built or create a new one. This example adds the requirement to an existing application.

Select **Edit** to edit the existing `Allow` rule.

Add a `Require` rule and select `Authentication Method` from the list. Choose `hwk` as the required `Authentication Method`. Select **Save rule**.

![Require Rule](~/assets/images/cloudflare-one/zero-trust-security/require-yubikey/require-hwk.png)

Optional: you can also configure Cloudflare Access to only show users Okta for this application if you have multiple other providers integrated. In the `Authentication` Tab, choose `Okta` as the only option to show users.

## Testing the rule

You can now test the rule. Visit the application and attempt to login using an app-based code or method other than a hardware security key. Access will block the attempt.

![Blocked](~/assets/images/cloudflare-one/zero-trust-security/require-yubikey/blocked-user.png)

If you sign out of Okta, and reattempt with a hardware key, Access will then allow the connection.

# Use Cloudflare R2 as a Zero Trust log destination

import { DashButton } from "~/components";

:::note
Only available on Zero Trust Enterprise plans.
:::

This tutorial covers how to build a [Cloudflare R2 bucket](/r2/buckets/) to store logs, and how to connect the bucket to the Zero Trust [Logpush service](/cloudflare-one/insights/logs/logpush/) to store logs persistently and export them into other tools.

## Before you begin

- Ensure Cloudflare R2 and the Zero Trust Logpush integration are included in your plan. For more information, contact your account team.

## Create a Cloudflare R2 bucket

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to the **R2 Overview** page.

   <DashButton url="/?to=/:account/r2/overview" />

2. Select **Create bucket**.
3. Enter an identifiable name for the bucket, then select **Create bucket**.

## Create an R2 API token

1. Return to **R2**, then select **Manage R2 API tokens**.
2. Select **Create API token**.
3. In **Permissions**, select **Object Read & Write**.
4. In **Specify bucket(s)**, choose _Apply to specific buckets only_. Select the bucket you created.
5. Configure other token settings to your preferences.
6. Select **Create API Token**.
7. Copy the **Access Key ID**, **Secret Access Key**, and endpoint URL values. You will not be able to access these values again.
8. Select **Finish**.

## Connect a Zero Trust Logpush job

1. In [Cloudflare One](https://one.dash.cloudflare.com/), go to **Insights** > **Logs**. Select **Manage Logpush**.
2. Select **Connect a service**.
3. Choose which data sets and fields you want to send to your bucket. Select **Next**.
4. Select **S3 Compatible**.
5. In **S3 Compatible Bucket Path**, enter the name of your bucket.
6. In **Bucket region**, enter `auto`.
7. Enter the values for **Access Key ID**, **Secret Access Key**, and **Endpoint URL** in their corresponding fields.
8. Select **Push**. If prompted, you do not need to prove ownership with a token challenge.

The Logpush job will send the selected Zero Trust logs to your R2 bucket.

# Implement regional private DNS servers with Gateway resolver policies

import { Render } from "~/components";

Gateway resolver policies allow you to route DNS queries to custom DNS resolvers based on various criteria. This tutorial demonstrates how to configure region-specific private DNS servers to ensure your users are directed to the closest internal resources based on their geographic location.

This approach is particularly useful for organizations with internal networks spanning multiple locations where DNS routes and manages access to private network resources.

By the end of this tutorial, you will have configured Gateway resolver policies to automatically route DNS queries to region-specific private DNS servers based on user location, providing optimal performance and access to internal resources.

This tutorial uses US and EU region servers as example private DNS servers.

## Prerequisites

Before you begin, make sure you have:

- An Enterprise Zero Trust account
- Private DNS servers deployed in multiple regions (for example, US, EU, and APAC)
- A [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) connecting your private DNS servers to Cloudflare
- Internal domains that need to be resolved (for example, `internal.example.com`)

## 1. Connect private DNS servers with Cloudflare Tunnel

First, connect your regional private DNS servers to Cloudflare using Cloudflare Tunnel.

For each region where you have a private DNS server, [create a tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#1-create-a-tunnel). For each tunnel, [add the private IP addresses](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/#2-add-private-network-routes) of your DNS servers. For example, `10.0.1.53/32` for the US region and `10.1.1.53/32` for the EU region.

Repeat this process for all regional DNS servers.

## 2. Create Gateway resolver policies for each region

Once your private DNS servers are connected to Cloudflare, configure Gateway resolver policies to route DNS queries to the appropriate regional DNS server based on user location.

### Create resolver policies for each region

For each region where you have a private DNS server:

1. Go to **Traffic policies** > **Resolver policies**.
2. Select **Add a policy**.
3. Name your policy based on the region (for example, `US Internal DNS`).
4. Create an expression to match internal domains and users in that region. For example, to match users in the United States:

   | Selector                      | Operator | Value                  | Logic |
   | ----------------------------- | -------- | ---------------------- | ----- |
   | Domain                        | in       | `internal.example.com` | And   |
   | Source Country IP Geolocation | in       | _United States_        |       |

5. In **Select DNS resolver**, select _Configure custom DNS resolvers_.
6. Enter the private IP address of your regional DNS server (for example, `10.0.1.53` for US or `10.1.1.53` for EU).
7. In the dropdown menu, choose _`<IP-address> - Private`_.
8. (Optional) Select **Add DNS resolver** and enter a secondary IP address to add a backup DNS resolver.
9. Select **Create policy**.
10. Repeat steps 1-9 for each region where you have a private DNS server. For example, to create a policy to match users in the EU region:

| Selector                      | Operator | Value                                                    | Logic |
| ----------------------------- | -------- | -------------------------------------------------------- | ----- |
| Domain                        | in       | `internal.example.com`                                   | And   |
| Source Country IP Geolocation | in       | _Austria_, _Belgium_, _France_, _Germany_, _Netherlands_ |       |

### Create a fallback resolver policy

Create a catch-all policy for users in regions without a dedicated DNS server, or if no policies match your traffic:

1. Go to **Traffic policies** > **Resolver policies**.
2. Select **Add a policy**.
3. Name your policy (for example, `Internal DNS Fallback`).
4. Create an expression to match internal domains:

   | Selector | Operator | Value                  |
   | -------- | -------- | ---------------------- |
   | Domain   | in       | `internal.example.com` |

5. In **Select DNS resolver**, select _Configure custom DNS resolvers_.
6. Enter the private IP address of your primary DNS server.
7. Select **Create policy**.

## 3. Configure policy order

Gateway will apply resolver policies based on [order of precedence](/cloudflare-one/traffic-controls/order-of-enforcement/#order-of-precedence). Ensure your policies are ordered from most specific to least specific:

1. Go to **Traffic policies** > **Resolver policies**.
2. Use the drag handle to reorder policies:
   - Resolver policies with regional coverage first
   - Your fallback resolver policy last

Gateway will apply the first matching policy. If no policies match your traffic, Gateway will apply the fallback resolver policy. The order between resolver policies with regional coverage does not matter.

## 4. Test your configuration

### Test from different regions

To test your configuration, deploy the Cloudflare One Client on a device in each region where you have a private DNS server and run a DNS query to an internal domain. For example, to test the US region:

1. [Deploy the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/manual-deployment/) on a device in the US region.
2. From the device, open a terminal and run:

   ```sh
   nslookup internal.example.com
   ```

3. Verify that the DNS query returns the expected IP address for your internal resource. The response should show the IP address that your US DNS server is configured to return for `internal.example.com`.
4. Repeat the test from devices in other regions to confirm they receive responses from their respective regional DNS servers. Each region may return different IP addresses based on your DNS server configuration.

### Verify in Gateway logs

1. Go to **Insights** > **Logs** > **DNS query logs**.
2. Filter for queries to `internal.example.com`.
3. Check the **Resolver IP** field to confirm queries are being routed to the correct regional DNS servers based on user location.

## Best practices

- **Use backup resolvers**: Configure secondary DNS resolvers for each region to ensure high availability.
- **Monitor DNS performance**: Use [Gateway Analytics](/cloudflare-one/insights/analytics/gateway/) to track DNS query performance and identify any issues with regional routing.
- **Implement network policies**: Combine resolver policies with [network policies](/cloudflare-one/traffic-controls/network-policies/) to control access to internal resources based on user identity and device posture.
- **Consider virtual networks**: If you have overlapping IP address spaces across regions, use [virtual networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) to isolate traffic.
- **Test failover scenarios**: Regularly test what happens when a regional DNS server becomes unavailable to ensure your backup resolvers work as expected.

## Related resources

- [Resolver policies](/cloudflare-one/traffic-controls/resolver-policies/)
- [Connect private networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/)
- [Gateway Analytics](/cloudflare-one/insights/analytics/gateway/)
- [Virtual networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/)

# Protect access to Amazon S3 buckets with Cloudflare Zero Trust

import { DashButton } from "~/components";

This tutorial demonstrates how to secure access to Amazon S3 buckets with Cloudflare Zero Trust so that data in these buckets is not publicly exposed on the Internet. You can combine Cloudflare Access and AWS VPC endpoints. Enterprise may also use Cloudflare Gateway egress policies with dedicated egress IPs.

## Method 1: Via Cloudflare Access and VPC endpoints

```mermaid
flowchart TB
    cf1[/Cloudflare One Client or clientless users/]--Access policy-->cf2{{Cloudflare}}
    cf2--Cloudflare Tunnel-->vpc1

    subgraph VPC
    vpc1[EC2 VM]-->vpc2[VPC endpoint]
    end
    vpc2-->s3_1

    subgraph S3 service
    s3_1([S3 bucket])
    end

    i1[/Users outside </br> Zero Trust/]-. "S3 access denied" .->s3_1
```

### Prerequisites

- S3 bucket to be protected by Cloudflare Zero Trust
- AWS VPC with one EC2 virtual machine (VM) hosting the [Cloudflare Tunnel daemon](/cloudflare-one/networks/connectors/cloudflare-tunnel/)
- S3 bucket and AWS VPC configured in the same [AWS region](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html)

### 1. Create a VPC endpoint in AWS

1. In the [AWS dashboard](https://aws.amazon.com/console/), go to **Services** > **Networking & Content Delivery** > **VPC**.
2. Under **Virtual private cloud**, go to **Endpoints**.
3. Select **Create endpoint** and name the endpoint.
4. Choose _AWS services_ as the service category.
5. In **Services**, search and select the S3 service in the same region of the VPC. For example, for the AWS region **Europe (London) - eu-west-2**, the corresponding S3 service is named `com.amazonaws.eu-west-2.s3` with a type of Gateway.
6. In **VPC**, select your VPC that contains the EC2 VM hosting the Cloudflare tunnel daemon.
7. In **Route tables**, select the route table associated with the VPC.
8. In **Policy**, choose _Full access_.
9. Select **Create endpoint**.

After you create the VPC endpoint, a new entry in the VPC route table with the target being your VPC endpoint. The entry will have the format `vpce-xxxxxxxxxxxxxxxxx`.

### 2. Set up a bucket policy for VPC access

1. Go to **Services** > **Storage** > **S3**.
2. In Amazon S3, go to **Buckets** > **\<your-S3-bucket>** > **Permissions**.
3. Disable **Block all public access**.
4. In **Bucket policy**, add the following policy:

```json
{
	"Version": "2012-10-17",
	"Id": "VPCe",
	"Statement": [
		{
			"Sid": "VPCe",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:*",
			"Resource": [
				"arn:aws:s3:::<your-S3-bucket01>",
				"arn:aws:s3:::<your-S3-bucket01>/*"
			],
			"Condition": {
				"StringEquals": {
					"aws:SourceVpce": "<your-vpc-endpoint>"
				}
			}
		}
	]
}
```

Your bucket policy will allow your VPC to access your S3 bucket.

### 3. Enable static website hosting for the S3 bucket

1. Return to Amazon S3, then go to **Buckets** > **\<your-S3-bucket01>** > **Properties**.
2. In **Static website hosting**, select **Edit**.
3. Enable **Static website hosting**.
4. Specify the Index and Error documents for the S3 bucket.
5. Select **Save changes**.

A bucket website endpoint will be available at `http://<your-S3-bucket01>.s3-website.<aws-region>.amazonaws.com`. Because of the bucket policy, this website endpoint will only be accessible from the VPC with the VPC endpoint configured.

### 4. Add a published application to the Cloudflare Tunnel

1. In the Cloudflare dashboard, go to **Networking** > **Tunnels**.

   <DashButton url="/?to=/:account/tunnels" />

2. Select your tunnel, then go to the **Routes** tab.
3. Select **Add route**, then select **Published application**.
4. Enter a subdomain your organization will use to access the S3 bucket. For example, `s3-bucket.<your-domain>.com`.
5. In **Service URL**, enter `http://<your-S3-bucket01>.s3-website.<aws-region>.amazonaws.com`.
6. In **Additional application settings** > **HTTP Settings**, input the **HTTP Host Header** as `<your-S3-bucket01>.s3-website.<aws-region>.amazonaws.com`.
7. Select **Save hostname**.

Your Cloudflare Tunnel will terminate at the AWS VPC using your public hostname.

### 5. Restrict S3 access with an Access policy

1. Go to **Access controls** > **Applications**.
2. Select **Create new application**.
3. Select **Self-hosted and private**.
4. Select **Add public hostname** and enter the public hostname used by your Tunnel. For example, `s3-bucket.<your-domain>.com`.
5. Add [Access policies](/cloudflare-one/access-controls/policies/) to determine which users and applications may access your bucket. You can optionally create a [service token](/cloudflare-one/access-controls/service-credentials/service-tokens/) policy to automatically authenticate access to your S3 bucket.
6. Follow the remaining [self-hosted application creation steps](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/) to publish the application.

Users and applications that successfully authenticate via Cloudflare Access can access your S3 bucket at `https://s3-bucket.<your-domain>.com`.

## Method 2: Via Cloudflare Gateway egress policies

:::note

This method is only available on Enterprise plans.
:::

```mermaid
flowchart TB
    cf1[/Cloudflare One Client users/]--Egress policy-->cf2{{Cloudflare}}
    cf2--Egress with dedicated IP-->i1[Internet]
    i1-->s3_1

    subgraph S3 Service
    s3_1([S3 bucket])
    end

    i2[/Users outside </br> Zero Trust/]-. "IPs denied" .->s3_1
```

### Prerequisites

- Cloudflare Zero Trust account with [dedicated egress IPs](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/)
- S3 bucket to be protected by Cloudflare Zero Trust

### 1. Set up a bucket policy to restrict access to a specific IP address

1. In the [AWS dashboard](https://aws.amazon.com/console/), go to **Services** > **Storage** > **S3**.
2. Go to **Buckets** > **\<your-S3-bucket02>** > **Permissions**.
3. Disable **Block all public access**.
4. In **Bucket policy**, add the following policy:

```json
{
	"Version": "2012-10-17",
	"Id": "SourceIP",
	"Statement": [
		{
			"Sid": "SourceIP",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:*",
			"Resource": [
				"arn:aws:s3:::<your-S3-bucket02>",
				"arn:aws:s3:::<your-S3-bucket02>/*"
			],
			"Condition": {
				"IpAddress": {
					"aws:SourceIp": "<your-dedicated-ip>/32"
				}
			}
		}
	]
}
```

### 2. Enable static website hosting for the S3 bucket

1. Return to your bucket, then go to **Properties**.
2. In **Static website hosting**, select **Edit**.
3. Enable **Static website hosting**.
4. Specify the Index and Error documents for the S3 bucket.
5. Select **Save changes**.

A bucket website endpoint will be available at `http://<your-S3-bucket02>.s3-website.<aws-region>.amazonaws.com`. Because of the bucket policy, the website endpoint will only be accessible to traffic sourced from the dedicated egress IP specified.

### 3. Setup a dedicated egress IP policy

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Egress policies**. Select **Add a policy**.
2. Create a policy that specifies which proxied traffic Gateway should assign a [dedicated egress IP](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/) to. For more information, refer to [Egress policies](/cloudflare-one/traffic-controls/egress-policies/).
3. In **Select an egress IP**, choose _Use dedicated Cloudflare egress IPs_. Select the dedicated egress IP defined in your bucket policy.
4. Select **Create policy**.

Traffic proxied by Gateway and assigned your specified egress IP can access your S3 bucket at `http://<your-S3-bucket02>.s3-website.<aws-region>.amazonaws.com`.

# Use Cloudflare Tunnels with Kubernetes client-go credential plugins

This tutorial explains how to use Cloudflare Tunnels with Kubernetes client-go credential plugins for authentication. By following these steps, you can securely access your Kubernetes cluster through a Cloudflare Tunnel using the `kubectl` command-line tool.

## Prerequisites

- A Cloudflare account
- The Cloudflare Tunnel client (`cloudflared`) installed on your machine
- Access to a Kubernetes cluster
- `kubectl` installed on your machine

## 1. Set up a Cloudflare Tunnel

1. Authenticate `cloudflared` with your Cloudflare account:

   ```sh
   cloudflared tunnel login
   ```

2. Create a new tunnel:

   ```sh
   cloudflared tunnel create k8s-tunnel
   ```

3. Configure your tunnel by creating a configuration file named `config.yml`:

   ```yaml
   tunnel: <TUNNEL_ID>
   credentials-file: /path/to/credentials.json
   ingress:
     - hostname: k8s.example.com
       service: tcp://kubernetes.default.svc.cluster.local:443
     - service: http_status:404
   ```

   Replace `<TUNNEL_ID>` with your tunnel ID and adjust the hostname as needed.

4. Start the tunnel:

   ```sh
   cloudflared tunnel run k8s-tunnel
   ```

## 2. Configure the Kubernetes API server

Ensure your Kubernetes API server is configured to accept authentication from Cloudflare Tunnels. This may involve setting up an authentication webhook or configuring the API server to trust the Cloudflare Tunnel's client certificates.

## 3. Set up client-go credential plugin

1. Create a script named `cloudflare-k8s-auth.sh` with the following content:

   ```bash
   #!/bin/bash

   echo '{
     "apiVersion": "client.authentication.k8s.io/v1beta1",
     "kind": "ExecCredential",
     "status": {
       "token": "'"$(cloudflared access token -app=https://k8s.example.com)"'"
     }
   }'
   ```

   Make the script executable:

   ```sh
   chmod +x cloudflare-k8s-auth.sh
   ```

2. Update your `~/.kube/config` file to use the credential plugin:

   ```yaml
   apiVersion: v1
   kind: Config
   clusters:
     - cluster:
         server: https://k8s.example.com
       name: cloudflare-k8s
   users:
     - name: cloudflare-user
       user:
         exec:
           apiVersion: client.authentication.k8s.io/v1beta1
           command: /path/to/cloudflare-k8s-auth.sh
           interactiveMode: Never
   contexts:
     - context:
         cluster: cloudflare-k8s
         user: cloudflare-user
       name: cloudflare-k8s-context
   current-context: cloudflare-k8s-context
   ```

## 4. Use kubectl with Cloudflare Tunnel

Now you can use `kubectl` commands as usual. The client-go credential plugin will automatically handle authentication through the Cloudflare Tunnel:

```sh
kubectl get pods
```

## Troubleshooting

If you encounter issues:

- Ensure `cloudflared` is running and the tunnel is active
- Check that your `~/.kube/config` file is correctly configured
- Verify that the Kubernetes API server is properly set up to accept authentication from Cloudflare Tunnels
- Review the Cloudflare Tunnel logs for any error messages

For more information, refer to the [Cloudflare Tunnels documentation](/cloudflare-one/networks/connectors/cloudflare-tunnel/) and the [Kubernetes client-go credential plugins documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins).

# Use virtual networks to change user egress IPs

import { Details, TabItem, Tabs, APIRequest, DashButton } from "~/components";

:::note

Only available on Enterprise plans.

:::

This tutorial gives administrators an easy way to allow their users to change their egress IP address between any of your assigned dedicated egress IP addresses. Your users can choose which egress IP to use by switching virtual networks directly from in the Cloudflare One Client.

Changing egress IPs can be useful in quality assurance (QA) and other similar scenarios in which users both use their local egress location and either switch to or simulate other remote locations.

## Before you begin

Make sure you have:

- [Deployed the Cloudflare One Client](/cloudflare-one/team-and-resources/devices/cloudflare-one-client/deployment/) on your users' devices.
- [Configured tunnels](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/) to connect your private network to Cloudflare. This tutorial assumes you have:
  - Created two tunnels [through the dashboard](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).
  - Routed `10.0.0.0/8` through one tunnel.
  - Routed `192.168.88.0/24` through the other tunnel.
- Received multiple [dedicated egress IP addresses](/cloudflare-one/traffic-controls/egress-policies/dedicated-egress-ips/).

## Create a virtual network for each egress route

First, create [virtual networks](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) corresponding to your dedicated egress IPs.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the Cloudflare dashboard, go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. In **Virtual networks**, select **Create virtual network**.
3. Name your virtual network. We recommend using a name related to the location of the corresponding dedicated egress IP. For example, if your users will egress from the Americas, you can name the virtual network `vnet-AMER`.
4. Select **Save**.
5. Repeat Steps 2-4 for each dedicated egress IP you want users to switch between. For example, you can create another virtual network called `vnet-EMEA` for egress from Europe, the Middle East, and Africa.

</TabItem>

<TabItem label="API">

1.  Create a [virtual network](/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/cloudflared/tunnel-virtual-networks/) corresponding to one of your dedicated egress IPs. We recommend using a name related to the location of the corresponding dedicated egress IP. For example, if your users will egress from the Americas, you can name the virtual network `vnet-AMER`.

        <APIRequest
        	path="/accounts/{account_id}/teamnet/virtual_networks"
        	method="POST"
        	json={{
        		comment: "Virtual network to egress from the Americas",
        		is_default: false,
        		name: "vnet-AMER",
        	}}
        />

    For more information, refer to [Create a virtual network](/api/resources/zero_trust/subresources/networks/subresources/virtual_networks/methods/create/).

2.  Repeat Step 1 for each dedicated egress IP you want users to switch between. For example, you can create another virtual network called `vnet-EMEA` for egress from Europe, the Middle East, and Africa.

</TabItem> </Tabs>

## Assign each virtual network to each tunnel

After creating your virtual networks, route your private network CIDRs over each virtual network. This ensures that users can reach all services on your network regardless of which egress IP they use.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. Go to **Networking** > **Routes**.

   <DashButton url="/?to=/:account/magic-networks/routes" />

2. Select **Create route**, then choose **Tunnel CIDR**.
3. Select the tunnel that routes `10.0.0.0/8` and enter `10.0.0.0/8` as the network.
4. Under **Virtual network**, choose your first virtual network. For example, `vnet-AMER`.
5. Select **Create route**.
6. To route `10.0.0.0/8` over another virtual network, repeat Steps 2-5 using a different virtual network (for example, `vnet-EMEA`).
7. Repeat for each virtual network you created and for each private network tunnel route.

</TabItem>

<TabItem label="API">

1.  Assign your first virtual network to your private network route. For example, assign `vnet-AMER` to your tunnel that routes `10.0.0.0/8`:

        <APIRequest
        	path="/accounts/{account_id}/teamnet/routes/{route_id}"
        	method="PATCH"
        	json={{
        		network: "10.0.0.0/8",
        		tunnel_id: "<TUNNEL_UUID>",
        		virtual_network_id: "<VNET_AMER_UUID>",
        	}}
        />

    For more information, refer to [Update a tunnel route](/api/resources/zero_trust/subresources/networks/subresources/routes/methods/edit/).

2.  Repeat this process for each virtual network you created. For example:

        <APIRequest
        	path="/accounts/{account_id}/teamnet/routes/{route_id}"
        	method="PATCH"
        	json={{
        		network: "10.0.0.0/8",
        		tunnel_id: "<TUNNEL_UUID>",
        		virtual_network_id: "<VNET_EMEA_UUID>",
        	}}
        />

3.  Repeat Steps 1-2 for each private network tunnel route.

</TabItem> </Tabs>

Each tunnel connected to your private network should have each of your virtual networks assigned to it. For example, if you have tunnels routing `10.0.0.0/8` and `192.168.88.0/24`, both tunnels should have the `vnet-AMER` and `vnet-EMEA` virtual networks assigned.

| Tunnel       | CIDR              | Virtual network |
| ------------ | ----------------- | --------------- |
| **Tunnel 1** | `10.0.0.0/8`      | `vnet-AMER`     |
|              | `10.0.0.0/8`      | `vnet-EMEA`     |
| **Tunnel 2** | `192.168.88.0/24` | `vnet-AMER`     |
|              | `192.168.88.0/24` | `vnet-EMEA`     |

## Create virtual network egress policies

Next, assign your dedicated egress IPs to each virtual network using Gateway egress policies.

<Tabs syncKey="dashPlusAPI"> <TabItem label="Dashboard">

1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Zero Trust** > **Traffic policies** > **Egress policies**.

2. Select **Add a policy**.

3. Name your policy. We recommend including the country or region traffic will egress from.

4. Add the virtual network with the _Virtual Network_ selector. For example:

   | Selector        | Operator | Value       |
   | --------------- | -------- | ----------- |
   | Virtual Network | is       | _vnet-AMER_ |

5. In **Select an egress IP**, choose **Use dedicated Cloudflare egress IPs**. Choose the dedicated IPv4 and IPv6 addresses you want traffic to egress with.

6. Select **Create policy**.

7. Repeat Steps 1-6 to create a separate egress policy for each virtual network you created.

</TabItem>

<TabItem label="API">

1.  Add a Gateway egress policy that matches the corresponding virtual network. For example:

        <APIRequest
        	path="/accounts/{account_id}/gateway/rules"
        	method="POST"
        	json={{
        		action: "egress",
        		description: "Egress via North America by connecting to vnet-AMER",
        		enabled: true,
        		filters: [
        			"egress"
        		],
        		name: "Egress AMER vnet",
        		precedence: 0,
        		traffic: "net.vnet_id == <VNET_AMER_UUID>",
        		rule_settings: {
        			egress: {
        				ipv4: "<DEDICATED_IPV4_ADDRESS>",
        				ipv4_fallback: "<SECONDARY_DEDICATED_IPV6_ADDRESS>", // Note: Typo in original JSON - suggests IPv6 for fallback
        				ipv6: "<DEDICATED_IPV6_ADDRESS>",
        			}
        		},
        	}}
        />

    For more information, refer to [Create a Zero Trust Gateway rule](/api/resources/zero_trust/subresources/gateway/subresources/rules/methods/create/).

2.  Repeat Step 1 to create an egress policy for each virtual network you created.

</TabItem> </Tabs>

Each policy you create should correspond to a different primary dedicated egress IP.

## Test virtual network egress

<Details header="Windows, macOS, and Linux">

1. On your user's device, log in to your Zero Trust organization in the Cloudflare One Client.

2. In a terminal, run the following command to check the default egress IP address.

   ```sh
   curl ifconfig.me -4
   ```

   The command should output your organization's default egress IP.

3. In the client GUI, use the **VNET** dropdown to switch to a virtual network you created.
   <Details header="Version 2026.1 and earlier">

   In the Cloudflare One Client, select the gear icon > **Virtual Networks**.

   </Details>

4. Check the egress IP address by running `curl ifconfig.me -4` again. The command should output the IP address specified in your egress policy.

</Details>

<Details header="iOS and Android">

1. On your user's device, log in to your Zero Trust organization in the Cloudflare One Agent app.
2. In a browser, go to [ifconfig.me](https://ifconfig.me/). Your organization's default egress IP should appear in **IP Address**.
3. In Cloudflare One Agent, go to **Advanced** > **Connection options** > **Virtual networks**. Choose a virtual network you created.
4. Check the egress IP address by reloading the browser page from Step 1. The IP address specified in your egress policy should appear in **IP Address**.

</Details>

While your users are connected to a virtual network, their traffic will route via the dedicated egress IP specified. You can repeat these steps to test that each virtual network is egressing from the correct IP.