VPN was designed in 1996 for a world where corporate networks had a defined perimeter. Zero Trust Network Access (ZTNA) was designed for a world where the perimeter doesn’t exist — where users work from anywhere, applications live in multiple clouds, and “inside the network” is no longer a meaningful security concept.
This guide explains the architectural difference, the identity verification model behind ZTNA, and how to migrate from legacy VPN to a modern ZTNA deployment.
The Core Problem: VPN’s Trust Model
Traditional VPN authenticates once and grants network-level access. A user connects, and the client receives a private IP address on your corporate subnet. From that point, the VPN treats all traffic as trusted — the user can reach file servers, databases, internal APIs, and management interfaces beyond what their job requires.
This “authenticate once, trust everything” model is why ransomware spreads so effectively through corporate networks. The attacker compromises one endpoint, establishes a VPN session, and uses that implicit trust to move laterally.
ZTNA inverts this model: never trust, always verify, at the application layer.
ZTNA Architecture
How ZTNA Works
The ZTNA connector runs inside your network and maintains an outbound-only connection to the ZTNA cloud control plane. Your applications never need inbound firewall rules opened — the connector initiates the connection, not the user’s device.
Identity Verification Model
ZTNA integrates directly with your Identity Provider (IdP) using OIDC or SAML. Every application access request triggers a fresh authorization check:
| Signal | VPN | ZTNA |
|---|---|---|
| User authentication | Once at tunnel start | Per-app connection + CAE |
| Device trust | IP address only | MDM certificate + compliance check |
| Application scope | Entire subnet | Specific app:port |
| Context evaluation | None | Time, location, risk score |
| Session revocation | Manual disconnect | Real-time (CAE) |
Keycloak as ZTNA Identity Broker
Keycloak can serve as the OIDC identity broker for ZTNA solutions:
# Keycloak realm configuration for ZTNA
# Create a dedicated client for the ZTNA provider
POST /admin/realms/corporate/clients
{
"clientId": "ztna-provider",
"protocol": "openid-connect",
"publicClient": false,
"redirectUris": ["https://your-ztna-provider.com/callback"],
"attributes": {
"access.token.lifespan": "300", // 5 min - ZTNA re-verifies frequently
"use.refresh.tokens": "true",
"pkce.code.challenge.method": "S256"
}
}
Configure device posture claims using Keycloak’s Protocol Mapper to pass device compliance status in the access token:
{
"name": "device-compliance",
"protocol": "openid-connect",
"protocolMapper": "oidc-hardcoded-claim-mapper",
"config": {
"claim.name": "device_compliant",
"claim.value": "true",
"id.token.claim": "false",
"access.token.claim": "true"
}
}
The ZTNA policy engine reads the device_compliant claim and blocks access from non-compliant devices at the network layer, before the application ever receives the request.
ZTNA vs VPN: Complete Comparison
Security Model
| Aspect | VPN | ZTNA |
|---|---|---|
| Access scope | Full network subnet | Single application |
| Lateral movement | Possible — user can reach any accessible IP | Impossible — no network adjacency |
| Credential theft impact | Full network compromise | One app compromised |
| Session re-evaluation | Never (until disconnect) | Continuous (CAE) |
| Zero-day exploits | VPN appliance is a public attack surface | Control plane is SaaS, connector is outbound-only |
| Unmanaged devices | Usually blocked by IP/cert | Clientless ZTNA enables browser-based access |
Performance Characteristics
VPN routes all traffic through a central gateway, causing backhauling — a user in Austin accessing Salesforce has traffic routed through the corporate data center in Dallas, then back to Salesforce’s cloud, doubling latency.
ZTNA uses split tunneling by default:
- SaaS apps: Direct to cloud, no backhauling
- Private apps: Routed via nearest ZTNA PoP → private connector
- Internet traffic: Direct, unrouted through ZTNA
The result: most users see improved performance for SaaS applications immediately after switching.
Cost Comparison
| Item | VPN | ZTNA |
|---|---|---|
| Hardware | VPN concentrators ($5K-$50K) | None (SaaS) |
| Licensing | Per device or concurrent users | Per user/month |
| Bandwidth | All traffic through corporate WAN | Only private app traffic |
| Operational | VPN appliance patching, firmware updates | Managed by vendor |
| Incident response | Broad blast radius investigations | App-scoped forensics |
For enterprises with 500+ remote users, ZTNA typically reduces total cost by 30-60% when factoring in appliance hardware, bandwidth, and operational overhead.
Provider Comparison
Cloudflare Access
Cloudflare Access is the ZTNA component of Cloudflare Zero Trust. Strong for:
- Web application access (HTTP/HTTPS)
- SSH/RDP via browser-rendered clients
- Tight integration with Cloudflare CDN (performance benefit)
- Free tier for up to 50 users
# Cloudflare Access — configure application with Keycloak IdP
# Via Cloudflare dashboard or Terraform:
resource "cloudflare_access_application" "private_app" {
zone_id = var.zone_id
name = "Internal Dashboard"
domain = "dashboard.corp.example.com"
session_duration = "24h"
}
resource "cloudflare_access_policy" "corp_users" {
application_id = cloudflare_access_application.private_app.id
name = "Corp Users Only"
decision = "allow"
include {
email_domain = ["corp.example.com"]
}
require {
# Device posture via Cloudflare WARP client
device_posture = [cloudflare_device_posture_rule.compliant.id]
}
}
Zscaler Private Access (ZPA)
ZPA leads in enterprise deployments. Strong for:
- Large-scale deployments (100K+ users)
- Complex segmentation policies
- Integration with Okta and Entra ID
- App Connectors behind firewall with no inbound rules
Palo Alto Prisma Access
Prisma ZTNA + SASE. Strong for:
- Customers with existing Palo Alto Next-Gen Firewall
- Unified security policy across ZTNA and web filtering
- Advanced threat prevention inline with access
OAuth Token Flow in ZTNA
ZTNA relies on standard OAuth 2.0 flows for application access tokens. Understanding this flow helps diagnose access failures:
If you see invalid_grant errors in ZTNA access logs, the root cause is usually an expired or replayed authorization code — see OAuth invalid_grant Error: Complete Troubleshooting Guide.
For the token verification mechanics ZTNA uses, see How to Decode JWT Tokens in JavaScript and the JWT Decoder tool.
Implementing ZTNA with Entra ID
Microsoft Entra Private Access is Microsoft’s ZTNA solution, deeply integrated with Entra ID Conditional Access:
# Entra Private Access — configure Quick Access connector
# Install the Private Network Connector on a domain-joined server
# 1. Register connector via Entra admin center
# 2. Configure Quick Access application
# 3. Set Conditional Access policy requiring MFA + compliant device
$policy = @{
DisplayName = "ZTNA - Require Compliant Device"
State = "enabled"
Conditions = @{
Users = @{ IncludeGroups = @("All Corp Users") }
Applications = @{ IncludeApplications = @("$EntraPrivateAccessAppId") }
}
GrantControls = @{
Operator = "AND"
BuiltInControls = @("mfa", "compliantDevice")
}
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $policy
For broader Entra ID configuration patterns for Zero Trust, see Zero Trust Architecture Implementation: A Practical Guide for IAM Engineers.
Migration Strategy: VPN → ZTNA
Phase 1: Application Discovery (Week 1-2)
Before migrating, inventory which applications are accessed via VPN and categorize by protocol:
# Analyze VPN firewall logs to find top private app destinations
# (adjust for your firewall log format)
awk '{print $7}' /var/log/vpn-access.log | \
sort | uniq -c | sort -rn | head -20
Applications typically fall into three categories:
- Web/HTTPS: Immediate ZTNA candidates (highest ROI)
- SSH/RDP: ZTNA with clientless browser rendering
- Custom protocols (UDP, raw TCP): Requires ZTNA TCP proxy or keep on VPN
Phase 2: Pilot Deployment (Week 3-6)
Start with your top 5 web applications and 20-50 pilot users:
- Deploy ZTNA connector in your DMZ or private subnet
- Onboard pilot applications to ZTNA portal
- Configure IdP integration with Keycloak/Okta/Entra ID
- Enable device posture checking via MDM certificates
- Keep VPN active as fallback
Measure: user support tickets, latency (compare P95 before/after), authentication success rate.
Phase 3: Full Rollout (Month 2-4)
Onboard remaining web/SSH/RDP applications. For mTLS-secured services (internal microservices), ZTNA and mTLS complement each other — the ZTNA layer handles user identity while mTLS handles service-to-service authentication. For implementation details, see mTLS Certificate Authentication for Microservices in Kubernetes.
Phase 4: VPN Decommission
Retire client VPN for remote users. Keep site-to-site VPN/SD-WAN for office-to-office connectivity where network-level access is required.
Security Hardening
Block Lateral Movement at the Source
With ZTNA, lateral movement is structurally impossible — users never get a network-level IP address inside your private subnet. However, if an application itself is compromised, you still need application-layer controls.
For APIs behind ZTNA, implement OAuth scopes to enforce least privilege:
# FastAPI + Keycloak OIDC token validation behind ZTNA
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer
security = HTTPBearer()
async def verify_ztna_token(credentials = Depends(security)):
token = credentials.credentials
# ZTNA already verified user identity, but validate token claims
payload = decode_and_validate_jwt(token, KEYCLOAK_PUBLIC_KEY)
# Enforce application-level scope
if "app:admin" not in payload.get("scope", "").split():
raise HTTPException(status_code=403, detail="Insufficient scope")
# Check device compliance claim (set by Keycloak mapper)
if not payload.get("device_compliant", False):
raise HTTPException(status_code=403, detail="Device not compliant")
return payload
For Non-Human Identity (NHI) access behind ZTNA — service accounts, CI/CD pipelines, AI agents — use short-lived credentials rather than static API keys. See NHI Secrets Sprawl: Fixing the Non-Human Identity Credential Crisis.
FAQ
Does ZTNA work for IoT and OT devices?
Standard ZTNA clients require a software agent, which IoT/OT devices cannot run. For these environments, consider:
- Network-based ZTNA (policy enforced at the switch/firewall level via device certificates)
- Microsegmentation instead of ZTNA for device-to-device communication
- ZTNA only for human access to OT management interfaces
What happens if the ZTNA control plane goes down?
Most enterprise ZTNA providers offer 99.99% uptime SLAs. If the control plane is unreachable, connectors fail-closed (no access) rather than fail-open (all access). This is the opposite of VPN, where a failed concentrator causes a complete outage. Design for this with VPN fallback for critical systems during migration.
How does ZTNA handle service-to-service calls?
ZTNA handles human-to-application access. For service-to-service calls between microservices, use mTLS with workload identity (SPIFFE/SPIRE) or the OAuth 2.0 Client Credentials flow. ZTNA and workload identity are complementary layers in a full Zero Trust architecture.

