How to Detect Netlify Hosting on Any Website

September 15, 2026 · 10 min read

Netlify is one of the platforms that made the Jamstack deployment model mainstream: connect a Git repository, get a build pipeline, a global edge network, automatic HTTPS, and a preview URL for every branch. Plenty of sites you use every day sit on it, and most of them do not advertise the fact anywhere on the page.

They advertise it in the response headers. Netlify stamps a server: Netlify banner and a family of x-nf-* headers on essentially every response it serves, and custom domains usually resolve through a CNAME chain that names Netlify outright. This guide walks through those signals, shows you how to read them by hand with curl and dig, and then shows how one DetectZeStack API call returns Netlify — along with the framework running on top of it — in structured JSON you can run across a whole domain list.

What Netlify Hosting Is and Why It Is Worth Detecting

Netlify is a platform for front-end builds, static assets, and serverless functions. You point it at a repository, it runs your build command, and it publishes the output to an edge network with a CDN in front. Like Vercel, Cloudflare Pages, and Render, it is a Platform-as-a-Service: the team ships code, the platform owns the servers.

A confirmed Netlify detection is a small fact that carries a lot of context:

The same workflow applies to every hosting platform DetectZeStack recognizes — see the companion guides for Vercel, AWS hosting, and CDN and hosting providers generally. The fingerprints change; the pipeline does not.

The Signals That Reveal Netlify Hosting

Netlify is detectable from two independent layers that fail in different situations. HTTP headers are the loudest and most specific signal, and the one a reverse proxy can rewrite. DNS is quieter and narrower, and survives when the headers are gone. Reading both turns a guess into a confirmation.

HTTP Response Headers (the x-nf-* Prefix)

Every response served from Netlify's edge carries a recognizable header set:

Any one of these confirms Netlify served the response. The x-nf- prefix matters most for detection because it is the part most likely to survive a proxy that rewrites the Server banner — which is why DetectZeStack matches the whole prefix rather than one specific header name.

DNS CNAME Chains (.netlify.app, .netlify.com, netlifyglobalcdn.com)

Every Netlify site gets a subdomain on .netlify.app, and a custom domain usually reaches the platform through a CNAME. Three suffixes show up in practice:

DNS records are public. Unlike a response header, a company cannot strip the CNAME without actually moving the domain somewhere else, which makes this the layer that catches a site whose headers have been rewritten.

Why Header-Only Checks Miss Custom-Domain Netlify Sites

A header-only check breaks in one direction and a DNS-only check breaks in the other.

Headers disappear behind a proxy. If a site puts another CDN or reverse proxy in front of Netlify, that proxy terminates the request and writes its own Server banner. The Netlify origin is still there; the evidence at the edge is not. Sometimes the x-nf-request-id passes through, sometimes it does not.

CNAMEs are not always there to find. A bare apex domain usually cannot use a CNAME at all, so Netlify publishes A records for that case instead — as of September 15, 2026, apex-loadbalancer.netlify.com resolves to 75.2.60.5 and 99.83.231.61. DetectZeStack's DNS layer matches CNAME targets, not IP addresses, so an apex on A records produces no DNS-source detection; the scan falls back to www. and then to the header signal. On the same date, www.smashingmagazine.com returned server: Netlify and an x-nf-request-id while publishing A records rather than a Netlify CNAME. Header detection catches it; a DNS-only check would not.

The practical takeaway: neither layer is complete alone. Headers cover apex-only and A-record setups; DNS covers proxied and header-rewritten ones. Running both in one pass is why a detection API beats a shell one-liner past a handful of domains.

LayerSignalWhat It Tells You
HTTP header server: Netlify Netlify served the response (loud, rewritable)
HTTP header x-nf-request-id Netlify edge handled the request; survives most banner rewrites
HTTP header cache-status: "Netlify Edge" Netlify CDN cache outcome for this request
DNS CNAME *.netlify.app / *.netlify.com Domain points at Netlify (cannot be stripped)
DNS CNAME *.netlifyglobalcdn.com Domain routed through Netlify's global CDN tier

Detect Netlify Hosting Manually in Your Browser or Terminal

Every signal above is something you can confirm yourself in a few seconds. Start with the headers:

$ curl -sI https://www.netlify.com | grep -i "server:\|x-nf\|cache-status"
server: Netlify
cache-status: "Netlify Edge"; hit; ttl=31491324
x-nf-request-id: 01M2JJHAH0YWRPW0EG62KE0NA6

That is real output from September 15, 2026. The server: Netlify line alone settles it; x-nf-request-id is your backup when a site rewrites the banner. In a browser the same information lives in DevTools: Network tab, reload, click the top document request, read the Response Headers panel.

curl -I and dig CNAME, and What Each One Cannot Tell You

Next, confirm from DNS, which no proxy controls:

$ dig +short www.adyen.com CNAME
adyen.netlifyglobalcdn.com.

A CNAME ending in .netlify.app, .netlify.com, or .netlifyglobalcdn.com proves the domain points at Netlify regardless of what the headers say. Try the same query against an apex domain and you will often get nothing back — that is the A-record case described above, not evidence of absence.

Both checks stop scaling the moment you have a list. Headers need one live HTTP request per domain with your own timeouts, retries, and redirect handling; DNS needs CNAME chain resolution and a suffix table; and reconciling the two answers into one verdict is code you now own. That is the gap a detection API closes.

Detect Netlify Hosting Programmatically with the DetectZeStack API

DetectZeStack checks both layers in the same pass: it fetches the page and fingerprints the response headers, resolves the domain's CNAME against a hosting signature table, inspects the TLS certificate, and merges everything into one deduplicated technology list. Netlify comes back under two categories, PaaS and CDN.

Live curl Example Against /analyze

Here is a real scan of a custom-domain Netlify site. adyen.com is a good example because the Netlify signal arrives alongside the framework stack sitting on top of it:

$ curl -s "https://detectzestack.p.rapidapi.com/analyze?url=adyen.com" \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: detectzestack.p.rapidapi.com"
{
  "url": "https://www.adyen.com/",
  "domain": "www.adyen.com",
  "technologies": [
    {
      "name": "HSTS",
      "categories": ["Security"],
      "confidence": 100,
      "description": "HTTP Strict Transport Security (HSTS) informs browsers that the site should only be accessed using HTTPS.",
      "website": "https://www.rfc-editor.org/rfc/rfc6797#section-6.1",
      "source": "http"
    },
    {
      "name": "Let's Encrypt",
      "categories": ["SSL/TLS certificate authority"],
      "confidence": 70,
      "source": "tls"
    },
    {
      "name": "Netlify",
      "categories": ["PaaS", "CDN"],
      "confidence": 100,
      "description": "Netlify providers hosting and server-less backend services for web applications and static websites.",
      "website": "https://www.netlify.com/",
      "icon": "Netlify.svg",
      "source": "http"
    },
    {
      "name": "Nuxt.js",
      "categories": ["JavaScript frameworks", "Web frameworks", "Web servers", "Static site generator"],
      "confidence": 100,
      "description": "Nuxt is a Vue framework for developing modern web applications.",
      "website": "https://nuxt.com",
      "icon": "Nuxt.js.svg",
      "source": "http"
    },
    {
      "name": "Vue.js",
      "categories": ["JavaScript frameworks"],
      "confidence": 100,
      "description": "Vue.js is an open-source model-view-viewmodel JavaScript framework for building user interfaces and single-page applications.",
      "website": "https://vuejs.org",
      "icon": "vue.svg",
      "source": "http"
    }
  ],
  "categories": {
    "CDN": ["Netlify"],
    "JavaScript frameworks": ["Nuxt.js", "Vue.js"],
    "PaaS": ["Netlify"],
    "Security": ["HSTS"],
    "SSL/TLS certificate authority": ["Let's Encrypt"],
    "Static site generator": ["Nuxt.js"],
    "Web frameworks": ["Nuxt.js"],
    "Web servers": ["Nuxt.js"]
  },
  "meta": { "status_code": 200, "tech_count": 7, "scan_depth": "full" },
  "cached": true,
  "response_ms": 0
}

The technologies array above is trimmed for readability — the full scan returned seven entries, as meta.tech_count reports. Note cached: true and response_ms: 0: this domain had been scanned recently, so the result came from cache. A cold scan returns a real timing — a fresh scan of netlify.com the same day returned "cached": false, "response_ms": 1981. Caching is why a large batch run finishes faster than a naive per-domain estimate suggests.

Reading the Netlify Entry in the technologies Array

Three fields on the Netlify entry are worth understanding, because they tell you how the detection was made and therefore how much to trust it:

Only one Netlify entry ever appears, no matter how many layers agree — the merge deduplicates by technology name and keeps the highest-signal source. The version and cpe fields are omitted because Netlify is a hosted platform, not a versioned software package with a CPE identifier. For which detections do carry one, see detecting vulnerable technologies with CPE.

One more field matters for list building: meta.scan_depth. "full" means the HTTP fetch succeeded and header fingerprinting ran, so a missing Netlify entry is a real negative. "partial" means the HTTP request was blocked or timed out and only the DNS and TLS layers completed — Netlify can still surface from the CNAME there, but a miss is an unknown, not proof of absence. Partial scans belong in a retry queue, not your rejects file.

Free Preview with /demo (No API Key Required)

You can try all of this right now against the public demo endpoint, which needs no API key. It is IP-rate-limited, so use it for spot checks rather than bulk work. Here it is filtered down to the Netlify entry:

$ curl -s "https://detectzestack.com/demo?url=netlify.com" \
  | jq '.technologies[] | select(.name == "Netlify")'
{
  "name": "Netlify",
  "categories": ["PaaS", "CDN"],
  "confidence": 100,
  "description": "Netlify providers hosting and server-less backend services for web applications and static websites.",
  "website": "https://www.netlify.com/",
  "icon": "Netlify.svg",
  "source": "http"
}

Swap in any domain you are curious about. For a yes-or-no answer instead of the full object, the authenticated /check endpoint takes a tech parameter and returns a detected boolean with the confidence and categories:

$ curl -s "https://detectzestack.p.rapidapi.com/check?url=adyen.com&tech=Netlify" \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" \
  | jq '{domain, technology, detected, confidence}'

Scaling to a List of Domains with /analyze/batch

POST /analyze/batch accepts up to 10 URLs per request and analyzes them concurrently. The response carries a results[] array where each item has either a full analysis under result or an error string, plus successful, failed, and total_ms counters. Here is a complete loop that reads domains.txt and writes every Netlify-hosted domain to a CSV:

#!/usr/bin/env bash
set -euo pipefail

HOST="detectzestack.p.rapidapi.com"
KEY="YOUR_KEY"

echo "domain,confidence,source" > netlify-hits.csv

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)
         | . as $r
         | $r.result.technologies[]
         | select(.name == "Netlify")
         | [$r.result.domain, .confidence, .source]
         | @csv' >> netlify-hits.csv
  sleep 1
done < <(xargs -n 10 < domains.txt)

The select(.result != null) guard keeps failed fetches from breaking the pipeline — those items carry an error string instead and belong in a retry pass. Emitting source next to confidence is worth the extra column: it separates header-confirmed hits from DNS-only ones without a re-scan. For throughput tuning and a Python version, see how to batch scan 1,000 websites.

Comparing Two Sites Side by Side with POST /compare

POST /compare takes 2 to 10 URLs and returns each domain's technologies along with a unique list per domain and a shared list across all of them. It is the fastest way to show that two companies in the same market made different hosting decisions:

$ curl -s -X POST "https://detectzestack.p.rapidapi.com/compare" \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["netlify.com", "adyen.com"]}' \
  | jq '{shared, per_domain: [.domains[] | {domain, unique}]}'

Live scans of those two on September 15, 2026 both return Netlify under PaaS and CDN, so Netlify lands in shared — while the unique lists diverge: www.netlify.com ships Astro 5.18.2, and www.adyen.com ships Nuxt.js and Vue.js. Same platform, two different front-end bets.

Netlify Plus the Rest of the Stack: Frameworks, CDNs, and Migrations

Netlify is filed under both PaaS and CDN. That is accurate rather than redundant — it is where the site is hosted and the network that serves it. Two consequences follow.

First, a Netlify detection is often the only CDN entry on the domain. If you are segmenting by CDN and expecting Cloudflare, Fastly, or CloudFront, remember that Netlify sites frequently have no separate CDN layer — the platform is the CDN, so treating “no third-party CDN” as “no CDN” mislabels them. The broader CDN and hosting provider guide covers how those categories overlap.

Second, Netlify tells you nothing about the rendering model on its own. The platform started as static hosting and grew into server-rendered framework output and serverless functions. The framework entries in the same scan disambiguate: Astro on netlify.com, Nuxt.js and Vue.js on adyen.com. Both are Netlify; they are not the same kind of site. See detecting the JavaScript framework a site uses.

The Netlify-versus-Vercel signals are cleanly separable: Netlify uses server: Netlify, x-nf-* headers, and .netlify.app CNAMEs; Vercel uses server: Vercel, x-vercel-id, and cname.vercel-dns.com. Scan a market's domain list for both and you get an adoption map of two competing platforms. The Vercel detection guide covers that side in the same detail.

Because hosting changes are rare and deliberate, re-scanning the same list on a schedule turns this into a migration feed: a domain that loses its Netlify entry and gains a Vercel or CloudFront one just moved platforms. See tracking technology changes over time for how to run that as a recurring job.

Common Use Cases for Netlify Detection

All of them run on the same primitive: one /analyze call per domain, filtered for the Netlify entry, scaled with /analyze/batch.

Get Your API Key and Start Detecting Netlify Hosting

The free tier includes 100 requests per month with no credit card — enough to validate the pipeline on a sample of your domain list before scaling it up. Sign-up through RapidAPI is instant:

  1. Get a key at rapidapi.com/mlugoapx/api/detectzestack.
  2. Spot-check a domain you already know: curl -s "https://detectzestack.com/demo?url=yourdomain.com" | jq '.technologies[].name'
  3. Run /analyze/batch against your first 100 domains and keep the Netlify hits, carrying the source field through so you can tell header-confirmed from DNS-only later.

Conclusion

Detecting Netlify hosting comes down to reading two layers and knowing where each one fails. The server: Netlify banner, the x-nf-request-id header, and the cache-status: "Netlify Edge" value confirm it instantly when Netlify serves the response directly. The .netlify.app, .netlify.com, and .netlifyglobalcdn.com CNAME chains confirm it from public DNS when the headers have been rewritten. Neither covers every case alone. A single DetectZeStack /analyze call runs both, deduplicates the result, and returns Netlify under PaaS and CDN with the source and confidence attached, alongside the framework actually running on top. Swap the jq filter and the same pipeline segments by framework, CDN, or analytics vendor instead.

Related Reading

Try DetectZeStack Free

100 requests per month, no credit card required. Header, DNS, and TLS detection included on every plan.

Get Your Free API Key

Get API updates and tech detection tips

Join the mailing list. No spam, unsubscribe anytime.