cloudflare/cloudflared · error

parse CA certificate %s

Error message

parse CA certificate %s

What it means

After successfully reading the CA file, CreateTunnelConfig feeds the PEM bytes to x509.CertPool.AppendCertsFromPEM. That function returns false when the bytes contain no parseable PEM certificate blocks (it does not return an error). Cloudflared surfaces this as "parse CA certificate <path>" to indicate the file was readable but its content is not a valid PEM-encoded certificate (or is an empty/garbage file).

Source

Thrown at tlsconfig/origin_ca.go:86

	}

	if !certPool.AppendCertsFromPEM(customOriginCA) {
		return nil, fmt.Errorf("error appending custom CA to cert pool")
	}
	return certPool, nil
}

func CreateTunnelConfig(caCert string, serverName string) (*tls.Config, error) {
	tlsConfig := &tls.Config{ServerName: serverName}
	if caCert != "" {
		caCertPEM, err := os.ReadFile(caCert) //nolint:gosec
		if err != nil {
			return nil, fmt.Errorf("read CA certificate %s: %w", caCert, err)
		}

		rootCAPool := x509.NewCertPool()
		if !rootCAPool.AppendCertsFromPEM(caCertPEM) {
			return nil, fmt.Errorf("parse CA certificate %s", caCert)
		}
		tlsConfig.RootCAs = rootCAPool
	}

	if tlsConfig.RootCAs == nil {
		rootCAPool, err := x509.SystemCertPool()
		if err != nil {
			return nil, errors.Wrap(err, "unable to get x509 system cert pool")
		}
		cfRootCA, err := GetCloudflareRootCA()
		if err != nil {
			return nil, errors.Wrap(err, "could not append Cloudflare Root CAs to cloudflared certificate pool")
		}
		for _, cert := range cfRootCA {
			rootCAPool.AddCert(cert)
		}
		tlsConfig.RootCAs = rootCAPool
	}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Inspect the file: it must contain blocks like `-----BEGIN CERTIFICATE-----`. Run `openssl x509 -in <path> -text -noout` to validate.
  2. Convert DER to PEM if needed: `openssl x509 -inform der -in cert.der -out cert.pem`.
  3. Verify you are pointing at the CA/roots bundle, not a private key or leaf certificate chain without CERTIFICATE PEM blocks.
  4. Re-download or re-export the CA bundle in PEM format, or omit caCert to use the system pool plus Cloudflare roots.

Example fix

// before: file contains DER, AppendCertsFromPEM fails
// $ openssl x509 -inform der -in origin-ca.der -out origin-ca.pem
tlsCfg, err := tlsconfig.CreateTunnelConfig("/etc/cloudflared/origin-ca.pem", "example.com")
Defensive patterns

Strategy: validation

Validate before calling

func isPEMCertFile(path string) error {
	b, err := os.ReadFile(path)
	if err != nil {
		return err
	}
	if !bytes.Contains(b, []byte("-----BEGIN CERTIFICATE-----")) {
		return fmt.Errorf("%s has no PEM CERTIFICATE blocks", path)
	}
	return nil
}

Try / catch

tlsCfg, err := tlsconfig.CreateTunnelConfig(caPath, serverName)
if err != nil {
	if strings.HasPrefix(err.Error(), "parse CA certificate") {
		log.Fatal().Msgf("%s is not a PEM certificate; convert with openssl x509 -inform der", caPath)
	}
	log.Fatal().Err(err).Msg("failed to build tunnel TLS config")
}

Prevention

When it happens

Trigger: CreateTunnelConfig called with a caCert path whose contents are not PEM certificates: the file holds a DER/ binary certificate, a private key, a CSR, concatenated junk, an empty file, or PEM blocks of a non-CERTIFICATE type.

Common situations: Users download a certificate in DER format instead of PEM; they point the flag at a private key or the certificate chain of the wrong entity; the file was truncated by a failed download or contains only an intermediate without any CERTIFICATE blocks; editors saved the file with HTML error-page content.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/c29e274d31018cc7. Report an issue: GitHub.