By David Mattia
May 23, 2025•11 min read
When you include an Airgap.js bundle on your site, you are including:
This is all present in the one script, which is why we call it your “bundle”. In this section we explore the download part of the script itself, along with everything else that happens on the network side before the download can begin.
Before we dive into the details of why Airgap loads so quickly, head to the PageSpeed Insights page for our company homepage. Here, we can use transcend.io as an example of a website using our own consent manager, but you can use this tool to test your own site for Airgap’s performance as well. This is especially useful if you are deploying your Airgap.js bundle to a dev/staging environment before launching it in production.
Under the “Reduce JavaScript execution time” diagnostic, you should see that the airgap.js script and related scripts can be parsed and evaluated quickly. In our test, this completes in 73 milliseconds for the main script for our bundle.
Under the “Minimize third-party usage” diagnostic, you can see that our main airgap.js file is very small (53kb) and that it blocked the main thread for virtually no time (0 ms).
Under the “Avoid chaining critical requests” column you can see the requests that must complete before other content on the page can load. In our case, because we’ve chosen to load airgap.js asynchronously, it does not need to be in our critical path.
If you go to that PageSpeed site, you will likely see slightly different values as each load re-computes the bundle size and load time.
The above image was captured simply by going to https://transcend.io, opening the Network tab, and searching for the airgap.js file. As you can see in the image, the 54.3kb file was downloaded from start to finish in under 75 ms. With throttling to simulate 4G networks, the times are often close to 200-250 ms.
We built Airgap.js with a number of rules that keeps our size small:
Airgap.js treats users differently based on their location and the customer’s configuration for that region. The dynamic regime detection that allows us to customize the user experience happens inside our CDN, meaning that the 30-70ms download time shown earlier is possible even with location-specific bundles.
How do we do this efficiently? We first need to understand how Cloudflare works.
When a user requests a file from Cloudflare, the request first goes to one of the over 330 edge locations closest to the user. The edge location keeps a cache of requests made to the CDN and, if it has a copy of the file to send, it sends it back immediately without asking the origin server (the location containing the actual CDN files) for anything. But if the edge location does not have a cached file to send to the user that meets their requirements, it asks the origin to provide such a file. As the edge location passes the file onto the user, it updates its cache.
Cloudflare allows dynamic code, called Workers, to run during any request to modify the caching behavior and responses. These functions run inside the edge location itself, meaning the code is executing close to your user’s physical location, which can greatly reduce latency on cache hits where the origin server does not need to be talked to at all.
We used these Workers to dynamically lookup users’ country and region information based on their IP address sent with each request. These workers run extremely efficiently, as they run at the edge locations and exclusively are checking the region information and updating caches if necessary.
In the graphs above, you can see that the average worker runtime is only 11 milliseconds, with a p90 of just 20 milliseconds.
With this pattern, we then needed to make sure that two users talking to the same edge location who were in different states/regions (such as users in Minnesota and Wisconsin talking to an edge location in Ohio) would not share the same cache key, as we would want to have the `countryRegionName` field reflect the state of the requestor for both users. To do so, we updated the cache key on the edge location so that the country and regions the users are from are included as cache key name—meaning that we don’t just cache bundles per edge location, but per edge location per region that has requested them. As most edge locations only receive requests from a small area of the world, we can be sure that there are not too many cache keys per edge location and that our cache-hit-rate should stay quite high.
As of the time of this writing, 99.8% of requests to our CDN for airgap.js bundles have been cache hits in the recent past across all customers. The more traffic you have to your website, the more likely it is that your cache hit rate would be even higher.
After the Airgap.js script loads (which can be done asynchronously) to prevent itself from affecting initial site load and render times), it will initialize itself to regulate network requests and cookies.
After initializing, Airgap.js can regulate all traffic on your website. It does this by looking at the user consent preferences, user region, the hostname/cookie name of the request being made, and the setting selected in our admin dashboard for what purpose each hostname/cookie serves on your site. With those inputs, it can then allow, reject, or quarantine each request as your site loads and operates. The effect of this operation can be seen per request, and is typically between 35-350μs/request.
Transcend supplies a built-in user interface (UI) for users to control their preferences, but you are welcome to use your own UI as well. In either case, the UI can be asynchronously loaded so that it does not block any site functionality. Unlike most consent management platforms, Airgap.js often doesn’t need to appear as soon as your website loads, meaning the time to download the UI largely goes unnoticed. The UI is a separate download from the main Airgap.js Bundle, but uses the same CDN with HTTP/2 so your browser should be able to efficiently fetch the UI components after fetching the main bundle.
The following performance metrics were measured on an Apple M1 Pro laptop under light to medium load, using the transcend.io bundle configuration:
As a stress test, we benchmarked re-processing transcend.io's entire homepage (equivalent to calling document.documentElement.innerHTML = document.documentElement.innerHTML) and measured an average total latency of 24.6ms while processing ~250 request-causing elements per call. Performance overhead scales roughly linearly with the size of your bundle configuration.
To test the performance impact for yourself, you can install an Airgap.js bundle onto a site locally (without deploying the bundle to affect all of your users) through something called a UserScript. These instructions show you how to inject a bundle into your site so you can test important functionality and performance impacts before fully deploying the bundle into production.
There is no official browser API for intercepting outbound HTTP requests. Airgap.js regulates core JavaScript and HTML tools such as `fetch` APIs, `<script>` tags, `<img>` tags, and dozens of other ways that websites can talk to each other.
You can read about our journey to find the most secure and efficient way of intercepting network calls in this blog post, where we cover how we tried using sandboxed iframes, dynamic Content Security Policies, and other ideas before eventually settling on a combination of Content Security Policies and “Patchers” that override the global interfaces to inspect request metadata before sending each request through the native capabilities.
This Patcher paradigm is extremely lightweight, leading to the small footprint added for each request.
Airgap.js can send telemetry to Transcend’s backend that allow for us to provide you with aggregated, anonymized analytics about your user’s usage of the site, and can also be used to auto-classify the hostnames/cookies your site is using to ensure your settings are always up to date.
Our telemetry system was architected to avoid accidental collection of personal data by only recording encountered request domains and matching data flow and cookie rules, among other privacy-preserving anonymous statistics.With this approach, the performance and privacy impact of telemetry data is extremely minimal.
So far, we’ve discussed that Airgap.js can be downloaded quickly, initialized quickly, and is efficient while regulating traffic. But our claim isn’t just that Airgap.js is often quick to use—it's that it often can improve site performance. How?
It depends on the specifics of the site that it’s regulating, but essentially because Airgap.js can block requests or even entire scripts from running until a user consents to the purposes behind those resources (if they consent at all), your main thread can avoid being blocked by tools your user does not consent to.
In the example above, we can see this on a website that uses a number of common tools like Adobe Tag Manager, Google Tag Manager, Facebook, Google Analytics, Adroll, and more. Many of these scripts can block the main thread for hundreds of milliseconds or even a few seconds. When exploring commonly used websites on https://pagespeed.web.dev/, it’s not at all uncommon to see these blocking third-party requests take up multiple seconds of time on the main thread.
When Airgap.js initializes, it will regulate each of these scripts (and the requests the scripts potentially send out) and may block many of them depending on the user’s previous consent settings (if you save their preferences to localStorage or your backend, which is configurable) or the default consent settings for the area where the user made the request from.
In summary, If your site blocks more time on the main thread than it takes to download and initialize Airgap.js, then congratulations: Respecting your user’s privacy actually made their web experience more performant. And we think that’s a pretty cool change compared to the dark patterns and nearly-unusable web many users have had to deal with for the past few years.
By David Mattia