Skip to content

[Solution guide] Build customer portal zero trust access no vpn sg#30661

Draft
marciocloudflare wants to merge 6 commits intoproductionfrom
marcio/build-customer-portal-zero-trust-access-no-vpn-sg
Draft

[Solution guide] Build customer portal zero trust access no vpn sg#30661
marciocloudflare wants to merge 6 commits intoproductionfrom
marcio/build-customer-portal-zero-trust-access-no-vpn-sg

Conversation

@marciocloudflare
Copy link
Copy Markdown
Contributor

@marciocloudflare marciocloudflare commented May 7, 2026

Summary

Closes PCX-21162

Documentation checklist

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

This pull request requires reviews from CODEOWNERS as it changes files that match the following patterns:

Pattern Owners
* @cloudflare/pcx-technical-writing, @cloudflare/product-owners

@@ -0,0 +1,297 @@
---
pcx_content_type: solution-guide
title: Build a customer portal with Zero Trust access (Zero Trust Free plan)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title: Build a customer portal with Zero Trust access (Zero Trust Free plan)
title: Publish a customer portal with Zero Trust access (Zero Trust Free plan)


In this stage you install `cloudflared`, create a tunnel, and map a public hostname to your local service.

### Prerequisites
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest moving prereqs up a level and higher up on the page


The fastest path uses the **Get Started** flow in the Cloudflare dashboard. It creates a tunnel, installs `cloudflared` on your host, and creates an Access application in one workflow.

1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Get Started**.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check /cloudflare-one/setup/secure-private-apps/private-web-app/ to see if this content can be turned into partials

9. Select **Continue**.

<Render
file="access/secure-private-apps-shared-steps"
Copy link
Copy Markdown
Contributor

@ranbel ranbel May 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's some inconsistency in the example URL, i.e. we show grafana.example.com and dashboard.example.com in earlier steps and portal.example.com at the end.

If you prefer to create the tunnel and Access application separately:

- For a hand-driven dashboard flow, refer to [Create a tunnel (dashboard)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/) and then [Publish a self-hosted application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/).
- For an automated, API-driven setup (useful for Terraform or CI), refer to [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- For an automated, API-driven setup (useful for Terraform or CI), refer to [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/).
- For an automated, API-driven setup, refer to [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/) or [Create a tunnel (Terraform)](/cloudflare-one/networks/connectors/cloudflare-tunnel/deployment-guides/terraform/).


A single tunnel can publish multiple applications by mapping different subdomains to different local services. For example, `portal.example.com` reaches `localhost:3000` and `admin.example.com` reaches `localhost:8080`.

To add a second published application to an existing tunnel, refer to [Published applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/). Each route maps one public hostname to one local service. Follow that page using the `customer-portal` tunnel you created in the previous step.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To add a second published application to an existing tunnel, refer to [Published applications](/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/). Each route maps one public hostname to one local service. Follow that page using the `customer-portal` tunnel you created in the previous step.
To add a second published application to an existing tunnel, refer to [Add a published application route](/cloudflare-one/networks/routes/add-routes/#add-a-published-application-route). Each route maps one public hostname to one local service. Follow that page using the `customer-portal` tunnel you created in the previous step.


## Add identity-based access control

To control which employees and clients can reach the portal, put [Cloudflare Access](/cloudflare-one/access-controls/) in front of it. Access checks every request against a policy before it reaches your origin, and the policy can require an identity provider login, a one-time PIN, a service token, or other context such as country or device posture.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Device posture requires having a managed corporate device (running the CF1 Client or with mTLS certs installed). If you're focusing on clientless access then I would leave this out.

Suggested change
To control which employees and clients can reach the portal, put [Cloudflare Access](/cloudflare-one/access-controls/) in front of it. Access checks every request against a policy before it reaches your origin, and the policy can require an identity provider login, a one-time PIN, a service token, or other context such as country or device posture.
To control which employees and clients can reach the portal, put [Cloudflare Access](/cloudflare-one/access-controls/) in front of it. Access checks every request against a policy before it reaches your origin, and the policy can require an identity provider login, a one-time PIN, a service token, or other context such as country.

- [Okta](/cloudflare-one/integrations/identity-providers/okta/) for Okta-based SSO.
- [Microsoft Entra ID](/cloudflare-one/integrations/identity-providers/entra-id/) for Microsoft 365 organizations.
- [GitHub](/cloudflare-one/integrations/identity-providers/github/) when most users already have GitHub accounts (useful for development teams).
- [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) for external clients who do not log in with your IdP. One-time PIN is enabled by default and requires no additional configuration.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) for external clients who do not log in with your IdP. One-time PIN is enabled by default and requires no additional configuration.
- [One-time PIN](/cloudflare-one/integrations/identity-providers/one-time-pin/) for external clients who do not log in with your IdP.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is turned off by default in a new account, unless the dash team has recently changed the behavior. The dash quick start flow may be turning it on for you.


1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Access controls** > **Applications**.

2. Find your customer portal application, select **Configure**, then go to the **Policies** tab.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Access apps flow was recently updated in the dash. See #30646 and #30651 for the new instructions.

- Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `acme.example.com`).
- On each application's policy, use an `Allow` action with an `Include` rule that references the client's rule group.

If your identity provider exposes group membership (for example, Google Workspace groups, Okta groups, or Microsoft Entra ID security groups), you can also map IdP groups to applications without maintaining email lists in Cloudflare. Refer to the IdP-specific page for the group claim configuration.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If your identity provider exposes group membership (for example, Google Workspace groups, Okta groups, or Microsoft Entra ID security groups), you can also map IdP groups to applications without maintaining email lists in Cloudflare. Refer to the IdP-specific page for the group claim configuration.
If your identity provider exposes group membership (for example, Google Workspace groups, Okta groups, or Microsoft Entra ID security groups), you can also map IdP groups to applications without maintaining email lists in Cloudflare. Refer to the [IdP-specific page](/cloudflare-one/integrations/identity-providers/) for the group claim configuration.


1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/) and go to **Zero Trust** > **Access controls** > **Applications**.

2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to the **Policies** tab.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to the **Policies** tab.
2. Find the application the automated system should reach (for example, your internal API), select **Configure**, then go to **Access policies**.


4. Configure the policy:

- **Action**: _Service Auth_
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to use the same policy format shown in ?

A typical pattern:

- Create one rule group per client containing the client contacts' email addresses.
- Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `acme.example.com`).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `acme.example.com`).
- Create a separate Access application for each client's dashboard, mapped to the corresponding hostname (for example, `client-acme.example.com`).

@alainsignorelli59-cmd
Copy link
Copy Markdown

alainsignorelli59-cmd commented May 7, 2026 via email

Store both values in environment variables or a secrets manager. Do not commit them to source control.

:::note[Service token expiration]
Service tokens expire on the duration you selected when creating them. To extend a token before it expires or to rotate a compromised one, refer to the Service tokens documentation.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Service tokens expire on the duration you selected when creating them. To extend a token before it expires or to rotate a compromised one, refer to the Service tokens documentation.
Service tokens expire on the duration you selected when creating them. To extend a token before it expires or to rotate a compromised one, refer to the [Service tokens documentation](/cloudflare-one/access-controls/service-credentials/service-tokens/#renew-service-tokens).

Each log entry contains the user email, IP address, the application requested, the identity provider used, whether the attempt was allowed, and a Ray ID for tracing.

:::note[Per-request logs require Enterprise]
Authentication logs record every login. To audit individual HTTP requests made during an authenticated session, refer to the Per-request logs section in the Access authentication logs reference.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Authentication logs record every login. To audit individual HTTP requests made during an authenticated session, refer to the Per-request logs section in the Access authentication logs reference.
Authentication logs record every login. To audit individual HTTP requests made during an authenticated session, refer to the [Per-request logs section](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/#per-request-logs) in the Access authentication logs reference.


### Run more than one tunnel replica

A single `cloudflared` instance is a single point of failure. To make the tunnel highly available, run `cloudflared` on more than one host. Cloudflare load-balances requests across all replicas of the same tunnel.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A single `cloudflared` instance is a single point of failure. To make the tunnel highly available, run `cloudflared` on more than one host. Cloudflare load-balances requests across all replicas of the same tunnel.
A single `cloudflared` instance is a single point of failure. To ensure that the application remains available if a `cloudflared` instance goes down, run `cloudflared` on more than one host.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To load balance traffic, users need to setup Cloudflare Load Balancing. Replicas only handle failover scenarios.

Anyone with the tunnel token can run the tunnel. Store it in a secrets manager and rotate it if it is exposed. Refer to [Rotate a token without service disruption](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/#rotate-a-token-without-service-disruption) for the rotation procedure.
:::

### Require managed devices for sensitive applications (paid)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloudflare One Client and device posture are available on free plans.

Anyone with the tunnel token can run the tunnel. Store it in a secrets manager and rotate it if it is exposed. Refer to [Rotate a token without service disruption](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/remote-tunnel-permissions/#rotate-a-token-without-service-disruption) for the rotation procedure.
:::

### Require managed devices for sensitive applications (paid)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Require managed devices for sensitive applications (paid)
### Require managed devices for sensitive applications

- [Cloudflare Tunnel overview](/cloudflare-one/networks/connectors/cloudflare-tunnel/) — concept page covering outbound-only connections.
- [Tunnel with firewall](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) — IPs and hostnames to allowlist when egress is restricted.
- [Tunnel useful terms](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/) — glossary of tunnel and connector concepts.
- [Create a tunnel (API)](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/) — automated tunnel creation for Terraform or CI.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


- [Cloudflare Tunnel overview](/cloudflare-one/networks/connectors/cloudflare-tunnel/) — concept page covering outbound-only connections.
- [Tunnel with firewall](/cloudflare-one/networks/connectors/cloudflare-tunnel/configure-tunnels/tunnel-with-firewall/) — IPs and hostnames to allowlist when egress is restricted.
- [Tunnel useful terms](/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/tunnel-useful-terms/) — glossary of tunnel and connector concepts.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd replace the Tunnel useful terms link with Published applications: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/

- [Access policies](/cloudflare-one/access-controls/policies/) — reference for actions, rule types, and selectors.
- [Common policies](/cloudflare-one/access-controls/policies/common-policies/) — example policies for typical scenarios.
- [Rule groups](/cloudflare-one/access-controls/policies/groups/) — reusable rule sets for per-tenant access.
- [Choose an application type](/cloudflare-one/access-controls/applications/choose-application-type/) — comparison of self-hosted, SaaS, infrastructure, and bookmark applications.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [Choose an application type](/cloudflare-one/access-controls/applications/choose-application-type/) — comparison of self-hosted, SaaS, infrastructure, and bookmark applications.

(This guide is only using self-hosted apps.)

**Identity providers**

- [Identity providers overview](/cloudflare-one/integrations/identity-providers/) — index of supported providers.
- [Google Workspace](/cloudflare-one/integrations/identity-providers/google-workspace/) — Google Workspace integration steps.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add Microsoft Entra ID to this list, or leave out the IdP-specific links.

**Logs and monitoring**

- [Access authentication logs](/cloudflare-one/insights/logs/dashboard-logs/access-authentication-logs/) — full reference for authentication log fields.
- [Tunnel notifications](/cloudflare-one/networks/connectors/cloudflare-tunnel/monitor-tunnels/notifications/) — tunnel health alert configuration.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the end of this guide, you will have:

- An internal web application reachable through a public hostname on your domain (for example, `dashboard.example.com`).
- Access policies that allow employees through their corporate identity provider and named clients through one-time PIN.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is a "named client"?


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

When you need to give employees, clients, or contractors access to internal applications, you have to decide how users prove who they are, which applications each user can reach, and how to grant temporary access to automated systems. Common scenarios include exposing internal admin panels to staff, sharing client-specific dashboards with external customers, or letting CI/CD pipelines reach internal APIs. This guide replaces VPN-style network access with per-application access policies tied to user identity, using Cloudflare Tunnel to connect your application to Cloudflare and Cloudflare Access to enforce who can reach it. The core workflow runs on the [Zero Trust Free plan](https://www.cloudflare.com/plans/zero-trust-services/); some hardening features in the final stage require paid Zero Trust plans.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardening features mentioned below should all be available on Free. Some logging functionality requires Enterprise.

- tunnel
- warp-client
sidebar:
label: Build a customer portal
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
label: Build a customer portal
label: Publish a customer portal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants