Skip to main content

Cloudflare module

Uploads the certificate to a Cloudflare zone as a Custom Certificate so Cloudflare serves it at the edge, and updates it in place on every renewal. It is the cloud sibling of the on-prem F5/NetScaler modules.

Overview

  • Type: cloudflare, credential type: cloudflare_api_token (JSON {"api_token":"..."}), slot default.
  • Per target: resolve the zone → resolve the custom-certificate identity → POST (create) or PATCH (update in place) /zones/{zone_id}/custom_certificates → persist the returned id for the next renewal.
  • Rollback: supported — a rollback re-uploads a previous retained certificate version to the zone (Rollback). Extra eligibility rule: the version must have at least 14 days of remaining validity (min_validity_floor — the same floor Cloudflare imposes on any upload).
  • Scope: only per-zone Custom Certificates. SSL for SaaS / Custom Hostnames (/zones/{id}/custom_hostnames, for serving TLS on your customers' domains) is a separate API and out of scope.

Requirements

  • Business or Enterprise plan. Free/Pro zones cannot upload custom certificates — the module surfaces this as CLOUDFLARE_PLAN.
  • API token with Zone → SSL and Certificates → Edit on the target zone (Read suffices for dry-run/health-check).
  • The certificate must not expire in under 14 days at upload time. This floor also gates rollback eligibility: a previous version with under 14 days of validity left is reported ineligible with reason min_validity_floor.
  • The private key must be unencrypted; for ECDSA keys any EC PARAMETERS block is stripped automatically before upload.

Target configuration

FieldRequiredDescription
zone_idone ofThe Cloudflare zone id (32-hex). Wins over zone_name.
zone_nameone ofThe zone domain (e.g. example.com); resolved to an id via GET /zones?name= when zone_id is empty.
certificate_idnoPin an existing custom certificate to update in place. Empty → remembered id, else hosts match, else create new.
bundle_methodnoubiquitous (default, broadest trust) · optimal (shortest chain) · force (upload the chain as-is).
typenosni_custom (default, recommended) · legacy_custom (non-SNI legacy clients; incompatible with BYOIP).
geo_restrictionsnous · eu · highest_security — restricts where the private key is stored (Geo Key Manager v1). The finer-grained v2 policy expression is not currently exposed.
{
"zone_name": "example.com",
"type": "sni_custom",
"bundle_method": "ubiquitous"
}

Identity resolution

Cloudflare custom certificates carry no tag or metadata field, so a lost id is recovered by matching the certificate's covered hostnames. The module resolves which certificate to update in order:

  1. Explicit certificate_id (verified to exist).
  2. The id remembered from a previous run (remote state), verified to still exist — an out-of-band delete in the dashboard falls through.
  3. Hosts match — list the zone's custom certificates and adopt the one whose hosts cover the primary domain.
  4. None found → create a new custom certificate.

If more than one certificate covers the primary domain, the run fails with CLOUDFLARE_CONFLICT and asks for an explicit certificate_id rather than guessing.

Update re-keys the id (sni_custom). A PATCH on a modern (sni_custom) certificate makes Cloudflare delete the old id and return a new one, so the id from the response — not the one sent — is what gets persisted. Consequence for a pinned certificate_id: it is a first-run adoption hint. After CertAutoPilot adopts and updates it, the pinned id is re-keyed and no longer exists; from then on CertAutoPilot tracks the (new) id in its own remote state. You can safely leave the pin set — on the next renewal the original pin 404s and the module falls back to the remembered successor id (and, failing that, hosts match) rather than erroring. A pin that never resolved and has no remembered state (a typo/wrong id) still fails with CLOUDFLARE_NOT_FOUND. legacy_custom ids are stable across updates.

Multiple certificates on one zone

A single Cloudflare target (one zone) can be referenced by many CertAutoPilot certificates — each is uploaded as its own custom certificate and tracked independently per certificate (remote_states.<certID>), so certificates never clobber each other's id or history. This is the same "one target, many certificates" model as IIS/F5.

But Cloudflare caps custom certificates per zone by plan:

PlanCustom certificates per zone
Free / Pronone (custom certs not available)
Business1 modern (sni_custom) + 1 legacy (legacy_custom)
Enterprise1 modern + 1 legacy included; more can be purchased (no fixed published ceiling)

So on Business a zone effectively holds one sni_custom certificate. In practice this fits the common case: a Cloudflare zone is one domain, and a single wildcard certificate (example.com + *.example.com) covers the whole zone — deploy one certificate per zone. If you genuinely need several distinct custom certificates on one zone, use Enterprise with purchased slots. Exceeding the quota surfaces as CLOUDFLARE_PLAN/CLOUDFLARE_QUOTA.

If two custom certificates in a zone overlap on a hostname, Cloudflare's own certificate priority decides which serves (legacy outranks modern; then priority/recency). CertAutoPilot does not manage priority — set it in the Cloudflare dashboard for overlapping certs. Non-overlapping certificates need no priority management.

Credential

Create a Cloudflare API Token credential (Settings → Distribution → Credentials):

{ "api_token": "your-cloudflare-api-token" }

Create the token in the Cloudflare dashboard under My Profile → API Tokens → Create Custom Token with permission Zone → SSL and Certificates → Edit and the target zone in Zone Resources.

Verification

Cloudflare edge propagation is asynchronous. For an explicit "new cert is live" signal, add a tls_fingerprint validation endpoint pointed at the domain with retries.

Error codes

CodeMeaningResolution
CLOUDFLARE_AUTHToken rejected or lacks permissionGrant Zone → SSL and Certificates → Edit and include the zone.
CLOUDFLARE_PLANZone plan disallows custom certificatesUpgrade the zone to Business or Enterprise.
CLOUDFLARE_NOT_FOUNDZone or pinned certificate id not foundVerify zone_id/zone_name and any pinned certificate_id.
CLOUDFLARE_CONFLICT>1 custom cert covers the hostnamePin an explicit certificate_id to disambiguate.
CLOUDFLARE_VALIDATIONCloudflare rejected the cert/keyCheck the PEM chain, key match, and ≥14-day validity.
CLOUDFLARE_QUOTARate limit exceededRetry after a brief wait.
CLOUDFLARE_CONNECTCould not reach the Cloudflare APICheck outbound egress to api.cloudflare.com.