Skip to content

Quick Start

This guide walks you through creating your first HTTPRoute to expose a Kubernetes service through Cloudflare Tunnel.

Prerequisites

Ensure you have completed:

Create a Gateway

The chart installs the cloudflare-tunnel GatewayClass, but you create the Gateway that HTTPRoutes attach to. Create one in the controller namespace:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: cloudflare-tunnel
  namespace: cloudflare-tunnel-system
spec:
  gatewayClassName: cloudflare-tunnel
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      allowedRoutes:
        namespaces:
          from: All

Apply it:

kubectl apply --filename gateway.yaml

Cloudflare terminates TLS at its edge, so the listener carries no certificateRefs — the port/protocol are accepted for route binding but the actual TLS is handled by Cloudflare. Once the controller reconciles it, the Gateway's status address is set to <tunnel-id>.cfargotunnel.com:

kubectl get gateway cloudflare-tunnel --namespace cloudflare-tunnel-system \
  --output jsonpath='{.status.addresses[*].value}'

Deploy a Sample Application

First, deploy a simple application to expose:

kubectl create deployment nginx --image=nginx:latest
kubectl expose deployment nginx --port=80

Create an HTTPRoute

Create an HTTPRoute to expose the nginx service through Cloudflare Tunnel:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nginx
  namespace: default
spec:
  parentRefs:
    - name: cloudflare-tunnel
      namespace: cloudflare-tunnel-system
  hostnames:
    - nginx.example.com  # Replace with your domain
  rules:
    - backendRefs:
        - name: nginx
          port: 80

Apply the route:

kubectl apply --filename httproute.yaml

Verify the Route

Check that the HTTPRoute is accepted:

kubectl get httproute nginx --output jsonpath='{.status.parents[*].conditions}'

Expected output includes "type":"Accepted","status":"True".

Configure DNS

The controller sets the Gateway address to TUNNEL_ID.cfargotunnel.com. Create a CNAME record pointing your hostname to this address:

Type Name Target
CNAME nginx YOUR_TUNNEL_ID.cfargotunnel.com

External-DNS

If you have external-dns configured with Gateway API source, DNS records are created automatically. See External-DNS Integration for setup.

Access Your Application

Once DNS propagates, access your application at https://nginx.example.com.

Cloudflare automatically provides:

  • TLS certificate (via Universal SSL)
  • DDoS protection
  • Web Application Firewall (WAF)
  • Caching (configurable)

Path-Based Routing

Route different paths to different services:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-routes
spec:
  parentRefs:
    - name: cloudflare-tunnel
      namespace: cloudflare-tunnel-system
  hostnames:
    - api.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v1
      backendRefs:
        - name: api-v1
          port: 8080
    - matches:
        - path:
            type: PathPrefix
            value: /v2
      backendRefs:
        - name: api-v2
          port: 8080

Multiple Hostnames

Route multiple hostnames to the same service:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: multi-host
spec:
  parentRefs:
    - name: cloudflare-tunnel
      namespace: cloudflare-tunnel-system
  hostnames:
    - app.example.com
    - www.example.com
    - "*.staging.example.com"
  rules:
    - backendRefs:
        - name: web-app
          port: 80

Advanced Routing

The in-process L7 proxy is the only data plane in v3, so full Gateway API matching and filter support is always available. Below are short examples of the most common patterns.

Header-Based Routing

Route requests to different backends based on an HTTP header value:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-route
spec:
  parentRefs:
    - name: cloudflare-tunnel
      namespace: cloudflare-tunnel-system
  hostnames:
    - app.example.com
  rules:
    - matches:
        - headers:
            - name: X-Version
              value: beta
      backendRefs:
        - name: beta-service
          port: 80

Weighted Traffic Splitting

Split traffic between two backends by weight:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
spec:
  parentRefs:
    - name: cloudflare-tunnel
      namespace: cloudflare-tunnel-system
  hostnames:
    - app.example.com
  rules:
    - backendRefs:
        - name: stable-service
          port: 80
          weight: 90
        - name: canary-service
          port: 80
          weight: 10

Request Redirect

Redirect HTTP requests to a different host or path:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: redirect-route
spec:
  parentRefs:
    - name: cloudflare-tunnel
      namespace: cloudflare-tunnel-system
  hostnames:
    - old.example.com
  rules:
    - filters:
        - type: RequestRedirect
          requestRedirect:
            hostname: new.example.com
            statusCode: 301

L7 proxy configuration

These features are served by the always-on in-process L7 proxy. See the L7 Proxy Guide for proxy configuration options and tuning.

Troubleshooting

Route Not Accepted

Check controller logs:

kubectl logs --selector app.kubernetes.io/name=cloudflare-tunnel-gateway-controller \
  --namespace cloudflare-tunnel-system

Common issues:

  • Gateway not found (wrong namespace or name in parentRefs)
  • Cloudflare API error (invalid credentials or permissions)
  • Service not found (wrong service name or namespace)

SSL Certificate Errors

Cloudflare's free Universal SSL covers:

  • example.com
  • *.example.com

For multi-level subdomains like app.dev.example.com, you need Advanced Certificate Manager.

Next Steps