TLS GREASE and Extension Order as a Fingerprinting Surface (2026)

10 min read

A TLS handshake starts before any page loads. The first message, the ClientHello, already tells the server a good deal about the software that sent it: which cipher suites it offers, which extensions it lists and in what arrangement, and whether it sprinkles in reserved GREASE values. That is why TLS fingerprints such as JA4 are a standard tool for bot management and fraud teams, and why developers who test sites or care about privacy should know what their own browser sends.

This tutorial explains GREASE and extension ordering from the side of a site owner, a QA engineer or a privacy-minded user: what they are, how JA4 handles them, and how to inspect a ClientHello yourself.

What We're Learning

By the end of this, you'll understand:

  • How GREASE values work and why Chrome uses them
  • Why extension order differs between TLS stacks, and what changed in Chrome
  • How JA4 turns a ClientHello into a fingerprint
  • How to capture and read your own ClientHello
  • Why the handshake is a property of the browser build, not of the page

Prerequisites

  • Basic understanding of TLS handshakes (ClientHello, supported ciphers, extensions); for the in-page side of fingerprinting, see our WebGL fingerprinting guide
  • Familiarity with JA4 fingerprinting concepts — read that first if you're new to TLS fingerprinting
  • A packet capture tool (Wireshark, tcpdump, or tls.peet.ws for browser-based capture)
  • A browser to inspect, such as the one you use every day or a JustBrowser profile from the 7-day trial

Step 1: Understand What GREASE Does

GREASE stands for Generate Random Extensions And Sustain Extensibility. RFC 8701 formalized it, and Chrome's TLS stack implements it.

The problem GREASE solves: network middleboxes (firewalls, load balancers, proxies) sometimes break when they see TLS values they don't recognize. When the TLS spec adds new extensions, these middleboxes might reject the connection because the ID "looks wrong."

The solution: randomly insert reserved, meaningless values into every handshake. If a middlebox rejects them, operators notice and fix their equipment, which keeps the ecosystem healthy for future real extensions.

The reserved GREASE values follow a specific pattern: 0x?A?A where ? is the same hex digit twice. So 0x0A0A, 0x1A1A, 0x2A2A, through 0xFAFA. Sixteen possible values.

Chrome uses them in several places:

  • Cipher suites: a GREASE value in the cipher list
  • Extensions: a GREASE value in the extension list
  • Supported groups: a GREASE value in the elliptic curve list
  • Other fields: RFC 8701 also reserves GREASE values for ALPN identifiers, signature algorithms, versions and PSK key-exchange modes, so a stack can add them there too

The specific value is chosen per connection, but the pattern of where GREASE appears is a trait of the stack. Firefox's TLS stack doesn't use GREASE in the same way, so GREASE presence is one of several traits that help tell TLS stacks apart. For a server, that is useful information: a ClientHello is evidence about the software behind it. Note that JA4 itself ignores GREASE values wherever it finds them, so they don't change a JA4 fingerprint; you see them in a raw capture.

Step 2: Understand Extension Ordering

TLS extensions appear in a particular arrangement in the ClientHello, and each TLS stack has its own habits. A simplified, illustrative list of extensions a Chromium-based ClientHello typically contains:

  1. server_name (SNI)
  2. extended_master_secret
  3. renegotiation_info
  4. supported_groups
  5. ec_point_formats
  6. session_ticket
  7. application_layer_protocol_negotiation (ALPN)
  8. status_request
  9. signature_algorithms
  10. signed_certificate_timestamp
  11. key_share
  12. psk_key_exchange_modes
  13. supported_versions
  14. compress_certificate
  15. a GREASE extension
  16. padding

Treat that as the rough shape rather than a fixed sequence. Since early 2023, Chrome has randomized the order of its ClientHello extensions on each connection (Google's stated aim is to reduce ecosystem brittleness, the same goal as GREASE), so a fixed order is no longer a trait of Chrome. JA4 sorts the extension list before hashing it, so a shuffled order still produces the same fingerprint, and the JA4 spec offers an original-order variant (JA4_o) for when the sequence itself matters. The set of extensions, the count and the cipher list still say a lot about the stack.

Firefox's stack offers a different set, with extensions such as delegated_credentials and record_size_limit that Chromium doesn't send, and a different count.

For a defender, the useful logic is consistency: a connection whose TLS traits, HTTP headers and in-page browser properties all describe the same kind of browser is ordinary, and one where they disagree is worth a closer look. That is the idea behind layered bot detection.

Step 3: Capture Your Own ClientHello

Let's see what your browser actually sends.

Option A: Browser-based capture (easier)

  1. Open tls.peet.ws in the browser you want to inspect
  2. The site captures your TLS fingerprint and displays the raw ClientHello
  3. Look for the extension list, noting the arrangement and any GREASE values

Option B: Wireshark (more control)

  1. Start Wireshark, filter for tls.handshake.type == 1 (ClientHello only)
  2. Navigate to any HTTPS site in the browser
  3. Find the ClientHello packet and expand the TLS layer
  4. Read the "Extension: ..." entries in order

What to look for:

  • GREASE present? Look for values in the 0x?A?A pattern
  • GREASE position? Note which lists carry one
  • GREASE rotation? Refresh and capture again; the value should change between connections
  • Extension arrangement? Capture twice and see whether the order shifts

This part is tedious, and it is also the only way to see the handshake itself, because no page script can.

Option C: JA4 databases

Sites like ja4db.com catalogue JA4 fingerprints. You can compare your captured fingerprint against known browser populations to see which software it corresponds to.

Step 4: Compare Against Expected Baselines

A simplified picture of how two TLS stacks differ:

Chromium-based browser:
- Extensions include: server_name, extended_master_secret, supported_groups,
  ec_point_formats, session_ticket, alpn, status_request, signature_algorithms,
  signed_certificate_timestamp, key_share, psk_key_exchange_modes,
  supported_versions, compress_certificate, a GREASE extension, padding
- GREASE in cipher list: yes (one value)
- GREASE in supported_groups: yes (one value)
- Extension order: may vary between connections

And a Firefox-style stack:

Firefox:
- Extensions include: server_name, extended_master_secret, supported_groups,
  ec_point_formats, session_ticket, alpn, status_request, delegated_credentials,
  key_share, supported_versions, signature_algorithms, psk_key_exchange_modes,
  record_size_limit
- GREASE: not used in the same way

The takeaway is that extension count, set and GREASE presence differ by browser family. A browser's TLS traits are a product of its build, which is why they make a useful signal for the defender.

Step 5: Why the Handshake Belongs to the Browser Build

TLS happens in the network stack. For Chromium-based browsers, that is BoringSSL. The ClientHello is constructed and sent before any page loads, before any JavaScript runs.

That has two practical consequences:

  • A page script cannot change it. Anything that runs inside a page works at the application layer; TLS operates at the transport layer. Extensions and in-page scripts have no hook into cipher suites, extension lists or GREASE insertion.
  • It reflects the engine you installed. The handshake is whatever the browser's own network stack produces. A browser built on a recent Chromium produces a handshake typical of that engine.

This is one reason JustBrowser is built as a custom Chromium engine with native C++ integration rather than as an extension: the browser's own settings come from the engine, not from scripts layered on top. The engine is patched on every upstream Chromium release, so it stays current with upstream.

Step 6: Using This for QA and Defence

Knowing what a ClientHello contains is useful in a few legitimate situations:

  • Testing your own bot rules. If you run a site and use a bot-management product, capture the ClientHello from the browsers your real visitors use and confirm your rules treat them as ordinary traffic. A rule that flags a mainstream browser's handshake is a false positive you want to find before customers do.
  • Debugging blocked sessions on your own infrastructure. When a legitimate user or a test runner is challenged, comparing the TLS fingerprint, headers and browser properties shows which layer disagrees.
  • Privacy review. Seeing what your browser reveals before any page loads is a useful reminder that the network layer is part of your footprint.
  • Automation in test environments. If you drive a browser with Playwright, Puppeteer or Selenium against a staging site you own, check that your staging bot rules allow your test runner's fingerprint.

Common Questions While Inspecting

No GREASE values in the capture. Check which stack produced the handshake. Some TLS stacks don't use GREASE, and a command-line HTTP client or an HTTP library will look different from a browser.

GREASE values never change between connections. Capture several handshakes and compare. A stack that rotates GREASE will show different values from one connection to the next.

The JA4 fingerprint matches nothing in the database. That can be a custom or rarely used client, a very new browser build the database hasn't catalogued, or software that isn't a mainstream browser. It is a prompt to investigate rather than a verdict.

Next Steps

You've got the fundamentals on GREASE and extension ordering. Some directions to explore:

  • HTTP fingerprinting: After TLS, the HTTP layer adds more signals. JA4H fingerprints the HTTP request itself (method, headers, cookies and language), and the HTTP/2 SETTINGS frame also varies by client.
  • Profile setup: our multi-account browser setup checklist covers the profile side.
  • Session resumption: TLS session tickets and 0-RTT behavior vary by stack.
  • Browser-side checks: Our explainer on fingerprint checker pages covers what in-page tests do and do not see.

For analytics that don't depend on cookies, JustAnalytics offers cookieless, privacy-first analytics. And if you run paid campaigns and worry about bot traffic, ClickzProtect does bot detection for affiliates and traffic operators, and its click-fraud cost breakdown shows why network-layer signals matter.

Frequently Asked Questions

What is TLS GREASE and why does it matter for fingerprinting?

GREASE (Generate Random Extensions And Sustain Extensibility, RFC 8701) is a mechanism Chrome uses to keep network middleboxes from breaking when new TLS features appear. Chrome inserts reserved, throwaway values in specific slots of the ClientHello: cipher suites, extensions, supported groups. Their presence and placement is a trait of the TLS stack that sent the handshake, so it is one input to a TLS fingerprint such as JA4.

How does extension ordering differ between browsers?

Different TLS stacks assemble their ClientHello differently, so the set and arrangement of extensions is a trait of the stack. Some browsers also shuffle extension order between connections, which is why JA4 hashes a sorted extension list while the raw JA4_r form keeps the order as sent.

Why can't a page's JavaScript change the TLS handshake?

Because the handshake happens in the browser's network stack (BoringSSL for Chromium) before any page loads and before any JavaScript runs. Page scripts control what happens on the page, not what happens in the socket. The ClientHello is a property of the browser build itself.

How do I inspect my own browser's ClientHello?

Use a TLS fingerprint inspector such as tls.peet.ws, or capture traffic with Wireshark and filter on tls.handshake.type == 1. Check whether GREASE values appear in the cipher suites, extensions and supported groups, whether the value changes between connections, and how the extension list is arranged.


Try JustBrowser

A multi-account browser for teams, built on a custom Chromium engine with native C++ integration, not extension-based. Each profile is its own browser, with its own cookies, storage, proxy and 40+ identity parameters. Local REST API + CDP for Playwright, Puppeteer, Selenium. Windows x64 and macOS (Apple Silicon). 7-day free trial, card required — then $9.99/month or $99.99/year, unlimited profiles, free team seats.

Get started → · How it differs from Multilogin / GoLogin / AdsPower

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy