Local Port Scanner

Scan open ports on localhost — detect running services and exposed vulnerabilities from your browser

Scan Your Ports

What to scan

Why This Matters

Real-world incidents that show why knowing your open ports matters.

OpenClaw CVE-2026-25253 (Feb 2026)

AI agent framework bound to 0.0.0.0:18789 by default. 30,000+ instances exposed. 1-click RCE: visiting a malicious page steals the gateway token via the Control UI (trusts gatewayUrl from query string without validation), letting the attacker connect to the victim's local gateway and execute code. Even loopback-only instances were exploitable because the victim's browser initiates the connection. 512 vulnerabilities found in audit, 8 critical. Patched in 2026.1.29.

Source: The Hacker News

Meta & Yandex localhost spying (2025)

Meta and Yandex apps found communicating through localhost ports on Android devices to track users across apps.

Source: TechReport

ShadowRay (2024-2025)

Attackers exploited Ray AI framework dashboards exposed on port 8265 (bound to 0.0.0.0 by default, no auth). Used for crypto mining and compute theft. Grew from thousands to 230,000 exposed instances.

Source: Oligo Security

0.0.0.0 Day (2024)

Oligo Security disclosed an 18-year-old browser vulnerability. Malicious websites could bypass browser protections by using 0.0.0.0 instead of 127.0.0.1 to reach local services, enabling remote code execution. Affected Chrome, Firefox, Safari on macOS/Linux. Chrome patched it in v128-133.

Source: Oligo Security

Ollama port 11434 (2024)

175,000+ Ollama AI servers found exposed with no authentication. CVE-2024-37032 allowed remote code execution. Attackers resold stolen GPU compute through marketplaces.

Source: Wiz

eBay, Best Buy & others scanning your ports (2020)

Websites like eBay, Chick-fil-A, Best Buy, Kroger, and Macy's were caught scanning visitors' localhost ports for fingerprinting and bot detection — without consent. Brave browser now blocks this.

Source: Nullsweep

Embed in Your Website

Add local port scanning to your own site with a single script tag. Your integration automatically gets updated scan techniques, service definitions, and CVE data.

Scan known vulnerable ports

<script src="https://localportscan.com/scanner.js"></script>
<script>
  LocalPortScan.scan('vulnerable').then(results => {
    results.filter(r => r.open).forEach(r => {
      console.log(`Port ${r.port} is open (${r.service})`);
    });
  });
</script>
// Console output:
Port 6379 is open (Redis)
Port 8080 is open (HTTP-Proxy)
Port 27017 is open (MongoDB)

Scan popular ports with progress callbacks

<script src="https://localportscan.com/scanner.js"></script>
<script>
  LocalPortScan.scan('popular', {
    onOpen(port, service) {
      console.log(`OPEN: ${port} (${service})`);
    },
    onProgress(scanned, total) {
      console.log(`${scanned}/${total}`);
    },
    onDone(results) {
      console.log('Done.', results.filter(r => r.open).length, 'open');
    }
  });
</script>
// Console output:
OPEN: 3000 (Dev/Grafana)
20/150
OPEN: 5432 (PostgreSQL)
40/150
...
Done. 2 open

Check if a single service is running

<script src="https://localportscan.com/scanner.js"></script>
<script>
  LocalPortScan.scan([5432]).then(([pg]) => {
    console.log(pg);
  });
</script>
// Result object:
{port: 5432, open: true, service: "PostgreSQL", cves: [{id: "CVE-2019-9193", desc: "COPY command — arbitrary command execution"}]}

Send results to a webhook

<script src="https://localportscan.com/scanner.js"></script>
<script>
  LocalPortScan.scan('vulnerable').then(results => {
    const open = results.filter(r => r.open);
    if (open.length) {
      fetch('https://your-server.com/webhook', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ports: open })
      });
    }
  });
</script>
// POST body:
{"ports":[{"port":6379,"open":true,"service":"Redis","cves":[{"id":"CVE-2022-0543","desc":"Lua sandbox escape — RCE via eval"}]}]}

API Reference

MethodDescription
LocalPortScan.scan(ports, options?)Scan ports. scan([80, 443, 8080]) or scan('vulnerable'). Presets: 'vulnerable', 'popular', 'dev', 'well-known', 'common', 'full'. Returns Promise<[{port, open, service, cves?}]>
LocalPortScan.scanRange(start, end, options?)Scan a port range. scanRange(3000, 9000). Returns Promise<[{port, open, service, cves?}]>
LocalPortScan.stop()Stop the current scan. onDone will not fire and the promise will not resolve.
LocalPortScan.identify(port)Get service name. identify(3306)"MySQL", identify(99999)null
LocalPortScan.ready()Promise that resolves when config is loaded. scan() waits automatically, so you only need this for early access to services or config.
LocalPortScan.isScanning()Returns true while a scan is running, false otherwise.
LocalPortScan.presetsArray of preset names. ["vulnerable", "popular", "dev", "well-known", "common", "full"]
LocalPortScan.servicesPort-to-name map. {80: "HTTP", 443: "HTTPS", 3306: "MySQL", ...}
LocalPortScan.configFull config object. {services: {...}, cves: {...}, popularPorts: [...], vulnerablePorts: [...]}

Options: timeout (ms, default 1500), concurrency (default 20), onOpen(port, service), onProgress(scanned, total), onDone(results)

FAQ

What can this tool do?

It runs entirely in the browser using JavaScript. It scans all 65,535 ports with no exclusions. It identifies 120+ services (MySQL, Redis, Docker, Kubernetes, etc.) by port number. Timeout, concurrency, and port ranges are configurable. You can embed it in your own site with a single script tag.

How does the detection work technically?

fetch() with mode: 'no-cors' against http://127.0.0.1:<port>/. Open ports complete the TCP handshake and the server sends a response (even 400/RST after headers) — the promise resolves with an opaque Response. Closed ports get ECONNREFUSED at the TCP level — the promise rejects with TypeError. This is a binary signal from the network stack, not a timing side-channel. No calibration, no thresholds.

Why fetch() and not WebSocket/Image/iframe timing?

We tested all of them. On localhost, RTT is sub-millisecond — timing differences between open and closed ports are indistinguishable, especially under concurrent load where the browser batches and throttles connections. The Chrome iframe chrome-error:// trick (PortSwigger technique) is patched in modern Chromium. Firefox iframe.onload only fires for open ports but doesn't work on Chrome. fetch() no-cors is the only method that gives a clean binary signal across all Chromium-based browsers.

What about the ~85 Chromium-restricted ports?

Chromium's net/base/port_util.cc defines kRestrictedPorts[] — ports like 21 (FTP), 22 (SSH), 25 (SMTP), 6667 (IRC). The browser returns ERR_UNSAFE_PORT before any TCP attempt, which looks identical to a closed port. We don't skip them — scanning them causes no false positives, and some users run non-standard services on these ports.

What's the 0.0.0.0 Day vulnerability and how does it relate?

Oligo Security disclosed (Aug 2024) that browsers allowed public websites to reach local services via 0.0.0.0, bypassing Chromium's Private Network Access (PNA) checks that block 127.0.0.1. This 18-year-old flaw meant any website could probe and exploit localhost services on macOS/Linux. It was used in ShadowRay attacks targeting Ray AI dashboards (port 8265). Chrome patched it in v128-133 by blocking 0.0.0.0. This tool helps you audit what's actually listening — because if we can reach it, so could a malicious page before the fix.

Why does the "Known vulnerable" preset exist?

Many popular tools bind to 0.0.0.0 or 127.0.0.1 with no authentication by default: Redis (6379), MongoDB (27017), Elasticsearch (9200), Docker API (2375), Ollama (11434), Jupyter (8888), Kubernetes kubelet (10250), Prometheus (9090). These have documented CVEs and have been mass-exploited. The preset lets you quickly check if any are listening on your machine.

Can any website do this same scan on my machine?

Yes. Any page you visit can run fetch('http://127.0.0.1:<port>/') in no-cors mode and determine if the port is open. eBay, Best Buy, Macy's, and Chick-fil-A were caught doing this for bot detection and fingerprinting. Brave now blocks it by default. This tool shows you exactly what any website can see on your machine.

Why only localhost? Can I scan LAN IPs?

Chromium's Private Network Access (PNA) spec, shipping since Chrome 94+, blocks public websites from making requests to private IPs (10.x, 172.16.x, 192.168.x). Requests to 127.0.0.1 are currently still possible via no-cors fetch (the 0.0.0.0 bypass is patched, but loopback probes still work in most browsers). LAN scanning from a hosted website would require the target to send CORS preflight headers, which local services don't.

How do I embed the scanner in my own tool?

Include <script src="https://localportscan.com/scanner.js"></script>. The LocalPortScan global exposes .scan(), .scanRange(), .stop(), and .identify(). See the integration examples above.

The scan shows no open ports — what's wrong?

Privacy extensions (uBlock Origin, Privacy Badger, NoScript) and Brave Shields can block requests to 127.0.0.1. Try disabling your ad blocker or shields for this site. Safari also has limited support for localhost probes — use a Chromium-based browser for accurate results.

What are the browser compatibility differences?

Chromium-based (Chrome, Edge, Brave): full support, fetch() no-cors works reliably. Firefox: works but may have different timing characteristics for the ERR_UNSAFE_PORT set. Safari: limited — WebKit has stricter localhost restrictions and may block some probes silently.

What happens with HTTPS services / mixed content?

The scanner probes over HTTP. For ports where the service speaks TLS only (443, 8443, etc.), the TCP connection still succeeds — the TLS negotiation starts, which is enough for the fetch to resolve. The port will correctly show as open. Clickable links for known SSL ports use https://.