The Save-Data
client hint request
header
available in Chrome, Opera, and Yandex browsers lets developers deliver lighter,
faster applications to users who opt-in to data saving mode in their browser.
The need for lightweight pages
Everyone agrees that faster and lighter web pages provide a more satisfying user experience, allow better content comprehension and retention, and deliver increased conversions and revenue. Google research has shown that "…optimized pages load four times faster than the original page and use 80% fewer bytes. Because these pages load so much faster, we also saw a 50% increase in traffic to these pages."
And, although the number of 2G connections is finally on the decline, 2G was still the dominant network technology in 2015. The penetration and availability of 3G and 4G networks is growing rapidly, but the associated ownership costs and network constraints are still a significant factor for hundreds of millions of users.
These are strong arguments for page optimization.
There are alternative methods for improving site speed without direct developer involvement, such as proxy browsers and transcoding services. Although such services are quite popular, they come with substantial drawbacks — simple (and sometimes unacceptable) image and text compression, inability to process secure (HTTPS) pages, only optimizing pages visited via a search result, and more. The very popularity of these services is itself an indicator that web developers are not properly addressing the high user demand for fast and light applications and pages. But reaching that goal is a complex and sometimes difficult path.
The Save-Data
request header
One fairly straightforward technique is to let the browser help, using the
Save-Data
request header. By identifying this header, a web page can customize
and deliver an optimized user experience to cost- and performance-constrained
users.
Supported browsers (below) allow the user to enable a *data saving- mode that gives the browser permission to apply a set of optimizations to reduce the amount of data required to render the page. When this feature is exposed, or advertised, the browser may request lower resolution images, defer loading of some resources, or route requests through a service that applies other content-specific optimizations such as image and text resource compression.
Browser support
- Chrome 49+ advertises
Save-Data
when the user enables the "Data Saver" option on mobile, or the "Data Saver" extension on desktop browsers. - Opera 35+ advertises
Save-Data
when the user enables "Opera Turbo" mode on desktop, or the "Data savings" option on Android browsers. - Yandex 16.2+ advertises
Save-Data
when Turbo mode is enabled on desktop or mobile browsers.
Detecting the Save-Data
setting
To determine when to deliver the "light" experience to your users, your
application can check for the Save-Data
client hint request header. This
request header indicates the client's preference for reduced data usage due to
high transfer costs, slow connection speeds, or other reasons.
When the user enables the data saving mode in their browser, the browser appends
the Save-Data
request header to all outgoing requests (both HTTP and HTTPS).
As of this writing, the browser only advertises one *on- token in the header
(Save-Data: on
), but this may be extended in the future to indicate other user
preferences.
Additionally, it's possible to detect if Save-Data
is turned on in JavaScript:
if ('connection' in navigator) {
if (navigator.connection.saveData === true) {
// Implement data saving operations here.
}
}
Checking for the presence of the connection
object within the navigator
object is vital, as it represents the Network Information API, which is only
implemented in Chrome, Chrome for Android, and Samsung Internet browsers. From
there, you only need to check if navigator.connection.saveData
is equal to
true
, and you can implement any data saving operations in that condition.
If your application uses a service
worker, it can
inspect the request headers and apply relevant logic to optimize the experience.
Alternatively, the server can look for the advertised preferences in the
Save-Data
request header and return an alternate response — different
markup, smaller images and video, and so on.
Implementation tips and best practices
- When using
Save-Data
, provide some UI devices that support it and allow users to easily toggle between experiences. For example:- Notify users that
Save-Data
is supported and encourage them to use it. - Allow users to identify and choose the mode with appropriate prompts and intuitive on/off buttons or checkboxes.
- When data saving mode is selected, announce and provide an easy and obvious way to disable it and revert back to the full experience if desired.
- Notify users that
- Remember that lightweight applications are not lesser applications. They don't
omit important functionality or data, they're just more cognizant of the
involved costs and the user experience. For example:
- A photo gallery application may deliver lower resolution previews, or use a less code-heavy carousel mechanism.
- A search application may return fewer results at a time, limit the number of media-heavy results, or reduce the number of dependencies required to render the page.
- A news-oriented site may show fewer stories, omit less popular categories, or provide smaller media previews.
- Provide server logic to check for the
Save-Data
request header and consider providing an alternate, lighter page response when it is enabled — e.g., reduce the number of required resources and dependencies, apply more aggressive resource compression, etc.- If you're serving an alternate response based on the
Save-Data
header, remember to add it to the Vary list —Vary: Save-Data
— to tell upstream caches that they should cache and serve this version only if theSave-Data
request header is present. For more details, see the best practices for interaction with caches.
- If you're serving an alternate response based on the
- If you use a service worker, your application can detect when the data saving
option is enabled by checking for the presence of the
Save-Data
request header, or by checking the value of thenavigator.connection.saveData
property. If enabled, consider whether you can rewrite the request to fetch fewer bytes, or use an already fetched response. - Consider augmenting
Save-Data
with other signals, such as information about the user's connection type and technology (see NetInfo API). For example, you might want to serve the lightweight experience to any user on a 2G connection even ifSave-Data
is not enabled. Conversely, just because the user is on a "fast" 4G connection doesn't mean they aren't interested in saving data — for example, when roaming. Additionally, you could augment the presence ofSave-Data
with theDevice-Memory
client hint to further adapt to users on devices with limited memory. User device memory is also advertised in thenavigator.deviceMemory
client hint.
Recipes
What you can achieve via Save-Data
is limited only to what you can come up
with. To give you an idea of what's possible, let's run through a couple of use
cases. You may come up with other use cases of your own as you read this, so
feel free to experiment and see what's possible!
Checking for Save-Data
in server side code
While the Save-Data
state is something you can detect in JavaScript via the
navigator.connection.saveData
property, detecting it on the server side is
sometimes preferable. JavaScript can fail to execute in some cases. Plus,
server side detection is the only way to modify markup before it's sent to the
client, which is involved in some of Save-Data
s most beneficial use cases.
The specific syntax for detecting the Save-Data
header in server side code
depends on the language used, but the basic idea should be the same for any
application back end. In PHP, for example, request headers are stored in the
$_SERVER
superglobal
array at indexes
starting with HTTP_
. This means you can detect the Save-Data
header by
checking the existence and value of the $_SERVER["HTTP_SAVE_DATA"]
variable
like so:
// false by default.
$saveData = false;
// Check if the `Save-Data` header exists and is set to a value of "on".
if (isset($_SERVER["HTTP_SAVE_DATA"]) && strtolower($_SERVER["HTTP_SAVE_DATA"]) === "on") {
// `Save-Data` detected!
$saveData = true;
}
If you place this check before any markup is sent to the client, the $saveData
variable will contain the Save-Data
state, and will be available anywhere for
use on the page. With this mechanism illustrated, let's look a few examples of
how we can use it to limit how much data we send to the user.
Serve low resolution images for high resolution screens
A common use case for images on the web involves serving images in sets of two:
One image for "standard" screens (1x), and another image that's twice as large
(2x) for high resolution screens (e.g., Retina
Display). This class of high
resolution screens is not necessarily limited to high end devices, and is
becoming increasingly common. In cases where a lighter application experience is
preferred, it might be prudent to send lower resolution (1x) images to these
screens, rather than larger (2x) variants. To achieve this when the Save-Data
header is present, we simply modify the markup we send to the client:
if ($saveData === true) {
// Send a low-resolution version of the image for clients specifying `Save-Data`.
?><img src="butterfly-1x.jpg" alt="A butterfly perched on a flower."><?php
}
else {
// Send the usual assets for everyone else.
?><img src="butterfly-1x.jpg" srcset="butterfly-2x.jpg 2x, butterfly-1x.jpg 1x" alt="A butterfly perched on a flower."><?php
}
This use case is a perfect example of how little effort it takes to accommodate
someone who is specifically asking you to send them less data. If you don't like
modifying markup on the back end, you could also achieve the same result by
using a URL rewrite module such as Apache's
mod_rewrite
. There
are examples of how to achieve
this with
relatively little configuration.
You could also extend this concept to CSS background-image
properties by
simply adding a class to the <html>
element:
<html class="<?php if ($saveData === true): ?>save-data<?php endif; ?>">
From here, you can target the save-data
class on the <html>
element in your
CSS to change how images are delivered. You could send low resolution background
images to high resolution screens as shown in the above HTML example, or omit
certain resources altogether.
Omit non-essential imagery
Some image content on the web is simply non-essential. While such imagery can
make for nice asides to content, they may not be desirable by those trying to
squeeze all they can out of metered data plans. In what is perhaps the simplest
use case of Save-Data
, we can use the PHP detection code from earlier and omit
non-essential image markup altogether:
<p>This paragraph is essential content. The image below may be humorous, but it's not critical to the content.</p>
<?php
if ($saveData === false) {
// Only send this image if `Save-Data` hasn't been detected.
?><img src="meme.jpg" alt="One does not simply consume data."><?php
}
This technique can certainly have a pronounced effect, as you can see in the figure below:
Of course, omitting images isn't the only possibility. You can also act on
Save-Data
to forego sending other non-critical resources, such as certain
typefaces.
Omit non-essential web fonts
While web fonts don't usually make up nearly as much of a given page's total payload as images often do, they're still quite popular. They don't consume an insignificant amount of data, either. Furthermore, the way browsers fetch and render fonts is more complicated than you might think, with concepts such as FOIT, FOUT, and browser heuristics making rendering a nuanced operation.
It might stand to reason then that you might want to leave out non-essential web
fonts for users who want leaner user experiences. Save-Data
makes this a
reasonably painless thing to do.
For example, let's say you've included Fira
Sans from Google
Fonts on your site. Fira Sans is an excellent body
copy font, but maybe it isn't so crucial to users trying to save data. By adding
a class of save-data
to the <html>
element when the Save-Data
header is
present, we can write styles that invoke the non-essential typeface at first,
but then opts out of it when the Save-Data
header is present:
/* Opt into web fonts by default. */
p,
li {
font-family: 'Fira Sans', 'Arial', sans-serif;
}
/* Opt out of web fonts if the `save-Data` class is present. */
.save-data p,
.save-data li {
font-family: 'Arial', sans-serif;
}
Using this approach, you can leave the <link>
snippet from Google Fonts in
place, because the browser speculatively loads CSS resources (including web
fonts) by first applying styles to the DOM, and then checking if any HTML
elements invoke any of the resources in the style sheet. If someone happens by
with Save-Data
on, Fira Sans will never load because the styled DOM never
invokes it. Arial will kick in, instead. It's not as nice as as Fira Sans, but
it may be preferable to those users trying to stretch their data plans.
Summary
The Save-Data
header does not have much nuance; it is either on or off, and
the application bears the burden of providing appropriate experiences based on
its setting, regardless of the reason.
For example, some users might not allow data saving mode if they suspect there will be a loss of app content or function, even in a poor connectivity situation. Conversely, some users might enable it as a matter of course to keep pages as small and simple as possible, even in a good connectivity situation. It's best for your app to assume that the user wants the full and unlimited experience until you have a clear indication otherwise via an explicit user action.
As site owners and web developers, let's take on the responsibility of managing our content to improve the user experience for data- and cost-constrained users.
For more detail on Save-Data
and excellent practical examples, see Help Your
Users Save Data
.