Proof of concept that detects adblockers without Javascript by abusing 103 Early Hints
The application can be run either through a Docker container or directly on your machine using Node.js.
Using Docker simplifies the setup and ensures consistency across different environments. To launch the application in a Docker container, execute the following command:
npm run docker
This command wraps the process of building the Docker image and running the container. It ensures that you don't need to manually set up the environment on your local machine.
If you prefer running the application directly on your local environment, follow these steps:
Install the project dependencies using npm.
npm install
The application requires SSL certificates for HTTP2.
npm run certs
Start the application
npm run serve
The core idea behind this proof of concept is the use of 103 Early Hints response. By sending early hints prior to the actual response, the server can determine whether an adblocker is present based on the client's handling of these hints. If adblock is detected, the server can then serve an alternative page. This method is particularly effective because it doesn't depend on JavaScript, which can be disabled or manipulated by users.
┌───────┐ ┌──────┐ ┌───────┐ ┌──────┐
│Browser│ │Server│ │Browser│ │Server│
└───┬───┘ └──┬───┘ └───┬───┘ └──┬───┘
┌┴┐ GET /index.html ┌┴┐ ┌┴┐ GET /index.html ┌┴┐
│ │ ───────────────────>│ │ │ │ ───────────────────>│ │
│ │ │ │ │ │ │ │
│ │ 103 Early Hints │ │ │ │ 103 Early Hints │ │
│ │ <───────────────────│ │ │ │ <───────────────────│ │
╔═══════════╤╪═╪═════════════════════╪═╪═══╗ ╔═══════════╤╪═╪═════════════════════╪═╪═══╗
║ PREFETCH ││ │ │ │ ║ ║ PREFETCH ││ │ │ │ ║
╟───────────┘│ │ GET /adv.css?ABCDEF │┌┴┐ ║ ╟───────────┘│ │ GET /adv.css?ABCDEF │┌┴┐ ║
║ │ │ ────────────────────>│ │ ║ ║ │ │ ──────────────────X ││ │ ║
║ │ │ ││ │ ║ ║ │ │ ││ │ ║
║ │ │ 204 No Content ││ │──║───┐ ║ │ │ ││ │──║───┐
║ │ │ <────────────────────│ │ ║ │ ║ │ │ │└┬┘ ║ │
║ │ │ │└┬┘ ║ │ ╚════════════╪═╪═════════════════════╪═╪═══╝ │
╚════════════╪═╪═════════════════════╪═╪═══╝ │ │ │ │ │ ┌─────┴─────────────┐
│ │ │ │ ┌─────┴─────────────┐ │ │ │ │ │Prefetch timeout: │
│ │ 200 OK │ │ │Resource fetched: │ │ │ 200 OK │ │ │Adblock detected │
│ │<────────────────────│ │ │No adblock detected│ │ │<────────────────────│ │ └───────────────────┘
└┬┘ └┬┘ └───────────────────┘ └┬┘ └┬┘
┌───┴───┐ ┌──┴───┐ ┌───┴───┐ ┌──┴───┐
│Browser│ │Server│ │Browser│ │Server│
└───────┘ └──────┘ └───────┘ └──────┘
At the moment this technique only works in Firefox. Chrome does not allow adblockers to interact with resources loaded using early hints, nor does it display resources loaded using early hints in the developer console. Additionally, Safari does not support preload early hints at all. Browsers that do not fully support early hints can be easily detected by adding a harmless dummy resource to preload that will not be blocked by adblockers.
Currently, this unintended side-effect may not be a significant problem due to these factors. However, as browsers continue to expand their support for early hints, it could become a reliable method for detecting adblockers.
I have previously demonstrated other techniques for detecting adblockers during the server response. Although those require a more involved implementation, they are more effective and less likely to produce false positives.
Some good resource for learning more about 103 Early Hints
can be found here: