Awesome. There is a very surreal joy in building such standalone tools even just for learning. I have been working on a kitchen sync style "chat" app to experiment with new ideas (like integration with X/y/z). I just need to muster the courage to publish it!
100% agree. I've been "reinventing the wheel" (my own terminal color library, CLI framework, GNU Stow alternative, etc) and loving it! I get a lot of satisfaction from using these simple tools [0].
I say publish your work. Chances are, no one will care and you have a nice backup of your code. And if someone does find it useful, then great!
I wrote something like this [1] for the team at my old workplace. We mainly worked on static html files but some functionality required loading via http. Many of the devs were quite new to development in general, so I built a simple static server in Go, as an exe which they could set as the default for `.html` files, and therefore they could just open html files on the webserver via double-click as normal. The program would watch the directory (if not already open and watching) then open the right path in the default browser. I also built-in livereload, management via tray icon and a basic web UI.
Yeah, with the "go 1.21.0" line in the go.mod file, it only works on Go 1.21. Here's what I get on Go 1.20:
$ go run .
go: errors parsing go.mod:
.../static-server/go.mod:3: invalid go version '1.21.0': must match format 1.23
Eli, you might consider changing that to "go 1.20" so it works on Go 1.20 (and older, actually -- it's the 1.2.3 format that is getting in the way on Go pre-1.21).
https://caddyserver.com/ is implemented in Go, production-ready, and easy to setup with a one-liner (though personally I would use official binaries or compile from source rather than use the builds from a distro package manager)
What's the zero-config way to temporarily spin up an instance of Apache or Nginx that serves my current directory? The way Debian does this out of the box seems significantly higher-ceremony than that.
Caddy is amazing. I accidentally wiped my fairly complex nginx configuration (yay apt-get purge) and have been so spoiled by Guix that I did not care about etckeeper or ansible on my one Debian server.
Anyway I decided to try Caddy to quickly get up and running and had a good setup in minutes including certificates for multiple domains. Even added wildcard certificates which I never figured out with Lets Encrypt, just because I could.
On Mac, the defaults aren't privileged. (Not sure why. That's just how it is.)
The --browse flag is optional and unrelated to ports/privileges. Otherwise, that sounds about right. Caddy defaults to port 80 unless you give it a domain name, then it defaults to port 443 and redirects HTTP on port 80 to HTTPS.
I, too, would like to see this, but it'll be a lot of work. It's just not a top priority right now for me, unfortunately.
This is something I could prioritize if a business wanted to sponsor this; but right now the most pressing things are updating the docs, revamping our test suite, and I have a few action items for existing sponsors I need to prioritize too.
Anyway, no timeline -- but definitely something wanted!
I'm (very) unfamiliar with webdev and hosting. Is this 'oneliner' a secure way to host files? I have heard that using a reverse proxy, some 2FA on a cloud gateway that forwards to another location, with a VLAN for the file server, and containerization, etc are all the standard practice now for ensuring security. Is there anything that works toward making this goal more easily amenable (since it's not possible with one project), or should I just keep using python -m http.serve and not care?
It depends. What is your definition of "security"? There's lots of dimensions of security in the web serving space. If you list out your requirements and specify your threat model, then answers become a lot clearer.
But yes, in general: Caddy's one-liner is a safe way to serve static files in the sense that remote services can't upload files or escape memory bounds to run arbitrary code. It encrypts your connections for you, so they're basically safe from surveillance and modification.
The elements you're describing are all external factors that have nothing to do with serving static files specifically. For example, a reverse proxy just multiplexes requests coming in on a port to various backends, maybe making modifications along the way. Authentication will restrict access to only allowed users, if that's something you require. Containers are more a way of administering a system or mitigating very specific risks that have niche relevance with static Go programs; and VLANs are just ways of isolating network traffic, but again it's somewhat orthogonal to file serving.
Overall, `caddy file-server` is better than Python's simple HTTP server in every way, though: static binary, faster performing, production-ready (handles Range requests properly, and a few other details), automatic HTTPS, folder indices, etc...
Is there more to the philosophy of why caddy exists? I am trying to jump into a few open source projects to hone my programming skills and I have a harder time comprehending things if I don't know what was the overall intention of the authors.
Caddy 1 was created because I needed a quick and easy web server for a lot of my projects.
Caddy 2 was created when I got serious about it, and we had governments and enterprises starting to rely on the project.
Now we exist because we advocate for (and deliver!) HTTPS on every site, memory safety, dynamic configuration, extensibility, and many more features valuable in a modern web server.
I am trying to reason with what I know, but wouldn't something like "python -m http.server 9000" suffice? Or nginx? These are battle tested and python is there on most platforms.
Or is there more depth to this specific web server that others don't have?
No, because python's simple HTTP server isn't production ready (doesn't support Range requests last I checked, and quite a few other limitations). And nginx is not "quick and easy" by any means, doesn't grant me the security of memory safety (it was vulnerable to Heartbleed, for example -- I wrote Caddy shortly after Heartbleed), and doesn't give me automatic HTTPS.
Caddy's appeal when I last looked at it some years ago was that the configuration file format was very easy to read and use; and that configuration itself was minimal. It came/comes with Letsencrypt integration which was novel some years ago, so setting up a secure site was even easier for people who weren't used to it.
Its syntax for being configured as a reverse proxy (secure front-end to less secure back-end servers) is similarly pretty easy.
I haven't looked at it in a long time because I recall them screwing with their terms of service so it wasn't fully FOSS by the most liberal definition.
Caddy has ALWAYS had the Apache 2.0 license. For a time we did additionally offer officially licensed binaries for companies, but Caddy has never deviated from Apache 2.
Caddy's ease of use is one feature, but there are many more - like the ability to massively scale your TLS to thousands of sites reliably. And to use the on-line configuration API to make changes to your server. There's a ton to discover with Caddy, it's not just a tool for beginners. ;)
While I love Go, have we gotten this lazy that we need a package for this? Go does this in 3 lines minimum, like you describe in your blogpost. However, in your package you expose the ability to kill your server [0] without any security. That’s a huge vulnerability. I know you’ll say “It’s just a static server, meant for serving static stuff” but it will be indexed by pkg.go.dev, people will use this outside your intent. It is the way. At the very least, use a secret token.
> I know you’ll say “It’s just a static server, meant for serving static stuff” but it will be indexed by pkg.go.dev, people will use this outside your intent.
While true, I don't think the author should refrain from making code available based on the potential negatives from others using code they didn't even bother to read the documentation for.
You’re right, however, due to the nature of the go ecosystem, someone will use it - host their react app with it - and expose an endpoint that could shutdown their server. I think that warrants being called out for.
Just check a header for a secret key you generate when you startup. Easy peasy. This keeps you able to call it for testing (granted you read from stdout or passed the key to tests as a variable). Then some scripto ransomware User from Omgodisztan doesn’t shutdown your server from the tent he’s camped in with Starlink.
It's fine the way it is IMO. However, it might be worth caveating in the README that it's for local testing only, the same way you do in your blog post.
Mainly because of the shutdown endpoint, but also that the -cors flag returns "Access-Control-Allow-Origin: *" exposing you to arbitrary cross origin requests.
People are allowed to write code/tools that are useful to only themselves. They're also allowed to post about these tools. Nobody is going to make you use them.
That is the argument of the first MVP by a startup and then it is their onlien product.
I have this seen also in automotive. "This is no problem, because this is not connected to the Internet." Then a few years later you have a DefCon presentation "GM hack, you can control the whole car via the Internet".