Skip to content

Instantly share code, notes, and snippets.

@willurd
Last active November 13, 2024 13:44
Show Gist options
  • Save willurd/5720255 to your computer and use it in GitHub Desktop.
Save willurd/5720255 to your computer and use it in GitHub Desktop.
Big list of http static server one-liners

Each of these commands will run an ad hoc http static server in your current (or specified) directory, available at http://localhost:8000. Use this power wisely.

Discussion on reddit.

Python 2.x

$ python -m SimpleHTTPServer 8000

Python 3.x

$ python -m http.server 8000

Twisted (Python)

$ twistd -n web -p 8000 --path .

Or:

$ python -c 'from twisted.web.server import Site; from twisted.web.static import File; from twisted.internet import reactor; reactor.listenTCP(8000, Site(File("."))); reactor.run()'

Depends on Twisted.

Ruby

$ ruby -rwebrick -e'WEBrick::HTTPServer.new(:Port => 8000, :DocumentRoot => Dir.pwd).start'

Credit: Barking Iguana

Ruby 1.9.2+

$ ruby -run -ehttpd . -p8000

Credit: nobu

adsf (Ruby)

$ gem install adsf   # install dependency
$ adsf -p 8000

Credit: twome

No directory listings.

Sinatra (Ruby)

$ gem install sinatra   # install dependency
$ ruby -rsinatra -e'set :public_folder, "."; set :port, 8000'

No directory listings.

Perl

$ cpan HTTP::Server::Brick   # install dependency
$ perl -MHTTP::Server::Brick -e '$s=HTTP::Server::Brick->new(port=>8000); $s->mount("/"=>{path=>"."}); $s->start'

Credit: Anonymous Monk

Plack (Perl)

$ cpan Plack   # install dependency
$ plackup -MPlack::App::Directory -e 'Plack::App::Directory->new(root=>".");' -p 8000

Credit: miyagawa

Mojolicious (Perl)

$ cpan Mojolicious::Lite   # install dependency
$ perl -MMojolicious::Lite -MCwd -e 'app->static->paths->[0]=getcwd; app->start' daemon -l http://*:8000

No directory listings.

http-server (Node.js)

$ npm install -g http-server   # install dependency
$ http-server -p 8000

Note: This server does funky things with relative paths. For example, if you have a file /tests/index.html, it will load index.html if you go to /test, but will treat relative paths as if they were coming from /.

node-static (Node.js)

$ npm install -g node-static   # install dependency
$ static -p 8000

No directory listings.

PHP (>= 5.4)

$ php -S 127.0.0.1:8000

Credit: /u/prawnsalad and MattLicense

No directory listings.

Erlang

$ erl -s inets -eval 'inets:start(httpd,[{server_name,"NAME"},{document_root, "."},{server_root, "."},{port, 8000},{mime_types,[{"html","text/html"},{"htm","text/html"},{"js","text/javascript"},{"css","text/css"},{"gif","image/gif"},{"jpg","image/jpeg"},{"jpeg","image/jpeg"},{"png","image/png"}]}]).'

Credit: nivertech (with the addition of some basic mime types)

No directory listings.

busybox httpd

$ busybox httpd -f -p 8000

Credit: lvm

webfs

$ webfsd -F -p 8000

Depends on webfs.

IIS Express

C:\> "C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:C:\MyWeb /port:8000

Depends on IIS Express.

Credit: /u/fjantomen

No directory listings. /path must be an absolute path.

Meta

If you have any suggestions, drop them in the comments below or on the reddit discussion. To get on this list, a solution must:

  1. serve static files using your current directory (or a specified directory) as the server root,
  2. be able to be run with a single, one line command (dependencies are fine if they're a one-time thing),
  3. serve basic file types (html, css, js, images) with proper mime types,
  4. require no configuration (from files or otherwise) beyond the command itself (no framework-specific servers, etc)
  5. must run, or have a mode where it can run, in the foreground (i.e. no daemons)
@prestancedesign
Copy link

ClojureScript build tool Shadow-CLJS:

npx shadow-cljs serve

@f5b7
Copy link

f5b7 commented Oct 15, 2020

This is awesome! One note though, re: http-server:

Note: This server does funky things with relative paths. For example, if you have a file /tests/index.html, it will load index.html if you go to /test, but will treat relative paths as if they were coming from /.

That should be true for any of these. That's just how HTTP/HTML works. If you go to /test/ (note the trailing slash), then relative URLs will be relative to /test/. Relative URLs are always relative to the last slash in the URL.

The server can't control that; resolving relative URLs is the browser's job, and the browser doesn't know that /test is meant to be a directory. What some servers will do for you is redirect to the trailing-slash URL automatically, which other servers listed here may do—I'm not sure.

Thank you @Peeja, that explains why I couldn't reproduce those funky things 😉

@tinmarino
Copy link

Copying @moshe, I pushed a BaSh web server abache. I also prefer to copy it here: this list helped me a lot for a class (on sockets)

#!/usr/bin/env bash
# BaSh server with Netcat
#
# Home: https://github.com/tinmarino/abache
# Thanks: moshe


# Define HTTP header <- windows like and finish with 2 newlines
HEADER="HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Connection: close
Server: Abache
Date: $(date)
"

# Hardcode the html <head> tag
read -r -d '' HEAD <<'EOF'
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <title>BFS: Bash FileServer</title>
  <meta name="description" content="BaSh HTTP FileServer like python -m http.server\nFor teaching purpose">
  <meta name="author" content="Tinmarino">
</head>
EOF

serve_file(){
  # <- FILEPATH -> CONTENT
  if command -v bat &> /dev/null &&  command -v ansi2html &> /dev/null; then
    CONTENT="$(bat --style plain --theme "ansi-dark" --color always --pager "" "$FILEPATH" | ansi2html)"
  else 
    CONTENT="<pre>$(cat "$FILEPATH" < /dev/null)</pre>"
  fi
}

serve_dir(){
  # <- FILEPATH -> CONTENT
  # shellcheck disable=SC2086  # Double quote to prevent globbing
  read -r -d '' CONTENT <<EOF
$HEAD
<body>\n<table>\n
<tr>\n<td>Name</td><td>Size (bytes)</td></tr>
<tr>\n<td><a href="..">..</a></td><td></td></tr>
$(stat --printf='<tr>\n<td><a href="/%n">%n<a/></td><td>%s</td></tr>\n' $FILEPATH/*)
</tr>\n</table>\n</body>\n</html>\n
EOF
}

# Get port <- command line
if [[ -n "$1" ]]; then PORT=$1 ; else PORT=8080 ; fi
echo "Abache listening on port $PORT"

# Create FIFO (alias NamedPipe)
FIFO_FILE=/tmp/abache
[[ -p $FIFO_FILE ]] && rm $FIFO_FILE
mkfifo $FIFO_FILE

# Serve always
while true; do
  # shellcheck disable=SC2002  # Useless cat
  cat "$FIFO_FILE" | nc -l $PORT | while read -r line; do
    # Log
    echo "Get: at $(date)  $line"

    # Clause: Only respond to GET
    if ! echo "$line" | grep -q '^GET '; then continue; fi

    FILEPATH=$(echo "$line" | awk '{print $2}'| sed 's/%20/ /')
    FILEPATH=${FILEPATH:1}

    [[ -z "$FILEPATH" ]] && FILEPATH="."
    CONTENT="Error 404: Bad URL <= $FILEPATH"

    # Send file or direcotry content
    if [[ -e "$FILEPATH" ]];  then
      if [[ -f "$FILEPATH" ]]; then serve_file; else serve_dir; fi
    fi
    echo -e "$HEADER$CONTENT" > $FIFO_FILE
    break
  done
done

@NightMachinery
Copy link

function http-static-caddy() {
    caddy file-server -browse -listen "0.0.0.0:${1:-8000}"
}

@teknoraver
Copy link

bash one liner using nc:

nc -klp 8080 -c 'read get file http ; echo "HTTP/1.1 200 OK" ; echo ; cat ".$file"'

@diguage
Copy link

diguage commented Dec 1, 2020

caddy file-server -root e:\the-path -listen 0.0.0.0:8000 -browse 

///

caddy
v2.2.1 h1:Q62GWHMtztnvyRU+KPOpw6fNfeCD3SkwH7SfT1Tgt2c=

@cameronkerrnz
Copy link

There is netcat in almost any *nix OS

Good bonus of using netcat for this is that is that you see all of the request headers, which is useful if studying integration with regard to SSO mechanisms.

@akvadrako
Copy link

With a 7MB docker image:

docker run -u `id -u` -p 8043:8043 -v $PWD:/srv/http pierrezemb/gostatic

@cfjedimaster
Copy link

httpster (npm i -g httpster)

@gomako
Copy link

gomako commented Jan 30, 2021

Browser Sync
npm install -g browser-sync
browser-sync start
Many options for watching and reloading on file change, serving directories etc.

@vuchkov
Copy link

vuchkov commented Jan 30, 2021

+1 for https://httpbin.org/:

Run locally: $ docker run -p 80:80 kennethreitz/httpbin

@ocramz
Copy link

ocramz commented Jan 30, 2021

Haskell :

ciao : https://github.com/ocramz/ciao

Install : stack install

Serve CWD on default port 3000 : ciao -d ${CWD}
" on port 80 (must run as root): ciao -p 80 -d ${CWD}

@andresvia
Copy link

socat

socat -v TCP-LISTEN:8080,crlf,reuseaddr,fork 'SYSTEM:echo HTTP/1.0 200;echo Server\: socat hack;echo Content-Type\: text/plain;echo;echo ok'

@vistun
Copy link

vistun commented Feb 28, 2021

This a good one @vandot
edited below, works with semicolons

It still oneliner :)
golang
echo 'package main; import "net/http"; func main() {fs := http.FileServer(http.Dir(".")); http.Handle("/", fs); http.ListenAndServe(":8000", nil)}' > main.go; go run main.go; rm main.go

@textprotocol
Copy link

ucspi-tcp text://protocol server.

# tcpserver -v -c42 -o -D -H -P -l 0 -R 127.0.0.1 1961 timeout 1 ../../bin/publictext

https://github.com/textprotocol/publictext

@johnwyles
Copy link

@lethalman

I'd like to request a bash http server using /dev/tcp, anybody has a one-liner for that?

https://unix.stackexchange.com/a/49947

@marin-liovic
Copy link

marin-liovic commented May 13, 2021

replace all of the npm install with npx for a oneliner, e.g. npx superstatic -p 8000

@eikes
Copy link

eikes commented May 13, 2021

PowerShell

$Hso=New-Object Net.HttpListener;$Hso.Prefixes.Add("http://+:8000/");$Hso.Start();While ($Hso.IsListening){$HC=$Hso.GetContext();$HRes=$HC.Response;$HRes.Headers.Add("Content-Type","text/plain");$Buf=[Text.Encoding]::UTF8.GetBytes((GC (Join-Path $Pwd ($HC.Request).RawUrl)));$HRes.ContentLength64=$Buf.Length;$HRes.OutputStream.Write($Buf,0,$Buf.Length);$HRes.Close()};$Hso.Stop()

PowerShell from cmd.exe

PowerShell.exe -nop -enc JABIAHMAbwA9AE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAEgAdAB0AHAATABpAHMAdABlAG4AZQByADsAJABIAHMAbwAuAFAAcgBlAGYAaQB4AGUAcwAuAEEAZABkACgAIgBoAHQAdABwADoALwAvACsAOgA4ADAAMAAwAC8AIgApADsAJABIAHMAbwAuAFMAdABhAHIAdAAoACkAOwBXAGgAaQBsAGUAIAAoACQASABzAG8ALgBJAHMATABpAHMAdABlAG4AaQBuAGcAKQB7ACQASABDAD0AJABIAHMAbwAuAEcAZQB0AEMAbwBuAHQAZQB4AHQAKAApADsAJABIAFIAZQBzAD0AJABIAEMALgBSAGUAcwBwAG8AbgBzAGUAOwAkAEgAUgBlAHMALgBIAGUAYQBkAGUAcgBzAC4AQQBkAGQAKAAiAEMAbwBuAHQAZQBuAHQALQBUAHkAcABlACIALAAiAHQAZQB4AHQALwBwAGwAYQBpAG4AIgApADsAJABCAHUAZgA9AFsAVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBVAFQARgA4AC4ARwBlAHQAQgB5AHQAZQBzACgAKABHAEMAIAAoAEoAbwBpAG4ALQBQAGEAdABoACAAJABQAHcAZAAgACgAJABIAEMALgBSAGUAcQB1AGUAcwB0ACkALgBSAGEAdwBVAHIAbAApACkAKQA7ACQASABSAGUAcwAuAEMAbwBuAHQAZQBuAHQATABlAG4AZwB0AGgANgA0AD0AJABCAHUAZgAuAEwAZQBuAGcAdABoADsAJABIAFIAZQBzAC4ATwB1AHQAcAB1AHQAUwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAEIAdQBmACwAMAAsACQAQgB1AGYALgBMAGUAbgBnAHQAaAApADsAJABIAFIAZQBzAC4AQwBsAG8AcwBlACgAKQB9ADsAJABIAHMAbwAuAFMAdABvAHAAKAApAA==

What a handy one-liner!

@rjmunro
Copy link

rjmunro commented May 13, 2021

Note that if you want to share something with another machine, most of the solutions that bind to localhost or 127.0.0.1 won't work. Normally if you change localhost or 127.0.0.1 to 0, it will listen on all interfaces so you can download something from another machine.

E.g. change:

php -S 127.0.0.1:8000

to:

php -S 0:8000

@radiosilence
Copy link

npx serve

Yep, this is probably the easiest

@radiosilence
Copy link

npx serve

Yep, this is probably the easiest

@mmazzarolo
Copy link

With serverino:

npx serverino --port 8000

Use --secure to automatically generate a certificate and serve on SSL (at https://localhost:8000).

@pimbrouwers
Copy link

With Sergio - a Kestrel wrapper using Argu:

sergio

This will serve the current directory at https://[::]:8080.

To specify listener details:

sergio --listener localhost 8080

To display all options:

sergio --help

@dbohdan
Copy link

dbohdan commented May 13, 2021

Tcl

You will need Tcl 8.6 with Tcllib 1.19 or later.

echo 'package require httpd 4; ::httpd::server create HTTPD port 8000 myaddr 127.0.0.1 doc_root [pwd]; vwait forever' | tclsh

Credit to @rkeene.

@darkblue-b
Copy link

C99

klange/cgiserver

@vi
Copy link

vi commented May 13, 2021

websocat can serve specific explicit list of files on explicit URLs with explicit Content-Types.

websocat -s 1234 -F /index.html:text/html:./index.html -F /script.js:text/javascript:/path/to/thescript.js

There is no ability to automatically include more files based on existence on the filesystem, but sometimes 100% explicit approach may be beneficial.

@carlosneves0
Copy link

carlosneves0 commented May 13, 2021

docker run --rm --volume "$(pwd):/www:ro" --publish 80:80 docker.io/p3terx/darkhttpd:1.13 /www
docker image ls --format 'table {{.Repository}}:{{.Tag}}\t{{.Size}}' p3terx/darkhttpd:1.13
REPOSITORY:TAG          SIZE
p3terx/darkhttpd:1.13   91.7kB

docker run --rm --volume "$(pwd):/usr/share/nginx/html:ro" --publish 80:80 docker.io/library/nginx:1.20.0-alpine
docker image ls --format 'table {{.Repository}}:{{.Tag}}\t{{.Size}}' nginx:1.20.0-alpine
REPOSITORY:TAG        SIZE
nginx:1.20.0-alpine   22.6MB

@lpereira
Copy link

Lwan can be used as an one-liner web server, too: lwan -r /path/to/files/to/serve.

@wtarreau
Copy link

Surprised that the once most universal thttpd wasn't even mentioned given how simple and convenient it is:

$ thttpd
$ netstat -ltnp | grep thttpd
tcp6       0      0 :::8080                 :::*                    LISTEN      25130/thttpd        

@wtarreau
Copy link

A listening socket is exactly the type of thing I wouldn't want to see in a web browser!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment