Hacker News new | past | comments | ask | show | jobs | submit login
HTTP static server one-liners (gist.github.com)
286 points by known on Jan 18, 2020 | hide | past | favorite | 66 comments



I'm pushing the definition of "one liner" here, and it's got some major shortcomings (including directory traversal!), but here's one with Gnu awk:

  gawk '@load "filefuncs";func send(s,e,d,t,b) {;print "HTTP/1.0 "s" "e|&S;print "Content-Length: "b|&S;print"Content-Type: "t|&S;print d|&S;close(S);}func mt(f) {;c = "file -b --mime-type " f;r="";while ((c|getline z)>0) {;r=r z;}  ;close(c);return r;} ;BEGIN {RS=ORS="\r\n";while(1){;S="/inet/tcp/8080/0/0";while((S|&getline l)>0) {;split(l,f," ");if(f[1]=="GET"){p=substr(f[2],2)}if(p==""){p="index.html"}stat(p,s);if (s["type"]=="file") {;m=mt(p);while ((getline i <p)>0){o=o i}send(200,"OK",o,m,s["size"]);break;} ;n="<html>Not Found</html>";send(404,"Not Found",n,"text/html",length(n));break;}}}'
edit: Added better mime-type detection and 404s. It actually sorta works well now.


Oof. It wasn't handling binary files. Also fixed directory traversal, and you can optionally specify a content directory on the cmd line.

  gawk '@load"filefuncs";@load"readfile";func send(s,e,d,t,b){print"HTTP/1.0 "s" "e|&S;print"Content-Length: "b|&S;print"Content-Type: "t|&S;print d|&S;close(S);}func cf(x){split(x,y,"/");for(z in y){print "FOUND "y[z];if(y[z]==".."){return 0;}}return 1;}func mt(f){c="file -b --mime-type "f;r="";while((c|getline z)>0){r=r z;}close(c);return r;}BEGIN{if(ARGV[1]!=""){if(chdir(ARGV[1])){print "Failed to chdir to "ARGV[1];exit;}ARGC=1;}RS=ORS="\r\n";while(1){S="/inet/tcp/8080/0/0";while((S|&getline l)>0){split(l,f," ");if(f[1]=="GET"){p=substr(f[2],2)}if(p==""){p="index.html"}stat(p,s);if(cf(p)&&s["type"]=="file"){m=mt(p);o=readfile(p);send(200,"OK",o,m,s["size"]);break;}n="<html>Not Found</html>";send(404,"Not Found",n,"text/html"RS,length(n));break;}}}'


You are a hero good sir or madam. Is this redistributable with attribution?


Heh. Sure...licensed under WTFPL. http://www.wtfpl.net/about/


Very clever command but be warned, the link is NSFW.


There is of course rclone: https://rclone.org/commands/rclone_serve_http/

    rclone serve http .
You can replace . with any of your other cloud storage backends too (s3, google drive, dropbox, etc), or http with sftp, webdav, ftp, dlna. It works on Windos/macOS/Linux.

(Rclone author ;-)


rclone is our backup solution at work, huge props to you!


Rclone is a fantastic tool. Thanks!


Asked for an HTTPS one liner on stackoverflow a while back and got this answer:

    ruby -r webrick/https -e '
      WEBrick::HTTPServer.new(
        Port: 8000, DocumentRoot: ".",
        SSLEnable: true, SSLCertName: [%w[CN localhost]]).start'
https://stackoverflow.com/a/37986354/59325


openssl can also do it. no dirlisting though.

    openssl req -x509 -newkey rsa:4096 -keyout /tmp/key.pem -out /tmp/cert.pem -nodes && openssl s_server -WWW -port 8443 -cert /tmp/cert.pem -key /tmp/key.pem


Caddy is brilliant for this task.


For reference the HTTPS one-liner with Caddy [1] would be:

    caddy file-server --domain example.com
And similarly for HTTPS reverse proxy:

    caddy reverse-proxy --from example.com --to localhost:9000
[1]: https://caddyserver.com/

This plus hosts file can be a quick way to test secure cookies, secure headers, etc.


You could also tunnel any of the http servers through socat ssl-l.


Netcat ones are my favourite, I use them frequently: https://gist.github.com/willurd/5720255#gistcomment-2851087

The same thing, in `batch` for Windows: https://github.com/rahuldottech/netcat-http-server-for-windo...


Using `nix` I have an alias called `serve` that runs `nix run nixpkgs.caddy -c caddy -host 0.0.0.0 -port 8000 browse` which will download Caddy if it is not in my cache and then run it. It's one of my favorite aliases and I use it way more frequently than I'd expect.


Like some others in this thread I'll throw in my own shameless plug for `serve`: https://github.com/philippgille/serve

It's similar to the others mentioned here, with the nice extras that there's optional basic auth, optional HTTPS with automatic creation and use of a self-signed certificate, there are installers for most popular OS's package managers (snap, homebrew, scoop, chocolatey), tiny 2-5 MB zero-dependency binary and some other features.

I've had plans to add automatic Let's Encrypt certs, Onion service creation and reverse SSH functionality (either using one's own SSH server or one of the public ones) for a while and hope I'll get to them soon. Does anyone have any specific preferences for one of those or another feature specifically for the use case of serving files to another person as quickly as possible?


I'm surprised there's no one-liner for Node without dependency since it has a server by default.

Edit: Didn't know there was an `eval` flag for Node, so that will do the job:

node --eval "var fs = require('fs'), http = require('http'); http.createServer(function (req, res) { fs.readFile(__dirname + req.url, function (err,data) { if (err) { res.writeHead(404); res.end(JSON.stringify(err)); return;}res.writeHead(200);res.end(data); });}).listen(8080);"


Doesn't protect against folder climbing/traversal like `curl :8080/../../etc`.


Huh .. that'd be a fun post; see how many of these can be susceptible for climbing out of their web root. The author does list which ones support dir indexes.


hopefully you're not using a one-liner for your actual deployment. I use it for no stakes development


Caddy is a one-liner. Plenty of the one-liners are suitable for production. There are so many solutions that you might as well use one, even in development, that doesn't have such glaring issues, because you're basically training your muscle memory to reach for it.


Most of these one-liner probably aren't secure. As the title post says "Use this power wisely".


npx http-server -p 8080


It's missing my personal favorite:

ngrok http file:///Users/alan/share


You might want to include a disclaimer that you're the author, maintainer and person behind the company that operates ngrok ;)

Just wanted to chime in and say I love ngrok too, not allow allows you to easily create proxies, it allows you to create proxies that also works on the public internet (ngrok assigns you a x.ngrok.io address) and with tcp connections. Great work on it inconshreveable!



Another good option for static site development is devd [1] [2] which can automatically open the site in browser with livereload:

   devd -ol .
[1]: https://github.com/cortesi/devd

[2]: https://news.ycombinator.com/item?id=10436111


Some of these aren't one line but could be (for example the one for node.js can use npx instead)


I was pleased and surprised that the Python 3 one works with Termux on this cellphone. I've had trouble with Python socket stuff on here in the past, but now I suspect that may have been specific to IP multicast.


The busybox one works too


I've been using this:

    npx serve
(This is a node.js thing.)


Unlike `python3 -m http.server`, `npx serve` doesn’t redirect `/path/to/dir` to `/path/to/dir/`. This is a problem if you are using relative paths in your `/path/to/dir/index.html`.


For my purposes that hasn’t been a problem.

I guess I would normally just start with ‘path/to/dir/‘ or ‘/path/to/dir/index.html’


There is woof: http://www.home.unix-ag.org/simon/woof.html

When -U is specified, it provides an upload form, allowing file uploads.


  php -S localhost:8000


Lwan (https://lwan.ws) can also be used like this, although I wouldn't call it a "one liner" like many of the examples listed there:

    lwan -r /path/to/files/to/serve


I still have a copy of bash-httpd laying around, if any of y'all remember this: http://frakir.org/~morty/lue/bash-httpd.html


Don't forget socat.

Or tcpserver from djb

.


Does someone know if there is a way of doing this purely with POSIX tools?


  while TRUE; do { echo -e 'HTTP/1.1 200 OK\r\n'; cat /path/to/thing; } | nc -l 8000; done

From: https://krypted.com/


Neither echo -e nor nc are POSIX. I don't think there is a strictly POSIX one-liner.


`echo -e` should be easily replaceable with `printf` but nc remains to be a problem.


Is inetd "Posix"? It has command line switches to not fork, and to specify the config file. Maybe it takes a config file of "-" to mean stdin.


I doubt it. At least I can't find it anywere here:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c...


You are right, but it was the closest I could find.


printf(1) is POSIX and is a superset of echo.


If you use npx http-server -p 8080

You don't need to fully install globally


How many of these can output a "standard" access log?

Instructions for how to do that would be a helpful addition to this page.


The Crystal version can. I just commented an update for it that will run on 0.32.1, but for logging you can include the HTTP::LogHandler:

  crystal eval 'require "http/server"; server = HTTP::Server.new([HTTP::LogHandler.new, HTTP::StaticFileHandler.new(".")]); server.bind_tcp(8080); server.listen'


The Python one does.


rarun2 from radare2

   rarun2 listen=80 program=/usr/bin/printf arg1="HTTP/1.1 200 OK\r\nContent-type=text/html\r\nConnection: close\r\n\r\n"


Last revised in 2013 FYI.


My favourite...

    apt install nginx
Why settle for less.


Bit of a stretch but you can also use curl if you proxy it through my patchbay.pub[0] service:

curl https://patchbay.pub/abcd1234.html --data-binary @index.html

[0] https://patchbay.pub


Lots of people use these one liners for Dev/test environments.

Most of those people aren't aware how insecure they are.

For example, with the python SimpleHTTPServer I can simply request http://localhost/../../.ssh/id_rsa

All your SSH private keys are now mine. I can steal your browser cookie database and get into your email/Facebook/Twitter in the same way.

Any web page can request that, or any software on any machine on your local network.


> For example, with the python SimpleHTTPServer I can simply request http://localhost/../../.ssh/id_rsa

Eh! That’s not true. I just tried and both Python 2.7.16 and Python 3.7.6 return a "404 Not Found” when I try to download and existing file that is located one or more levels up from the directory where the HTTP server was started. Here is a screenshot showing both results: https://i.imgur.com/uizp7zV.png


Maybe behaviour has changed recently? In a past version someone has demonstrated this attack on me...


AFAICT SimpleHTTPServer has been protected against basic traversal attacks for a very long time.

There have been bugs though, like https://bugs.python.org/issue26657


Were you perhaps running from your home directory?

Did the directory you were serving contain any symbolic links to folders outside of that directory?


Huh? When I use SimpleHTTPServer with .. it just ignores the .. and keeps me in the same directory. Is the behavior different on different versions of python or something? Is there some special technique I don't know? I tried with '.' and with '%2e' .


I noticed that browsers and curl were doing something with .. on the client side. Try telnet to the port and typing the GET by hand.


You're partially right that curl is blocking '..'. But it's not blocking '%2e%2e'. I got it to avoid blocking '..' with the --path-as-is flag.

I redid the tests, with curl's --path-as-is flag, as well as with raw TCP (via netcat), and SimpleHTTPServer is still not vulnerable.



> For example, with the python SimpleHTTPServer I can simply request http://localhost/../../.ssh/id_rsa

I think my localhost is different from your localhost.


OP was suggesting that a website you were visiting was accessing this URL via JavaScript and posting it to OP's server.


Same-origin policy prevents that specific attack, although the same idea exists if you’re not protected from DNS rebinding on localhost, and `--bind 127.0.0.1` (or some other way to block the port) is necessary – but the Pythons’ built-in HTTP servers aren’t vulnerable to path traversal anyway.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: