Repository navigation
FAQ
Questions no one has actually asked, preemptively answered for the zero people reading this wiki. Now updated for the Zig era, which is the only era that matters.
It serves static files case-insensitively. That's it. The whole thing. You're reading the wiki for a project whose entire contribution to computer science is calling toLowercase() on a path and looking it up in a hash map.
But now it does it in Zig, so it's faster and morally superior.
We don't talk about that. That was a different time. We were young. We thought the .NET runtime was "fine." We thought Dictionary<string, string> was "good enough." We thought garbage collection was "acceptable."
Then we discovered Zig, and we realized we'd been living in a cave, mistaking shadows on the wall for file servers. The entire project was rewritten in Zig 0.15.2. Zero dependencies. Single static binary. No runtime. No libc. No GC. No looking back.
The C# version is gone. The commits remain as a historical record and a warning.
Because Zig is what happens when someone looks at C and says "what if we kept the good parts and threw away the last 50 years of accumulated mistakes." Because Zig cross-compiles to 9 architectures from a single build host without QEMU. Because @import("std") gives you an HTTP server, a thread pool, reader-writer locks, a hash map, and inotify bindings, and you don't need a single external dependency. Because the binary is statically linked and the container is FROM scratch and there is nothing in it except purpose.
Also Andrew Kelley seems like he knows what he's doing and at this point we're just following the light.
Because there are 14,000 of them and they came from a Windows share where someone thought IMG_0001.JPG, img_0001.jpg, and Img_0001.Jpg were three different creative decisions.
Also, if you were the kind of person who renames files, you wouldn't be here. You'd be organized. You'd have a build pipeline that normalizes asset paths. You'd have tests. You're not that person. I'm not that person. We're CaseDropper people. We solve problems with hash maps and denial.
You could. You absolutely could. nginx has case-insensitive rewriting, a decades-long track record, and documentation written by people who aren't deranged.
But nginx is written in C. CaseDropper is written in Zig. nginx needs a configuration file. CaseDropper needs three environment variables. nginx runs on an operating system. CaseDropper runs on the concept of an operating system — FROM scratch, no OS, just a binary in the void, serving files with the quiet intensity of a monk who has achieved enlightenment through hash maps.
Also, nginx can't cross-compile to 9 architectures from a single zig build command. We're not saying CaseDropper is better than nginx. We're saying CaseDropper compiles to loong64 and nginx has to think about it.
It indexes every file path into a std.StringHashMap at startup. Hash map lookups are O(1). Reads are concurrent via std.Thread.RwLock. The thread pool scales to your CPU count.
So it scales until you have more files than fit in memory, at which point you have bigger problems than casing. Or until Zig releases 1.0 and the standard library API changes, at which point we have bigger problems than casing. Both scenarios are equally distant.
Dead. Replaced by inotify. The old C# FileSystemWatcher was a well-meaning .NET abstraction that occasionally noticed files existed. It appended to the dictionary but never removed. Files were indexed forever. The hash map grew monotonically, like entropy, or a node_modules folder.
The Zig version uses Linux inotify directly — raw kernel file descriptors, not a managed wrapper — and does full rebuilds on changes. The old map is deallocated. A new map is constructed from a fresh directory walk. Deletions work. Renames work. Everything works because Zig doesn't hide the kernel behind seven layers of abstraction and a NuGet package.
Next question.
We've moved on. The branch name stays. Like the Zig rewrite, it is permanent, unexplained, and correct.
It serves files. The files arrive at the browser. The casing doesn't matter. The binary is statically linked. The container has no attack surface. The memory is manually managed. There is no garbage collection pause. There is no runtime startup delay. There is a hash map, and the hash map is fast, and the hash map is correct.
If that's your bar for production-ready, then yes. If your bar is higher than that, you should probably not be running a project you found by searching "case insensitive static file server" on GitHub at 2 AM.
You can, but I want you to think very carefully about what feature you'd be adding to a static file server that lowercases paths. Really sit with that.
Also, you'll need to write Zig. If you haven't written Zig before, this is a great opportunity to learn. And by "great opportunity" I mean "you will fight the compiler for three hours, emerge victorious, and understand memory allocation at a spiritual level." Andrew Kelley built a language that makes you a better programmer by refusing to let you be a worse one. Embrace it.
Because the project was rewritten in Zig and that demanded documentation. Not because Zig is complicated — it's the simplest thing in the project — but because when you rewrite something from C# to Zig and the binary shrinks from "needs a runtime" to "needs nothing" and the container goes from "has an operating system" to "has achieved nirvana through emptiness," you want to write it down. You want to tell someone. The wiki is that someone.
Yes. Next question.