Plugins & MIME types
What Your Browser Exposes via navigator.plugins
The PDF viewer's plugin entries, associated MIME types, and the pdfViewerEnabled flag.
Plugins & MIME types
Reading signal in your browser...
The expected shape on real browsers
- Desktop Chrome / Edge: 5 plugin objects, all pointing at the internal PDF Viewer; MIME types include application/pdf and text/pdf; pdfViewerEnabled = true.
- Firefox: plugins is empty or has a single generic PDF entry; MIME types include application/pdf.
- Safari: plugins is empty; the browser handles PDFs via the OS, not through the plugin API.
- Mobile Chrome / Safari: both usually empty.
Bot signals
- Desktop Chrome UA + empty plugins = old headless mode.
- Desktop Chrome UA + non-standard plugin count (say, 8) = someone patched navigator.plugins wrongly.
- Safari UA + non-empty plugins = spoofed UA.
Frequently asked questions
→Aren't plugins dead?
Almost. NPAPI plugins (Flash, Silverlight, Java applets) are gone. But the plugin/mimeTypes APIs still exist because Chrome exposes its internal PDF Viewer through them. On modern desktop Chrome you'll see 5 plugin entries pointing at the same PDF viewer, plus MIME types for application/pdf and text/pdf. Firefox and Safari expose different sets.
→Why is navigator.plugins.length === 0 suspicious?
Because real Chrome on desktop always has non-zero plugins (the built-in PDF viewer). Old headless Chrome versions returned an empty array, which is the classic detection trick. New headless mode (--headless=new) fixed this, but plenty of scrapers still run the old code path.
→What if I see too many plugins?
Also suspicious. Some stealth plugins over-populate navigator.plugins with 8+ fake entries to look 'more realistic'. Real Chrome has exactly 5 plugin objects, all backed by the PDF viewer. Anti-bot vendors know the exact expected list per Chrome version.
→Does pdfViewerEnabled help?
It's a newer property (Chromium 94+) that lets pages ask directly whether the browser will handle PDF navigation. Its value should be true on desktop Chrome and false when the user disabled the built-in viewer. Mismatch with navigator.plugins is a red flag.
→How should scrapers handle this?
Don't override navigator.plugins unless you know the expected list for your target Chrome version. Better: run new headless mode (--headless=new), which populates plugins correctly out of the box.