How to Use Client Certificates for mTLS in Cloudflare Workers

What is mTLS?

Mutual TLS (mTLS) is a security protocol where both the client and server authenticate each other using certificates — unlike standard TLS, where only the server is verified. A secure connection is only established if both validations succeed, ensuring that each party is who they claim to be.

The challenge of mTLS in the Workers runtime

In a typical Node.js app, you’d use undici to provide client certificates for mTLS.

import { request, Agent } from 'undici';

const agent = new Agent({
      connect: {
        key: process.env.KEY,
        cert: process.env.CERTIFICATE
      }
});

const response = await request(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      dispatcher: agent,
      body: JSON.stringify(requestBody)
});

Cloudflare Workers run on a V8 isolate runtime rather than a full Node.js server. This means Node-specific modules and low-level networking APIs are absent. In particular, Node’s built-in HTTP/HTTPS libraries are not supported in the Workers runtime.

So, how can you establish a secure connection to an external service from a worker?

The answer is Cloudflare’s Mutual TLS certificates.

How to use mTLS with Cloudflare Workers

Cloudflare allows you to upload client certificates using the mTLS certificate API, and then bind them to your Worker. These certificates are used automatically when making fetch requests via the bound Fetcher.

For this particular example, we’ll be using Hono to create a simple API that uses mTLS to connect to an external service.

First, set up your project:

npm create hono@latest cf-workers-mtls-example

Select the cloudflare-workers template.

Next, upload your certificate to Cloudflare:

Use Wrangler (installed with the Hono template) to upload your certificate and key in PEM format. Ensure you’re in the directory containing your cert.pem and key.pem files:

npx wrangler mtls-certificate upload --cert cert.pem --key key.pem --name app-cert

Wrangler will open a browser to authenticate with your Cloudflare account. Once complete, your certificate will be uploaded and ready to use.

Uploading mTLS Certificate app-cert...
Success! Uploaded mTLS Certificate app-cert
ID: b145510d-25ff-4370-89b0-46b42532af1b
Issuer: O=Internet Widgits Pty Ltd,ST=Some-State,C=AU
Expires on 4/5/2026

Now, we can use it in our worker.

  1. Configure wrangler

Update your wrangler.jsonc file to bind the certificate to your Worker. Replace the certificate_id with the ID from the upload step:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "cf-workers-mtls-example",
  "main": "src/index.ts",
  "mtls_certificates": [
    {"binding": "APP_CERT", "certificate_id": "b145510d-25ff-4370-89b0-46b42532af1b"}
  ]
}
  1. Properly configure the bindings for Hono.

Define the APP_CERT binding as a Fetcher in your Hono app. A Fetcher is a special type in Cloudflare Workers that allows you to make authenticated HTTP requests using the uploaded mTLS certificate:

import { Hono } from "hono";
import { cors } from "hono/cors";

type Bindings = {
  APP_CERT: Fetcher;
};

export const app = new Hono<{ Bindings: Bindings }>().use(
  "*",
  cors({
    origin: ["*"],
    allowHeaders: ["Content-Type", "Authorization"],
    allowMethods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
  })
);
  1. Use the certificate in your worker.

Use c.env.APP_CERT.fetch to make an mTLS-authenticated request to an external service:

app.get("/mtls-request", async (c) => {
  const response = await c.env.APP_CERT.fetch(
    "https://secure.api.com",
    {
      headers: {
        "Content-Type": "application/json",
      },
      method: "POST",
      body: JSON.stringify({
        "user": "crisog",
      }),
    }
  );

  const responseJson = await response.json();

  return c.json({ response: responseJson, status: response.status });
});
  1. Deploy your worker.
wrangler deploy

Once deployed, Wrangler will provide a URL to your worker.

  1. Test your worker.

Verify it works using curl.

curl https://cf-workers-mtls-example.crisog.workers.dev/mtls-request

That’s all you need to securely connect to external services using mTLS in Cloudflare Workers.

You can find the example code for this blog post here.