Find the secrets a website is leaving open
A site leaks secrets in more places than the .env file. Old backups, config files, private keys and logs all get left where anyone can download them. Attackers look for these first, because one file can be enough to get in. This checks a site for the ones that matter and shows what to close.
Why attackers go for these first
Every other way into a site costs effort. Finding an injection flaw means understanding the application; exploiting a known CVE means matching a version and adapting an exploit; guessing a password means being lucky at scale. Requesting a filename costs one HTTP request, works identically against every stack, and either returns a working credential or returns nothing. It is the cheapest attack that exists, which is why it is the first one tried and the one that runs continuously against every address on the internet.
The second reason is what the file yields. A vulnerability grants whatever that vulnerability grants; a credential file grants what the credential grants, which is usually more than the website itself has. A cloud key reaches every service in the account. A registry token publishes packages under the owner's name. A private key authenticates to whatever trusts it, which is rarely just one host. None of these are constrained by what the site was supposed to be able to do.
The third is that the scoreboard never resets. A file that was reachable for an hour is reachable forever to whoever fetched it in that hour, because they have the bytes. That is the property that separates this category from every other kind of finding: fixing a header protects everybody from that moment on, while closing a leaked file protects nobody who already has it.
The files that carry secrets
The catalogue behind this check is built by kind, not by filename, because the fix differs per kind while the paths change constantly. Each group below is one type of mistake and one type of response.
Environment and configuration files. The .env family, plus appsettings.json, database.yml, application.properties, web.config, docker-compose.yml and the framework equivalents. These exist only to be read by the server, so being served at all is the finding.
AI tool configuration. .mcp.json, .cursor/mcp.json, .claude/settings.json, .aider.conf.yml and their neighbours. These are the newest entries and among the most likely to fire, because the tools write them into the project root, and the project root is what gets uploaded. They carry a provider key that bills to the owner's card, and they name every server the assistant was connected to, half of which are internal.
Private keys. Anything beginning with the PEM armour line: SSH keys such as id_rsa, and TLS private keys such as server.key. A served TLS key means captured traffic can be decrypted and a certificate can be presented that browsers accept as the site's own.
Cloud and registry credentials. .aws/credentials, a Google service-account JSON, .kube/config, .npmrc with a publish token, .netrc, Docker registry logins. Each is a login to infrastructure rather than to the website.
Backups and database dumps. backup.sql, dump.sql, site.tar.gz, and the copies an editor leaves behind: config.php.bak, wp-config.php.old, files ending in a tilde, Vim swap files. A .bak of a PHP config is served as text rather than executed, which is how a file designed never to be readable becomes readable.
Version control folders and logs. A served .git directory hands over the source and every secret ever committed to it; application logs and stack traces name paths, versions and sometimes the credentials in a failed connection string.
How it looks in the report
A Deep Audit files each of these as its own finding under its own group, rather than as one "exposed files" heading. That split is deliberate: a leaked private key, a browsable log and a stale Docker config under one heading is a wall of text, and under separate headings it is several short findings with one action each.
The verdict is never "this path returned 200". A site that answers every request with a styled 404 page would otherwise produce a report full of nothing. Each finding is established from the response's own bytes: the document is tested against the format it claims to be, and reported only when it matches. The bytes that prove it are read, described in format terms, and discarded.
This is the active layer, which runs after domain ownership is verified. The free passive scan does not request unlinked paths on a domain it was merely pointed at, so it never reports these, and a clean passive report says nothing about them either way.
A private key is publicly downloadable
A private key is being served from the web root. Whatever that key authenticates, a server login, a deployment, a certificate, can now be authenticated by anyone who downloaded the file.
/id_rsa
Leaking vs closed
Leaking
Requests for a handful of predictable paths return real documents: a KEY=VALUE block, a PEM key, a JSON credential file, a SQL dump. No guessing was involved, because every one of those names is on a public list.
Closed
The published tree contains build output and nothing else. Configuration comes from the environment, backups live off the web server entirely, dot-files are denied at both the origin and the edge, and the paths above return 404.
The test that matters is not whether the site looks tidy, but what the paths actually answer with. Only a request from outside the network can establish that.
How to fix each kind
- Environment and config files: rotate every credential in the file, move it outside the document root, and switch the application to the host's environment variables. Cover the suffixed variants, not only the base name. (The exposed .env page covers this one in full.)
- AI tool configuration: delete the dot-directory from the deployment, rotate the provider key immediately, and check that account's usage for calls nobody made. Add the dot-directories to the deploy ignore list so the next deploy does not restore them.
- Private keys: rotate the key pair everywhere it is trusted. Remove the old public key from authorized_keys on every host it was added to. For a TLS key, revoke the certificate with the CA and issue a new pair. Deleting the file alone changes nothing, because the key is already copied.
- Cloud and registry credentials: deactivate the key at the provider first and issue a replacement, then remove the file. Check the audit trail (CloudTrail, the provider's activity log, the package registry's release history) for actions nobody took.
- Backups and dumps: move them off the web server entirely, not to a folder with a harder name. If the dump held customer data, the exposure has to be handled as a data incident and not only as a configuration fix.
- Editor leftovers: delete the .bak, .old, .save, .txt, tilde and .swp copies, and configure the server to refuse those extensions. A PHP config served as text is the same disclosure as the config itself.
- Version control and logs: deny dot-directories at the web server and the CDN, deploy a build artefact rather than cloning into the document root, and keep application logs outside the published tree. (The exposed .git page covers why blocking that folder is only the first half of the fix.)
- Then re-run the audit and confirm each finding is gone. Every step above changes something on the server, and only another request from outside proves which of them landed.
Run a free scan
The free passive scan grades headers, TLS and known CVEs on any site in about thirty seconds, with no signup. Verifying that a domain is owned adds the active layer, which requests the paths above by name and reports only the responses that really are the file they claim to be.
Check my websiteFAQ
- How would anyone find these files without a link to them?
- They do not need one. The filenames are conventions, shared by every project using the same framework, and the lists are public. Automated sweeps request a few hundred of them against every address they can reach, which is cheaper than deciding which addresses are worth trying.
- Does a 200 response mean a file is really exposed?
- No, and treating it that way is how these checks produce noise. A site that answers unknown paths with a styled error page returns 200 for everything. Each finding here is decided from the response body: the document has to match the format it claims to be before anything is reported.
- Nothing on the site is sensitive. Does this still matter?
- The files in this category are rarely about the site's own content. A cloud key reaches the whole account, a registry token publishes packages under the owner's name, and a private key authenticates to whatever trusts it. The blast radius is set by the credential, not by how interesting the website is.
- Why is this not part of the free scan?
- Because requesting unlinked paths on a domain without permission is probing rather than reading. CheckWeb runs these checks after domain ownership is verified, which is what makes the request legitimate, and is why the free report never claims a site is clean of them.
Related checks
- Exposed .env fileThe single most common entry in this catalogue, covered in full: what the file holds, the three ways it goes public, and the order to fix it in.
- Exposed .git directoryThe case where the secrets are historical: a repository served from the web root carries every credential ever committed to it.
- Directory listingWhat turns a folder of backups into a browsable menu, so finding the file no longer depends on knowing its name.