Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active July 3, 2025 03:26
Show Gist options
  • Select an option

  • Save ryanflorence/701407 to your computer and use it in GitHub Desktop.

Select an option

Save ryanflorence/701407 to your computer and use it in GitHub Desktop.
Node.JS static file web server. Put it in your path to fire up servers in any directory, takes an optional port argument.
var http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs")
port = process.argv[2] || 8888;
http.createServer(function(request, response) {
var uri = url.parse(request.url).pathname
, filename = path.join(process.cwd(), uri);
path.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return;
}
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
fs.readFile(filename, "binary", function(err, file) {
if(err) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary");
response.end();
});
});
}).listen(parseInt(port, 10));
console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown");
@einarolafs

Copy link
Copy Markdown

Agree with the last comment, replacing path.exist with fs.exist fixed an error that I did not fully understand. Otherwise, fantastic solution, perfect for beginners :)

@raidenz

raidenz commented Jun 23, 2015

Copy link
Copy Markdown

yes thanks brandt1871 path.exists is now called fs.exists

@fordnox

fordnox commented Jul 17, 2015

Copy link
Copy Markdown

Same issue here, path.exists is now called fs.exists

Please fix for future generations :)

@ratneshnavlakhe

Copy link
Copy Markdown

Thanks!!!

@Deele

Deele commented Sep 28, 2015

Copy link
Copy Markdown

Please, include package.json so that we could npm install. Thanks!

@byeblogs

Copy link
Copy Markdown

You saved my day brotha.. Thanks :) ~

@CustomsoftGH

Copy link
Copy Markdown

For those newbies out there, path.exists has been depricated to fs.exists.

@harismohammed

Copy link
Copy Markdown

thank u

@evenfrost

Copy link
Copy Markdown

If anyone is interested in ES6+ version, I've revamped it here.

@kostasx

kostasx commented May 23, 2016

Copy link
Copy Markdown

Small typo: fs = require("fs") is missing a comma at the end of the line, making port on the next line a global variable.
Thank you for sharing!

@archasek

archasek commented Jun 9, 2016

Copy link
Copy Markdown

Fixed the whole code. Enjoy :)

var http = require("http"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    port = process.argv[2] || 8888;

http.createServer(function(request, response) {

  var uri = url.parse(request.url).pathname
    , filename = path.join(process.cwd(), uri);

  fs.exists(filename, function(exists) {
    if(!exists) {
      response.writeHead(404, {"Content-Type": "text/plain"});
      response.write("404 Not Found\n");
      response.end();
      return;
    }

    if (fs.statSync(filename).isDirectory()) filename += '/index.html';

    fs.readFile(filename, "binary", function(err, file) {
      if(err) {        
        response.writeHead(500, {"Content-Type": "text/plain"});
        response.write(err + "\n");
        response.end();
        return;
      }

      response.writeHead(200);
      response.write(file, "binary");
      response.end();
    });
  });
}).listen(parseInt(port, 10));

console.log("Static file server running at\n  => http://localhost:" + port + "/\nCTRL + C to shutdown");

@CforED

CforED commented Jul 4, 2016

Copy link
Copy Markdown

Hi, Everyone:

Problem: There is a failure in the JavaScript code (server.js) to route a var to the following directory: ./public/index.html.

Symptoms: HTTP Error 403.14 - Forbidden.

Request for Support: What is the correct JavaScript to route var to a particular directory?

URL References:

1.) GitHub URL link: https://github.com/CforED/WebSite/blob/master/server.js

2.) Gist URL link: https://gist.github.com/CforED/177a2e73dfb87313576376fc00a2c9f4.js

3.) GitHub URL link to website:
https://github.com/CforED/WebSite/tree/master/public

Hal

@amejiarosario

Copy link
Copy Markdown

I created a new one that handles MIME types and uses ES6+

https://gist.github.com/amejiarosario/53afae82e18db30dadc9bc39035778e5

@kovid-rathee

Copy link
Copy Markdown

Got an error with path.exists, changed it to fs.exists and the snippet worked fine.

@duluca

duluca commented Feb 28, 2017

Copy link
Copy Markdown

@kovid-rathee I ran in to the same issue and your solution worked!

@Julia991

Copy link
Copy Markdown

Hey, guys what do you think about this article?
he best Node.js framework for your project: Express.js, Koa.js or Sails.js is express js the best framework for Node js?

@cmshiyas

Copy link
Copy Markdown

Awesome! I am new to Node.js and was trying to figure out how to configure this...This worked with a minor update as givne in another post creationix/howtonode.org#88
Thank you very much!

@robole

robole commented Jun 26, 2017

Copy link
Copy Markdown

ty. min req, what i was after

@dkebler

dkebler commented Oct 31, 2017

Copy link
Copy Markdown

path.exists has been deprecated. If you want a sync check then

  if (!fs.statSync(filename)) {
    response.writeHead(404, {'Content-Type': 'text/plain'})
    response.write('404 Not Found\n')
    response.end()
    return
  }

@dkebler

dkebler commented Oct 31, 2017

Copy link
Copy Markdown

even better how about the handler's fs stuff all async with callbacks

fs.stat(filename, function(err,stats) {
    if (err) {
      response.writeHead(404, {'Content-Type': 'text/plain'})
      response.write('404 Not Found\n')
      response.end()
      return
    }

    if (stats.isDirectory()) filename += '/index.html'

    fs.readFile(filename, 'binary', function(err, file) {
      if(err) {
        response.writeHead(500, {'Content-Type': 'text/plain'})
        response.write(err + '\n')
        response.end()
        return
      }
      response.writeHead(200)
      response.write(file, 'binary')
      response.end()
    })
  })

@Risyandi

Copy link
Copy Markdown

Thanks a lot :)

@mandaputtra

Copy link
Copy Markdown

Thanks a lot, but it can be hijacked

@XMB5

XMB5 commented Jun 10, 2018

Copy link
Copy Markdown

This is insecure, someone can request /../../../etc/shadow or similar to read any file they want.

@Abd-Elrazek

Copy link
Copy Markdown

thanks ...it's great, that why I'm looking for it

@CCorb

CCorb commented Feb 29, 2020

Copy link
Copy Markdown

path.exists is now called fs.exists

@unsaved

unsaved commented Mar 19, 2020

Copy link
Copy Markdown

This is insecure, someone can request /../../../etc/shadow or similar to read any file they want.

I guess you're not familiar with how path.join works, because as coded here it won't resolve to a directory outside of cwd directory branch.

@rpivo

rpivo commented Apr 12, 2021

Copy link
Copy Markdown

fs.exists is deprecated. fs.statSync can be used to check if file path exists, as in dkebler's code above.

@codespede

Copy link
Copy Markdown

Thank you for this!

@Anna-art125

Copy link
Copy Markdown

Incredible article, thanks for sharing it! I think this article will also be useful for you - Node js vs Python.

@JafarAkhondali

Copy link
Copy Markdown

I'm 15 years late to the party, but this is indeed insecure. PoC:
curl --path-as-is http://localhost:8888/../../../../../../../../etc/passwd

By default, when you send HTTP requests using curl, they'll be normalized. With --path-as-is flag you can prevent normalization, thus the exploit works. The same thing happens if you test the PoC in your browser. I think that's why most people didn't find the vulnerability.

This is insecure, someone can request /../../../etc/shadow or similar to read any file they want.

I guess you're not familiar with how path.join works, because as coded here it won't resolve to a directory outside of cwd directory branch.

No, I think you're not familiar with how path.join works, there is no mitigation for the directory boundary check here. We wrote a paper about this vulnerability as it spread almost EVERYWHERE.

You can test it in Node:
node -e 'console.log(path.join("/opt/app/www", "/../../../../etc/passwd")) // It will print /etc/passwd'

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