Skip to content
SnapFrom

The Request That Describes Your Device

· · 6 min read

A page can adapt to your device. The usual assumption is that it does this with JavaScript: the script checks the screen, reads navigator, times a download, and decides what to load next.

Some of it happens earlier than that. Before a line of the page’s own JavaScript runs, the browser can already have told the server roughly how much memory your device has, roughly how fast your connection is, and whether you have asked for less data. Those facts travel as HTTP request headers, which go out ahead of the response that would carry any script.

The values are deliberately imprecise, and the imprecision is the interesting part. Each one was blunted by the people who shipped it, because each one is a way to recognise you.

The one that travels without being asked

Most of these signals appear only if the server asks for them first. Save-Data is the exception.

MDN describes it as a “low entropy hint, and hence may be sent by the client even if not requested by the server”[1]. If the reader has switched on a data-saver mode, every request their browser makes carries Save-Data: on, whether or not anything is listening for it.

The meaning is narrow: on is “explicit user opt-in into a reduced data usage mode on the client”[1]. It is a stated preference, not a measurement. But the list of what a server may do in response is not narrow. MDN’s own examples are “smaller image and video resources, different markup and styling, disabled polling and automatic updates, and so on”[1]. Different markup is a different page.

There is a caching detail that shows how load-bearing it is meant to be. A response that changes with Save-Data has to declare that in a Vary header, otherwise the reader “is not served a lower-quality image from the cache when the Save-Data header is no longer present (e.g., after having switched from cellular to Wi-Fi)”[1]. Without that care, the downgraded version sticks around after the reason for it is gone.

The rest need one line of invitation

The device and network hints work differently. The server opts in once, by sending an Accept-CH response header that names the ones it wants[4]. From then on the browser attaches them to later requests to that origin.

The set is short, and each field is a single value:

  • Sec-CH-Device-Memory: 1 - approximate RAM in gigabytes[4].
  • Downlink: 1.7 - estimated bandwidth in Mbps[5].
  • RTT: 125 - estimated round-trip time in milliseconds, and this one “includes server processing time, unlike transport layer RTT”[6].
  • ECT: 2g - one of slow-2g, 2g, 3g, 4g[7].

None of that is JavaScript. It is header text on the request for the HTML, sent before the HTML arrives. By the time the page’s own code runs, the server has already had its chance to choose which page to send based on it.

Rounded on purpose

Every one of those numbers is quantised, and the specifications are unusually plain about why.

Device memory is “imprecise to curtail fingerprinting”[2]. The real figure is rounded to the nearest power of two and divided by 1024, then “clamped within lower and upper bounds to protect the privacy of owners of very low-memory or high-memory devices”[2]. The W3C draft is blunter still: the value is “rounded to a single significant bit”[3]. MDN’s illustration is a browser that “does not report below 2 or above 32”, leaving the value as one of 2, 4, 8, 16, 32[2]. The clamp means a high-memory machine reports the ceiling whatever its real RAM, and two machines with different amounts of memory routinely report the same figure.

Downlink is “rounded to the nearest 25 kilobits”, and MDN states directly that the rate “may be used as a fingerprinting variable, so values for the header are intentionally coarse to reduce the potential for its misuse”[5]. RTT is “rounded to the nearest 25 milliseconds to prevent fingerprinting”[6].

The shape is the same every time. The people adding the feature classified their own feature as a way to track people, and shipped a deliberately worse version of it rather than not shipping it.

effectiveType is a guess wearing four labels

ECT, and its JavaScript twin navigator.connection.effectiveType, look like they report your connection type. They do not.

The value is “determined using a combination of recently observed, round-trip time and downlink values”[8]. It is a performance estimate, sorted into four bins. MDN’s own example makes the gap explicit: “2g might be used to represent a slow Wi-Fi connection with high latency and low bandwidth”[7]. Nothing about the label is a statement about the radio.

The bin boundaries are a fixed table. 3g means an estimated round trip of at least 270 ms or a downlink of at most 700 kbps; 4g is everything above that[10]. And the table is old. The spec says the numbers are “based on real user measurement on Chrome on Android, as captured in April 2017”[10]. A connection today is scored against a nine-year-old snapshot of what Android phones were seeing.

The honest field - the actual connection technology, wifi or cellular or ethernet - exists in the spec as navigator.connection.type[9]. Chromium never shipped it to the open web. What it exposes is effectiveType, downlink, rtt and saveData, and not type[11]. The one value that would say something definite about your network is the one that was held back.

Only one browser sends any of it

In MDN’s classification, the Network Information API is “not Baseline because it does not work in some of the most widely-used browsers”[9]. Concretely: Firefox has never implemented it, and neither has Safari, on the desktop or on iOS[11]. The Device Memory API has the same gap.

So this is a description your device offers that only one browser engine will send, and the other two decline to. A server that tunes its output to Sec-CH-Device-Memory or ECT is tuning it for Chromium users in particular, and the tuning is invisible to the person on the receiving end. Nothing in the page says a lighter version was picked, or what picked it.

What this costs you

A recognisability signal you did not knowingly send. The netinfo spec’s own Privacy Considerations name the uses: “fingerprint a user based on characteristics of a particular network”, track “transitions between one or more networks”, and “infer user location (e.g. are they home, at work, or in transit)”[10]. Coarse values make that harder, not impossible. The spec’s own position is that it “does not expose anything that is not already available to a sufficiently-motivated attacker”[10] - a claim about novelty, not about safety.

A different page, chosen for you, with no note saying so. The W3C draft’s motivating examples are serving “search lite - a 10KB search results page used for low-end devices”, a cut-down video player, lighter map tiles[3], and letting analytics “normalize their metrics against the device-class”[3]. Being sorted into “low-end” is what the feature is for. You are not told which bin you landed in.

It is on by default wherever it exists. Save-Data needs the reader to opt in[1]. The device and network hints do not - once a server sends Accept-CH, the browser complies[4]. The reader’s only controls are indirect: turning off the data-saver setting removes Save-Data, and using Firefox or Safari removes the rest[11].

The headers are in every request and the specs describe them without euphemism. What changes is the order of events: the negotiation over what your device can handle is over by the time the page you asked for arrives, and your side of it was a handful of rounded numbers you never chose to send.

Sources

  1. Save-Data header - MDN Web Docs, Mozilla, accessed

    Supports: Save-Data is a network client hint that 'indicates the client's preference for reduced data usage'. It 'is a low entropy hint, and hence may be sent by the client even if not requested by the server using an Accept-CH response header', and 'should be used to reduce data sent to the client irrespective of the values of other client hints that indicate network capability, like Downlink and RTT'. 'A value of On indicates explicit user opt-in into a reduced data usage mode on the client. When communicated to origins, this allows them to deliver alternative content to reduce the data downloaded such as smaller image and video resources, different markup and styling, disabled polling and automatic updates, and so on.' The default is off. On caching: the Vary header on a response 'ensures that responses should be separately cached based on the value of the Save-Data header. This can ensure that the user is not served a lower-quality image from the cache when the Save-Data header is no longer present (e.g., after having switched from cellular to Wi-Fi).'

  2. Navigator: deviceMemory property - MDN Web Docs, Mozilla, accessed

    Supports: The deviceMemory read-only property 'returns the approximate amount of device memory in gigabytes'. 'The reported value is imprecise to curtail fingerprinting. It's approximated by rounding the actual memory to the nearest power of 2, then dividing that number by 1024. It is then clamped within lower and upper bounds to protect the privacy of owners of very low-memory or high-memory devices.' The value is 'A floating point number coarsened to a power of two value, clamped to implement-defined limits', and 'for example, if a browser does not report below 2 or above 32 then the value is one of: 2, 4, 8, 16, 32.' The property is annotated as available only in a secure context.

  3. Device Memory API (Working Draft) - W3C, Web Performance Working Group, accessed

    Supports: Section 1 lists the motivating use cases for a device-class signal: 'Serving a light version of the site or specific components', with examples 'Serve "search lite" - a 10KB search results page used for low-end devices', 'Serve a light version of video player in a social media web application', 'Serve lightweight tile images in a map web application'; and 'Normalizing metrics: analytics need to be able to normalize their metrics against the device-class'. It notes that device identification 'based on advertised User-Agent, and other characteristics of the client, are commonly used to select and provide optimized content' but rely on 'commercial device databases, which are costly, hard to integrate, and hard to maintain'. Section 5: 'Sec-CH-Device-Memory Client Hint header and JavaScript API will only be available to HTTPS secure contexts. To reduce fingerprinting risk, the reported value is rounded to a single significant bit, as opposed to reporting the exact value. In addition, an implementation-specific upper and lower bound is placed on the reported values.'

  4. Sec-CH-Device-Memory header - MDN Web Docs, Mozilla, accessed

    Supports: The Sec-CH-Device-Memory request header 'is used in device client hints to indicate the approximate amount of available RAM on the client device, in gigabytes'. 'Client hints are accessible only on secure origins. A server has to opt in to receive the Sec-CH-Device-Memory header from the client, by first sending the Accept-CH response header.' 'The amount of device RAM can be used as a fingerprinting variable, so values for the header are intentionally coarse to reduce the potential for its misuse. Values are only reported in powers of two, and are clamped to an implementation-defined minimum lower value and a maximum upper value.' Example request value: 'Sec-CH-Device-Memory: 1'.

  5. Downlink header - MDN Web Docs, Mozilla, accessed

    Supports: The Downlink request header 'is used in Client Hints to provide the approximate bandwidth in Mbps of the client's connection to the server', and 'allows a server to choose what information is sent based on the network bandwidth. For example, a server might choose to send smaller versions of images and other resources on low bandwidth networks.' The directive is 'The downlink rate in Mbps, rounded to the nearest 25 kilobits. The downlink rate may be used as a fingerprinting variable, so values for the header are intentionally coarse to reduce the potential for its misuse.' A server must opt in with Accept-CH; example value 'Downlink: 1.7'. MDN notes it is 'likely to change often, which effectively makes the resource uncacheable' if placed in Vary.

  6. RTT header - MDN Web Docs, Mozilla, accessed

    Supports: The RTT request header 'is a network client hint which provides the approximate round trip time on the application layer, in milliseconds. The RTT hint includes server processing time, unlike transport layer RTT.' 'The RTT value is rounded to the nearest 25 milliseconds to prevent fingerprinting, although there are many other mechanisms an attacker might use to obtain similar round-trip information.' A server must opt in with Accept-CH; example value 'RTT: 125'.

  7. ECT header - MDN Web Docs, Mozilla, accessed

    Supports: The ECT request header 'is used in Client Hints to indicate the effective connection type: slow-2g, 2g, 3g, or 4g'. 'The value represents the "network profile" that best matches the connection's latency and bandwidth, rather than the actual mechanisms used for transferring the data. For example, 2g might be used to represent a slow Wi-Fi connection with high latency and low bandwidth, while 4g might represent a fast fiber-based broadband network.' The value 'might also be used as a starting point for determining what information is sent, which is further refined using information in RTT and Downlink hints.' A server must opt in with Accept-CH; example value 'ECT: 2g'.

  8. NetworkInformation: effectiveType property - MDN Web Docs, Mozilla, accessed

    Supports: The effectiveType read-only property 'returns the effective type of the connection meaning one of slow-2g, 2g, 3g, or 4g. This value is determined using a combination of recently observed, round-trip time and downlink values.'

  9. Network Information API - MDN Web Docs, Mozilla, accessed

    Supports: The Network Information API 'provides information about the system's connection in terms of general connection type (e.g., wifi, cellular, etc.). This can be used to select high definition content or low definition content based on the user's connection.' MDN marks the feature 'Limited availability' with the note: 'This feature is not Baseline because it does not work in some of the most widely-used browsers.' The connection technology enum NetworkInformation.type has values including bluetooth, cellular, ethernet, none, wifi, wimax, other and unknown, and is itself marked experimental.

  10. Network Information API (Editor's Draft) - Web Incubator Community Group (WICG), accessed

    Supports: The effective connection type table fixes the thresholds: slow-2g at minimum RTT 2000 ms and maximum downlink 50 Kbps; 2g at 1400 ms / 70 Kbps; 3g at 270 ms / 700 Kbps; 4g at 0 ms / infinity. 'The absolute values provided above are based on real user measurement on Chrome on Android, as captured in April 2017. The user agent MAY update these values in the future to reflect changes in the measurement data.' The Privacy Considerations section states the API can be used to 'Fingerprint a user based on characteristics of a particular network (e.g. type and downlink estimates) at a point in time', to fingerprint 'based on transitions between one or more networks', and to 'Infer user location (e.g. are they home, at work, or in transit) based on above criteria', while concluding 'it does not expose anything that is not already available to a sufficiently-motivated attacker' and that a client wanting to mitigate 'should disable JavaScript, monitor that all outbound requests are made to trusted origins, and make diligent use of anonymizing VPN/proxy services'. An example use case for navigator.connection.type: a media app 'could check navigator.connection.type prior to playback. When it's set to cellular, it could advise users that their mobile network operator might be charging for the bandwidth'.

  11. Network Information API - browser support - caniuse.com, accessed

    Supports: The feature's standardization status is listed as 'unofficial'. Firefox and Safari report no support in any version, including current releases and Safari on iOS. Chrome and Edge report partial support: navigator.connection exposes effectiveType, downlink, rtt and saveData, but not type or downlinkMax.