There is a recurring confusion among teams that are adopting Cloudflare Zero Trust: treating Access and Tunnel as interchangeable parts of the same system, when in practice they are products with completely different responsibilities. An engineer who configures the Tunnel and assumes authentication is resolved is publishing a private service without any identity gates. Understanding what each piece does — and when it's worth using one without the other — is what separates a well-architected implementation from a loophole waiting to be exploited.
What Cloudflare Access does
Access is an identity proxy. It sits in front of a hostname — app.empresa.com, for example — and intercepts each request before it reaches the origin. When the user does not have a valid session, Access redirects to the configured identity provider. After successful authentication, Access evaluates the policy associated with that application: does the user belong to the permitted group? Did the authentication use MFA? Is the device compliant? If all conditions are met, Access issues a session JWT and the request goes to the origin.
The source can be anything — a server with a public IP, an internal service, a Tunnel. Access doesn't care where the source is; he cares about whoever is trying to get there. This distinction is important because it means that Access works in front of applications that already have a public IP, without the need for a Tunnel. If you have an application with a public IP that needs access control based on corporate identity, Access solves this without moving the application or installing cloudflared.
What Cloudflare Tunnel does
Tunnel solves a different problem: how to make a private server reachable over the internet without opening gateways in the firewall. The cloudflared daemon, installed on the server or in a container on the same network, establishes an encrypted outbound connection to the Cloudflare edge. From there, Cloudflare acts as an intermediary — inbound traffic reaches the edge and travels down the already established tunnel to the internal service.
The result is that the internal server does not have a public IP, does not have port 80 or 443 open to the internet, and does not need an entry rule in the security group or firewall. The only traffic that enters is that which comes from the cloudflared process itself running locally. A server completely isolated from direct external access is now reachable by authorized users through the Cloudflare edge.
cloudflared can be configured as a systemd service, Docker container, or Kubernetes deployment. Modern setup uses the Cloudflare dashboard to manage the Tunnel — no local YAML file, with configuration propagated via API. A single process cloudflared can expose multiple services on different hostnames: app1.empresa.com goes to port 3000 locally, app2.empresa.com goes to port 4000, db-admin.empresa.com goes to pgAdmin on port 5050.
When to use Tunnel without Access
Tunneling without Access is a valid and common scenario: you want to route traffic from a public application through the Cloudflare edge for DDoS and CDN protection, without additional authentication. The service is publicly accessible, but traffic reaches the server only via Cloudflare — without direct exposure of the originating IP. Cloudflare protects against volumetric attacks and the server is hidden.
Other use: shared local development. A developer wants to show a prototype running locally to someone outside the network. cloudflared tunnel --url localhost:3000 creates a publicly accessible temporary URL without DNS or firewall configuration. No Access, no authentication — but useful for the specific case.
The risk is to assume that Tunnel implies protection. It doesn't imply. Tunnel is connectivity. Without Access at the front, any request that reaches the hostname configured in Cloudflare reaches your internal service.
When to use Access without Tunnel
Access without Tunnel makes sense when the source already has a public IP — a server on EC2 with an elastic IP, an application on a PaaS, an API endpoint with a public address. Access acts as an identity proxy in front of this public address, without the need for cloudflared.
The critical operational detail in this scenario: the origin needs to accept traffic only from the Cloudflare edge. If the server continues to accept requests from any IP, Access can be bypassed by simply accessing the IP directly. The correct practice is to configure the server to accept traffic only from Cloudflare's IP ranges, or to use an authenticated header that Access injects and the application verifies.
The combination that truly delivers Zero Trust
The combination of the two is what implements the full Zero Trust model: the Tunnel makes the private server reachable via Cloudflare; Access requires authentication and policy evaluation before allowing any request to go down the Tunnel. The internal server never has direct exposure to the internet and never receives unauthenticated requests.
The flow: user accesses the hostname → Access checks session, redirects to IdP if necessary → after authentication and policy check, Access forwards the request to the edge → edge goes down the Tunnel → cloudflared delivers it to the local process. The internal server only sees traffic already authorized by Access.
For machine-to-machine access — CI/CD pipelines, monitoring agents, integrations — Access issues service tokens: a client ID and a secret that replace the human SSO flow. The automated service includes these tokens in the request header, and Access recognizes them as a trusted service identity, applying the associated policy. Every access via service token also generates an audit event.
The vision of those who architect the system
A point that was missed during the design phase: Tunnel has no egress cost at Cloudflare. Traffic passing through cloudflared is not charged for bandwidth on the Team plan. This has real cost implications compared to alternatives that charge per GB transferred — and influences the decision to use Cloudflare Tunnel versus similar tunneling solutions.
The central architectural decision when adopting both components is: where are the access policies? Access allows you to create granular policies per application — one group has access to the admin panel, another has read-only access to monitoring, external collaborators only access the documentation portal. This granularity does not exist in traditional VPN. The cost of maintaining this granularity is the ongoing management of policies as teams change and applications evolve. Automating this via Terraform or Cloudflare's API is the step that transforms access management from a manual task into a controlled process.
