ACME Client for Node.js
acme-client is a simple and unopinionated Node.js library designed to interact with ACME (Automatic Certificate Management Environment) APIs, such as those provided by Let's Encrypt, Buypass, Google, and ZeroSSL. It adheres to RFC 8555 for ACME protocol communication. The current stable version is 5.4.0, requiring Node.js >= 16.0.0. The library primarily focuses on certificate management tasks, including account registration, order processing, and challenge fulfillment, supporting both RSA and ECDSA keys through native Node.js cryptography. It differentiates itself by being unopinionated and providing direct control over the ACME workflow, rather than an 'auto-mode' by default (though auto mode is available), and ships with TypeScript types for improved developer experience. While major versions have specific Node.js requirements, the project appears to release updates as needed, rather than on a fixed cadence.
Common errors
-
ERR_OSSL_EVP_UNSUPPORTED: Unsupported cipher algorithm
cause This error typically indicates an incompatibility between Node.js's OpenSSL version and the key or certificate format being processed, often due to deprecated algorithms or features.fixEnsure your Node.js version is up-to-date (v16+ is recommended for `acme-client` v5+). If generating keys, use modern algorithms (e.g., RSA 2048-bit or ECDSA P-256) and standard encodings (PKCS8 for private keys, SPKI for public keys). -
Error: Invalid response from ACME server (status: 400, type: urn:ietf:params:acme:error:malformed)
cause The ACME server rejected a request due to malformed data, often an issue with the request payload, headers, or a cryptographic signature.fixVerify that all required fields are present and correctly formatted in your ACME client calls. Check for special characters in contact emails, ensure private keys are valid PEM, and that account/order URLs are correct. Enable debug logging (`acme.setLogger`) to see the full request and response for more details. -
Error: Account terms of service must be agreed
cause The ACME server requires explicit agreement to its terms of service before an account can be registered or used.fixWhen calling `client.createAccount()`, ensure you set `termsOfServiceAgreed: true`. You should only do this after your application (or end-user) has reviewed and accepted the current terms of service.
Warnings
- breaking Version 5.x of `acme-client` requires Node.js version 16 or newer. Older Node.js versions (v10, v8, v4) are compatible with earlier major versions (v4.x, v3.x, v2.x/v1.x respectively).
- breaking Older versions of `acme-client` (pre-v5) might have different API signatures or return types. Always consult the upgrade guide or changelog when moving between major versions.
- gotcha When using external account binding (EAB), ensure that the `kid` and `hmacKey` are correctly provided in the client constructor. Incorrect EAB credentials will lead to account creation or update failures.
- gotcha It's critical to store ACME account private keys securely and persist them across application restarts. Losing the account key means losing control over certificates associated with that account.
Install
-
npm install acme-client -
yarn add acme-client -
pnpm add acme-client
Imports
- Client
const Client = require('acme-client').Client;import { Client } from 'acme-client'; - directory
import directory from 'acme-client/directory';
import { directory } from 'acme-client'; - AcmeClient
import type { Client as AcmeClient } from 'acme-client';
Quickstart
import { Client, directory } from 'acme-client';
import { createPrivateKey } from 'crypto';
async function runAcmeClient() {
// In a real application, load this from a secure source or generate it once
const accountPrivateKey = createPrivateKey({
type: 'rsa',
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
}).export({ type: 'pkcs8', format: 'pem' }).toString();
console.log('Using account private key (truncated):', accountPrivateKey.substring(0, 50) + '...');
const client = new Client({
directoryUrl: directory.letsencrypt.staging,
accountKey: accountPrivateKey,
});
console.log('ACME client initialized with Let\'s Encrypt staging directory.');
// Example: Register a new ACME account or get existing one
const account = await client.createAccount({
termsOfServiceAgreed: true,
contact: ['mailto:test@example.com'],
});
console.log('ACME account registered/fetched:', account);
const accountUrl = client.getAccountUrl();
console.log('Account URL:', accountUrl);
// In a real scenario, you'd now proceed to create an order, generate a CSR, and fulfill challenges.
// This quickstart only covers client initialization and account registration.
}
runAcmeClient().catch(console.error);