Image for Cloudflare Command Center: Domains, DNS, Zero Trust, and Tunnels from Beginner to Expert Part 16: Service Tokens, APIs, and Machine-to-Machine Access
Technology Sep 11, 2026 • 17 min read

Cloudflare Command Center: Domains, DNS, Zero Trust, and Tunnels from Beginner to Expert Part 16: Service Tokens, APIs, and Machine-to-Machine Access

Master Cloudflare Access service tokens for machine-to-machine auth, CI/CD pipelines, and API protection with non-human identity management.

Share:
Lee Foropoulos

Lee Foropoulos

17 min read

Continue where you left off?
Text size:

Contents

Part 15 walked through Cloudflare Access policies for human users: identity providers, login flows, group-based rules, and the mechanics of how Cloudflare intercepts a browser request and forces authentication before anything reaches your origin. That foundation matters. Build on it. Because Part 16 is where the model breaks open and gets genuinely interesting.

Humans aren't the only things calling your protected services. Your CI/CD pipeline calls them. Your monitoring stack calls them. Your internal microservices call each other across boundaries that a browser login flow can't touch. If your Zero Trust deployment only accounts for people sitting at keyboards, you've secured maybe half of your actual attack surface. The other half is running on a cron schedule at 3 AM and has never once seen a login page.

This part covers service tokens, the Cloudflare API, and the full pattern of machine-to-machine access inside a Zero Trust architecture. By the end, you'll have a working mental model for how non-human principals authenticate, how to issue and scope credentials correctly, and how a real GitHub Actions workflow calls a protected internal endpoint without any human in the loop.

Why Machine Identity Is the Missing Piece in Zero Trust

The human-centric blind spot in most Zero Trust deployments

Every Zero Trust guide starts the same way. Verify the user. Verify the device. Enforce least-privilege access based on identity. That's correct. It's also incomplete in a way that most guides never acknowledge, because the implicit assumption underneath all of it is that the principal requesting access is a person.

It isn't always. In most production environments, it isn't even usually.

Multiple monitors at a technical workstation showing infrastructure dashboards
Machine-to-machine traffic often outnumbers human-initiated requests in modern infrastructure. Most Zero Trust deployments don't account for it.

CI/CD pipelines need to call deployment APIs. Health-check bots need to hit internal endpoints every 30 seconds. Microservices need to authenticate to each other across network segments. None of these can complete an OAuth flow. None of them can solve a CAPTCHA or respond to a push notification. If your Access policies only have rules for identity-provider-authenticated users, all of that traffic either gets blocked entirely or, more dangerously, gets routed around your Zero Trust layer through a gap someone carved out to make things work.

That gap is where incidents start.

The machines running your infrastructure at 3 AM need identities too. If they don't have them, they have something worse: implicit trust.
62%
of API breaches in 2024 involved unmanaged or undocumented machine credentials, according to Salt Security's State of API Security report

Cloudflare Access addresses this directly through service tokens: a credential mechanism designed specifically for non-human principals. Think of a service token as the machine equivalent of a user identity. It doesn't log in. It presents a credential pair at the edge, Cloudflare validates it, and if it matches a policy rule, the request goes through. Same enforcement model. No browser required.

What happens when bots, pipelines, and services go unmanaged

Unmanaged machine access doesn't look like a security problem until it is one. It looks like a hardcoded API key in a .env file that someone committed three years ago. It looks like a shared credential that five different services use because rotating it would require updating all of them. It looks like a monitoring agent that bypasses your Access layer because nobody wanted to figure out how to authenticate it properly.

These aren't hypothetical failure modes. They're the default outcome when machine identity gets treated as an afterthought. Service tokens give you a path out of that pattern: named credentials, scoped to specific applications, with configurable expiry windows and a clear audit trail.

Understanding Cloudflare Service Tokens

What a service token actually is

A service token in Cloudflare Access is a credential pair issued by Cloudflare and scoped to one or more Access-protected applications. It has two components: a Client ID and a Client Secret. The Client ID is a stable identifier you can reference in policies and logs. The Client Secret is a secret string that authenticates the token holder at request time.

Abstract digital security concept with credential authentication visual
Service tokens are issued at the Cloudflare edge and validated before any traffic reaches your origin server.

Service tokens don't connect to your identity provider at all. There's no SAML assertion, no OAuth exchange, no OIDC token. The credential pair is entirely self-contained within Cloudflare Access. That's intentional. It means you don't need to create a "service account" user in your identity provider, manage its MFA enrollment, or worry about what happens when your IdP has an outage. The token lives in Cloudflare and is validated at the Cloudflare edge before the request ever moves toward your origin.

How edge validation works

When a request arrives carrying CF-Access-Client-Id and CF-Access-Client-Secret headers, Cloudflare evaluates those values against the service tokens attached to the matching Access application policy. If the credentials are valid and the policy allows the Service Auth action, Cloudflare forwards the request to your origin and injects a signed JWT for downstream validation. If the credentials are invalid, Cloudflare returns a 403 before your origin ever sees the request.

Client ID and Client Secret: how the credential pair works

The Client ID is a UUID-format identifier that's safe to log and reference in policy rules. It doesn't need to be kept secret. The Client Secret is the sensitive half. It's shown exactly once in the Cloudflare dashboard at token creation time. If you don't copy it then, it's gone. You can only revoke the token and generate a new one.

In practice, this means your workflow for issuing service tokens should always include an immediate handoff to a secrets management system: GitHub Actions secrets, HashiCorp Vault, AWS Secrets Manager, or equivalent. Never paste the secret into a config file and commit it. Never send it over Slack. Copy it once, store it in the right place, and treat it with the same seriousness you'd treat a private key.

Service tokens vs. mutual TLS vs. API keys

These three mechanisms solve overlapping problems in different ways. Mutual TLS is strong but operationally heavy. It requires certificate issuance, distribution, rotation, and client-side certificate management. It's excellent for high-security environments and Cloudflare supports it, but it's not the right first tool for most teams.

Traditional API keys are simple and widely understood, but they're also flat credentials with no native scoping, no built-in expiry enforcement, and no audit integration. They end up hardcoded in places they shouldn't be.

Service tokens sit between those two points. They're scoped to specific Access applications, they have configurable expiry windows, they appear in Cloudflare's audit logs with their name attached, and they don't require certificate infrastructure. For most machine-to-machine access patterns inside a Cloudflare-protected environment, service tokens are the right starting point.

Creating and Configuring a Service Token in the Dashboard

Step-by-step: generating a service token

Navigate to Zero Trust > Access > Service Auth > Service Tokens in the Cloudflare dashboard. The interface is straightforward. Click Create Service Token, give it a name, and set a duration.

Dashboard interface on a laptop screen showing configuration options
Service tokens are created in the Zero Trust dashboard under Access > Service Auth. Name them descriptively so your audit logs are readable six months from now.

The name matters more than it seems. This string appears in your Cloudflare audit logs, in your Access policy rules, and in whatever secrets management system you store the credentials in. A name like github-actions-prod or datadog-monitoring-us-east is immediately readable. A name like token1 or service-key creates operational confusion the moment you have more than two tokens in the list.

1
time the Client Secret is displayed. Copy it immediately or generate a new token

After you click Create, Cloudflare displays both the Client ID and the Client Secret in a modal. This is the only moment you'll ever see the Client Secret. Copy both values into your secrets manager before closing that modal. There's no recovery option. There's no "show secret again" button. If you close the modal without copying, you're starting over.

No second chances on the Client Secret

Cloudflare does not store a recoverable copy of the Client Secret after token creation. If you lose it, revoke the token and create a new one. Build a handoff procedure into your token issuance workflow so this doesn't happen in production at an inconvenient moment.

Attaching a service token to an Access application

Creating the token is only half of the configuration. The token doesn't grant access to anything until you attach it to an Access application via a policy rule.

Open the Access application you want to protect. Go to Policies and either edit an existing policy or create a new one. Add a rule with the Service Auth action. In the rule conditions, select Service Token as the selector and choose the token you just created from the dropdown. Save the policy.

From this point forward, any request that presents the correct Client ID and Client Secret headers and matches this application's hostname will be authenticated at the edge as that service token. Requests without valid credentials get a 403. The origin never sees unauthenticated traffic.

Setting token duration and expiry policies

When you create a service token, you choose its duration. The options range from a few months to multiple years, and there's a trade-off in both directions.

Short-lived tokens reduce the blast radius of a credential compromise. If a token expires in 30 days, a leaked credential has a bounded window of usefulness. The operational cost is that you need a renewal process, and if that process fails silently, your pipeline breaks at the worst possible time.

Long-lived tokens are operationally simpler but create the kind of stale credential sprawl that causes problems. A token set to expire in three years is often forgotten. It outlives the service it was created for. It accumulates access it shouldn't have.

A reasonable middle ground for most teams is 90-day tokens with automated renewal reminders. Cloudflare shows active tokens and their expiry dates in the Service Tokens list view. Build a calendar check or a monitoring alert around that list so expirations don't catch you off guard.

Writing Access Policies for Non-Human Identities

The 'Service Auth' policy action vs. Allow and Block

Cloudflare Access policies support several actions. Allow permits access after the authentication flow completes, which for human users means completing a login through an identity provider. Block denies access unconditionally. Bypass skips the Access layer entirely for matching traffic.

Service Auth is different from all three. It's specifically designed for non-human principals. When a policy rule uses the Service Auth action, Cloudflare skips the interactive login flow entirely and evaluates the request purely on the credential headers. No redirect to an identity provider. No session cookie. Just: does this request carry a valid service token that matches an active policy rule for this application?

Security policy configuration interface showing rule conditions
Access policies for machine traffic use the Service Auth action, which evaluates credentials at the edge without triggering an interactive login flow.

This distinction matters operationally. If you accidentally apply an Allow policy to machine traffic, the pipeline hits the Access login redirect and fails immediately. If you apply a Bypass policy, you've removed protection entirely. Service Auth is the correct action for service token rules.

A service token attached to a Bypass policy isn't Zero Trust. It's just a credential attached to an open door.

Combining service token rules with other policy conditions

Service token validation alone is a single factor. It tells you that the request carries a valid credential. It doesn't tell you where the request is coming from, what network it traversed, or whether the credential is being used in an expected context.

For higher-sensitivity applications, layer additional conditions on top of the service token rule. Cloudflare Access lets you add IP range conditions, ASN conditions, and request header conditions within the same policy rule. A GitHub Actions runner operates from a published set of IP ranges. A monitoring agent runs from a known internal network. A deployment service calls from a specific VPC. All of those constraints are expressible in Access policy rules.

Restricting tokens by IP, country, and request attributes

The practical configuration looks like this: your Service Auth rule requires a matching service token AND an IP address within the allowed range. Both conditions must be true for the policy to match. A stolen credential presented from an unexpected IP fails at the edge before reaching your origin.

A sample policy structure in JSON terms looks like this:

json
1{
2  "name": "GitHub Actions Production Access",
3  "decision": "service_auth",
4  "include": [
5    {
6      "service_token": {
7        "token_id": "your-token-id-here"
8      }
9    }
10  ],
11  "require": [
12    {
13      "ip": {
14        "ip": ["192.30.252.0/22", "185.199.108.0/22"]
15      }
16    }
17  ]
18}

The include block matches the service token. The require block enforces the IP constraint. Both must be satisfied. Avoid writing policies that attach a service token to every application in your account. Scope each token to the minimum set of applications it actually needs to reach. Token sprawl is real and it compounds quietly.

Real-World Example: GitHub Actions Calling a Protected Internal Endpoint

Architecture overview: GitHub Actions → Cloudflare Access → origin API

The pattern is straightforward. You have an internal API protected by a Cloudflare Access application. You have a GitHub Actions workflow that needs to call that API as part of a deployment or health-check job. The workflow runs on GitHub's infrastructure, which has no persistent session with Cloudflare and no ability to complete an interactive login. Service tokens solve this exactly.

Developer working on a laptop with code and workflow diagrams visible
The GitHub Actions to Cloudflare Access to origin API pattern is one of the most common machine-to-machine configurations in modern deployment workflows.

The request flow works like this: GitHub Actions sends an HTTP request to your protected hostname. The request carries two custom headers: CF-Access-Client-Id and CF-Access-Client-Secret. Cloudflare intercepts the request at the edge, validates the headers against the service token attached to that application's policy, and if validation passes, forwards the request to your origin with a signed CF-Access-Jwt-Assertion header attached. Your origin sees a normal authenticated request. GitHub Actions sees a 200. Nobody had to log in.

Storing credentials as GitHub Actions secrets

Never put the Client ID or Client Secret directly in your workflow YAML. GitHub Actions has a built-in secrets system that encrypts values at rest and masks them in logs. Use it.

In your repository settings, navigate to Settings > Secrets and variables > Actions and create two secrets:

  • CF_ACCESS_CLIENT_ID containing the Client ID
  • CF_ACCESS_CLIENT_SECRET containing the Client Secret

These values are available to your workflow as environment variables and are never exposed in log output.

"Treat a service token Client Secret with the same discipline you'd apply to an SSH private key. It authenticates a machine identity to a protected system. The fact that it's a string rather than a file doesn't make it less sensitive."

403
HTTP status code returned by Cloudflare when service token credentials are missing, invalid, or expired

Sending the CF-Access-Client-Id and CF-Access-Client-Secret headers

Here's a complete workflow that calls a protected internal endpoint and verifies the response:

yaml
1name: Health Check Protected API
2
3on:
4  schedule:
5    - cron: '*/30 * * * *'
6  workflow_dispatch:
7
8jobs:
9  health-check:
10    runs-on: ubuntu-latest
11    steps:
12      - name: Call protected internal API
13        env:
14          CF_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }}
15          CF_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }}
16        run: |
17          response=$(curl --silent --write-out "\n%{http_code}" \
18            --header "CF-Access-Client-Id: $CF_CLIENT_ID" \
19            --header "CF-Access-Client-Secret: $CF_CLIENT_SECRET" \
20            https://internal-api.yourdomain.com/health)
21
22          http_code=$(echo "$response" | tail -n1)
23          body=$(echo "$response")
24
25          if [ "$http_code" != "200" ]; then
26            echo "Error calling protected API"
27            exit 1
28          fi
29
30          # Handle successful response
31          echo "API call was successful"

Here is Part 16 of 23 in the series "Cloudflare Command Center: Domains, DNS, Zero Trust, and Tunnels from Beginner to Expert".

Service Tokens, APIs, and Machine-to-Machine Access

You're now ready to tackle the final pieces of your Cloudflare Command Center puzzle. In this part, you'll learn how service tokens can be used as a defense-in-depth strategy for your API security. This is where things get really interesting.

As we discussed earlier, a capstone article in a 13-part technical series exists for one reason: because knowledge without a system is just trivia. The grimoire methodology closes that gap. In the context of Cloudflare Command Center, service tokens are used to protect your APIs from unauthorized access.

Integrating with HashiCorp Vault

HashiCorp Vault is an amazing tool for securely managing secrets. Including service tokens. Dynamic secrets allow you to generate and manage tokens on-the-fly, making it easier to switch between different environments or services. To integrate with HashiCorp Vault, you'll need to create a service token and then store it in the vault.

Once you've got your token stored away safely, you can retrieve it using the Cloudflare API endpoints. This way, you can keep track of your tokens without having them scattered all over your codebase or configuration files.

AWS Secrets Manager

AWS Secrets Manager is another powerful tool for managing secrets. Including service tokens. With this service, you can easily retrieve and rotate your service tokens, ensuring that they remain secure at all times.

To get started with AWS Secrets Manager, you'll need to create a secret and then store it in the manager. Once it's stored, you can use the Cloudflare API endpoints to retrieve your token.

GitHub Secrets

GitHub Secrets is another great tool for managing secrets. Including service tokens. With this feature, you can easily store and manage your service tokens directly in your repository.

To get started with GitHub Secrets, you'll need to create a secret and then store it in your repository. Once it's stored, you can use the Cloudflare API endpoints to retrieve your token.

Your Machine-to-Machine Access Checklist

Now that we've covered some of the key concepts for service tokens, let's take a step back and review what needs to be done for machine-to-machine access.

Here are the essential steps:

  • Verify all non-human callers have dedicated service tokens. No shared human credentials
  • Confirm tokens are scoped to only the applications they need
  • Ensure Client Secrets are stored in a secrets manager, not in code
  • Validate that Access policies use Service Auth action, not Allow
  • Test that expired or revoked tokens return the expected 401 error

Ongoing Maintenance Checklist

To keep your machine-to-machine access checklist up-to-date and secure, here's what you need to do:

Ongoing Machine Access Maintenance 0/4

Where Machine Identity Leaves You

Zero Trust that only covers people is Zero Trust with a back door. This part closed that door. You saw why machine identity is the missing piece, how a Cloudflare service token works as a Client ID and Client Secret pair, and how it compares to mutual TLS and API keys. You created a token, attached it to an Access application, and set an expiry that matches how the token is actually used. Then you wrote Service Auth policies, narrowed them with IP, country, and request conditions, and walked the full GitHub Actions example, from storing the credentials as secrets to sending the CF-Access-Client-Id and CF-Access-Client-Secret headers on the request.

A service token is a password that never gets tired, never forgets, and never asks why. Treat it with more suspicion than any human credential, not less.

Three habits matter more than any single setting. Give every machine its own token, so revoking one doesn't break everything else. Scope each token to only the applications it needs. And keep the secret in a secrets manager, never in code. Do those three things and a leaked credential becomes an inconvenience instead of an incident.

So far Access has been answering one question: who, or what, is allowed in? Part 17 moves one layer out and asks which traffic should reach your origin at all. It covers firewall rules, the WAF, rate limiting, and bot protection basics, so the requests that should never get through are filtered before they cost you anything.

How was this article?

Share

Link copied to clipboard!

You Might Also Like

Lee Foropoulos

Lee Foropoulos

Business Development Lead at Lookatmedia, fractional executive, and founder of gotHABITS.

🔔

Never Miss a Post

Get notified when new articles are published. No email required.

You will see a banner on the site when a new post is published, plus a browser notification if you allow it.

Browser notifications only. No spam, no email.

0 / 0