Abdullatif Eymash and Kathleen Juell
EnglishThe author selected the Apache Software Foundation to receive a donation as part of the Write for DOnations program.
IntroductionWebP is an open image format developed by Google in 2010 based on the VP8 video format. Since then, the number of websites and mobile applications using the WebP format has grown at a fast pace. Both Google Chrome and Opera support the WebP format natively, and since these browsers account for about 74% of web traffic, users can access websites faster if these sites use WebP images. There are also plans for implementing WebP in Firefox.
The WebP format supports both lossy and lossless image compression, including animation. Its main advantage over other image formats used on the web is its much smaller file size, which makes web pages load faster and reduces bandwidth usage. Using WebP images can lead to sizeable increases in page speed. If your application or website is experiencing performance issues or increased traffic, converting your images may help optimize page performance.
In this tutorial, you will use the command-line tool cwebp to convert images to WebP format, creating scripts that will watch and convert images in a specific directory. Finally, you’ll explore two ways to serve WebP images to your visitors.
PrerequisitesWorking with WebP images does not require a particular distribution, but we will demonstrate how to work with relevant software on Ubuntu 16.04 and CentOS 7. To follow this tutorial you will need:
A server set up with a non-root sudo user. To set up an Ubuntu 16.04 server, you can follow our Ubuntu 16.04 initial server setup guide. If you would like to use CentOS, you can set up a CentOS 7 server with our Initial Server Setup with CentOS 7 tutorial.
Apache installed on your server. If you are using Ubuntu, you can follow step one of How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 16.04. If you are using CentOS, then you should follow step one of How To Install Linux, Apache, MySQL, PHP (LAMP) stack On CentOS 7. Be sure to adjust your firewall settings to allow HTTP and HTTPS traffic.
mod_rewrite installed on your server. If you are using Ubuntu, you can follow our guide on How To Rewrite URLs with mod_rewrite for Apache on Ubuntu 16.04. On CentOS 7 mod_rewrite is installed and activated by default.
Step 1 — Installing cwebp and Preparing the Images DirectoryIn this section, we will install software to convert images and create a directory with images as a testing measure.
On Ubuntu 16.04, you can install cwebp, a utility that compresses images to the .webp format, by typing:
sudo apt-get updatesudo apt-get install webpOn CentOS 7, type:
sudo yum install libwebp-toolsTo create a new images directory called webp in the Apache web root (located by default at /var/www/html) type:
sudo mkdir /var/www/html/webpChange the ownership of this directory to your non-root user sammy:
sudo chown sammy: /var/www/html/webpTo test commands, you can download free JPEG and PNG images using wget. This tool is installed by default on Ubuntu 16.04; if you are using CentOS 7, you can install it by typing:
sudo yum install wgetNext, download the test images using the following commands:
wget -c "https://upload.wikimedia.org/wikipedia/commons/2/24/Junonia_orithya-Thekkady-2016-12-03-001.jpg?download" -O /var/www/html/webp/image1.jpgwget -c "https://upload.wikimedia.org/wikipedia/commons/5/54/Mycalesis_junonia-Thekkady.jpg" -O /var/www/html/webp/image2.jpgwget -c "https://cdn.pixabay.com/photo/2017/07/18/15/39/dental-care-2516133_640.png" -O /var/www/html/webp/logo.pngNote: These images are available for use and redistribution under the Creative Commons Attribution-ShareAlike license and the Public Domain Dedication.
Most of your work in the next step will be in the /var/www/html/webp directory, which you can move to by typing:
cd /var/www/html/webpWith the test images in place, and the Apache web server, mod_rewrite, and cwebp installed, you are ready to move on to converting images.
Step 2 — Compressing Image Files with cwebpServing .webp images to site visitors requires .webp versions of image files. In this step, you will convert JPEG and PNG images to the .webp format using cwebp. The general syntax of the command looks like this:
cwebp image.jpg -o image.webpThe -o option specifies the path to the WebP file.
Since you are still in the /var/www/html/webp directory, you can run the following command to convert image1.jpg to image1.webp and image2.jpg to image2.webp:
cwebp -q 100 image1.jpg -o image1.webpcwebp -q 100 image2.jpg -o image2.webpSetting the quality factor -q to 100 retains 100% of the image quality; if not specified, the default value is 75.
Next, inspect the size of the JPEG and WebP images using the ls command. The -l option will show the long listing format, which includes the size of the file, and the -h option will make sure that ls prints human readable sizes:
ls -lh image1.jpg image1.webp image2.jpg image2.webpOutput-rw-r--r-- 1 sammy sammy 7.4M Oct 28 23:36 image1.jpg-rw-r--r-- 1 sammy sammy 3.9M Feb 18 16:46 image1.webp-rw-r--r-- 1 sammy sammy 16M Dec 18 2016 image2.jpg-rw-r--r-- 1 sammy sammy 7.0M Feb 18 16:59 image2.webpThe output of the ls command shows that the size of image1.jpg is 7.4M, while the size of image1.webp is 3.9M. The same goes for image2.jpg (16M) and image2.webp (7M). These files are almost half of their original size!
To save the complete, original data of images during compression, you can use the -lossless option in place of -q. This is the best option to maintain the quality of PNG images. To convert the downloaded PNG image from step 1, type:
cwebp -lossless logo.png -o logo.webpThe following command shows that the lossless WebP image size (60K) is approximately half the size of the original PNG image (116K):
ls -lh logo.png logo.webpOutput-rw-r--r-- 1 sammy sammy 116K Jul 18 2017 logo.png-rw-r--r-- 1 sammy sammy 60K Feb 18 16:42 logo.webpThe converted WebP images in the /var/www/html/webp directory are about 50% smaller than their JPEG and PNG counterparts. In practice, compression rates can differ depending on certain factors: the compression rate of the original image, the file format, the type of conversion (lossy or lossless), the quality percentage, and your operating system. As you convert more images, you may see variations in conversion rates related to these factors.
Step 3 — Converting JPEG and PNG Images in a DirectoryWriting a script will simplify the conversion process by eliminating the work of manual conversion. We will now write a conversion script that finds JPEG files and converts them to WebP format with 90% quality, while also converting PNG files to lossless WebP images.
Using nano or your favorite editor, create the webp-convert.sh script in your user’s home directory:
nano ~/webp-convert.shThe first line of the script will look like this:
~/webp-convert.shfind $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \)This line has the following components:
find: This command will search for files within a specified directory.$1: This positional parameter specifies the path of the images directory, taken from the command line. Ultimately, it makes the location of the directory less dependent on the location of the script.-type f: This option tells find to look for regular files only.-iname: This test matches filenames against a specified pattern. The case-insensitive -iname test tells find to look for any filename that ends with .jpg (*.jpg) or .jpeg (*.jpeg).-o: This logical operator instructs the find command to list files that match the first -iname test (-iname "*.jpg") or the second (-iname "*.jpeg").(): Parentheses around these tests, along with the -and operator, ensure that the first test (i.e. -type f) is always executed.The second line of the script will convert the images to WebP using the -exec parameter. The general syntax of this parameter is -exec command {} \;. The string {} is replaced by each file that the command iterates through, while the ; tells find where the command ends:
~/webp-convert.shfind $1 -type f -and \( -iname "*.jpg" -o -iname "*.jpeg" \) \-exec bash -c 'commands' {} \;In this case, the -exec parameter will require more than one command to search for and convert images:
bash: This command will execute a small script that will make the .webp version of the file if it doesn’t exist. This script will get passed to bash as a string thanks to the -c option.'commands': This placeholder is the script that will make .webp versions of your files.The script inside 'commands' will do the following things:
Create a webp_path variable.Test whether or not the .webp version of the file exists.Make the file if it does not exist.The smaller script looks like this:
~/webp-convert.sh...webp_path=$(sed 's/\.[^.]*$/.webp/' output.log 2>&1 &At this point, you have converted the JPEG and PNG files in /var/www/html/webp to the WebP format, and set up watchers to do this work using the webp-watchers.sh script. It is now time to explore options to deliver WebP images to your website visitors.
Step 5 — Serving WebP Images to Visitors Using HTML ElementsIn this step, we will explain how to serve WebP images with HTML elements. At this point there should be .webp versions of each of the test JPEG and PNG images in the /var/www/html/webp directory. We can now serve them to supporting browsers using either HTML5 elements () or the mod_rewrite Apache module. We’ll use HTML elements in this step.
The element allows you to include images directly in your web pages and to define more than one image source. If your browser supports the WebP format, it will download the .webp version of the file instead of the original one, resulting in web pages being served faster. It is worth mentioning that the element is well-supported in modern browsers that support the WebP format.
The element is a container with and elements that point to particular files. If we use to point to a .webp image, the browser will see if it can handle it; otherwise, it will fall back to the image file specified in the src attribute in the element.
Let’s use the logo.png file from our /var/www/html/webp directory, which we converted to logo.webp, as an example with . We can use the following HTML code to display logo.webp to any browser that supports WebP format, and logo.png to any browser that does not support WebP or the element.
Create an HTML file located at /var/www/html/webp/picture.html:
nano /var/www/html/webp/picture.htmlAdd the following code to the web page to display logo.webp to supporting browsers using the element:
/var/www/html/webp/picture.htmlSave and close the file.
To test that everything is working, navigate to http://your_server_ip/webp/picture.html. You should see the test PNG image.
Now that you know how to serve .webp images directly from HTML code, let’s look at how to automate this process using Apache’s mod_rewrite module.
Step 6 — Serving WebP Images Using mod_rewriteIf we want to optimize the speed of our site, but have a large number of pages or too little time to edit HTML code, then Apache’s mod_rewrite module can help us automate the process of serving .webp images to supporting browsers.
First, create an .htaccess file in the /var/www/html/webp directory using the following command:
nano /var/www/html/webp/.htaccessThe ifModule directive will test if mod_rewrite is available; if it is, it can be activated by using RewriteEngine On. Add these directives to the .htaccess:
/var/www/html/webp/.htaccess RewriteEngine On# further directivesThe web server will make several tests to establish when to serve .webp images to the user. When a browser makes a request, it includes a header to indicate to the server what the browser is capable of handling. In the case of WebP, the browser will send an Accept header containing image/webp. We will check if the browser sent that header using RewriteCond, which specifies the criteria that should be matched in order to carry out the RewriteRule:
/var/www/html/webp/.htaccess...RewriteCond %{HTTP_ACCEPT} image/webpEverything should be filtered out but JPEG and PNG images. Using RewriteCond again, add a regular expression (similar to what we used in the previous sections) to match the requested URI:
/var/www/html/webp/.htaccess...RewriteCond %{REQUEST_URI} (?i)(.*)(\.jpe?g|\.png)$The (?i) modifier will make the match case-insensitive.
To check if the .webp version of the file exists, use RewriteCond again as follows:
/var/www/html/webp/.htaccess...RewriteCond %{DOCUMENT_ROOT}%1.webp -fFinally, if all previous conditions were met, RewriteRule will redirect the requested JPEG or PNG file to its associated WebP file. Notice that this will redirect using the -R flag, rather than rewrite the URI. The difference between rewriting and redirecting is that the server will serve the rewritten URI without telling the browser. For example, the URI will show that the file extension is .png, but it will actually be a .webp file. Add RewriteRule to the file:
/var/www/html/webp/.htaccess...RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1\.webp [L,T=image/webp,R]At this point, the mod_rewrite section in the .htaccess file is complete. But what will happen if there is an intermediate caching server between your server and the client? It could serve the wrong version to the end user. That is why it is worth checking to see if mod_headers is enabled, in order to send the Vary: Accept header. The Vary header indicates to caching servers (like proxy servers) that the content type of the document varies depending on the capabilities of the browser which requests the document. Moreover, the response will be generated based on the Accept header in the request. A request with a different Accept header might get a different response. This header is important because it prevents cached WebP images from being served to non-supporting browsers:
/var/www/html/webp/.htaccess... Header append Vary Accept env=REDIRECT_acceptFinally, at the end of the .htaccess file, set the MIME type of the .webp images to image/webp by using the AddType directive. This will serve the images using the right MIME type:
/var/www/html/webp/.htaccess...AddType image/webp .webpThis is the final version of our .htaccess file:
/var/www/html/webp/.htaccess RewriteEngine OnRewriteCond %{HTTP_ACCEPT} image/webp RewriteCond %{REQUEST_URI} (?i)(.*)(\.jpe?g|\.png)$RewriteCond %{DOCUMENT_ROOT}%1.webp -f RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1\.webp [L,T=image/webp,R]Header append Vary Accept env=REDIRECT_acceptAddType image/webp .webpNote: You can merge this .htaccess with another .htaccess file, if it exists. If you are using WordPress, for instance, you should copy this .htaccess file and paste it at the top of the existing file.
Let’s put what we have done in this step into practice. If you have followed the instructions in the previous steps, you should have logo.png and logo.webp images in /var/www/html/webp. Let’s use a simple tag to include logo.png in our web page. Create a new HTML file to test the setup:
nano /var/www/html/webp/img.htmlEnter the following HTML code in the file:
/var/www/html/webp/img.htmlSave and close the file.
When you visit the web page using Chrome by visiting http://your_server_ip/webp/img.html, you will notice that the served image is the .webp version (try opening the image in a new tab). If you use Firefox, you will get a .png image automatically.
ConclusionIn this tutorial, we have covered basic techniques for working with WebP images. We have explained how to use cwebp to convert files, as well as two options to serve these images to users: HTML5’s element and Apache’s mod_rewrite.
In order to customize the scripts from this tutorial, you can look at some of these resources:
To learn more about the features of the WebP format and how to use the conversion tools, see the WebP documentation.To see more details about the usage of element, see its documentation on MDN.To fully understand how to use mod_rewrite, see its documentation.Using the WebP format for your images will reduce file sizes by a considerable amount. This can lower bandwidth usage and make page loads faster, particularly if your web site uses a lot of images.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Learn more about our products
About the authorsAbdullatif Eymashauthor
Kathleen Juelleditor
Still looking for an answer?Ask a questionSearch for more helpWas this helpful? 10 CommentsThis textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign In or Sign Up to Comment1ac0ee2491ba43788e8b1a6ed3 • August 25, 2022This comment has been deleted
chrismarketing92 • April 17, 2021Hello,
Is it possible to follow this procedure on the nginx webserver? is there a tutorial about it?
thank you very much
tschopo • December 1, 2020The .htaccess code contains a logical error.
The Vary header is only set when the Client sends HTTP_ACCEPT image/webp.
But the Vary header should also be sent if the client does not accept image/webp because if not, the caching system will store png/jpg in the cache and overwrite the webp version even for the clients that have webp enabled…
You can observe the error when you have caching enabled on your server and follow these steps:
Step 1. Call website: Webp images are served
Step 2. Deactivate webp in Firefox (about:config, image.http.accept to “/”) and reload wesite: Webp images are not served (as expected)
Step 3. Reactivate webp in Firefox (about:config, image.http.accept to “image/webp,/”) and reload website: webp images are still not served
The Vary header should be sent for all images, not only when the client accepts webp:
Header append Vary Accept Woosung Choi • May 22, 2020Thank you!How can I convert image.jpg to image.jpg.webp?
Karim Hosein • April 11, 2020This does not work for my use case, Apache2 2.4.41 running on Ubuntu 18.04.4 (64-bit) with PHP 7.4.4, when aliased directories are used. I really do not know if it did. First time trying it, and it does not work.
Yes, I emptied my cache first. Yes, I restarted the server first. Still always getting the PNG or JPEG JFIF file, instead of the WebP files. Yes, both browsers understand image/webp. I can load a WebP file directly. Yes, I did check the files served, (size and nature), and they are the big JPEG JFIF or PNG files, and not the smaller WebP files. I even had a different image with the same filename, ( testimg.jpg and testimg.webp, but of different actual images), so that I can tell them apart. (Both images are fuchsia text on a dark blue/green gradient. In one, the text says, “This is a JPEG JFIF image testimg.jpg”, (with the text box on a blue background), while the other says, “This is a WebP image testimg.webp”, (with the text box on a transparent bg).
Indeed,
works with both browsers, to serve the WebP files specified. If the two first lines are removed, I get the JPEG files.
After intense reading, the issue has become clear. This solution only works with images within the document root, and sub-directories. On my server, I have several sites, and many of my images are served from a common image directory, in an aliased location.
[BEGIN unnecessary detail for clarity’s sake ]
So my server root, ($SR), is at [ /var/www ], with my main document root, ($DR), [ {http}://Phenobuntu/ ], at [ /var/www/html ], and I have other sites at [ $SR/wp ], [ $SR/drpl ], [ $SR/Projects/GanttProject ]. To top it off, the last three sites are Aliased as, [ {http}://wpbuntu/ ], [ {http}://drplbuntu/ ], and [ {http}://gnttbuntu/ ] respectively.In addition to this, I have my common CSS, PHP scripts, and images, in different directories, as, [ $SR/src/css ], [ $SR/src/theme ], and [ $SR/src/image ], respectively, (so as to maintain a consistent look across sites). These are aliased as, [ /css ], [ /theme ], and, [ / image], respectively, for each site.
Therefore, [ {http}://Phenobuntu/image/testimg.jpg ], will reference precisely the same image as, [ {http}://wpbuntu/image/testimg.jpg ], [ {http}://drplbuntu/image/testimg.jpg ], and [ {http}://gnttbuntu/image/testimg.jpg ] but, [ {http}://phenobuntu/index.html ], [ {http}://wpbuntu/index.html ], [ {http}://drplbuntu/index.html ], and [ {http}://gnttbuntu/index.html ], are all different files.
This solution, (as with all other solutions I have tried), all assume that all images are being served from the same directory —or sub-directory— as ${DOCUMENT_ROOT}. In my case, this is not so. It will work for files in the [ wpbuntu/wp-content/uploads/ ] directories and subs, or at, [ phenobuntu/iso/image/ ] but not for images at [ wpbuntu/image/ ] nor, [ phenobuntu/image/ ] directories and subs, et al.
[/END unnecessary detail for clarity’s sake ]
So I needed to come up with a solution which works globally, for aliased directories. That is why the solution was not working for me. The solution was incomplete for all use cases.
This was the solution I figured out for Apache2 version ≥2.4.35, where aliases are being used. The code has intensive internal documentation.
# BEGIN WebP replacement#Backward compatibility ruleset for#rewriting image.jpeg, image.jpg, or image.png to image.webp#when and only when image.webp exists.#This particular script IS case-sensitive, and will not work#for image.JPEG, image.JPG, image.PNG, or iMaGe.JpEg.#It will work, however, to replace, iMaGe.jpeg, with iMaGe.webp.#In other words, the filename MUST be a case-sensitive match, AND#the file suffix MUST be lower-case.#(This assumes that your server is case- sensitive. Some servers#may be set to case-insensitive mode. In that case, the rule will#still work.Options Indexes FollowSymLinksRequire all granted# We can possibly assume that mod_rewrite is installed and enabled.# This can be a dangerous assumption, and ought to be tested# with conditional clause.# I was testing, and certain options do not work within in statement. # I live on the edge! (I know my server).# Please un-comment this line. If it does not work, AND you are certain# that mod_rewrite is installed, comment it.# Turn on rewrite engine.RewriteEngine on# Ensure this matches your document root. (Usually optional).RewriteBase /# Do we care if the JPEG JFIF or PNG file exists? (I do not)!# Many CMS themes only allow a JPEG or PNG file in certain places.# This ensures that if you cannot tell the theme to use a WebP file, # use a JPEG file in the theme, and the WebP file will be served.# Besides, if the original file did not exist, a 404 error would # have been returned. May as well get an image instead.# If we care that the JPEG/PNG exists, # (althoug it is not needed since we have a WebP replacement),# then uncomment the next line.#RewriteCond %{CONTEXT_DOCUMENT_ROOT}/$1.$2 -f# We re-direct only if the browser accepts WebP images.RewriteCond %{HTTP_ACCEPT} image/webp# We re-direct only if the WebP equivalent exists.# If one has neglected to upload the replacement file, # no substitution will be made.RewriteCond %{CONTEXT_DOCUMENT_ROOT}/$1.webp -f# The first rule replaces the [ image.jpe?g|png ] with [ image.webp ]# in the browser interface, and requires another server request.# The second rule will leave the original request in the browser,# but still serve the WebP file.# Un-comment only one rewrite rule!RewriteRule ^(.*)\.(jpe?g|png)$ %{CONTEXT_PREFIX}/$1.webp [R,L,T=image/webp]#RewriteRule ^(.*)\.(jpe?g|png)$ %{CONTEXT_PREFIX}/$1.webp [L,T=image/webp]# End the statement, if it was invoked.# Comment this line if the conditional was not used.# %{CONTEXT_PREFIX} and %{CONTEXT_DOCUMENT_ROOT} are new Apache2# version ≥ 2.4.35 variables, which may not exists, or be the same thing,# on older/other HTTP servers. These variables somewhat take into consideration# the use of Aliases, and other types of server-config/site-config# directory re-directs. # END WebP replacementThis .htaccess file has to go in all document root directories involved. That is, in my case, I need to put it in [ $SR/html ], [ $SR/wp ], [ $SR/drpl ], [$SR/joomla ], [ $SR/Projects/GanntProject ], and [ SR$/src/image ].
For existing redirects in existing .htaccess files, place the snippet at the top of the file, to occur before other redirects. E.g., for Wordpress, place this snippet above the line marked, “# BEGIN WordPress” in the .htaccess file.
jesusdelagarza • July 24, 2019Hi,
Nice and helpful article, thank you!
What will happen if we don’t convert previous images and we just want to use it for new images, so if a previous jpg image doesn’t have the any related webp image, will this code will still server the jpg image?
Thank you!
Bjørn Rosell • September 23, 2018Also check out this solution:https://github.com/rosell-dk/webp-convert/blob/master/docs/webp-on-demand/webp-on-demand.md
Bjørn Rosell • September 23, 2018To make the Vary header work on LiteSpeed too, do this:
# Apache appends "REDIRECT_" in front of the environment variables, but LiteSpeed does not.# These next line is for Apache, in order to set environment variables without "REDIRECT_"SetEnvIf REDIRECT_accept 1 accept=1# Make CDN caching possible.# The effect is that the CDN will cache both the webp image and the jpeg/png image and return the proper# image to the proper clients (for this to work, make sure to set up CDN to forward the "Accept" header)Header append Vary Accept env=acceptntk • April 26, 2018Hi,Very helpful article - Thank you!Can you add all the steps to achieve the same redirect (.jpg to .webp) with Nginx?As you know it is not that easy to convert an .htaccess file to nginx syntax.regardsNikolay Kabaivanov
arpitsadhanapal • April 18, 2018This comment has been deleted
This work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.