caddyserver/caddy · error
encrypted private keys are not supported; please decrypt the
Error message
encrypted private keys are not supported; please decrypt the key first
What it means
The TLS file certificate loader (tls.certificates.load_files / FileLoader) reads the PEM key file and inspects its first 40 bytes; if they contain the string ENCRYPTED — the marker header of PKCS#8/PKCS#1 encrypted key PEMs such as '-----BEGIN ENCRYPTED PRIVATE KEY-----' — it refuses to load, because Go's tls.X509KeyPair cannot consume passphrase-protected keys and Caddy has no prompt/decrypt step. The error tells you to decrypt out-of-band.
Source
Thrown at modules/caddytls/fileloader.go:101
certData, err := os.ReadFile(pair.Certificate)
if err != nil {
return nil, err
}
keyData, err := os.ReadFile(pair.Key)
if err != nil {
return nil, err
}
var cert tls.Certificate
switch pair.Format {
case "":
fallthrough
case "pem":
// if the start of the key file looks like an encrypted private key,
// reject it with a helpful error message
if strings.Contains(string(keyData[:40]), "ENCRYPTED") {
return nil, fmt.Errorf("encrypted private keys are not supported; please decrypt the key first")
}
cert, err = tls.X509KeyPair(certData, keyData)
default:
return nil, fmt.Errorf("unrecognized certificate/key encoding format: %s", pair.Format)
}
if err != nil {
return nil, err
}
certs = append(certs, Certificate{Certificate: cert, Tags: pair.Tags})
}
return certs, nil
}
// Interface guard
var (View on GitHub (pinned to 50e54ee279)
Solutions
- Decrypt the key to an unencrypted PEM: openssl rsa -in encrypted.key -out decrypted.key (RSA) or openssl pkey -in encrypted.key -out decrypted.key, then point the config at decrypted.key.
- Restrict the decrypted file's permissions to the Caddy user (chmod 600) since it is now plaintext.
- Prefer letting Caddy automate certificates (ACME) instead of supplying encrypted files.
- For automated pipelines, decrypt in a pre-deploy step fed from a secrets manager, never committing the plaintext key.
Example fix
# before $ openssl genrsa -aes256 -out site.key 2048 # encrypted tls /path/cert.pem /path/site.key -> error # after $ openssl pkey -in site.key -out site.plain.key # enter passphrase once tls /path/cert.pem /path/site.plain.key
Defensive patterns
Strategy: validation
Validate before calling
// Detect encrypted PEM keys before handing them to the loader.
func isEncryptedKey(keyPath string) (bool, error) {
data, err := os.ReadFile(keyPath)
if err != nil {
return false, err
}
head := data
if len(head) > 40 {
head = head[:40]
}
return strings.Contains(string(head), "ENCRYPTED"), nil
} Try / catch
if err != nil && strings.Contains(err.Error(), "encrypted private keys are not supported") {
// decrypt out-of-band: openssl pkey -in enc.key -out plain.key; update config path
} Prevention
- Generate keys unencrypted for server use: openssl genrsa -out site.key 2048 (no -aes* flag).
- Decrypt in a pre-deploy step from your secrets manager; never expect Caddy to prompt.
- chmod 600 decrypted keys and restrict them to the service user.
- Prefer ACME-automated certificates to avoid handling key files at all.
When it happens
Trigger: tls directive with load / pointing at a certificate/key pair whose key file begins with '-----BEGIN ENCRYPTED PRIVATE KEY-----' (openssl enc-protected RSA/EC keys).
Common situations: Keys generated with 'openssl genrsa -aes256' or exported from vaults/browsers with a passphrase; CI copying secured keys into place without decrypting; operators assuming Caddy will prompt for the passphrase (it won't, even interactively).
Related errors
- unable to add %s to trust pool: %v
- parsing certificate in %s: %v
- no CERTIFICATE pem block found in %s
- loading certificate loader modules: %s
- loading certificates: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/ccd7327ada979783.
Report an issue: GitHub.