Dealing with CloudFlare's: cf_chl_jschl_tk & cf_chl_captcha_tk?


The problem:

When my website is set to “I’m under attack” mode, once a user passes the CloudFlare screen they are redirected to my website with a large, and rather long query get parameter:


enter image description here

Why this is bad:

  • Ugly link shared around online – (I have seen this happen a few times already)
  • I cannot redirect the get parameters away – (They are not accessible to check if set to redirect away)
  • Ugly urls – (We spend lots of time writing clean/pretty urls for our applications)
  • POST data is set on the page – (you cannot refresh without ‘resubmitting’ the CF authentication)

Proposed solution:

The way I see to avoid this would be to check if the get parameters are set and then redirect back to the same page with the parameters removed. (Making sure not to lose any other query parameters if set)

I have written a function to achieve this:

function checkAndRemoveCloudFlareParams() {
    if (isset($_GET['__cf_chl_jschl_tk__']) && ! empty($_GET['__cf_chl_jschl_tk__'])
     || isset($_GET['__cf_chl_captcha_tk__']) && ! empty($_GET['__cf_chl_captcha_tk__'])) {

        $new_uri = '?';
        $uri = explode('?', $_SERVER['REQUEST_URI']);
        $uri = $uri[0];

        // Get any other params to put back on later
        foreach ($_GET as $key => $var) {
            if ($key !== '__cf_chl_jschl_tk__' && $key !== '__cf_chl_captcha_tk__') {
                $new_uri .= $key . '=' . $var . '&';

        if ($new_uri !== '?') {
            $new_uri = rtrim($new_uri, '&');
            $uri .= $new_uri;

        header('Location: ' . $uri);

When I test this locally by manually entering the _GET parameters it works. However when deployed up to my live site, the _GET query parameters are not there or available to access when loaded directly from CloudFlare. I believe CloudFlare adds the parameters after the page has been loaded? Maybe through Javascript push states(?) Has anyone else dealt with this before?

I have spoken to CloudFlare about this already and they said this is how it will work from now on as there were problems with their old system. Unfortunately these query parameters are very ugly and are making me start to twitch from annoyance of seeing it all the time 😉

Any advice on how to deal with these params and get rid of them? Kind regards


JS solution:

    var reg = /[\?&](__cf_chl_jschl_tk__|__cf_chl_captcha_tk__|__cf_chl_managed_tk__|__cf_chl_tk)=[^&]+/
    history.replaceState && reg.test( && history.replaceState(
      null, '', location.pathname +, '').replace(/^&/, '?') + location.hash

Updated in 2022 to deal with What exactly is the `#:~:text=` location hash in an URL? because it is not present in location.hash

Answered By – M-A-X

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published