Serving Static Files with Node.js

Share this article

In this article we’re going to look at setting up a static file server using Node. There’s plenty of modules available to help us do this, but we’re going to focus on three of them: node-static, paperboy, and http-server.

All of these modules are available via npm (Node Package Manager). npm has been packaged with node since version 0.6.3, but if you haven’t got it installed it’s easy enough to do with this one-liner:

curl http://npmjs.org/install.sh | sh

Let’s have a look at each static file server in turn, starting with node-static.

node-static

node-static can be installed via npm with the following command:

npm install node-static

To use it: include the module in your script via the require function, create a server, pass it your preferred options, and tell it to serve files:

var static = require('node-static'),
  http = require('http'),
  util = require('util');

var webroot = './public',
  port = 8080;

var file = new(static.Server)(webroot, { 
  cache: 600, 
  headers: { 'X-Powered-By': 'node-static' } 
});

http.createServer(function(req, res) {
  req.addListener('end', function() {
    file.serve(req, res, function(err, result) {
      if (err) {
        console.error('Error serving %s - %s', req.url, err.message);
        if (err.status === 404 || err.status === 500) {
          file.serveFile(util.format('/%d.html', err.status), err.status, {}, req, res);
        } else {
          res.writeHead(err.status, err.headers);
          res.end();
        }
      } else {
        console.log('%s - %s', req.url, res.message); 
      }
    });
  });
}).listen(port);

console.log('node-static running at http://localhost:%d', port);

Here, when we create a new instance of a node-static server, we pass it:

  • the directory we want it to serve files from by way of the `webroot` variable (node-static defaults to serving files from the current directory unless you tell it otherwise)
  • a cache value of 600, so each file served will be cached for 10 minutes (the default value for cache is 3600, meaning files are cached for 1 hour)
  • a custom ‘X-Powered-By’ header (I’ve used X-Powered-By solely as an example – you may, or may not, want to disclose the software you’re server is running)

We then use the http module’s createServer function, and add an event handler for the request’s end event where we use our instance of node-static to serve the files.

When we call file.serve, we pass it an optional callback function that let’s us customise how we handle any errors:

  • we check err.status, and serve either 404.html or 500.html accordingly
  • if there’s an error and the status code is something other than 404 or 500, we send the status code and the headers back to the client explicitly – when you pass a callback function to file.serve and there is an error, node-static will not respond to the client by itself.

paperboy

paperboy provides a fluent API for setting up your server that lets you configure the port and IP address, set HTTP headers, and handle events pre-request and post-response. You can also tell it what to do in case of a error. Let’s see how we might use paperboy to create a static file server:

var paperboy = require('paperboy'),
    http = require('http'),
    path = require('path');

var webroot = path.join(__dirname, 'public'),
    port = 8080;

http.createServer(function(req, res) {
  var ip = req.connection.remoteAddress;
  paperboy
    .deliver(webroot, req, res)
    .addHeader('X-Powered-By', 'Atari')
    .before(function() {
      console.log('Request received for ' + req.url);
    })
    .after(function(statusCode) {
      console.log(statusCode + ' - ' + req.url + ' ' + ip);
    })
    .error(function(statusCode, msg) {
      console.log([statusCode, msg, req.url, ip].join(' '));
      res.writeHead(statusCode, { 'Content-Type': 'text/plain' });
      res.end('Error [' + statusCode + ']');
    })
    .otherwise(function(err) {
      console.log([404, err, req.url, ip].join(' '));
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('Error 404: File not found');
    });
}).listen(port);

console.log('paperboy on his round at http://localhost:' + port);

After importing the modules we require, we use a couple of variables to store the webroot we want documents served from, and the port we want our server to listen on. Note that the current version of paperboy (0.0.3) doesn’t appear to support a webroot specified with a relative filepath e.g. ./public. We therefore use path.join(__dirname, ‘public’) to give us the absolute path to the public folder we want to serve files from (__dirname is one of Node’s globals and gives us the name of the directory that the currently executing script lives in).

We pass webroot to the deliver function so paperboy knows which directory to serve files from. addHeader then adds some HTTP headers. It takes two arguments: the name of the header, and the header’s value. We then use the before, after, error, and otherwise functions to add event handlers via some callback functions:

  • before will fire it’s callback function just before the requested file is delivered. To stop the file being served you can return false from the callback function.
  • after will fire it’s callback function once a response has been sent back to the client and the file has been delivered. The callback function is passed a single argument representing the status code of the HTTP response sent back to the client
  • error fires if something goes wrong during the processing of the request. The callback is passed two arguments: the HTTP status code of the response, and the error message. Note that you’ll need to close the connection to the server yourself in your error callback function; the example above does this with the line response.end(…).
  • otherwise fires if no matching file was found in webroot, or if false was returned from the before callback function. The callback function is passed a single argument containing details of the error. paperboy’s default callback function shows a simple HTTP 404 File Not Found page.

Lastly we tell the server which port to listen on. Note that when it receives a request for ‘/’, paperboy appends index.html and serves that file by default, as long as index.html exists.

http-server

Finally, lets have a look at http-server. Installation is done via npm, but make sure you pass npm the -g switch to make http-server available globally:

npm install http-server -g

Installing http-server this way makes it available via the command line:

$> type http-server
http-server is hashed (/usr/local/bin/http-server)

As it’s available from the command line, you won’t have to write any code to get it up and running. All you have to do is type:

$> http-server
Starting up http-server, serving ./public on port: 8080
http-server successfully started: http://localhost:8080
Hit CTRL-C to stop the server

This will start the serving files out of the ./public directory if it exists, or the current working directory if it doesn’t. The default port used by http-server is 8080, but you can change this, and a few other options, via command line switches:

  • -p to specify which port to listen on
  • -a to tell it which IP address to bind to
  • -i to configure whether to display autoIndex (defaults to true)
  • -s or –silent to turn off console logging
  • -h or –help to display this list of available commands

In this article, we’ve looked at three static file server modules available in Node: node-server, paperboy, and http-server. While they all achieve the same goal, which one you choose to use might be down to how much code you want to write, and how much control you need to exercise, over your Node static file server.

Of course, you might decide to write your own static file server from scratch. If you do, you could do a lot worse than to peruse the source code of the three servers we’ve looked at today:

Frequently Asked Questions about Serving Static Files with Node.js

What is the purpose of serving static files in Node.js?

Serving static files in Node.js is a fundamental aspect of creating web applications. Static files are files that clients download as they are from the server. Examples include images, CSS files, and JavaScript files. These files are used to add styles, images, and interactivity to your web pages. By serving these files, you can create a more dynamic and interactive user experience on your website.

How can I serve static files using Express.js?

Express.js is a popular web application framework for Node.js. To serve static files using Express.js, you need to use the express.static built-in middleware function. Here’s a basic example:

const express = require('express');
const app = express();

app.use(express.static('public'));

app.listen(3000, () => {
console.log('Server is running on port 3000');
});
In this example, Express.js will serve the static files located in the ‘public’ directory.

What is the role of the ‘http’ module in serving static files?

The ‘http’ module in Node.js is used to create an HTTP server that listens to server ports and gives a response back to the client. When serving static files, the ‘http’ module can be used to handle requests and responses, including delivering the static files to the client.

How can I serve static files in a specific directory?

To serve static files in a specific directory, you can specify the directory name when setting up your static file server. For example, if you’re using Express.js, you can use the express.static middleware function like this:

app.use('/static', express.static('public'));
In this example, all files in the ‘public’ directory will be served at routes that start with ‘/static’.

Can I serve multiple static directories in Node.js?

Yes, you can serve multiple static directories in Node.js. If you’re using Express.js, you can do this by calling the express.static middleware function multiple times, like this:

app.use(express.static('public'));
app.use(express.static('files'));
In this example, both the ‘public’ and ‘files’ directories will be served as static directories.

How can I handle errors when serving static files?

Handling errors when serving static files is crucial for maintaining a robust Node.js application. You can handle errors by listening for the ‘error’ event on the response object. If an error occurs while serving a file, the ‘error’ event will be emitted.

How can I set the Content-Type header when serving static files?

The Content-Type header can be set using the response.setHeader method. This method takes two arguments: the name of the header and the value to set. For example, to set the Content-Type header to ‘text/html’, you can do this:

response.setHeader('Content-Type', 'text/html');

How can I serve static files with gzip compression?

Gzip compression can be used to reduce the size of the static files served by your Node.js application. To serve static files with gzip compression, you can use the ‘compression’ middleware in Express.js.

Can I serve static files over HTTPS in Node.js?

Yes, you can serve static files over HTTPS in Node.js. To do this, you need to create an HTTPS server instead of an HTTP server. This requires an SSL certificate.

How can I cache static files in Node.js?

Caching static files can improve the performance of your Node.js application by reducing the load on your server. You can implement caching by setting the ‘Cache-Control’ header in your responses. This header controls how, and for how long, the client caches the response.

Ian OxleyIan Oxley
View Author

Ian Oxley has been building stuff on the Web professionally since 2004. He lives and works in Newcastle-upon-Tyne, England, and often attends local user groups and meetups. He's been known to speak at them on occasion too. When he's not in front of a computer Ian can be found playing guitar, and taking photos. But not usually at the same time.

javascriptnode.jsnodejs
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form