LOG-004 is the one that’s uncomfortable to write, which is exactly why it’s getting written. A .env file under this site was reachable over the public internet, containing a database password and an encryption key. Here’s what that actually looked like, and the specific config mistake that caused it.
What was actually blocked
Caddy, the web server this site runs on, had a rule meant to keep dotfiles like .env from ever being served:
@blocked {
path /.env* /.git* /composer.* ...
}
handle @blocked {
error 404
}
That pattern matches /.env at the site root and nothing else. /.env is blocked. /portfolio/kyle/includes/.env is not — the leading slash in the glob anchors it to the top level, and a path matcher that looks like it should catch “any .env file anywhere” only ever meant “the one at the root.” A nested project directory with its own .env sailed straight past it.
What that exposed
A database password and an encryption key, sitting in plain text, servable to anyone who requested the exact path. No exploit was needed — no injection, no auth bypass, just a URL a search engine or a scanner could have found on its own.
The fix, and why it took real widening
The corrected rule doesn’t anchor to the root at all:
path /.env* */.env* /composer.* /*.md \
*/includes/config.php */generate_passwords.php
The */.env* form matches a dotfile at any depth, not just the top. Two other stale files got swept up in the same pass once we went looking properly — a config include and a leftover password-generation script, neither of which had any business being reachable either. Every blocked path was re-verified as a 404 afterward, not assumed.
Where it actually stood
The credentials behind that file were already retired by the time this was found — not a mitigation we get credit for, just how the timing happened to fall. That doesn’t make the misconfiguration less real. A rule that reads as “block all dotfiles” but only blocks the root’s is a specific, checkable class of mistake, and the only real fix is checking it: request the path you think is blocked, from a fresh connection, and confirm the 404 yourself rather than trusting what the rule appears to say.