cdnjs is one of the most widely used free public CDNs on the web. It is a community-run, open-source library-delivery service hosted on Cloudflare's network, and it lets a developer drop a single <script> tag into a page to pull in jQuery, Font Awesome, Lodash, or almost any popular open-source package without hosting a file. That convenience is exactly why “loads assets from cdnjs” is a useful detection signal: it tells you something concrete about how a team builds and ships its front end.
This guide walks through detecting cdnjs on a website—what it actually is, where it shows up in a page's source, how to spot it manually, and how a single DetectZeStack API call returns cdnjs along with the specific libraries and versions it is delivering. We will start with a one-line check you can run right now.
What Is cdnjs and Why Detect It
cdnjs (short for “CDN for JavaScript”) is a free, open-source content delivery service that mirrors thousands of popular front-end libraries and serves them from the cdnjs.cloudflare.com hostname. Where an infrastructure CDN like Cloudflare or Amazon CloudFront proxies a whole site's traffic, cdnjs does something narrower: it hands out versioned copies of other people's open-source code so a page does not have to bundle and self-host them.
A confirmed cdnjs reference is a small but specific window into a team's front-end habits:
They lean on public CDNs for dependencies. Loading libraries fromcdnjs.cloudflare.comrather than bundling and self-hosting them is a deliberate choice. It often signals a leaner build setup—static sites, server-rendered pages, or templates where a script tag is faster than running a bundler.
It usually arrives with the library it delivers. cdnjs rarely travels alone. The same page that references it is almost always loading a detectable library—jQuery, Font Awesome, a charting or animation library—so one detection often hands you a slice of the front-end stack as well.
It hints at a supply-chain posture. Teams that pull live third-party scripts from a public CDN have a different dependency surface than teams that vendor everything. For security tooling, software-composition-analysis, or subresource-integrity products, that distinction is the qualifier.
The same detection pipeline applies to any technology DetectZeStack recognizes. We have companion guides for the sibling library-delivery CDN —the fingerprint changes, the workflow stays the same.
How cdnjs Shows Up in a Website's Source
cdnjs is a library-delivery CDN. It exists to serve versioned, open-source assets—a specific build of jQuery, a pinned release of Font Awesome, a particular Lodash version—straight from Cloudflare-hosted mirrors. A company does not put its own domain behind cdnjs; it references cdnjs from inside its pages to fetch other people's code.
That has a direct consequence for detection. Finding cdnjs on a site does not tell you who hosts the site or how its traffic is proxied. It tells you the page intentionally pulls at least one third-party asset from cdnjs, and it points you at which asset when that asset is itself fingerprintable.
Script tags pointing to cdnjs.cloudflare.com
Every cdnjs reference resolves to the same hostname. In the page source it looks like an ordinary script or stylesheet link:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
The constant across all of them is the cdnjs.cloudflare.com host. That single string is the primary fingerprint: if it appears in a script src or a link href in the served HTML, the page references cdnjs. It is also why cdnjs implies Cloudflare—the service is hosted on Cloudflare's network, so a cdnjs match means the page is pulling code from a Cloudflare-hosted origin, distinct from the whole site being proxied through Cloudflare.
Library and version fingerprints cdnjs leaves behind
cdnjs URLs are unusually informative because the path is structured: /ajax/libs/<library>/<version>/<file>. The example above encodes the library (jquery) and the exact version (3.6.0) right in the URL. That structure means two things show up in a single page fetch—the delivery method (cdnjs) and the delivered library, frequently with its version parsed straight from the path.
| Signal | Example | What It Tells You |
|---|---|---|
| cdnjs host reference | cdnjs.cloudflare.com | cdnjs confirmed (CDN) |
| Delivered library + version | .../ajax/libs/jquery/3.6.0/jquery.min.js | jQuery confirmed, version parsed |
| Self-hosted library | /static/js/jquery.min.js | Library detected, no cdnjs signal |
| No cdnjs reference | (none in HTML) | Page does not load assets from cdnjs |
Manual Ways to Detect cdnjs (and Their Limits)
Because cdnjs lives in the page body, you can spot the raw signal yourself without any tooling. The fastest check is a single curl piped into grep:
$ curl -s https://example.com | grep -o "cdnjs\.cloudflare\.com" | head -1
cdnjs.cloudflare.com
One match is enough to confirm the page references cdnjs. To see exactly what it is pulling, widen the pattern to capture the full URLs and read the library and version out of each path:
$ curl -s https://example.com \
| grep -oE "cdnjs\.cloudflare\.com/ajax/libs/[^\"']+" | sort -u
cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css
This works, but it has real limits when you move past one site. The reference only appears if it is present in the server-rendered HTML—a script injected later by client-side JavaScript, or one loaded through a tag manager, may not show up in a plain curl. You also have to parse each path yourself to turn it into structured data, and running this across hundreds of domains means writing your own concurrency, retry, and timeout handling. That is the gap a detection API closes: it normalizes the same body signal into clean JSON and runs it at scale.
Detect cdnjs Programmatically with the DetectZeStack API
When DetectZeStack fetches a page, it scans the HTML for script and link references to cdnjs.cloudflare.com. A match returns cdnjs under the CDN category at confidence 100, with source: "http"—because the evidence came directly from the HTTP response body.
You can try it right now against the public demo endpoint—no API key required. The demo is IP-rate-limited, so use it for spot checks rather than bulk scans:
$ curl -s "https://detectzestack.com/demo?url=example.com" \
| jq '.technologies[] | select(.name == "cdnjs")'
{
"name": "cdnjs",
"categories": ["CDN"],
"confidence": 100,
"description": "cdnjs is a free distributed JS library delivery service.",
"website": "https://cdnjs.com",
"icon": "cdnjs.svg",
"source": "http",
"version": "",
"cpe": ""
}
cdnjs is matched directly off the cdnjs.cloudflare.com reference in the page and categorized under CDN. It carries source: "http" because the evidence came from the HTTP response body, and the version field is empty—cdnjs itself is a delivery network, not a versioned library, so there is nothing to read a version from. The libraries it delivers are where versions show up.
Reading the technologies, version, and categories fields
When you want the full stack for a domain rather than a filtered slice, call /analyze with your API key. A page that loads jQuery from cdnjs comes back like this:
$ curl -s "https://detectzestack.p.rapidapi.com/analyze?url=example.com" \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: detectzestack.p.rapidapi.com"
{
"url": "https://example.com",
"domain": "example.com",
"technologies": [
{
"name": "cdnjs",
"categories": ["CDN"],
"confidence": 100,
"description": "cdnjs is a free distributed JS library delivery service.",
"website": "https://cdnjs.com",
"icon": "cdnjs.svg",
"source": "http",
"version": "",
"cpe": ""
},
{
"name": "jQuery",
"categories": ["JavaScript libraries"],
"confidence": 100,
"description": "jQuery is a JavaScript library designed to simplify HTML DOM tree traversal and manipulation, as well as event handling, CSS animation, and Ajax.",
"website": "https://jquery.com",
"icon": "jQuery.svg",
"source": "http",
"version": "3.6.0",
"cpe": ""
},
{
"name": "Cloudflare",
"categories": ["CDN"],
"confidence": 100,
"description": "Cloudflare is a web-infrastructure and website-security company that provides content-delivery-network services, DDoS mitigation, and ICANN-accredited domain registration services.",
"website": "https://www.cloudflare.com",
"icon": "CloudFlare.svg",
"source": "http",
"version": "",
"cpe": ""
}
],
"categories": {
"CDN": ["cdnjs", "Cloudflare"],
"JavaScript libraries": ["jQuery"]
},
"meta": { "status_code": 200, "tech_count": 3, "scan_depth": "full" },
"cached": false,
"response_ms": 1842
}
Three entries from one page. cdnjs is the delivery method; jQuery is the thing being delivered, with its version parsed from the script URL; and Cloudflare appears because cdnjs is hosted on Cloudflare's network and its fingerprint implies Cloudflare. The top-level categories map groups every detection, so you can pull all CDNs with .categories["CDN"] without iterating the array. The meta object carries the HTTP status_code, the tech_count, and the scan_depth; response_ms and cached sit at the top level.
One field to watch for list building is meta.scan_depth. A value of "full" means the HTTP fetch succeeded and body detection ran. A value of "partial" means the site blocked or timed out the HTTP request and only DNS and TLS layers completed—and since cdnjs lives in the body, an absent cdnjs entry on a "partial" scan tells you nothing. Those domains belong in a retry queue, not your rejects file.
The honest limitation: cdnjs detection depends on the HTML actually containing the reference. If a script is injected later by client-side JavaScript that never runs during a server-side fetch, or if the reference sits behind a tag manager, it may not appear in the fetched body. As with any body-fingerprint signal, absence of a detection is an unknown, not proof the site never touches cdnjs.
Detecting cdnjs Across Many Sites at Scale
For list building, POST /analyze/batch accepts up to 10 URLs per request and analyzes them concurrently. Each entry in the response carries either a full analysis result or an error for domains that could not be fetched:
$ curl -s -X POST "https://detectzestack.p.rapidapi.com/analyze/batch" \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" \
-H "Content-Type: application/json" \
-d '{"urls": ["example.com", "getbootstrap.com", "wordpress.org"]}'
The response wraps one result object per URL, each with the same shape as a single /analyze response:
{
"results": [
{ "url": "example.com", "result": { "...full analysis..." : "" } },
{ "url": "getbootstrap.com", "result": { "...full analysis..." : "" } },
{ "url": "wordpress.org", "result": { "...full analysis..." : "" } }
],
"total_ms": 2341,
"successful": 3,
"failed": 0
}
Because each result matches the single-domain shape, the filtering logic is identical whether you scan one domain or a thousand. Here is a complete, copy-pasteable pipeline using nothing but bash, curl, and jq. It reads domains.txt (one domain per line), sends batches of 10 to /analyze/batch, and appends every domain where cdnjs is detected to cdnjs_leads.csv, recording the tech count alongside it:
#!/usr/bin/env bash
# find-cdnjs.sh — filter a domain list down to cdnjs-confirmed sites
KEY="YOUR_KEY"
HOST="detectzestack.p.rapidapi.com"
echo "domain,tech_count" > cdnjs_leads.csv
# Process domains.txt in batches of 10 (the /analyze/batch maximum)
xargs -n 10 < domains.txt | while read -r batch; do
urls=$(printf '%s\n' $batch | jq -R . | jq -s '{urls: .}')
curl -s -X POST "https://$HOST/analyze/batch" \
-H "X-RapidAPI-Key: $KEY" \
-H "X-RapidAPI-Host: $HOST" \
-H "Content-Type: application/json" \
-d "$urls" |
jq -r '.results[]
| select(.result != null)
| .result as $r
| select([$r.technologies[].name] | index("cdnjs"))
| [$r.domain, ($r.meta.tech_count | tostring)]
| @csv' >> cdnjs_leads.csv
done
wc -l cdnjs_leads.csv
A 1,000-domain list becomes 100 batch calls. The index("cdnjs") guard keeps a domain whenever cdnjs appears in its technology list, and select(.result != null) skips domains that failed to resolve (those come back with an error field instead of a result). For a deeper treatment of batch throughput, retries, and a production Python scanner, see and the broader and, for one specific provider, .
curl -s "https://detectzestack.com/demo?url=yourdomain.com" | jq '.technologies[].name'
Conclusion
Detecting cdnjs comes down to reading one thing well: a cdnjs.cloudflare.com reference in the page HTML. Because cdnjs is a library-delivery CDN rather than an infrastructure one, it is detected from the body, not from DNS or headers—and the real prize is usually the library it carries, whose name and version are encoded right in the URL path and returned alongside it. A single /analyze call answers the one-domain question; /analyze/batch turns a raw domain list into a cdnjs-confirmed list; and meta.scan_depth tells you which negatives are real and which are unknowns worth a retry. Swap the jq filter and the same pipeline segments by front-end library, infrastructure CDN, or backend runtime instead.
Related Reading
— The other major library-delivery CDN (ajax.googleapis.com), with the delivered library and version encoded in the URL path
— One of the most common libraries delivered over cdnjs, and its own fingerprints
— Deep dive on /analyze/batch throughput, retries, and a Python scanner
Lead Enrichment Pipeline with Tech Detection — Turning raw detections into scored, routable leads
SOCIAL SHARE CARD GENERATOR