Getting client TLS version using Request



I’m currently looking at the following blog post that was written on the 4th May 2018 in regards to using Cloudflare Workers to inspect client TLS:

However when looking and trying to implement my own version of this code, I seem unable to access the CF part of the request object, and when attempting to use it seems to start the tlsVersion is undefined, Even though when inspecting the Request object I am able to see CF there, Though I have noticed this is empty.

Has anybody else had this issue or does anybody know a way around this?

Kind Regards


Hi @d.delay,

The object is only available in production. The preview (meaning both the playground and the workers editor in the dashboard) has technical limitations which prevent us from providing the complete production environment to worker scripts, and is one notable feature which is lacking.

Try deploying the following script on a route:

addEventListener('fetch', event => {
  event.respondWith(new Response(JSON.stringify(

In the preview, I see a blank page when triggering that worker. In production, I receive the following response body: {"tlsVersion":"TLSv1.2"}. Let me know if that works for you!



Hi Harris,

Thanks for your response!

How silly of me to assume this would work in the test environment DOH, of course there would be no TLS version as its all virtual!

I was still unable to get the script working from the blog post so re-wrote it to the below, Just in case anybody else gets stuck with this. I have confirmed this working in IE6 with TLS1, The IIS config is also included in case anybody struggles with this.

> addEventListener('fetch', event => {
>     //Pass Through on Exception thrown.
>     event.passThroughOnException()
>     //Respond with the Custom Headers so IIS can re-direct.
>     event.respondWith(addHeaders(event.request))
> })
> async function addHeaders(request) {
>   //TRY/CATCH for Pass Through
> 	try{
>   //Lets set TLS to non incase we are non-HTTPs
> 	var tls = "NONE"
>   //Lets grab the initial request headers from the client and copy them so we are able to modify these, As oldHeaders is ReadOnly
> 	let oldHeaders = request.headers
> 	let newHeaders = new Headers(oldHeaders)
>   //Check if the TLS version is available via the CloudFlare API.
> 	if({
>     //Set the TLS version if available
> 		tls =
> 	}
>     //Append the TLS version as a custom header attribute in the request we make to the server
>     newHeaders.append('X-Client-SSL-Protocol', tls)
>     //Create a request to the server with the modified headers and return the respose
>     return await fetch(request, { headers: newHeaders })
> 	}catch(ex){
>     //Throw an exception if error, This will then trigger the Pass Through/
> 		throw ex
> 	}
> }
> //IIS Config:
> /*
>         <rewrite>
>             <rules>
>                 <rule name="Re-Write TLSv1" enabled="true" stopProcessing="false">
>                     <match url="(.*)" />
>                     <conditions>
>                         <add input="{HTTP_X_Client_SSL_Protocol}" pattern="^TLSv1$" />
>                     </conditions>
>                     <action type="Rewrite" url="placeholder.html" />
>                 </rule>
>             </rules>
>         </rewrite>
> */


You’re very welcome, Damien! And the question was a good one, not silly at all.

One quick comment on the script you posted: you don’t need the try-catch block to make the passthrough work correctly. You can just let the exception propagate like normal. The reason the script in @zproser’s blog post needed a try-catch block was exclusively in order to report the error to Sentry. If you don’t need to report the error to such a service, then you can forgo the try-catch.