The first open-sourced, crowd-sourced logic for detecting most of the web technologies powering the internet today. Designed for transparency, speed, and community-driven contributions β this framework identifies frameworks, CMSs, analytics, and more using simple, readable rules anyone can extend.
- π― Multiple detection methods (global variables, selectors, network requests, etc.)
- π Detailed detection results with categories
- π Fast and efficient scanning
- π Support for custom fingerprints
- π§ͺ Built-in test fixtures for validation
- π¦ Bundled core fingerprints for easy deployment
Coming soon:
- π Cloudflare Workers SDK
- π Python SDK
- π PHP SDK
- πΉ Go SDK
Each SDK will maintain the same core detection logic while providing language-specific optimizations and idiomatic APIs.
pnpm add whats-that-techimport { findTech } from 'whats-that-tech';
const results = await findTech({
url: 'https://example.com',
headless: true
});
console.log(results);import { findTech } from 'whats-that-tech';
const results = await findTech({
url: 'https://example.com',
headless: true,
onProgress: (progress) => {
console.log(progress);
}
});
console.log(results);# Basic example
pnpm tsx examples/simple-raw-results.ts
# Example with progress updates
pnpm tsx examples/chunking-batch-crawl.tsurl(string): The URL to analyzeheadless(boolean, optional): Whether to run in headless mode (default: true)timeout(number, optional): Timeout in milliseconds (default: 30000)categories(string[], optional): Specific categories to detectexcludeCategories(string[], optional): Categories to exclude from detectioncustomFingerprintsDir(string, optional): Directory containing custom fingerprints (Node.js only). Each technology should have its own JSON file (e.g.,customDir/wordpress/wordpress.json). Takes precedence over default fingerprints.customFingerprintsFile(string, optional): Path (Node.js) or URL (Node.js/Cloudflare) to a single JSON file containing all custom fingerprints. Takes precedence overcustomFingerprintsDirand default fingerprints.debug(boolean, optional): Enable debug logging to see fingerprint loading and detection details.onProgress(function, optional): Callback for progress updates.
The onProgress callback receives an object with the following properties:
current(number): Current URL being processedtotal(number): Total number of URLs to processcurrentUrl(string): URL currently being processedstatus('processing' | 'completed' | 'error'): Current statuserror(string, optional): Error message if status is 'error'
Returns a promise that resolves to an array of DetectionResult objects, each containing:
name(string): Name of the detected technologycategories(string[]): Categories the technology belongs todetected(boolean): Whether the technology was detected
When debug mode is enabled (debug: true), the SDK will output detailed information about:
const results = await findTech({
url: 'https://example.com',
debug: true // Enable debug logging
});Debug output includes:
- Current working directory
- Fingerprint search paths (local core, node_modules, dist)
- Successfully loaded fingerprints
- Technologies detected during scanning
Example debug output:
Current working directory: /your/project
Looking for fingerprints in:
- Local core: /your/project/core
- Node modules: /your/project/node_modules/whats-that-tech-core
- Dist core: /your/project/dist/core.json
- Root core: /your/project/core.json
Loading fingerprints from: /your/project/node_modules/whats-that-tech-core
Loaded fingerprint for wordpress
Loaded fingerprint for shopify
...
Detected wordpress with categories: ["cms"]
Detected shopify with categories: ["ecommerce"]This is particularly useful when:
- Developing new fingerprints
- Debugging detection issues
- Understanding where fingerprints are being loaded from
- Verifying which technologies are being detected
The SDK uses technology fingerprints defined in JSON format. By default, it loads a bundled core.json file (in production/packaged builds) or looks for fingerprints in specific locations during development (see below).
Fingerprints are loaded with the following priority (highest first):
customFingerprintsFile: If provided (as a local path in Node.js or a URL in Node.js/Cloudflare), this single JSON file containing all fingerprints is loaded and used exclusively.customFingerprintsDir: IfcustomFingerprintsFileis not used and this directory path is provided (Node.js only), the SDK attempts to load JSON files from subdirectories within this path (e.g.,yourDir/techName/fingerprint.json). If successful, these fingerprints are used exclusively.- Default Paths: If neither custom option is used, the SDK searches for fingerprints in default locations:
- Packaged/Production: Looks for
dist/core.jsonrelative to the package. - Local Development (
tsx/ts-node): Looks fordist/core.jsonrelative to thesrc/utilsdirectory. - Note: The previous behavior of looking in
node_modules/whats-that-tech-coreor a localcoredirectory during development is simplified; the build process now typically handles generating thedist/core.jsonneeded for development runs.
- Packaged/Production: Looks for
You have two ways to use custom fingerprints:
-
Single File (
customFingerprintsFile): Provide a path or URL to a JSON file that matches the structure of thecore.jsonbundle (i.e., a root object where keys are technology names and values are the corresponding fingerprint objects).// Node.js (local path or URL) const results = await findTech({ url: 'https://example.com', customFingerprintsFile: './path/to/my-fingerprints.json' // or 'https://example.com/my-fingerprints.json' }); // Cloudflare Worker (URL only) // (Assuming findTech is imported appropriately for the worker context) const results = await findTech({ url: 'https://example.com', customFingerprintsFile: 'https://example.com/my-fingerprints.json' }, env);
-
Directory (
customFingerprintsDir- Node.js only): Create a directory and place individual fingerprint JSON files within subdirectories named after the technology.my-fingerprints/ βββ my-cool-tech/ β βββ fingerprint.json βββ another-tech/ βββ config.json // File name must end with .jsonconst results = await findTech({ url: 'https://example.com', customFingerprintsDir: './my-fingerprints' });
Your custom fingerprint JSON (either the single file or individual files in the directory) should follow this structure:
// Example for a single technology (e.g., in my-fingerprints/my-cool-tech/fingerprint.json
// OR as a value within the main object of customFingerprintsFile)
{
"name": "your-technology",
"categories": ["category1", "category2"],
"detectors": {
"globalVariables": ["window.yourTech"],
"selectorExists": ["[data-your-tech]"],
"requestUrlRegex": "your-tech\\.js"
}
}To run the example files:
# Run the simple example
pnpm tsx examples/simple-raw-results.ts
# Run other examples (if available)
pnpm tsx examples/exclude-pixels.ts
pnpm tsx examples/chunking-batch-crawl.tsEach technology also has test fixtures to ensure accurate detection:
*.pass.html: Positive detection case*.fail.html: Negative case
These fixtures help validate detectors and prevent false positives/negatives.
The core technology fingerprints are maintained in a separate repository: whats-that-tech-core. To contribute to the core fingerprints:
-
Clone the core repository locally:
pnpm setup:local
This will create a
coredirectory in your project root. Once cloned, the SDK will automatically use this localcoredirectory instead of the one from node_modules, allowing you to make and test changes immediately. -
Make your changes to the fingerprints in the
coredirectory:- Add new fingerprint JSON files in the appropriate category directory
- Add test fixtures (pass.html and fail.html) in the tech's test directory
- Update existing fingerprints as needed
- Your changes will be immediately reflected when running the SDK locally
-
Create a new branch in the core repository:
cd core git checkout -b feature/your-new-tech git add . git commit -m "feat: add new tech detection" git push origin feature/your-new-tech
-
Create a Pull Request to the whats-that-tech-core repository
To contribute to the SDK itself:
- Fork the SDK repository
- Create a new branch for your changes
- Make your changes
- Commit and push your changes
- Create a PR to the SDK repository
Built for:
- Technical SEO experts
- Dev tool makers
- Security researchers
- Growth hackers
- Competitive analysts
MIT License
Copyright Β© 2024 tzi-labs
This package also includes an entry point specifically for Cloudflare Workers. The core findTech function works similarly, but with a few key differences:
- Environment: It requires the Cloudflare environment object (
env) containing the Puppeteer browser binding (MYBROWSER). - Fingerprints:
- It does not use the hardcoded
FINGERPRINTSconstant anymore. - By default, it expects fingerprints to be provided via the
customFingerprintsFileoption (URL only). - The
customFingerprintsDiroption is not supported in the Cloudflare environment.
- It does not use the hardcoded
- Dependencies: Uses
@cloudflare/puppeteer.
// Example worker entry (e.g., src/worker.ts)
import { findTech } from 'whats-that-tech/cloudflare'; // Note the /cloudflare import
export default {
async fetch(request: Request, env: { MYBROWSER: any }, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const targetUrl = url.searchParams.get('url');
const customFpFile = url.searchParams.get('fingerprints'); // Example: get URL from query param
if (!targetUrl) {
return new Response('Missing target URL parameter', { status: 400 });
}
if (!customFpFile) {
return new Response('Missing fingerprints URL parameter', { status: 400 });
}
try {
const results = await findTech({
url: targetUrl,
customFingerprintsFile: customFpFile // Must be a URL
}, env);
return new Response(JSON.stringify(results), { headers: { 'Content-Type': 'application/json' } });
} catch (error) {
console.error('Error in findTech:', error);
return new Response(`Error detecting tech: ${error instanceof Error ? error.message : String(error)}`, { status: 500 });
}
},
};Make sure your wrangler.toml includes the browser binding:
[[browser]]
binding = "MYBROWSER"To run the example files:
# Run the simple example
pnpm tsx examples/simple-raw-results.ts
# Run other examples (if available)
pnpm tsx examples/exclude-pixels.ts
pnpm tsx examples/chunking-batch-crawl.tsEach technology also has test fixtures to ensure accurate detection:
*.pass.html: Positive detection case*.fail.html: Negative case
These fixtures help validate detectors and prevent false positives/negatives.
