How the free scan works
PlugLeak's free scan is a passive check of one public URL. It downloads what a visitor's browser can already download, searches that text for a fixed set of leaked secrets and missing protections, and scores what it finds. A scan runs only after you confirm you own the app or have permission to check it.
Every request is a GET. The scanner does not log in, submit forms, change anything on the target, query your database, or test Row Level Security policies.
What the scan fetches
- The page you submit. The URL must be http or https on port 80 or 443, with no username or password. If you leave the scheme off, https is assumed. IP addresses are rejected, as are localhost, names ending in .localhost, .local, or .internal, and any hostname with no dot. The HTML body is read up to 2 MB. Up to 4 redirects are followed, and each hop is checked against the same rules. If DNS returns a private, loopback, link-local, multicast, or other non-public address, the connection is refused before it opens.
- Same-site JavaScript. From the HTML it collects script src URLs and modulepreload or preload links to .js or .mjs files. Inside those files it looks for quoted chunk paths under /assets/, /_next/static/chunks/, and /static/js/. A script is downloaded only when its host matches the page (a leading www is ignored) or is a subdomain of that host. Scripts are fetched in batches of 5, up to 2.5 MB each. No new batch starts once 25 files or 12 MB of JavaScript have been fetched, so a scan reads at most a few more than 25 files. Only HTTP 200 bodies are searched.
- Two well-known paths. One request each to /.env and /.git/config on the site origin, up to 20 KB, with redirects turned off and a 6 second timeout.
- The TLS certificate, when the final URL is https. It connects to port 443 and reads the issuer, the expiry date, and whether the certificate is trusted. This check does not send a separate HTTP request.
HTTP requests identify themselves with the User-Agent PlugLeakScanner/0.1 (+passive security check; contact via site), so you can see PlugLeak in your logs.
What it looks for
Secrets are searched in the HTML and in the same-site scripts that were downloaded:
- Stripe live secret or restricted keys (sk_live_ and rk_live_) — critical.
- Stripe test secret keys (sk_test_) — medium. They cannot move live money; they show that a secret reached the frontend.
- Anthropic API keys (sk-ant-) — critical.
- OpenAI-style API keys (sk-, including sk-proj-, sk-svcacct-, and sk-admin-) — critical. A key that starts with sk-ant- is classified as Anthropic, not OpenAI.
- AWS access key IDs (AKIA or ASIA followed by 16 characters) — high. This is the key id, not proof that the secret access key shipped with it.
- PEM private key blocks — critical.
- GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_, and github_pat_) — critical.
- Supabase secret keys (sb_secret_) — critical.
- JSON Web Tokens. A payload whose role is service_role is critical, because that key bypasses Row Level Security. A role of anon on a token that looks like Supabase is informational: the anon key is designed to be public, and it is only safe if Row Level Security is enabled.
- Supabase publishable keys (sb_publishable_) — informational, for the same reason as the anon key.
- A Supabase project URL (a 20-character project ref on supabase.co) — informational. The finding states that the database was not queried.
- /.env is critical only when the response is HTTP 200, the body does not look like an HTML page, and at least two lines look like NAME=value assignments. Up to eight variable names are shown. The values are not stored. Otherwise the report records that /.env is not publicly exposed.
- /.git/config is high when the response is HTTP 200 and the body contains both [core] and repositoryformatversion. Otherwise the report records that /.git is not publicly exposed.
- On the HTML response only, it checks Content-Security-Policy (medium if missing), Strict-Transport-Security when the page was fetched over HTTPS (low if missing), X-Frame-Options or a CSP frame-ancestors directive (low if both are missing), X-Content-Type-Options containing nosniff (low if missing), Referrer-Policy (low if missing), and Permissions-Policy (informational if missing). If the server sends X-Powered-By, that is informational.
- If you submitted an http URL and the site is still http after redirects, that is high: the site does not redirect to HTTPS. Submitting an https URL does not separately probe plain HTTP.
- A trusted certificate with 14 or more days left is a pass, and the report includes the issuer and expiry date. Fewer than 14 days left is medium. A certificate that is presented but not trusted (including an expired one) is high. If the TLS check itself fails, the finding is medium.
- If the page returns HTTP 400 or higher, the scan still finishes and adds an informational note that results may be incomplete.
What it does not check
- It does not test Row Level Security policies, connect to your Supabase project, run SQL, or read or write any database.
- It does not log in, submit forms, or send POST, PUT, or DELETE requests.
- It does not try exploits, fuzz parameters, or bypass authentication.
- It does not crawl other pages, a sitemap, or source maps. One scan is the URL you submitted, its same-site scripts, /.env, /.git/config, and the certificate.
- It does not download JavaScript hosted on a different domain.
- It does not follow redirects for the /.env and /.git/config requests.
- It does not see server-only code, secrets that were not shipped to the browser, or whether one logged-in user can read another user's data.
- It does not check dependency vulnerabilities, open ports, storage-bucket rules, or auth-provider settings.
- It does not scan IP addresses, ports other than 80 and 443, or hosts that resolve to a private or internal network.
- It does not store a full secret. Evidence saved on the report is the first 6 characters of a match followed by ****.
How long it takes
The JavaScript crawl uses a 25 second budget. It stops starting new batches when 8 seconds or less of that budget remain, or once 25 script files or 12 MB of JavaScript have been fetched, whichever comes first.
The page fetch times out after 8 seconds. Each script times out after 7 seconds. The /.env, /.git/config, and TLS checks time out after 6 seconds. Those file and certificate checks run after the script crawl, so a slow target can run a little past 25 seconds. The API route allows up to 30 seconds.
The report shows the measured duration. The rest of the site describes a typical scan as about 20 seconds.
How the score is calculated
The score starts at 100. Each critical finding subtracts 40, each high 25, each medium 10, and each low 4. Informational notes and passing checks subtract nothing. If a critical or high secret was found in the HTML or scripts, or the site does not redirect to HTTPS, it subtracts 10 more. The result is clamped between 0 and 100. The same secret value is only counted once.
Free results and the paid fix report
The score out of 100 is free and shown as soon as the scan finishes. Before you enter an email, problem titles are replaced with a generic category and the explanation stays hidden. Passing checks keep their real titles.
After you enter an email, each finding shows its title, plain-English detail, where it was found, and the masked evidence. Copy-paste fix prompts stay locked. The email is also how the report link is sent, and how you hear when weekly monitoring launches. There is no spam; you can unsubscribe any time.
The paid fix report is a one-time purchase, not a subscription. Fix report is $9. Unlock every copy-paste fix prompt for this scan. On the report, that is one copy-paste prompt per issue, written for Lovable, Bolt, and Cursor, including what to rotate and how to verify.
10 fix reports is $29. Unlock this report + 9 credits for re-scans after every deploy. The pack issues a credit code with 9 uses left. A code can be redeemed on a later scan after you unlock that report with your email.
Payments are processed by Stripe. Card numbers are not seen or stored by PlugLeak.
A deep RLS check for verified owners, and weekly plus after-deploy monitoring, are marked coming soon. They are not part of the scan described here.
Prices are shown on the pricing section of the homepage.
Privacy
Secrets are masked (first 6 characters + ****) before the report is saved. Full secrets are not stored. For a public .env file, variable names may appear and the values do not.
The saved report holds the URL, findings, score, and timestamp so its private link can be opened again. Your IP address is used in memory to rate-limit scans and is not stored with the report.
If you enter an email, it is stored with the source (scan, waitlist, or checkout), the scanned URL, a timestamp, and whether you purchased. It may be added to the email list to send the report link and product updates. You can unsubscribe any time, or email hello@plugleak.com to be deleted.
The full policy is on the privacy page.
Related guides
FAQ
What HTTP requests does a free scan make?+
Only GET requests. It downloads the URL you submit (up to 2 MB of HTML, following at most 4 redirects), then same-site JavaScript from script tags, modulepreload or preload links, and chunk paths under /assets/, /_next/static/chunks/, and /static/js/. It also requests /.env and /.git/config once each, without following redirects. If the final URL is HTTPS, it opens a TLS connection to port 443 to read the certificate. It does not log in or submit forms.
How long does a scan take?+
The JavaScript crawl uses a 25 second budget and stops starting new batches when 8 seconds or less remain, or once about 25 script files or 12 MB of JavaScript have been fetched. The page fetch times out after 8 seconds, each script after 7 seconds, and the /.env, /.git, and TLS checks after 6 seconds. The API allows up to 30 seconds. The report shows the measured duration. A typical scan is described on the site as about 20 seconds.
Does it test Row Level Security or query my database?+
No. If your pages include a Supabase project URL, an anon JWT, or a publishable key, the report says so and reminds you that those are only safe when Row Level Security is on. The scanner does not connect to your Supabase project, run SQL, or evaluate policies. A deep RLS check for verified owners is marked coming soon and is not part of this scan.
Which secrets does it look for?+
In the HTML and the same-site scripts it downloaded, it looks for Stripe live secret or restricted keys (sk_live_, rk_live_), Stripe test secret keys (sk_test_), Anthropic keys (sk-ant-), OpenAI-style keys (sk-, sk-proj-, sk-svcacct-, sk-admin-), AWS access key IDs (AKIA, ASIA), PEM private key blocks, GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_, github_pat_), and Supabase secret keys (sb_secret_). It decodes JWTs: role service_role is critical, and role anon on a Supabase-looking token is informational. Matches are masked to the first 6 characters plus **** before the report is saved.
Will it crawl my whole site and third-party scripts?+
No. It scans the single URL you submit, not the rest of your site. JavaScript is downloaded only when it is same-site: the same host, ignoring a leading www, or a subdomain of that host. Scripts on a different domain are not fetched. /.env and /.git/config are requested once each, and redirects on those two URLs are not followed.
What is free, and what is the paid fix report?+
The score out of 100 is free and shown immediately. Until you enter an email, problem titles are generic and the details stay hidden. After your email, you see each finding's title, explanation, location, and masked evidence. Fix prompts stay locked. Fix report is $9, one-time. Unlock every copy-paste fix prompt for this scan. 10 fix reports is $29, one-time. Unlock this report + 9 credits for re-scans after every deploy. There is no subscription. Payments are processed by Stripe.
What will the scanner refuse?+
It only accepts http and https URLs on ports 80 and 443, with no username or password, and the URL must be under 500 characters. IP addresses are rejected, as are localhost, names ending in .localhost, .local, or .internal, and any hostname with no dot. DNS answers that point at a private, loopback, link-local, or other non-public address are blocked. You have to confirm you own the app or have permission to check it. More than 6 scans from the same network in 10 minutes, or more than 120 scans a minute in total, asks you to wait.
Is a high score a security audit?+
No. The report says: "Automated passive check, not a security audit. A clean result doesn't guarantee your app is secure." The score starts at 100 and subtracts 40 for each critical finding, 25 for high, 10 for medium, and 4 for low, then another 10 if a critical or high secret was found in the HTML or scripts, or the site does not redirect to HTTPS. Informational notes do not change the score. The scan cannot see server-only code, logged-in behavior, or whether your database policies are correct.
Run a scan
Paste a URL you own or have permission to check.