cloudflare-zero-trust
Cloudflare TunnelやAccessの構築、認証設定、エラー対応、Terraform連携、プライベートネットワーク接続、監査ログ設定など、Zero Trustネットワークに関する幅広い課題を解決するSkill。
📜 元の英語説明(参考)
Use when working with Cloudflare Tunnel or Access - tunnel setup, authentication configuration, 502 Bad Gateway errors, Docker/Kubernetes deployment, service token management, private network routing (SSH/RDP/databases), WebSocket/gRPC connection issues, replica scaling problems, WARP routing, Terraform/IaC automation, local development with quick tunnels, audit logging setup, compliance requirements (SOC2/HIPAA), or advanced network debugging. Keywords - cloudflared, 502 error, service tokens, terraform, metrics port 20241, trycloudflare, Logpush, SIEM. CRITICAL - Authentication mandatory not optional.
🇯🇵 日本人クリエイター向け解説
Cloudflare TunnelやAccessの構築、認証設定、エラー対応、Terraform連携、プライベートネットワーク接続、監査ログ設定など、Zero Trustネットワークに関する幅広い課題を解決するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o cloudflare-zero-trust.zip https://jpskill.com/download/9155.zip && unzip -o cloudflare-zero-trust.zip && rm cloudflare-zero-trust.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9155.zip -OutFile "$d\cloudflare-zero-trust.zip"; Expand-Archive "$d\cloudflare-zero-trust.zip" -DestinationPath $d -Force; ri "$d\cloudflare-zero-trust.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
cloudflare-zero-trust.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
cloudflare-zero-trustフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Cloudflare Zero Trust
概要
Cloudflare Zero Trust は、Cloudflare Tunnel (セキュアな接続性) と Cloudflare Access (認証/認可) を使用して、VPN なしでアプリケーションへのセキュアなリモートアクセスを提供します。
中核となる原則: 認証はオプションではありません。すべてのトンネルは、最初からアクセス制御を備えている必要があります。
どのような時に使用するか
このスキルは、以下の場合に使用します。
- 任意のアプリケーションに対して Cloudflare Tunnel をセットアップする場合
- Cloudflare Access 認証を構成する場合
- 内部アプリケーションを安全に公開する場合
- VPN アクセスをゼロトラストモデルに置き換える場合
- OIDC/SSO プロバイダー (Azure AD、Okta、Google) を統合する場合
- Docker コンテナ内で
cloudflaredを実行する場合 - 502 Bad Gateway エラーをトラブルシューティングする場合
- ダッシュボードまたは CLI を介してトンネルを管理する場合
このスキルを必要とする兆候:
- 「ローカルアプリをインターネットに公開する必要がある」
- 「Cloudflare Tunnel をセットアップしている」
- 「トンネルに認証を追加する」
- 「Azure AD / Okta / OIDC を構成する」
- 「Cloudflare Tunnel で 502 Bad Gateway が発生する」
- 「Docker で
cloudflaredを実行する」
セキュリティ第一の原則
UNAUTHENTICATED TUNNEL = PUBLICLY EXPOSED SERVICE = SECURITY INCIDENT
認証は必須であり、オプションではありません。
もしあなたが以下のように考えているなら:
- 「認証は後で追加する」
- 「認証なしでも問題なく動作している」
- 「ログインでユーザーを混乱させたくない」
- 「内部アプリでは認証をスキップできないか?」
- 「まずはデモ、セキュリティは後で」
やめてください。これらはセキュリティ違反です。
正しい考え方:
- 認証のないトンネル = 公開されたサービス = 重大な脆弱性
- ユーザーの利便性 < セキュリティ要件
- 「内部のみ」はセキュリティ制御ではない
- デモはデフォルトで安全であるべき
クイックリファレンス
| タスク | リモート管理 (ダッシュボード) | ローカル管理 (config.yml) |
|---|---|---|
| 最適な用途 | GUI ユーザー、クイックセットアップ、チームコラボレーション | IaC、自動化、バージョン管理、CI/CD |
| セットアップ | ダッシュボード → トンネルの作成 → ルートの構成 | cloudflared tunnel create + config.yml |
| 変更 | クリックして更新 | config を編集し、サービスを再起動 |
| アクセス制御 | 常にダッシュボード経由 (Zero Trust → Access) | トンネルは config 経由、Access はダッシュボード経由 |
| Docker | 認証情報用の環境変数 | config.yml + 認証情報 JSON をマウント |
トンネルセットアップのワークフロー
トンネルを作成する前に、「ダッシュボード vs CLI の決定木」セクションの決定木を使用して、管理方法を選択してください。
digraph tunnel_setup {
rankdir=TD;
node [shape=box, style=rounded];
start [label="アプリを公開する必要がある", shape=ellipse];
requirements [label="要件を定義する\n• 誰がアクセスする必要があるか?\n• 認証プロバイダーは?\n• Docker またはホスト?"];
choose_method [label="管理方法を選択する", shape=diamond];
dashboard [label="ダッシュボード方式\n1. トンネルを作成する\n2. cloudflared をインストールする\n3. コネクタを実行する"];
config [label="構成ファイル方式\n1. cloudflared tunnel create\n2. config.yml を記述する\n3. サービスを開始する"];
access [label="アクセスを構成する\n公開前に", style="filled", fillcolor="#ffcccc"];
test [label="認証をテストする"];
deploy [label="ユーザーに対して有効にする"];
monitor [label="ログを監視する"];
start -> requirements;
requirements -> choose_method;
choose_method -> dashboard [label="GUI\nクイックスタート"];
choose_method -> config [label="IaC\n自動化"];
dashboard -> access;
config -> access;
access -> test;
test -> deploy;
deploy -> monitor;
}
重要: トンネルを公開する前に、Access 認証を構成してください。「一時的に」であっても、認証されていないトンネルをデプロイしないでください。
リモート管理トンネル (ダッシュボード)
どのような時に使用するか:
- チームが GUI を好む場合
- クイックな概念実証 (認証が必要な場合でも!)
- 視覚的なルート管理
- 技術的な知識が少ないチームメンバー
セットアップ:
- Zero Trust → Networks → Tunnels → Create
- コネクタタイプを選択: Cloudflared
- トンネルに名前を付ける: 例:
prod-api-tunnel - cloudflared をインストール オリジンサーバーに
- コネクタを実行: ダッシュボードからトークンをコピー
cloudflared service install <TOKEN> - ルートを構成: ダッシュボード → Public Hostname
- Hostname:
api.example.com - Service:
http://localhost:8080
- Hostname:
- Access を構成 (Access 構成セクションを参照)
利点:
- 視覚的な構成
- チームメンバーにとって簡単
- 組み込みのヘルスモニタリング
短所:
- バージョン管理されていない
- 自動化が難しい
- 変更にはダッシュボードを使用する必要がある
ローカル管理トンネル (config.yml)
どのような時に使用するか:
- Infrastructure as code
- CI/CD 自動化
- 構成のバージョン管理
- 複数の環境 (dev/staging/prod)
- Docker コンテナ
セットアップ:
-
トンネルを作成:
cloudflared tunnel create myapp-tunnel出力:
~/.cloudflared/<TUNNEL_ID>.json(認証情報) -
構成ファイルを作成 (
~/.cloudflared/config.yml):# === 必須フィールド (これらがないとトンネルが機能しません) === tunnel: <TUNNEL_ID> credentials-file: /etc/cloudflared/<TUNNEL_ID>.json ingress: # myapp.example.com をローカルサービスにルーティング - hostname: myapp.example.com service: http://localhost:8080 # 必須: オリジン URL originRequest: # === 危険 (間違った値 = セキュリティリスクまたは停止) === noTLSVerify: false # 特定の理由がない限り、true に設定しないでください (セキュリティリスク) # === 安全 (必要に応じて調整) === connectTimeout: 30s httpHostHeader: myapp.example.com # 複数のサービス例 - hostname: api.example.com service: http://localhost:8080 - hostname: admin.example.com service: http://localhost:3000 # 必須: キャッチオールルール (最後に記述する必要があります) - service: http_status:404 # === 安全 (自由に編集可能) === loglevel: info # または: debug, warn, error -
DNS をルーティング:
cloudflared tunnel route dns myapp-tunnel myapp.example.com -
トンネルを実行:
# フォアグラウンド (テスト) cloudflared tunnel run myapp-tunnel # サービスとして (本番環境) sudo cloudf
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Cloudflare Zero Trust
Overview
Cloudflare Zero Trust provides secure remote access to applications without VPN, using Cloudflare Tunnel (secure connectivity) and Cloudflare Access (authentication/authorization).
Core principle: Authentication is not optional. Every tunnel must have access controls from day one.
When to Use
Use this skill when:
- Setting up Cloudflare Tunnel for any application
- Configuring Cloudflare Access authentication
- Exposing internal applications securely
- Replacing VPN access with zero-trust model
- Integrating OIDC/SSO providers (Azure AD, Okta, Google)
- Running cloudflared in Docker containers
- Troubleshooting 502 Bad Gateway errors
- Managing tunnels via dashboard or CLI
Symptoms that trigger this skill:
- "Need to expose local app to internet"
- "Setting up Cloudflare Tunnel"
- "Add authentication to tunnel"
- "Configure Azure AD / Okta / OIDC"
- "502 Bad Gateway on Cloudflare Tunnel"
- "Run cloudflared in Docker"
Security-First Principle
UNAUTHENTICATED TUNNEL = PUBLICLY EXPOSED SERVICE = SECURITY INCIDENT
Authentication is mandatory, not optional.
If you find yourself thinking:
- "We'll add authentication later"
- "It's been working fine without auth"
- "Don't want to disrupt users with login"
- "Can we skip auth for internal apps?"
- "Demo first, secure later"
STOP. These are security violations.
Correct mindset:
- Tunnel without auth = exposed service = critical vulnerability
- User convenience < security requirement
- "Internal only" is not a security control
- Demos should be secure by default
Quick Reference
| Task | Remotely-Managed (Dashboard) | Locally-Managed (config.yml) |
|---|---|---|
| Best for | GUI users, quick setup, team collaboration | IaC, automation, version control, CI/CD |
| Setup | Dashboard → Create Tunnel → Configure routes | cloudflared tunnel create + config.yml |
| Changes | Click to update | Edit config, restart service |
| Access Control | Always via dashboard (Zero Trust → Access) | Tunnel via config, Access via dashboard |
| Docker | Env vars for credentials | Mount config.yml + credentials JSON |
Tunnel Setup Workflow
BEFORE creating tunnel, choose management approach using the decision tree in "Dashboard vs CLI Decision Tree" section below.
digraph tunnel_setup {
rankdir=TD;
node [shape=box, style=rounded];
start [label="Need to expose app", shape=ellipse];
requirements [label="Define requirements\n• Who needs access?\n• Auth provider?\n• Docker or host?"];
choose_method [label="Choose management", shape=diamond];
dashboard [label="Dashboard Method\n1. Create tunnel\n2. Install cloudflared\n3. Run connector"];
config [label="Config File Method\n1. cloudflared tunnel create\n2. Write config.yml\n3. Start service"];
access [label="Configure Access\nBEFORE going live", style="filled", fillcolor="#ffcccc"];
test [label="Test authentication"];
deploy [label="Enable for users"];
monitor [label="Monitor logs"];
start -> requirements;
requirements -> choose_method;
choose_method -> dashboard [label="GUI\nQuick start"];
choose_method -> config [label="IaC\nAutomation"];
dashboard -> access;
config -> access;
access -> test;
test -> deploy;
deploy -> monitor;
}
Critical: Configure Access authentication BEFORE exposing the tunnel. Never deploy unauthenticated tunnels, even "temporarily."
Remotely-Managed Tunnels (Dashboard)
When to use:
- Team prefers GUI
- Quick proof-of-concept (that still needs auth!)
- Visual route management
- Less technical team members
Setup:
- Zero Trust → Networks → Tunnels → Create
- Choose connector type: Cloudflared
- Name tunnel: e.g.,
prod-api-tunnel - Install cloudflared on origin server
- Run connector: Copy token from dashboard
cloudflared service install <TOKEN> - Configure routes: Dashboard → Public Hostname
- Hostname:
api.example.com - Service:
http://localhost:8080
- Hostname:
- Configure Access (see Access Configuration section)
Advantages:
- Visual configuration
- Easy for team members
- Built-in health monitoring
Disadvantages:
- Not in version control
- Harder to automate
- Must use dashboard for changes
Locally-Managed Tunnels (config.yml)
When to use:
- Infrastructure as code
- CI/CD automation
- Version control for config
- Multiple environments (dev/staging/prod)
- Docker containers
Setup:
-
Create tunnel:
cloudflared tunnel create myapp-tunnelOutputs:
~/.cloudflared/<TUNNEL_ID>.json(credentials) -
Create config file (
~/.cloudflared/config.yml):# === REQUIRED FIELDS (tunnel breaks without these) === tunnel: <TUNNEL_ID> credentials-file: /etc/cloudflared/<TUNNEL_ID>.json ingress: # Route myapp.example.com to local service - hostname: myapp.example.com service: http://localhost:8080 # REQUIRED: origin URL originRequest: # === DANGEROUS (wrong value = security risk or outage) === noTLSVerify: false # NEVER set true without specific reason (security risk) # === SAFE (tune based on your needs) === connectTimeout: 30s httpHostHeader: myapp.example.com # Multiple services example - hostname: api.example.com service: http://localhost:8080 - hostname: admin.example.com service: http://localhost:3000 # REQUIRED: Catch-all rule (must be last) - service: http_status:404 # === SAFE (change freely) === loglevel: info # or: debug, warn, error -
Route DNS:
cloudflared tunnel route dns myapp-tunnel myapp.example.com -
Run tunnel:
# Foreground (testing) cloudflared tunnel run myapp-tunnel # As service (production) sudo cloudflared service install sudo systemctl start cloudflared sudo systemctl enable cloudflared -
Configure Access (see next section)
Advantages:
- Version controlled
- Easy automation
- Consistent across environments
- Docker-friendly
Disadvantages:
- Requires editing config files
- Restart needed for changes
- More initial setup
Modern Protocols (WebSockets, HTTP/2, gRPC)
Modern Protocols (WebSockets, HTTP/2, gRPC)
MANDATORY when working with WebSockets, HTTP/2, or gRPC:
Load protocols.md for complete protocol configuration, tuning parameters, and troubleshooting.
Quick reference:
- WebSockets:
keepAliveTimeout: 300sfor stable connections - HTTP/2: Requires TLS origin with
http2Origin: true - gRPC: Use WARP routing (not public hostname)
Do NOT load for basic HTTP tunnel setup.
When to use:
- Containerized infrastructure
- Docker Compose environments
- Kubernetes/orchestration
- Portable deployments
Container-Specific Patterns
Critical networking difference (Docker/Kubernetes):
# ❌ WRONG - localhost doesn't resolve in containers
ingress:
- hostname: myapp.example.com
service: http://localhost:8080
# ✅ RIGHT - use service/pod name
# Docker Compose:
service: http://myapp:8080 # Container name from docker-compose.yml
# Kubernetes:
service: http://myapp-service:8080 # Service name
Credential mounting patterns:
# Docker Compose:
volumes:
- ./cloudflared/config.yml:/etc/cloudflared/config.yml:ro # Read-only
- ./cloudflared/credentials.json:/etc/cloudflared/credentials.json:ro
# Kubernetes:
# Use ConfigMap for config, Secret for credentials
Health check setup:
# Docker Compose:
healthcheck:
test: ["CMD", "cloudflared", "tunnel", "info"]
interval: 30s
timeout: 10s
retries: 3
# Kubernetes:
livenessProbe:
exec:
command: ["cloudflared", "tunnel", "info"]
initialDelaySeconds: 30
periodSeconds: 30
Redundancy for production:
# Docker Compose - use multiple instances behind load balancer
# Kubernetes - use replicas:
spec:
replicas: 2 # Minimum 2 for zero-downtime updates
Setup workflow:
- Create tunnel:
cloudflared tunnel create myapp-tunnel - Get credentials:
~/.cloudflared/<TUNNEL_ID>.json - Mount config + credentials in container
- Set service URL to container/service name (NOT localhost)
- Configure Access authentication
- Deploy with health checks enabled
Verification:
# Docker Compose
docker-compose logs cloudflared
docker-compose exec cloudflared cloudflared tunnel info
# Kubernetes
kubectl logs -l app=cloudflared
kubectl exec deploy/cloudflared -- cloudflared tunnel info
Private Network Routing (Non-HTTP Services)
Private Network Routing (Non-HTTP Services)
MANDATORY when exposing SSH, RDP, databases, or custom TCP/UDP protocols:
Load private-networks.md for complete private network configuration, WARP routing setup, container networking, and access policies.
Quick reference:
- SSH:
service: ssh://192.168.1.10:22 - RDP:
service: rdp://192.168.1.20:3389 - Database:
service: tcp://192.168.1.30:5432 - Use service names in Docker:
tcp://database:5432NOTtcp://localhost:5432
Do NOT load for HTTP application tunnels.
Configure Access BEFORE exposing tunnel to users.
Self-Hosted Applications
-
Zero Trust → Access → Applications → Add an application
-
Choose "Self-hosted"
-
Application Configuration:
- Name:
My Application - Session Duration:
24 hours(default) - Application domain:
myapp.example.com - Accept all available identity providers: Checked (or select specific)
- Name:
-
Add policies (at least one Allow policy required):
Example policies:
Email-based:
Name: Allow specific users
Action: Allow
Include: Emails → user1@example.com, user2@example.com
Domain-based:
Name: Allow company domain
Action: Allow
Include: Email domains → @company.com
Group-based (requires IdP groups):
Name: Allow admins group
Action: Allow
Include: Azure AD Groups → Admins
Multi-factor authentication:
Name: Require MFA for admins
Action: Allow
Include: Email domains → @company.com
Require: Authentication Method → mTLS, WARP, Service Token (select MFA method)
- Optional settings:
- Purpose justification: Require users to state reason for access
- Temporary authentication: Approve access requests manually
- IdP groups: Use groups from Azure AD, Okta, etc.
SaaS Applications
For integrated SaaS apps (Salesforce, Workday, etc.):
- Zero Trust → Access → Applications → Add an application
- Choose "SaaS"
- Select application from catalog
- Follow integration wizard (varies by app)
- Configure policies (same as self-hosted)
OIDC / SSO Integration
Azure AD (Entra ID)
Azure AD setup:
- Azure Portal → Azure Active Directory → App registrations → New registration
- Name:
Cloudflare Access - Redirect URI:
https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback - Register, copy Application (client) ID
- Certificates & secrets → New client secret, copy value
- API permissions → Add permission → Microsoft Graph → Group.Read.All (for group-based policies)
- Token configuration → Add groups claim → Security groups
Cloudflare configuration:
- Zero Trust → Settings → Authentication → Add new → Azure AD
- Name:
Azure AD - App ID:
<Application (client) ID> - Client secret:
<Secret value> - Directory (tenant) ID: From Azure AD overview
- Support groups: Checked (if using group-based policies)
- Test and Save
Use in policies:
Name: Allow Admins group
Action: Allow
Include: Azure AD Groups → select "Admins" group
Okta
- Okta Admin → Applications → Create App Integration → OIDC - Web Application
- Name:
Cloudflare Access - Grant type: Authorization Code
- Sign-in redirect URI:
https://<team-name>.cloudflareaccess.com/cdn-cgi/access/callback - Assignments: Select groups
- Copy Client ID and Client secret
Cloudflare:
- Zero Trust → Settings → Authentication → Add new → Okta
- Okta account URL:
https://your-domain.okta.com - App ID and Client secret
- Support groups: Checked
- Test and Save
Generic OIDC Provider
For providers not in catalog:
- Zero Trust → Settings → Authentication → Add new → OpenID Connect
- Name: Provider name
- App ID (Client ID)
- Client Secret
- Auth URL:
https://provider.com/oauth2/authorize - Token URL:
https://provider.com/oauth2/token - Certificate URL (JWKS):
https://provider.com/.well-known/jwks.json - Proof Key for Code Exchange (PKCE): Checked if supported
- Support groups: If provider supports groups claim
- Test and Save
Access Policy Design
Principle: Least privilege by default.
Policy evaluation order:
- Bypass policies (skip authentication)
- Block policies (explicit deny)
- Allow policies (grant access)
- Service Auth (API tokens, service-to-service)
Good policy patterns:
Production application with role-based access:
Policy 1: Block non-employees
Action: Block
Include: Everyone
Exclude: Email domains → @company.com
Policy 2: Allow developers
Action: Allow
Include: Azure AD Groups → Developers
Policy 3: Allow admins
Action: Allow
Include: Azure AD Groups → Admins
Require: MFA
Staging environment:
Policy 1: Allow dev team
Action: Allow
Include: Email domains → @company.com
Require: Azure AD Groups → Developers OR Admins
API with service authentication:
Policy 1: Service-to-service
Action: Service Auth
Include: Service Token → api-service-token
Service Authentication (M2M / API Access)
Service Authentication (M2M / API Access)
MANDATORY when configuring CI/CD pipelines, microservices, or any non-human authentication:
Load service-auth.md for service token creation, rotation workflows, security best practices, and M2M patterns.
Quick reference:
- Create token: Zero Trust → Access → Service Auth
- Use headers:
CF-Access-Client-Id,CF-Access-Client-Secret - Rotate every 90 days
- Store in secrets manager, never in code
Do NOT load for user authentication (use SSO/OIDC instead).
502 Bad Gateway
Error: 502 Bad Gateway - Unable to reach the origin service
Meaning: Tunnel is connected to Cloudflare, but can't reach your origin.
Systematic diagnosis:
digraph troubleshoot_502 {
rankdir=TD;
node [shape=box, style=rounded];
start [label="502 Bad Gateway", shape=ellipse, style=filled, fillcolor="#ffcccc"];
check_origin [label="Can you curl origin\nlocally?", shape=diamond];
origin_down [label="Origin service down\nRestart service", style=filled, fillcolor="#ffcccc"];
check_docker [label="Is cloudflared\nin Docker?", shape=diamond];
wrong_service [label="Using localhost?\nChange to container name:\nhttp://myapp:8080", style=filled, fillcolor="#ccffcc"];
check_tls [label="Origin uses\nHTTPS?", shape=diamond];
tls_issue [label="Self-signed cert?\nSet noTLSVerify: true\nOR add caPool", style=filled, fillcolor="#ccffcc"];
check_port [label="Port correct\nin config?", shape=diamond];
wrong_port [label="Fix port number\nin config.yml", style=filled, fillcolor="#ccffcc"];
firewall [label="Check firewall\nrules/logs", style=filled, fillcolor="#ffffcc"];
start -> check_origin;
check_origin -> origin_down [label="No"];
check_origin -> check_docker [label="Yes"];
check_docker -> wrong_service [label="Yes"];
check_docker -> check_tls [label="No"];
check_tls -> tls_issue [label="Yes"];
check_tls -> check_port [label="No"];
check_port -> wrong_port [label="No"];
check_port -> firewall [label="Yes"];
}
Quick diagnostic commands:
# 1. Test origin locally
curl -v http://localhost:8080
# Success? Continue. Connection refused? Origin is down.
# 2. Check cloudflared logs
sudo journalctl -u cloudflared -n 50 --no-pager # Systemd
docker logs cloudflared # Docker
# Look for: "dial tcp", "connection refused", "context deadline exceeded"
# 3. Test from cloudflared container (if Docker)
docker exec cloudflared curl http://myapp:8080
# Fails? Wrong service name or network issue
# 4. Run tunnel in foreground (see real-time errors)
sudo systemctl stop cloudflared
cloudflared tunnel run myapp-tunnel
Authentication Loop (Redirect Loop)
Symptom: Browser keeps redirecting to login, never reaches app
Systematic diagnosis:
digraph troubleshoot_auth_loop {
rankdir=TD;
node [shape=box, style=rounded];
start [label="Authentication Loop", shape=ellipse, style=filled, fillcolor="#ffcccc"];
check_policy [label="Does an Allow policy\nmatch your user?", shape=diamond];
policy_issue [label="Add/update Allow policy\nCheck email/groups match", style=filled, fillcolor="#ccffcc"];
check_idp [label="IdP redirect URI\nmatches Cloudflare?", shape=diamond];
idp_issue [label="Fix redirect URI:\nhttps://<team>.cloudflareaccess.com\n/cdn-cgi/access/callback", style=filled, fillcolor="#ccffcc"];
check_groups [label="Using group-based\npolicies?", shape=diamond];
groups_issue [label="Verify IdP sends group claims\nCheck 'Support groups' enabled", style=filled, fillcolor="#ccffcc"];
check_cookies [label="Try incognito mode\nClear all cookies", shape=diamond];
cookie_issue [label="Browser blocking 3rd-party\ncookies or privacy mode active", style=filled, fillcolor="#ffffcc"];
check_session [label="Session duration\nadequate?", shape=diamond];
session_issue [label="Increase session duration\nDefault 24h may be too short", style=filled, fillcolor="#ccffcc"];
escalate [label="Check Cloudflare Access logs\nfor specific error", style=filled, fillcolor="#ffffcc"];
start -> check_policy;
check_policy -> policy_issue [label="No"];
check_policy -> check_idp [label="Yes"];
check_idp -> idp_issue [label="No"];
check_idp -> check_groups [label="Yes"];
check_groups -> groups_issue [label="Yes"];
check_groups -> check_cookies [label="No"];
check_cookies -> cookie_issue [label="Still loops"];
check_cookies -> check_session [label="Works in\nincognito"];
check_session -> session_issue [label="No"];
check_session -> escalate [label="Yes"];
}
Quick diagnostic steps:
# 1. Verify Access policy matches you
# Dashboard → Zero Trust → Access → Applications → <Your App> → Policies
# Check: Does an Allow policy include your email/domain/group?
# 2. Test in incognito window (eliminates cookie issues)
# If works in incognito → Clear cookies
# If still loops → Policy or IdP issue
# 3. Check IdP redirect URI
# Must exactly match: https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback
# 4. Verify group claims (if using group-based policies)
# Azure AD: Token configuration → Add groups claim
# Okta: Profile → Edit → Include in token
# Cloudflare: Settings → Authentication → <IdP> → Support groups ✓
# 5. Check Access logs for specific error
# Dashboard → Zero Trust → Logs → Access
# Look for: policy evaluation failures, IdP errors
Tunnel Not Connecting
Symptom: Tunnel shows "Inactive" or "Down" in dashboard
Troubleshooting:
-
Check cloudflared is running:
sudo systemctl status cloudflared # or: docker ps | grep cloudflared -
Verify credentials file exists:
ls -la ~/.cloudflared/<TUNNEL_ID>.json -
Firewall requirement: Cloudflared needs outbound HTTPS (443) to
*.argotunnel.com -
Restart tunnel:
sudo systemctl restart cloudflared
DNS Not Resolving
Symptom: nslookup myapp.example.com returns no records
Fix:
-
Check DNS record exists:
- Cloudflare dashboard → DNS → Records
- Look for CNAME:
myapp.example.com→<TUNNEL_ID>.cfargotunnel.com
-
Create if missing:
cloudflared tunnel route dns myapp-tunnel myapp.example.com -
Verify: DNS record should be "Proxied" (orange cloud) in Cloudflare dashboard
Common Mistakes
Advanced Troubleshooting
MANDATORY when basic troubleshooting fails or for performance debugging:
Load advanced-troubleshooting.md for network path analysis, performance tuning, certificate debugging, connection limits, and metrics interpretation.
Quick reference:
- Metrics ports: 20241-20245 (auto-selected)
- Connection limits: 4 per instance, 100 total (25 replicas max)
- Default keepAliveConnections: 100
Do NOT load for simple 502 or DNS issues.
1. Deploying Without Authentication
❌ Wrong:
# Create tunnel
cloudflared tunnel create myapp
# Route DNS
cloudflared tunnel route dns myapp myapp.example.com
# Run tunnel
cloudflared tunnel run myapp
# ⚠️ APP IS NOW PUBLICLY ACCESSIBLE
✅ Right:
# Create tunnel
cloudflared tunnel create myapp
# Route DNS
cloudflared tunnel route dns myapp myapp.example.com
# Configure Access FIRST (dashboard)
# THEN run tunnel
cloudflared tunnel run myapp
2. Using noTLSVerify: true Without Reason
❌ Wrong:
originRequest:
noTLSVerify: true # "Just in case"
✅ Right:
# Only if origin uses self-signed cert
originRequest:
noTLSVerify: false # Verify by default
# OR if self-signed:
# noTLSVerify: true
# caPool: /path/to/custom-ca.pem
3. Wrong Service URL in Docker
❌ Wrong:
# config.yml
ingress:
- hostname: app.example.com
service: http://localhost:8080 # Won't work in Docker!
✅ Right:
# config.yml
ingress:
- hostname: app.example.com
service: http://myapp:8080 # Use container name
4. Bypass Policy for "Internal" Apps
❌ Wrong:
Policy: Skip auth for internal
Action: Bypass
Include: Everyone
# ⚠️ Anyone with URL can access!
✅ Right:
Policy: Allow employees
Action: Allow
Include: Email domains → @company.com
5. Hardcoding Credentials in Config
❌ Wrong:
# config.yml checked into git
tunnel: abc123-def456-...
credentials-file: /etc/cloudflared/credentials.json
# ⚠️ credentials.json also in git!
✅ Right:
# .gitignore
*.json
credentials.json
Use environment-specific credentials, never commit.
Dashboard vs CLI Decision Tree
digraph decision {
rankdir=TD;
node [shape=box, style=rounded];
start [label="Need tunnel", shape=ellipse];
team [label="Team has IaC\nexperience?", shape=diamond];
automation [label="Need automation\nor CI/CD?", shape=diamond];
multi_env [label="Multiple\nenvironments?", shape=diamond];
cli [label="Use config.yml\n(Locally-managed)", style="filled", fillcolor="#ccffcc"];
dashboard [label="Use Dashboard\n(Remotely-managed)", style="filled", fillcolor="#ccccff"];
start -> team;
team -> automation [label="Yes"];
team -> dashboard [label="No"];
automation -> cli [label="Yes"];
automation -> multi_env [label="No"];
multi_env -> cli [label="Yes"];
multi_env -> dashboard [label="No"];
}
Browser Automation (Dashboard Management)
Note: Browser automation for dashboard management requires MCP claude-in-chrome tools.
Use cases:
- Automated tunnel creation via dashboard
- Bulk configuration changes
- Scheduled tunnel audits
- Programmatic route updates
Pattern:
// Pseudocode - requires claude-in-chrome MCP
const { navigate, click, fill } = mcp_claude_in_chrome;
// Navigate to Zero Trust dashboard
await navigate('https://one.dash.cloudflare.com/');
// Login flow (use saved session or credentials)
// ...
// Create tunnel
await navigate('Networks/Tunnels');
await click('Create a tunnel');
await fill('Tunnel name', 'new-tunnel');
await click('Save tunnel');
// Configure route
await click('Public Hostname');
await fill('Subdomain', 'myapp');
await fill('Domain', 'example.com');
await fill('Service', 'http://localhost:8080');
await click('Save');
Better alternative: Use Cloudflare API
# Get tunnels
curl -X GET "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/cfd_tunnel" \
-H "Authorization: Bearer ${API_TOKEN}"
# Create tunnel
curl -X POST "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/cfd_tunnel" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"name": "new-tunnel",
"tunnel_secret": "<base64-secret>"
}'
# Update config
curl -X PUT \
"https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"config": {
"ingress": [
{
"hostname": "myapp.example.com",
"service": "http://localhost:8080"
},
{
"service": "http_status:404"
}
]
}
}'
When to use browser automation vs API:
- API: Preferred for automation, CI/CD, scripts
- Browser: When API doesn't support feature, visual verification needed
API Automation
MANDATORY when automating via Cloudflare API, CI/CD pipelines, or GitOps:
Load api-automation.md for complete API reference including Access applications, policies, tunnel management, service tokens, IdP configuration, device posture, and analytics.
Quick reference:
- Base URL:
https://api.cloudflare.com/client/v4/ - Auth:
Authorization: Bearer ${API_TOKEN} - Rate limit: 1200 requests per 5 minutes
- All tunnel config updates apply immediately (no restart)
Do NOT load if using dashboard or Terraform (use terraform.md instead).
Local Development Workflows
MANDATORY when using quick tunnels, preview environments, or local dev setups:
Load local-development.md for quick tunnel patterns (trycloudflare.com), preview environments, hot reload setups, and webhook testing.
Quick reference:
- Quick tunnel:
cloudflared tunnel --url http://localhost:8080 - 200 concurrent request limit
- Random subdomain (changes each run)
- Free, no account needed
Do NOT load for production deployments.
Terraform / Infrastructure as Code
MANDATORY when managing Cloudflare with Terraform or other IaC tools:
Load terraform.md for complete provider setup, resource examples, multi-environment patterns, state management, and deployment workflows.
Quick reference:
- Provider:
cloudflare/cloudflarev4.0+ - Resources:
cloudflare_tunnel,cloudflare_access_application,cloudflare_access_policy - Use modules for reusability
Do NOT load for dashboard-based management.
Audit Logging & Compliance
MANDATORY for compliance requirements (SOC2, HIPAA, ISO 27001) or SIEM integration:
Load audit-logging.md for Logpush configuration, SIEM patterns, compliance checklists, log retention, and alerting strategies.
Quick reference:
- Every auth attempt logged (user, app, IP, timestamp, decision)
- Logpush to S3, Splunk, Datadog, custom SIEM
- Free: 24h retention, Paid: 6 months, Enterprise: 18 months
Do NOT load for basic tunnel setup.
Stop and reconsider if you find yourself:
- ✋ Creating tunnel without Access configuration
- ✋ Using Bypass policy for "internal" applications
- ✋ Accepting "we'll add auth later" as valid approach
- ✋ Prioritizing demo speed over security
- ✋ Setting
noTLSVerify: truewithout specific reason - ✋ Storing credentials in version control
- ✋ Exposing admin interfaces without MFA requirement
- ✋ Using "it's been working without auth" as argument
All of these are security incidents, not acceptable tradeoffs.
Command Quick Reference
# Create tunnel
cloudflared tunnel create <NAME>
# List tunnels
cloudflared tunnel list
# Run tunnel (foreground)
cloudflared tunnel run <NAME>
# Route DNS
cloudflared tunnel route dns <NAME> <HOSTNAME>
# Install as service
sudo cloudflared service install
# Service management
sudo systemctl start cloudflared
sudo systemctl stop cloudflared
sudo systemctl restart cloudflared
sudo systemctl status cloudflared
# Check logs
sudo journalctl -u cloudflared -f
# Tunnel info
cloudflared tunnel info <NAME>
# Delete tunnel
cloudflared tunnel delete <NAME>
# Update cloudflared
# macOS
brew upgrade cloudflare/cloudflare/cloudflared
# Linux
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
Sources
This skill is based on:
- Cloudflare Tunnel Documentation
- Cloudflare Access Documentation
- Designing ZTNA Access Policies
- Community troubleshooting patterns from January 2026