Mutual TLS authentication

The network traffic initiated by Dialogflow for webhook requests is sent on a public network. To ensure that traffic is both secure and trusted in both directions, Dialogflow optionally supports Mutual TLS authentication (mTLS). With mTLS, both the client (Dialogflow) and the server (your webhook server) present a certificate during a TLS handshake, which mutually proves identity.

Requesting mTLS

To request mTLS:

  1. Prepare your webhook HTTPS server to request the client certificate during the TLS handshake.
  2. Your webhook server should verify the client certificate upon receiving it.
  3. Install a certificate chain for your webhook server, which can be mutually trusted by both client and server. You should use Google Trust Services CA 1O1 (GTS CA 1O1). GTS CA 1O1 uses the GlobalSign R2 root (GS Root R2), which is owned and controlled by Google Trust Services. You can download it from:

Demo Node.js server

The following is a Node.js demo server:

  1. Prepare a self signed certificate and pem file.
  2. Prepare the certificate chain file:
    curl | openssl x509 -inform der >> ca-crt.pem
    curl | openssl x509 -inform der >> ca-crt.pem
  3. Save the following sample as server.js:
    var https = require('https');
    const express = require('express')
    var fs = require('fs');
    var options = {
      // Specify the key file for the server
      key: fs.readFileSync('./server-key.pem'),
      // Specify the certificate file
      cert: fs.readFileSync('./server-crt.pem'),
      // Specify the Certificate Authority certificate
      ca: fs.readFileSync('./ca-crt.pem'),
      // Requesting the client to provide a certificate, to authenticate the user.
      requestCert: true,
      // As specified as "true", so no unauthenticated traffic
      // will make it to the specified route specified
      rejectUnauthorized: false
    var app = express();
    app.use(function (req, res, next) {
      if (!req.client.authorized) {
        //return res.status(401).send('Client cert failed. User is not authorized\n');
      // Examine the cert itself, and even validate based on that!
      var cert = req.socket.getPeerCertificate();
      if (cert.subject) {
        console.log('Client Certificate: ',cert);
        console.log('Client Certificate Common Name: '+cert.subject.CN);
        console.log('Client Certificate Location: '+cert.subject.L);
        console.log('Client Certificate Organization Name: '+cert.subject.O);
        console.log('Client Certificate Email Address: '+cert.subject.emailAddress);
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end("hello world from client cert\n");
    var listener = https.createServer(options, app).listen(4433, function () {
      console.log('Express HTTPS server listening on port ' + listener.address().port);
  4. Run server.js.
  5. Set requestCert to be true.
    curl -k
  6. Send the client certificate. SSL handshake successfully completes.
    curl -v -s -k --key client-key.pem --cert client-crt.pem https://localhost:4433
    openssl s_client -key client-key.pem -cert client-crt.pem -connect localhost:4433 -CAfile ca-crt.pem

Best Practice

To make sure the webhook requests are initiated from your own Dialogflow agents, you should verify the project ID in the resource names of the webhook requests (for example, session) in your webhook server.


If client certification fails (for example, no client certificate is sent from the client or the client certificate is not signed correctly), the TLS handshake fails and the session terminates.

Common error messages:

Error message Explanation
Failed to verify client's certificate: x509: certificate signed by unknown authority Dialogflow sends its client certificate to the external webhook, but the external webhook cannot verify it. This may be because the external webhook didn't install the CA chain correctly. Dialogflow's current CA is Google Trust Services CA 1O1. GTS CA 1O1 uses the GlobalSign R2 root, which is owned and controlled by Google Trust Services.