Excluding files from Cloudflare Pages
Spoiler: I ended up just adding the folder I wanted to exclude to .gitignore, but learned some interesting things along the way.
There is no .cfignore
Git has .gitignore. Docker has .dockerignore. Surely Cloudflare has .cfignore? It doesn't. Despite community requests, and Claude thinking there is (until being asked to confirm it) ... there's no built-in way to exclude files from a Pages deployment. At this point, since it seems like Pages is going away, it never will.
The build step workaround
So, if you want something in your repo but not deployed (in my case, a .claude folder) - the solution is to add a build step. Even for a static site with no build process.
After Cluading up a few alternative approaches, using tar was actually one of the cleanest:
mkdir -p dist && tar --exclude='.claude' --exclude='.git' --exclude='dist' -c . | tar -x -C dist
Set your build output directory to dist and you're done.
It's really important to exclude the dist folder, or you end up with recursive duplication (ask me how I know).
The ghost file
One kind of strange thing I learned in the process. Before adding the build step, I'd already deployed the site a few times with my .claude/settings.json file. Even after fixing the build, that file was still accessible on the live site.
I deleted all old deployments. Still there. I purged the Cloudflare cache. Still there. It's probably still there now.
Turns out Cloudflare Pages has an asset preservation cache that keeps old files around for up to a week, even after they're removed from deployments. It's seperate to the CDN cache and unlike it, there's no way to manually purge it.
This is fine for my harmless settings file. But imagine accidentally deploying an API key or password. You'd have to rotate the credential anyway, but having no way to remove it from the CDN for a week feels like a gap? To be honest I kind of assume I am missing something. Let me know if I am.
Back to that spoiler
If you don't need the file version-controlled, just add it to .gitignore. I decided I could live without it under version control after all and while the other approach worked fine, I want this setup to be really simple and foresee a point where I do a proper build step elsewhere at some point anyway.
But either way: don't deploy secrets to Pages. Seems like you can't force-purge them.