Configure mTLS with Custom Certificates in Axios

Mutual TLS (mTLS) ensures two-way cryptographic authentication between a client and a server, requiring the client to prove its identity using a certificate. In a Node.js environment, configuring mTLS with custom certificates and private keys in Axios is achieved by leveraging Node's native https module to create a custom https.Agent that is passed directly into the Axios configuration.

Prerequisites

To configure mTLS, ensure you have the following certificate files available in PEM format:

  • Client Certificate (client.crt): The public certificate identifying the client.
  • Client Private Key (client.key): The private key associated with the client certificate.
  • CA Certificate (ca.crt - Optional): The root or intermediate Certificate Authority certificate used to verify the server's identity.

Note: mTLS programmatic configuration in Axios only works in the Node.js runtime. Web browsers handle TLS certificates at the operating system or browser level.

Step-by-Step Configuration

1. Import Required Modules

You will need the fs module to read your certificate files from disk, the https module to configure the TLS agent, and axios.

2. Initialize the HTTPS Agent

Create an instance of https.Agent containing your custom certificates:

const fs = require('fs');
const https = require('https');
const axios = require('axios');

// Load certificate and key files
const httpsAgent = new https.Agent({
  cert: fs.readFileSync('./certs/client.crt'),
  key: fs.readFileSync('./certs/client.key'),
  ca: fs.readFileSync('./certs/ca.crt'), // Optional: Provide if using a private or self-signed CA
  rejectUnauthorized: true               // Ensures server certificate validity is enforced
});

3. Attach the Agent to Axios

You can apply the httpsAgent globally by creating a custom Axios instance or attach it to individual requests.

Creating a reusable instance automatically applies mTLS to all requests made with that client:

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  httpsAgent: httpsAgent,
  headers: {
    'Content-Type': 'application/json'
  }
});

// Example Request
async function sendRequest() {
  try {
    const response = await apiClient.get('/secure-data');
    console.log('Response:', response.data);
  } catch (error) {
    console.error('Request failed:', error.message);
  }
}

sendRequest();

Method B: Per-Request Configuration

Pass the agent directly into the Axios request options:

axios.get('https://api.example.com/secure-data', { httpsAgent })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

Handling Encrypted Private Keys

If your private key is protected by a passphrase, provide the passphrase property inside the https.Agent configuration:

const httpsAgent = new https.Agent({
  cert: fs.readFileSync('./certs/client.crt'),
  key: fs.readFileSync('./certs/client.key'),
  passphrase: 'your-secure-passphrase',
  ca: fs.readFileSync('./certs/ca.crt')
});

Troubleshooting Common Issues

  • UNABLE_TO_VERIFY_LEAF_SIGNATURE or SELF_SIGNED_CERT_IN_CHAIN: The server's certificate chain cannot be verified. Ensure the correct ca certificate is supplied in the httpsAgent.
  • ERR_OSSL_PEM_NO_START_LINE: The certificate or key format is invalid. Ensure files are correctly encoded in standard PEM format (beginning with -----BEGIN CERTIFICATE----- or -----BEGIN RSA PRIVATE KEY-----).
  • File Paths: Use absolute paths (e.g., via path.resolve(__dirname, 'cert.pem')) to avoid runtime resolution errors if the application is executed from different working directories.