Post-quantum authentication reaches the origin connection

Cloudflare has extended post-quantum protection to the server-to-server leg of proxied traffic. Authenticated Origin Pulls (AOP) and Custom Origin Trust Store (COTS) now support ML-DSA signatures, letting customers establish fully post-quantum secure mutually authenticated TLS connections between Cloudflare and their origin servers.

The move addresses a shift in the threat model. Earlier work focused on post-quantum encryption to defend against harvest-now/decrypt-later attacks. Recent advances in quantum computing have compressed the timeline for migrating authentication as well: an attacker who can break classical credentials with a quantum computer could impersonate servers or clients. Cloudflare's roadmap targets full post-quantum security by 2029, and this release covers the first milestone on that path.

Why the origin leg is a different problem

A proxied request typically involves two connections: visitor-to-Cloudflare and Cloudflare-to-origin. Post-quantum encryption has been available on both since 2022 and 2023 respectively. Authentication is the remaining gap. For the visitor-to-Cloudflare leg, Cloudflare is collaborating with Google and others at the IETF on Merkle Tree Certificates (MTC), with initial deployments targeted for 2027. The origin leg, however, has requirements that make a faster path feasible.

On the origin connection Cloudflare acts as the client, which brings two advantages. First, connection pooling fans in requests from across Cloudflare's network into a smaller set of connections, amortizing the cost of connection setup. The overhead of "drop-in" post-quantum signatures is therefore more acceptable, and the performance optimizations of MTC are less critical. Second, the existing trust relationship between Cloudflare and its customers means the connection need not depend on the public WebPKI. Custom PKIs can be used without the overhead of intermediate certificates or Certificate Transparency. Customers with legacy origin systems can also use Cloudflare Tunnel, which forwards traffic over a connection protected by post-quantum encryption, with post-quantum authentication in progress.

This lets Cloudflare deploy ML-DSA authentication on the origin leg before post-quantum certificates land in the WebPKI. For customers who remain on the WebPKI, MTC support for the origin connection is planned for the future.

What's supported and how to configure it

ML-DSA support covers all FIPS 204 parameter sets: ML-DSA-44, ML-DSA-65, and ML-DSA-87. Cloudflare recommends ML-DSA-44 for most deployments, as it is the most performant and reaches NIST category 2 security strength.

Custom Origin Trust Store

When a zone uses Full (strict) SSL mode, Cloudflare authenticates the origin certificate against a default trust store containing commonly trusted CAs plus Cloudflare's origin CA. COTS — which requires Advanced Certificate Manager — replaces that default with a set of customer-controlled CAs. COTS now accepts ML-DSA CAs, so Cloudflare will trust any origin server certificate that chains to an uploaded post-quantum CA.

Authenticated Origin Pulls

AOP lets customers restrict origin traffic to requests that present a Cloudflare client certificate, establishing mutual TLS. It is available on all plan levels and supports three configuration levels: global, per-zone, and per-hostname. The per-zone and per-hostname levels now accept ML-DSA certificates and private keys in the FIPS 204 seed format. The global level is a larger change and will be addressed later.

Avoiding downgrade attacks

Supporting post-quantum authentication on both sides is necessary but not sufficient. If the verifying party continues to trust quantum-vulnerable mechanisms, an on-path attacker who can forge classical credentials remains a risk. The verifying side must remove trust in classical authentication entirely. The configuration guide for AOP and COTS details how to ensure an origin is secure against downgrades. Cloudflare notes the broader challenge of this transition, referencing the Chromium Security team's four-stage plan for the Web PKI.

Quick start via API

The following steps configure both products through the Cloudflare API. Dashboard instructions are available in the developer documentation. OpenSSL 3.5.0 or later is required, and private keys must be generated in the FIPS 204 seed-only encoding, which is the only format Cloudflare accepts on upload.

First, generate the certificate chains for the origin server (for COTS) and the Cloudflare client (for AOP).

# Create a private ML-DSA-44 CA for the origin server
openssl genpkey -algorithm mldsa44 \
  -provparam ml-dsa.output_formats=seed-only \
  -out origin-ca.key

openssl req -new -x509 -key origin-ca.key \
  -out origin-ca.crt -days 10950 \
  -subj "/CN=Origin Server CA"

# Create the origin server certificate (signed by the origin CA)
openssl genpkey -algorithm mldsa44 \
  -provparam ml-dsa.output_formats=seed-only \
  -out origin-server.key

openssl req -new -key origin-server.key \
  -out origin-server.csr \
  -subj "/CN=origin.example.com" \
  -addext basicConstraints=CA:FALSE \
  -addext keyUsage=digitalSignature \
  -addext subjectAltName=DNS:origin.example.com

openssl x509 -req -in origin-server.csr \
  -CA origin-ca.crt -CAkey origin-ca.key -CAcreateserial \
  -out origin-server.crt -days 5475 \
  -copy_extensions copy
# Create a private ML-DSA-44 CA for Authenticated Origin Pulls
openssl genpkey -algorithm mldsa44 \
  -provparam ml-dsa.output_formats=seed-only \
  -out aop-ca.key

openssl req -new -x509 -key aop-ca.key \
  -out aop-ca.crt -days 10950 \
  -subj "/CN=Authenticated Origin Pull CA"

# Create the client certificate Cloudflare will present (signed by the AOP CA)
openssl genpkey -algorithm mldsa44 \
  -provparam ml-dsa.output_formats=seed-only \
  -out aop-client.key

openssl req -new -key aop-client.key \
  -out aop-client.csr \
  -subj "/CN=cloudflare-aop-client" \
  -addext basicConstraints=CA:FALSE \
  -addext keyUsage=digitalSignature \
  -addext subjectAltName=DNS:cloudflare-aop-client

openssl x509 -req -in aop-client.csr \
  -CA aop-ca.crt -CAkey aop-ca.key -CAcreateserial \
  -out aop-client.crt -days 5475 \
  -copy_extensions copy

Upload the origin CA to COTS. Note that uploading a COTS CA replaces the default publicly-trusted CAs for the zone; only post-quantum CAs should be uploaded if downgrade attacks are a concern.

CA_CERT=$(jq -Rs . < origin-ca.crt)

curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/acm/custom_trust_store" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --json "{\"certificate\": $CA_CERT}"

Upload the client certificate for AOP. The example uses zone-level AOP; per-hostname configuration uses the /origin_tls_client_auth/hostnames/certificates endpoint instead.

CERT=$(jq -Rs . < aop-client.crt)
KEY=$(jq -Rs . < aop-client.key)

# Upload the ML-DSA client certificate
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/origin_tls_client_auth" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --json "{\"certificate\": $CERT, \"private_key\": $KEY}"

# Enable zone-level Authenticated Origin Pulls
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/origin_tls_client_auth/settings" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --json '{"enabled": true}'

Set the zone's SSL mode to Full (strict). COTS is only active in that mode; AOP without COTS requires Full or higher.

curl -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/ssl" \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --json '{"value": "strict"}'

Configure the origin server. For COTS, the origin presents the ML-DSA server certificate:

server {
    listen 443 ssl;
    ssl_certificate     /etc/ssl/origin-server.crt;
    ssl_certificate_key /etc/ssl/origin-server.key;
    ssl_protocols       TLSv1.3;
}

For AOP, the origin verifies Cloudflare's client certificate:

server {
    listen 443 ssl;
    ssl_client_certificate /etc/ssl/aop-ca.crt;
    ssl_verify_client      on;
}

For full post-quantum mutual TLS, configure both together:

server {
    listen 443 ssl;
    ssl_certificate        /etc/ssl/origin-server.crt;
    ssl_certificate_key    /etc/ssl/origin-server.key;
    ssl_client_certificate /etc/ssl/aop-ca.crt;
    ssl_verify_client      on;
    ssl_protocols          TLSv1.3;
}

Verifying the handshake

The Cloudflare-to-origin TLS handshake happens server-to-server and cannot be observed from outside by connecting to the proxied hostname. Each side must be verified separately.

Verify COTS. If the origin IP is directly reachable — for example during testing before the proxy is enabled — connect directly and validate the certificate:

openssl s_client -connect <ORIGIN_IP>:443 \
  -servername origin.example.com \
  -CAfile origin-ca.crt \
  -brief

Look for Signature type: mldsa44 in the output. If the origin only accepts Cloudflare IPs, check the origin's TLS logs or use a packet capture tool such as ssldump or tcpdump to confirm TLS 1.3 was negotiated with the ML-DSA certificate.

Verify AOP. Confirm that direct connections to the origin without a valid client certificate are rejected:

openssl s_client -connect <ORIGIN_IP>:443 \
  -servername origin.example.com \
  -brief

With ssl_verify_client enforced, this should fail with an SSL alert. For the full path, inspect origin server logs, since the mTLS handshake is invisible externally. NGINX can log the client certificate serial number or subject:

log_format pq_verify '$remote_addr - $ssl_client_serial $ssl_client_s_dn';
access_log /var/log/nginx/pq-verify.log pq_verify;

After sending a request through Cloudflare, the log should show the serial number of the uploaded aop-client.crt certificate. For the key agreement, the origin's TLS library must support X25519MLKEM768 and prefer it in the configuration; the negotiated group appears in origin logs or packet captures.

Inside the implementation

Rolling out ML-DSA support to origin-facing products touched two separate systems: the control plane, which manages TLS settings and certificate uploads, and the data plane, which establishes the actual TLS connections to origin servers.

Control plane updates

The control plane service behind Cloudflare's SSL/TLS configuration runs in a highly available configuration across critical data centers. It handles SSL/TLS settings updates and pushes them to a globally-distributed key-value store so data plane services can access them when processing live requests.

Enabling ML-DSA for Advanced Certificate Manager (ACM) and Custom Origin Certificates (COTS) meant teaching this service to parse and validate ML-DSA certificates. The challenge: the service is written in Go, and Go's standard X.509 and TLS libraries lacked ML-DSA support. The team implemented the required functionality in Cloudflare's CIRCL library as a stopgap. That works, but repeating this patching approach across every service needing post-quantum authentication would be impractical.

The longer-term fix arrives with Go 1.27, expected in August 2026, which will include native ML-DSA support. That will let Go-based services drop the CIRCL dependency and pick up post-quantum authentication through a routine version bump.

Data plane updates

With certificate uploads working, the next step was getting the data plane to actually use those certificates. That job falls to Pingora Origin, the Cloudflare service built on the open-source Pingora proxy framework that handles origin-bound connections at scale.

The TLS provider handles the security of those connections — and post-quantum authenticity is no exception. No exotic hardware is required; the change lands in BoringSSL. But the update wasn't trivial for Cloudflare. Pingora Origin had been running a four-year-old internal fork of BoringSSL, patched with additional functionality as needed. That arrangement worked well enough that the team kept deferring the upgrade — until post-quantum authentication support landed in BoringSSL in April 2026, making the update worthwhile.

The upgrade did not go perfectly. Among four years of accumulated changes was a commit enforcing KeyUsage rules in TLS certificates per RFC 5280. Specifications and real-world deployments don't always align, and despite weeks of testing and a slow rollout, a small number of customer certificates were flagged as invalid after the change — resulting in an incident on June 10, 2026. The team rolled back the change quickly and later shipped a patch that preserves support for RSA certificates with technically invalid KeyUsage extensions. With that resolved, fully post-quantum secure TLS to origins is live.

What's next

ML-DSA support is becoming standard across TLS libraries, and routine updates will carry post-quantum authentication into many applications. The arrival of Go 1.27 in August 2026 will make this especially straightforward for Go-based services. Cloudflare will continue upgrading its own systems as the ecosystem evolves; the PQC in Cloudflare Products tracker lists current post-quantum encryption and authentication support across the product line.