Skip to main content
menu

BMT Systems

  WEB & JAVASCRIPT  

Mobile Navigation Controls

Detecting WebP Support and Deploying WebP Images Efficiently

Thomas M. Brodhead

Overview

Browser support for WebP, Google’s preferred image format, is limited to Chrome, Opera, and a handful of browsers with a smaller market share. Lighthouse, the performance auditor built into Chrome’s Developer’s Tools, will prompt users to switch to WebP when an image size could be reduced with minimal loss of fidelity.

Problematic for the web developer is how to support this format, especially provided that its browser support is mainly limited to Chrome and Opera. JavaScript-based solutions, such as using Modernizr or a service worker routine, are complicated and come with a performance penalty.

The ideal solution would serve HTML to the browser that (1) references WebP images when WebP is supported and (2) references JPGs or PNGs when WebP is not supported. This, in fact, may be done. The solution is server-side for static HTML, and the solution requires conditional server-side code and conditional JavaScript editing of the HTML when it is served dynamically, e.g., in an SPA.

What follows is a six-step procedure that will allow your sites to toggle between standard image formats and WebP on supporting browsers. It employs simple algorithms on the server and browser side.

Step 1: Create WebP Images

Google supplies free software for converting images to WebP format. Download, install, and add it to your tool arsenal. Then, for every JPG, PNG, or similar non-WebP image required for a page, create a corresponding WebP image that is stored in the same folder as the standard images. Every image will therefore be duplicated twice, and originals and WebP duplicates will be located in the same directory.

Step 2: Determine WebP Support in the HTTP_ACCEPT Header

In the server code receiving the HTTP request, examine the HTTP_ACCEPT header (e.g., $_SERVER['HTTP_ACCEPT'] in PHP). It will indicate whether the browser can display WebP with "image/webp" in its response, e.g.:

text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

A PHP routine for detecting WebP support might therefore be:

brightness_high content_paste
// PHP:
// Assume no support...
$webp_support = false;
if (isset($_SERVER["HTTP_ACCEPT"])) {
    if (strpos($_SERVER["HTTP_ACCEPT"], "image/webp") > 0) {
        $webp_support = true;
    }
}

Step 3: Switch Image Extensions to WebP when Serving Static HTML

If you are serving static HTML—i.e., HTML that will not be updated by JavaScript in an SPA—then merely replace every reference to .JPG or .PNG in the HTML with .WEBP before sending the HTML to the browser. There’s a catch, though: the text “.jpg” may occur in the actual text of the site. So, you may not simply replace any .JPG text with .WEBP in pell-mell fashion: you will need to isolate “.jpg” in the src attributes of element tags. This can be accomplished with regex, such as in this PHP routine:

brightness_high content_paste
// We wish to capture '.jpg' in element tags ONLY:
// $1 = (<[^>]+) --> start with '<' and is followed by any character except '>'
// $2 = (src=[^>]+) --> start with 'src' and is followed by any character except '>'
// $3 = (\.jpg) --> is '.jpg'
$pattern = '/(<[^>]+)(src=[^>]+)(\.jpg)/mi';
$replacement = '$1$2.webp';
$html = preg_replace($pattern, $replacement, $html);

Step 4: Create a Custom data-webp-support Meta Attribute in the HEAD

If the HTML is being updated using JavaScript, perhaps in an SPA, let the HTML retrieved from the server reference JPGs or PNGs by default, but record in the HTML itself whether the browser can render WebP. That “memo” can thereafter be referenced by the JavaScript. This avoids expensive JavaScript routines that validate WebP support by creating invisible base64 images on-the-fly, a DOM manipulation that comes with a performance cost such as a forced reflow.

So, to record WebP support indicated by the server-side HTTP_ACCEPT header, we’ll create a custom attribute named data-webp-support on a web_author <meta> tag (this uses the technique outlined in Getting Your HEAD in Order: Custom Data). Then we’ll write true or false as the value of data-webp-support using the results of the server logic (stored in $meta_data_webp_support in our earlier PHP example).

Thus, if WebP is supported, we’ll set data-webp-support=false. If WebP is not supported, we’ll set data-webp-support=false. The result will be a custom <meta> element that will contain at least the data-webp-support attribute, if not others that you’ve found useful (and this technique may inspire similar solutions to other thorny problems):

brightness_high content_paste
<meta name=web_author content="Thomas M. Brodhead" data-webp-support=true>

Step 5: Read and Store the data-webp-support Attribute Value in JavaScript

Next, write a JavaScript routine that reads the value of the data-webp-support attribute and stores it in a dedicated variable. The variable should be accessible throughout the JavaScript routine that fetches HTML and injects it into the site. A sample routine would be:

brightness_high content_paste
// assume no WebP support, revise if the HTML indicates otherwise:
var webpSupport = 'false';
Array.prototype.forEach.call(document.getElementsByTagName('META'), function (element) {
    if (element.hasAttribute('data-webp-support')) {
        webpSupport = element.getAttribute('data-webp-support');
    }
});

Now we have a webpSupport JavaScript variable that indicates whether the client’s browser can render WebP images. Importantly, it was created without risking a forced reflow of the DOM.

Step 6: Use the Stored Value to Toggle Image Extensions

Next, when AJAXing content in and out of your site or SPA, reference the webpSupport variable and conditionally replace .JPG (or .PNG, or both) extensions in the src attributes of elements with .WEBP, using the same kind of regex logic as applied on the server:

brightness_high content_paste
// Replace references to JPG with WEBP if that format is supported (indicated in data-webp-support attribute in charset META element)
// However, we wish to capture '.jpg' in element tags ONLY:
//
// regex = /(<[^>]+)(src=[^>]+)(\.jpg)/gmi
//
// $1 = (<[^>]+) --> start with '<' and is followed by any character except '>'
// $2 = (src=[^>]+) --> start with 'src' and is followed by any character except '>'
// $3 = (\.jpg) --> is '.jpg'
// gmi = global, multi-line, case-insensitive
//
if (webpSupport === 'true') {
    response = response.replace(/(<[^>]+)(src=[^>]+)(\.jpg)/gmi, '$1$2.webp');
} else {
    response = response.replace(/(<[^>]+)(src=[^>]+)(\.webp)/gmi, '$1$2.jpg');
}

Putting it all together: your server detects WebP support from the header it receives, and then writes it to the HTML in a data-webp-support attribute in a <meta> element in the <head>. The JavaScript can then reference that DOM attribute to conditionally switch between default JPGs (or PNGs) and WebP when retrieving and writing HTML to the browser. So long as the default images and the WebP images are kept in the same directory, the site will retrieve them without any additional difficulty.

Aside