Detecting Client Browser Settings for Cookies and JavaScript

Published:
Updated:
Introduction and Prerequisites
This article describes methods for detecting whether a client browser accepts and returns HTTP cookies and whether the client browser runs JavaScript.  Most client browsers will, by default, be configured to use cookies and JavaScript, but some may not do that, and it may be important to your application design to be aware of these client-side factors as you build your web documents.  For example, most shopping carts rely on cookies to store a pointer to the contents of the cart.  If your client has cookies disabled, the shopping cart would not work.  Rather than simply fail, it would be a better design to be able to tell the client about the dependency.  Or you might have a gallery that used jQuery to provide an attractive client experience.  If you knew that JavaScript was disabled you would be able to adjust the gallery behavior. 

These concepts seems like common sense in application design, but the client/server relationship has a structure that hinders this common sense approach.  The order of request and response events is an important principle.  All communication is initiated by the client.  The client makes a request, the server-side scripts run, and the server-side scripts are complete before the response is sent to the browser.  As a result, any of the characteristics of the client browser that are not part of the request are invisible to the server-side scripts that create HTML documents.  And the server is a response-only device.  It cannot initiate communication with the client.  How, then, can we "look over the wall" to discern what the client browser will do with the cookies and JavaScript that we might send?

In order to understand the logic of these processes, you need to understand the stateless nature of HTTP protocols, and the order of events in a client/server system.  There are two articles here at E-E that can help with the background knowledge you'll need as you read this article.  If you're not familiar with these concepts, please take a moment to read these articles, then come back to this one.
HTTP Client/Server
PHP Sessions

Setting Up a Test Bed
Since we will want to be able to test our scripts, we will need a browser that can both accept and refuse cookies, and can enable and disable JavaScript.  We can make these settings in the Firefox browser.  At the time of this writing (November 2014), Firefox is currently at release 33+, and these instructions are applicable to a Windows version of that release.  Mozilla has a way of moving things around from release to release, and you may find in the future that the instructions are no longer directly applicable.  Nevertheless, the concepts will still be workable, and a Google search for these headlines will lead you to an implementation of the concepts.  Similar instructions for Internet Explorer and Google Chrome are included at the end of the article.

Enabling and Disabling Cookies in Firefox
The path for this process starts with the Firefox menu, hidden behind the three-bar menu button in the upper right of the browser window.  To enable cookies for your test, follow these steps:
1. Click the three-bar menu button to reveal the menu
2. Click "Options"
3. Click "Privacy"
4. Uncheck "Accept cookies from sites"
5. Click "Exceptions"
6. Type your base URL, for example, Experts-Exchange.com
7. Click "Allow for Session"
8. Close / OK the menu windows

To disable cookies, follow steps 1-5 above, then:
6. Scroll down to find and highlight your base URL
7. Click "Remove Site"
8. Close / OK the menu windows

After you're finished testing you may want to go back to step 4 and restore your preferred settings.

Enabling and Disabling JavaScript in Firefox
1. In the browser address bar, instead of a URL, type about:config
2. Dismiss any warning box that may appear
3. Scroll down the very long list of configuration options to find "javascript.enabled"
4. Click to highlight that line
5. Right-click to reveal a dialog box
6. Click "Toggle" to enable or disable JavaScript

After you've finished testing you may want to repeat the process to enable JavaScript.

Testing for Cookies
When a server sends a cookie to the client browser, the browser stores the cookie and returns the cookie with each subsequent HTTP request to the same URL.  This means that we must use a two-step process to first set and then detect a cookie.  We can accomplish both steps in a single script file, and we can package the two-step process into a one-line function call.  The function will return the client's IP address if cookies are in play, or boolean FALSE if not.

Our strategy uses the client IP address and a "digested" version of the browser name (HTTP_USER_AGENT) as part of a small temporary file name on our server.  If there is no cookie in the request, we write this file, set a cookie, and redirect the browser to our current URL.  This redirect will cause a new browser request that will reload the script.  Upon the reload, if the cookie is present in the request we know that the client is a well-behaved browser that will accept and return cookies.  If the cookie is not present, but the temporary file is present, then we know that the browser did not return the cookie.

This will be a dependable approach most of the time, since most of the time the client's IP address and browser will be unique and consistent for at least the duration of this (essentially instantaneous) pair of requests.  There may be edge cases where this approach might not work perfectly.  One example that comes to mind would be a setting where multiple client browsers share a single IP address.  If two identical browsers on the same IP address simultaneously visited the same same web page, and they had different settings for cookies, a race condition could ensue that might give a false indicator of the cookie status.
 
<?php // demo/cookie_check.php
                      /**
                       * This script tries to detect whether a browser is accepting and returning cookies
                       */
                      error_reporting(E_ALL);
                      
                      function browser_cookies()
                      {
                          // SETTINGS SHOULD ALLOW FOR SIMULTANEOUS ACCESS FROM MANY CLIENTS
                          $cookiename = 'cookie_check';
                          $client_agt = !empty($_SERVER['HTTP_USER_AGENT']) ? md5($_SERVER['HTTP_USER_AGENT']) : 'unknown';
                          $client_ipa = !empty($_SERVER['REMOTE_ADDR'])     ? $_SERVER['REMOTE_ADDR']          : '1.1.1.1';
                          $state_file = "cookie_check.$client_agt.$client_ipa.txt";
                      
                          // IF THERE IS A COOKIE OUR WORK IS DONE
                          $cookie_value = !empty($_COOKIE[$cookiename]) ? $_COOKIE[$cookiename] : FALSE;
                      
                          // IF THERE IS NO COOKIE
                          if (!$cookie_value)
                          {
                              // AND IF THE STATEFUL FILE EXISTS
                              if (file_exists($state_file))
                              {
                                  $cookie_value = FALSE;
                              }
                              // BUT IF THE STATEFUL FILE HAS NOT BEEN CREATED
                              else
                              {
                                  // CREATE THE STATEFUL FILE AND TRY TO SET A COOKIE
                                  file_put_contents($state_file, $client_ipa);
                                  setcookie($cookiename, $client_ipa);
                      
                                  // NOW FORCE A RELOAD OF THE WEB PAGE
                          	    header('Location: ' . $_SERVER['REQUEST_URI']);
                                  exit;
                              }
                          }
                      
                          // TIDY UP AFTER OURSELVES AND RETURN THE IP ADDRESS OR FALSE
                          @unlink($state_file);
                          return $cookie_value;
                      }
                      
                      
                      // USE CASE
                      if ($ip_addr = browser_cookies())
                      {
                          echo "THIS CLIENT ACCEPTS AND RETURNS COOKIES AT IP ADDRESS: $ip_addr";
                      }
                      else
                      {
                          echo "THIS CLIENT DOES NOT USE COOKIES";
                      }

Open in new window


Testing for JavaScript with Cookies
Conceptually this is nearly identical to testing for cookies, but there is one important protocol difference.  When we want to set a cookie and redirect the client browser to reload the web page, we can do so entirely in PHP without creating any browser output.  But we can't run any JavaScript without writing the JavaScript code to the client browser.  This means that the PHP header() function is unusable, since HTTP headers must come first and be complete before any browser output.  To get around this, we can use the meta-refresh tag in place of the header("Location: ...") function call.
 
<?php // demo/javascript_check.php
                      /**
                       * This script tries to detect whether a browser will run JavaScript
                       */
                      error_reporting(E_ALL);
                      
                      function browser_javascript()
                      {
                          // SETTINGS SHOULD ALLOW FOR SIMULTANEOUS ACCESS FROM MANY CLIENTS
                          $signalname = 'javascript';
                          $client_agt = !empty($_SERVER['HTTP_USER_AGENT']) ? md5($_SERVER['HTTP_USER_AGENT']) : 'unknown';
                          $client_ipa = !empty($_SERVER['REMOTE_ADDR'])     ? $_SERVER['REMOTE_ADDR']          : '1.1.1.1';
                          $state_file = "javascript_check.$client_agt.$client_ipa.txt";
                      
                          // IF THERE IS A JAVASCRIPT SIGNAL IN THE COOKIE OUR WORK IS DONE
                          $signal_value = !empty($_COOKIE[$signalname]) ? $_COOKIE[$signalname] : FALSE;
                      
                          // IF THERE IS NO JAVASCRIPT SIGNAL
                          if (!$signal_value)
                          {
                              // PREPARE A JAVASCRIPT SETCOOKIE TEST
                              $jstest = <<<EOJ
                      <script>
                      document.cookie = "$signalname=$client_ipa";
                      </script>
                      EOJ;
                      
                              // PREPARE A META-REFRESH TAG
                              $request = $_SERVER['REQUEST_URI'];
                              $refresh = <<<EOR
                      <meta http-equiv="refresh" content="0;URL='$request'" />
                      EOR;
                      
                              // IF THE STATE FILE EXISTS BUT THERE IS NO JAVASCRIPT SIGNAL
                              if (file_exists($state_file))
                              {
                                  $signal_value = FALSE;
                              }
                              // BUT IF THE STATEFUL FILE HAS NOT BEEN CREATED
                              else
                              {
                                  // CREATE THE STATEFUL FILE AND TRY TO SET A JAVASCRIPT COOKIE
                                  file_put_contents($state_file, $client_ipa);
                                  echo $jstest;
                      
                                  // NOW FORCE A RELOAD OF THE WEB PAGE
                            	    echo $refresh;
                                  exit;
                              }
                          }
                      
                          // TIDY UP AFTER OURSELVES AND RETURN THE IP ADDRESS OR FALSE
                          @unlink($state_file);
                          return $signal_value;
                      }
                      
                      
                      // USE CASE
                      if ($ip_addr = browser_javascript())
                      {
                          echo "THIS CLIENT RUNS JAVASCRIPT AT IP ADDRESS: $ip_addr";
                      }
                      else
                      {
                          echo "THIS CLIENT DOES NOT SUPPORT JS";
                      }

Open in new window

And now we have two PHP script functions that will let our main PHP scripts be aware of the current client settings for cookies and JavaScript.  But there is a cascade of dependencies here.  If the client does not accept and return cookies, we cannot use our browser_javascript() function to test for JavaScript. 

Testing for JavaScript Without Using Cookies
There is an HTML noscript tag that can be helpful.  This tag is used to wrap parts of the document that are to be evaluated by the browser if JavaScript is turned off.  One of the common uses of the noscript tag is to tell the client that he's missing a part of the web experience.  Example:

<noscript>
                         Your browsing experience will be much better with JavaScript enabled!
                      </noscript>

Open in new window


We can exploit the script and noscript tags to determine whether the client runs JavaScript.  Like the other methods used to test for cookies and JavaScript, we still need something in the HTTP request to tell us the status of the client browser.  In this example, we will add a single URL parameter to the end of the request string and redirect the browser to reload our script.  If JavaScript is turned off, the browser will run the noscript content, and our refreshed URL will include noJS.  Otherwise, the browser will run the script content, and our refreshed URL will include JS
 
<?php // demo/javascript_check_nocookie.php
                      /**
                       * This script tries to detect whether a browser will run JavaScript
                       */
                      error_reporting(E_ALL);
                      
                      function browser_javascript_nocookie()
                      {
                          $client_ipa = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '1.1.1.1';
                      
                          // IS THERE A SIGNAL STRING?
                          $nojs = isset($_GET['noJS']);
                          $js   = isset($_GET['JS']);
                          if ($nojs || $js)
                          {
                              if ($nojs) return FALSE;
                              if ($js)   return $client_ipa;
                          }
                          else
                          {
                              // PREPARE META-REFRESH TAGS WITH AN EXTRA URL PARAMETER
                              $requestjs = $_SERVER['REQUEST_URI'];
                              $requestno = $_SERVER['REQUEST_URI'];
                              if (strpos($_SERVER['REQUEST_URI'], '?'))
                              {
                                  $requestjs .= "&JS";
                                  $requestno .= "&noJS";
                              }
                              else
                              {
                                  $requestjs .= "?JS";
                                  $requestno .= "?noJS";
                              }
                              $refresh = <<<EOR
                      <noscript><meta http-equiv="refresh" content="0;URL='$requestno'" /></noscript>
                      <script>document.write("<meta http-equiv=\"refresh\" content=\"0;URL='$requestjs'\" />");</script>
                      EOR;
                      
                              // NOW FORCE A RELOAD OF THE WEB PAGE
                              echo $refresh;
                              exit;
                          }
                      }
                      
                      
                      if ($ip_addr = browser_javascript_nocookie())
                      {
                          echo "THIS CLIENT RUNS JAVASCRIPT AT IP ADDRESS: $ip_addr";
                      }
                      else
                      {
                          echo "THIS CLIENT DOES NOT SUPPORT JS";
                      }

Open in new window


Potential Issues
Is this a "best practices" way of determining whether a browser supports JavaScript? It works, but I think there can be some arguments against it. When you rewrite the URL with JS or noJS appended, you're exposing a data element that your client should not have to think about. If your client bookmarks the URL after the rewrite, the bookmark will permanently reflect the JS or noJS setting that was in play at the time the bookmark was created. The function will rely on this setting rather than testing for JavaScript each time. 

A Two-Step Redirect to Restore the Original URL
A pair of signal files can be incorporated into this logic to keep the stateful information between requests. This will let us rewrite the URL twice.  The initial page load will be ignorant of the state of JavaScript; it will prepare two signal files -- one to indicate support for JavaScript and one to indicate no JavaScript support. The first URL rewrite will trigger the JS or noJS determination in the browser and the resulting request will tell the server about JavaScript support via the added URL parameter.  With this information, the server can remove the complementary signal file.  The second rewrite will remove the JS or noJS signal string from the end of the URL.  The server can rely on the existence of the remaining signal file to know whether the browser has support for JavaScript.  In practice, this causes a flash in the browser address bar, as the URL is rewritten.  You can install this script and run it to see the process in action.
 
<?php // demo/javascript_check_nocookie.php
                      /**
                       * This script tries to detect whether a browser will run JavaScript
                       */
                      error_reporting(E_ALL);
                      
                      function browser_javascript_nocookie()
                      {
                          // SETTINGS SHOULD ALLOW FOR SIMULTANEOUS ACCESS FROM MANY CLIENTS
                          $client_agt = !empty($_SERVER['HTTP_USER_AGENT']) ? md5($_SERVER['HTTP_USER_AGENT']) : 'unknown';
                          $client_ipa = !empty($_SERVER['REMOTE_ADDR'])     ? $_SERVER['REMOTE_ADDR']          : '1.1.1.1';
                          $state_JS   = "js_check.JS.$client_agt.$client_ipa.txt";
                          $state_noJS = "js_check.noJS.$client_agt.$client_ipa.txt";
                      
                          // IS THERE A SIGNAL STRING IN THE URL?
                          $js   = isset($_GET['JS']);
                          $nojs = isset($_GET['noJS']);
                          if ($js || $nojs)
                          {
                              // REMOVE THE COMPLEMENTARY STATE FILE
                              if ($nojs)
                              {
                                  @unlink($state_JS);
                              }
                              elseif ($js)
                              {
                                  @unlink($state_noJS);
                              }
                      
                              // SET UP A REGULAR EXPRESSION TO RESTORE THE ORIGINAL URL
                              $rgx
                              = '#'         // REGEX DELIMITER
                              . '(&|\?)'    // AMPERSAND OR (ESCAPED) QUESTION MARK
                              . '(no)?'     // LITERAL STRING no, OPTIONAL
                              . 'JS'        // LITERAL STRING JS, REQUIRED
                              . '$'         // AT END OF STRING
                              . '#'         // REGEX DELIMITER
                              ;
                      
                              // REMOVE THE SIGNAL STRING FROM THE URL AND REFIRE THE REQUEST
                              $request = preg_replace($rgx, NULL, $_SERVER['REQUEST_URI']);
                              $refresh = <<<EOR
                      <meta http-equiv="refresh" content="0;URL='$request'" />
                      EOR;
                              echo $refresh;
                              exit;
                          }
                      
                          // THERE IS NO SIGNAL STRING IN THE URL
                          else
                          {
                              // DOES THE STATE FILE EXIST?
                              $js   = file_exists($state_JS);
                              $nojs = file_exists($state_noJS);
                              if ($nojs || $js)
                              {
                                  if ($nojs)
                                  {
                                      @unlink($state_noJS);
                                      $retval = FALSE;
                                  }
                                  elseif ($js)
                                  {
                                      @unlink($state_JS);
                                      $retval = $client_ipa;
                                  }
                                  return $retval;
                              }
                      
                              // THE STATE FILE DOES NOT EXIST
                              else
                              {
                                  // WRITE TWO STATE FILES
                                  file_put_contents($state_JS,   $client_ipa);
                                  file_put_contents($state_noJS, $client_ipa);
                      
                                  // PREPARE META-REFRESH TAGS WITH AN EXTRA URL PARAMETER
                                  $requestjs = $_SERVER['REQUEST_URI'];
                                  $requestno = $_SERVER['REQUEST_URI'];
                                  if (strpos($_SERVER['REQUEST_URI'], '?'))
                                  {
                                      $requestjs .= "&JS";
                                      $requestno .= "&noJS";
                                  }
                                  else
                                  {
                                      $requestjs .= "?JS";
                                      $requestno .= "?noJS";
                                  }
                      
                                  // REFIRE THE REQUEST WITH THE EXTRA URL PARAMETER
                                  $refresh = <<<EOR
                      <noscript><meta http-equiv="refresh" content="0;URL='$requestno'" /></noscript>
                      <script>document.write("<meta http-equiv=\"refresh\" content=\"0;URL='$requestjs'\" />");</script>
                      EOR;
                                  echo $refresh;
                                  exit;
                              }
                          }
                      }
                      
                      
                      // USE CASE
                      if ($ip_addr = browser_javascript_nocookie())
                      {
                          echo "THIS CLIENT RUNS JAVASCRIPT AT IP ADDRESS: $ip_addr";
                      }
                      else
                      {
                          echo "THIS CLIENT DOES NOT SUPPORT JS";
                      }

Open in new window


Overall, I think the best practices approach to the question of JavaScript is to design your site to work sensibly without JavaScript, and to use JavaScript (jQuery) to provide an enhanced user experience.

Summary
We have demonstrated convenient ways of getting information about the settings of the client's browser. These techniques will work dependably in almost all cases. They may give you better ways of creating a good browsing experience for your client community. The code snippets here have been tested in Firefox, Chrome and IE. You can copy them and install them on your server to see them in action.

Addendum
Further reading on the state of the art in client browser data storage (beyond cookies):
http://www.sitepoint.com/html5-browser-storage-past-present-future/

Enable/Disable Cookies in Internet Explorer 11
1. Click the gear in the upper right corner of the browser window
2. Click "Internet Options"
3. Click "Privacy" tab
4. Drag the slider to the top to disable cookies
5. Click "Default" to enable cookies

Enable/Disable JavaScript in Internet Explorer 11
1. Click the gear in the upper right corner of the browser window
2. Click "internet Options"
3. Click "Security" tab
4. Click "Custom Level"
5. Scroll down to "Scripting"
6. Select the appropriate setting under "Active scripting"

Find Cookies in Chrome 38
Type this into your browser address bar: chrome://settings/cookies

Enable/Disable Cookies and JavaScript in Chrome 38
https://support.google.com/accounts/answer/61416?hl=en
https://support.google.com/adsense/answer/12654?hl=en

References and Further Reading:
http://roy.gbiv.com/
https://www.ietf.org/rfc/rfc2616.txt
http://www.w3.org/TR/WCAG20-TECHS/H76.html
https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
http://en.wikipedia.org/wiki/Meta_refresh
http://www.w3schools.com/tags/ref_httpmethods.asp
http://www.w3schools.com/tags/tag_noscript.asp

Please give us your feedback!
If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!

 
1
23,600 Views

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.