Coder Social home page Coder Social logo

serve-static's Introduction

serve-static

NPM Version NPM Downloads Linux Build Windows Build Test Coverage

Install

This is a Node.js module available through the npm registry. Installation is done using the npm install command:

$ npm install serve-static

API

var serveStatic = require('serve-static')

serveStatic(root, options)

Create a new middleware function to serve files from within a given root directory. The file to serve will be determined by combining req.url with the provided root directory. When a file is not found, instead of sending a 404 response, this module will instead call next() to move on to the next middleware, allowing for stacking and fall-backs.

Options

acceptRanges

Enable or disable accepting ranged requests, defaults to true. Disabling this will not send Accept-Ranges and ignore the contents of the Range request header.

cacheControl

Enable or disable setting Cache-Control response header, defaults to true. Disabling this will ignore the immutable and maxAge options.

dotfiles

Set how "dotfiles" are treated when encountered. A dotfile is a file or directory that begins with a dot ("."). Note this check is done on the path itself without checking if the path actually exists on the disk. If root is specified, only the dotfiles above the root are checked (i.e. the root itself can be within a dotfile when set to "deny").

  • 'allow' No special treatment for dotfiles.
  • 'deny' Deny a request for a dotfile and 403/next().
  • 'ignore' Pretend like the dotfile does not exist and 404/next().

The default value is similar to 'ignore', with the exception that this default will not ignore the files within a directory that begins with a dot.

etag

Enable or disable etag generation, defaults to true.

extensions

Set file extension fallbacks. When set, if a file is not found, the given extensions will be added to the file name and search for. The first that exists will be served. Example: ['html', 'htm'].

The default value is false.

fallthrough

Set the middleware to have client errors fall-through as just unhandled requests, otherwise forward a client error. The difference is that client errors like a bad request or a request to a non-existent file will cause this middleware to simply next() to your next middleware when this value is true. When this value is false, these errors (even 404s), will invoke next(err).

Typically true is desired such that multiple physical directories can be mapped to the same web address or for routes to fill in non-existent files.

The value false can be used if this middleware is mounted at a path that is designed to be strictly a single file system directory, which allows for short-circuiting 404s for less overhead. This middleware will also reply to all methods.

The default value is true.

immutable

Enable or disable the immutable directive in the Cache-Control response header, defaults to false. If set to true, the maxAge option should also be specified to enable caching. The immutable directive will prevent supported clients from making conditional requests during the life of the maxAge option to check if the file has changed.

index

By default this module will send "index.html" files in response to a request on a directory. To disable this set false or to supply a new index pass a string or an array in preferred order.

lastModified

Enable or disable Last-Modified header, defaults to true. Uses the file system's last modified value.

maxAge

Provide a max-age in milliseconds for http caching, defaults to 0. This can also be a string accepted by the ms module.

redirect

Redirect to trailing "/" when the pathname is a dir. Defaults to true.

setHeaders

Function to set custom headers on response. Alterations to the headers need to occur synchronously. The function is called as fn(res, path, stat), where the arguments are:

  • res the response object
  • path the file path that is being sent
  • stat the stat object of the file that is being sent

Examples

Serve files with vanilla node.js http server

var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')

// Serve up public/ftp folder
var serve = serveStatic('public/ftp', { index: ['index.html', 'index.htm'] })

// Create server
var server = http.createServer(function onRequest (req, res) {
  serve(req, res, finalhandler(req, res))
})

// Listen
server.listen(3000)

Serve all files as downloads

var contentDisposition = require('content-disposition')
var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')

// Serve up public/ftp folder
var serve = serveStatic('public/ftp', {
  index: false,
  setHeaders: setHeaders
})

// Set header to force download
function setHeaders (res, path) {
  res.setHeader('Content-Disposition', contentDisposition(path))
}

// Create server
var server = http.createServer(function onRequest (req, res) {
  serve(req, res, finalhandler(req, res))
})

// Listen
server.listen(3000)

Serving using express

Simple

This is a simple example of using Express.

var express = require('express')
var serveStatic = require('serve-static')

var app = express()

app.use(serveStatic('public/ftp', { index: ['default.html', 'default.htm'] }))
app.listen(3000)

Multiple roots

This example shows a simple way to search through multiple directories. Files are searched for in public-optimized/ first, then public/ second as a fallback.

var express = require('express')
var path = require('path')
var serveStatic = require('serve-static')

var app = express()

app.use(serveStatic(path.join(__dirname, 'public-optimized')))
app.use(serveStatic(path.join(__dirname, 'public')))
app.listen(3000)

Different settings for paths

This example shows how to set a different max age depending on the served file type. In this example, HTML files are not cached, while everything else is for 1 day.

var express = require('express')
var path = require('path')
var serveStatic = require('serve-static')

var app = express()

app.use(serveStatic(path.join(__dirname, 'public'), {
  maxAge: '1d',
  setHeaders: setCustomCacheControl
}))

app.listen(3000)

function setCustomCacheControl (res, path) {
  if (serveStatic.mime.lookup(path) === 'text/html') {
    // Custom Cache-Control for HTML files
    res.setHeader('Cache-Control', 'public, max-age=0')
  }
}

License

MIT

serve-static's People

Contributors

amasad avatar dougwilson avatar esco avatar igchuk avatar jayk avatar jjmerino avatar mscdex avatar okyantoro avatar stsourlidakis avatar taueres avatar thestinger avatar tohuw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

serve-static's Issues

async setHeaders callback

Most API's in express (and node) are async, but setHeaders callback here isn't. Here is a current use case that I have:

setHeaders: function(path, res) {
  fs.stat(path, (e, stat) => res.set('X-Raw-Length', stat.size));
}

I'm doing this to show a progress bar on the client-side when loading a big resource. Content-length won't work when the content is gzipped.

Of course, this doesn't work because by the time fs.stat comes back the response might have ended.

Redirect to external website

Stumbled upon a weird behavior where serve-static would redirect to an external website when "asked nicely".

Reproduction Steps

Using express 4.10.6 and static-serve 1.7.1 on node 0.10.33.

1. Simple app.js

var app = require('express')();
app.use(require('serve-static')('assets'));
app.listen(80);

2. Start server

$ sudo node app.js

3. Open in Firefox http://localhost//www.google.com/%2e%2e

Request
GET //www.google.com/%2e%2e HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Response
HTTP/1.1 303 See Other
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Location: //www.google.com/%2e%2e/
Date: Sat, 03 Jan 2015 01:13:49 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Redirecting to <a href="//www.google.com/%2e%2e/">//www.google.com/%2e%2e/</a>

4. You get redirected to Google...


It works in Firefox, Safari and probably IE, not in Chrome.
Setting static-serve’s option redirect: false seems to fix it (but redirect: true is the default).

It looks like many applications could be affected.
A quick test on apps listed on http://expressjs.com/resources/applications.html does not disappoint:

send emits directory in that case, which triggers the redirection.

Question: Is it possible to access the origin from setHeaders()?

Hallo,

I'm having a problem to setup multiples domain to be Access-Control-Allow-Origin.
I don't like to use * because of security issues, so I was trying to apply this solution bellow:

https://stackoverflow.com/questions/24897801/enable-access-control-allow-origin-for-multiple-domains-in-node-js

Is it possible to access the origin on the parameters returned on setHeaders?

setHeaders(res, path, stat) {}

Thank you for all help. ❤️

How can I set different max-age for different types of file?

Hi there,
I'm using version footprint to manage my static assets (js/css/images), I would like to give html files max-age:0, and give all other assets max-age:'365d'

Currently I'm doing this by writing my own middleware, is there a way to do it using serve-static? maybe some filter function?

Error when empty file served.

serve-static - 1.9.0
node - 0.10.26

When an empty file is served, we now this this error

fs.js:1477
      throw new Error('start must be <= end');
            ^
Error: start must be <= end
    at new ReadStream (fs.js:1477:13)
    at Object.fs.createReadStream (fs.js:1438:10)
    at SendStream.stream (/Users/shakyshane/code/angular-bs/node_modules/serve-static/node_modules/send/index.js:668:19)
    at SendStream.send (/Users/shakyshane/code/angular-bs/node_modules/serve-static/node_modules/send/index.js:576:8)
    at onstat (/Users/shakyshane/code/angular-bs/node_modules/serve-static/node_modules/send/index.js:600:10)
    at Object.oncomplete (fs.js:107:15)

reduced test case showing the bug https://github.com/shakyShane/serve-static-bug

example to reproduce

var connect     = require("connect");
var http        = require("http");
var serveStatic = require("serve-static");

var app = connect();

app.use(serveStatic("./src"));

var server = http.createServer(app).listen();

console.log("http://localhost:" + server.address().port);

weird redirect issue when using apache proxy

So, I had no idea why my app was breaking, but apparently it was being redirected for no apparent reason.

apache redirects /mypath to my node app

when it wasn't behind apache it was fine, but then suddenly it was being picked up by the serve-static middleware. and only sometimes. i'm using serve static for /vendor and /public. Several sub applications nested under the main node app each have a public and a vendor dir. Only the sub apps that have a vendor are having this issue ( very weird since the hooks are identical ).

normally(not behind apache) going to /subapp will load the sub app just fine, doesn't pick up the directory serve-static.

when nested under apache, going to /mypath/subapp, your application will redirect my app to /subapp/, which is not picked up by apache. This is totally unexpected, and it should be going to /mypath/subapp/. I would think this middleware would just give me files not mess with my redirects at all. So I had to set redirect: false.

I think this should be opt-in, since this middleware is supposed to be boilerplate basic serving files and not redirecting. That or make the detect for redirect better since it is being overzealous and breaking my url.

Wrong behavior when handling discontinuous ranges.

I use serve-static middleware with expressjs to serve a big file, when I request discontinuous partial content of that big file, serve-static will response with the whole file and the status code is 200 rather than 206.
e.g. content of my big file (localhost:5052/test.txt) is:

0123456789

If I request with:

GET /test.txt HTTP/1.1
Host: localhost:5052
Range: bytes=1-3,6-8 // note, discontinuous
Cache-Control: no-cache

The response will be the whole file

0123456789

But if the range is continuous, it works fine.

Please give some advises. Thanks

feature request: enable mapping url to a different file via a callback

There are situations when I need to map the request url (pathname & query) to a different file.
For example when I want to modify a file, store the modified file someplace else then send it to client.

For this to work we would need a callback in middleware options to return the file path on disk:

getFilePath: (request: ExpressRequest) => string

This is useful specially when working on a middleware that would compress and/or resize images on the fly using pathname and query string.

Omitted `Content-Type` header when passing index file

Hi,
I'm using this middleware in order to serve static files. My configuration is something like this:

    serveStatic('./public', {
      index: isDev ? 'index.html' : 'index.prod.html'
    })

But when navigating on the root, since I have the X-Content-Type-Options: nosniff header enabled, I've noticied that the index.html file is returned without the Content-type header, so the HTML get not rendered by the browser:

image

I think that header should be present, shouldn't it?

"Cache-Control: public, max-age=0" is set by default

Hello,
I am using this module for a while and I encountered the following problem. I cannot remove the Cache-Control header. It is always set by default to some value. I noticed that this behaviour comes from the "send" module, which you use:

if (!res.getHeader('Cache-Control')) {
  res.setHeader('Cache-Control', 'public, max-age=' + Math.floor(this._maxage / 1000));
}

I managed to make a work-around with the "on-headers" module, mentioned in issue #5 which looks like this:

var onHeaders = require('on-headers');
onHeaders(function() {
  this.removeHeader('Cache-Control');
}

But this approach looks pretty sketchy. So the question is why can't this be configured somewhere in the options, because after researching Apache and Nginx I saw that they don't add the Cache-Control header by default and I want to accomplish that behaviour. Besides it looks like the right approach to me because after looking at the HTTP1.1 spec I found:

Unless specifically constrained by a cache-control (section 14.9)
directive, a caching system MAY always store a successful response
(see section 13.8) as a cache entry, MAY return it without validation
if it is fresh, and MAY return it after successful validation.

So, it seems that is up to the Content Delivery Networks (Akamai, etc) to decide whether something is cachable by default. So is there a reason for forcing the Cache-Control by default in this module?

Use seconds instead of milliseconds for maxAge

The max-age value in cache-control is supposed to be passed in in seconds (max-age spec.) The middleware accepts milliseconds for maxAge paramter, which creates a bit of confusion around conversion from one unit to another.

As a reference where seconds are used for max-age - the CORS middleware passes along seconds instead of milliseconds.

Not sure what's the best way to disambiguate the situation and start using seconds. Maybe allowing to pass in an additional property "seconds: true" to the middleware could be one of the approaches. Not sure. Just found this behavior confusing and have cought some major bugs that in the codebase that were coming out of the second/millisecond confusion.

Using datejs causing 412 Precondition Failed errors

I recently re-installed my node_modules after a refactor, but without changing any versions in my package.json. I spent a few hours debugging an issue yesterday that I thought was part of my refactor, but it turned out to be a breaking change caused by serve-static's update to [email protected]. This version of send added support for the conditional request headers, which Chrome uses when requesting files that it has cached.

Currently, all of my static files that my browser has cached are being served as a 500 Internal Server Error because send sees that the precondition has failed (If-Modified-Since fails because the file has not been modified), causing my error handler to catch a PreconditionFailedError: Precondition Failed. send is supposed to return a proper response with a 412 status code in this case, but if there is a listener on the send stream's error event, it will instead throw the HTTP error as an error object via next(error). This bubbles down into the last-resort error handler for Express, which I have configured to return a 500 error code, and in development mode, a description of the error to the client.

I do not have fallthrough: false set on my call to express.static(...), and according to the documentation, this should cause the 412 error to fall through as a client error instead of letting it bubble through next(err) down into my last-resort error handler.

Single file serve?

Would a shortcut to res.sendFile make sense to be added to serve-static?

i would say

//serving just my index file
router.get('/',  express.static(path.join(__dirname, 'public', 'index.html')));

Makes a lot of sense. Any thoughts?

option for private files analogous to dotfiles

I don't know if this would even work because I don't know if Windows has something like this, but I'm wondering if you would be open to adding an option (or accepting a PR) to handle private files (files that do not have world-readable) in the same way that dotfiles is handled.

A public file or directory is typically mode 644 or 755

A private file or directory is typically mode 400 or 500

My suggestion is that an option like privatefiles: 'ignore' would treat any file lacking world-readable permission (the o of ugo does not have 4) the same as dotfiles: 'ignore' is currently treated.

how to prevent a file from being served

Hi, lets say I have a file with extension .ts or file mysecret.yaml and I don't want it served.
How can I filter/prevent some file extensions and some files from being served?

Disable symlinks

If I'm doing .use(express.static('/var/www/html')) and some attacker manages to ln -s /etc/passwd /var/www/html, then http://host/passwd will serve up /etc/passwd. Is there any way to tell serve-static not to follow symlinks, or to restrict them so that they're only followed to files within the directory being served?

I'm essentially asking for Apache's FollowSymLinks or nginx's disable_symlinks.

FR: Ability to always call next()

This was brought up in #68 (comment) but I was wondering if this could be revisited. We may want to have logging/other tasks done per request but not block on sending a response. I also found that res.on("finish") doesn't fire if a client disconnects mid-request.

Something like the fallthrough option that fires next() on the "end" event of the underlying stream.

Corrupted file served if file is changed during serving.

Hi, I've got such a code for my server:

import path from 'node:path'
import url from 'node:url'
import portfinder from 'portfinder'
import connect from 'connect'
import { WebSocketServer } from 'ws'
import serveStatic from 'serve-static'
import logger from 'morgan'

export const DEFAULT_PORT = 8080

let dirname = path.dirname(url.fileURLToPath(import.meta.url))
// Path of a file that needs to be injected into the bundle for live-reload to work.
export const LIVE_RELOAD_LISTENER_PATH = path.join(dirname, 'live-reload.js')

export async function start({ root, assets, port }) {
    assets = assets ?? path.join(root, 'assets')

    const freePort = await portfinder.getPortPromise({ port: port ?? DEFAULT_PORT })

    const setHeaders = (res) => {
        res.setHeader('Cache-Control', 'no-store')
    }

    const app = connect()
        .use(logger('dev', { skip: (req, res) => res.statusCode < 400 }))
        .use(serveStatic(root, {setHeaders}))
        .use('/assets', serveStatic(assets, {setHeaders}))

    const server = app.listen(freePort)
    const wsServer = new WebSocketServer({ server, clientTracking: true, path: '/live-reload' })

    var serverUrl = `http://localhost:${freePort}`
    console.log('Serving %s', serverUrl)

    return {
        port: freePort,
        reload() {
            wsServer.clients.forEach(sock => sock.send('reload'))
        },
    }
}

And we have observed a very interesting behavior. Namely, when it is serving our WASM file (100MB+), if the WASM is being re-build just when serving starts, then it is served to the browser corrupted. What's interesting is that after it is rebuilt, browser refreshes dont help - the same corrupted file is being served. What helps is restarting the above server to serve non-corrupted file. Does anyone have any idea why this happens and if this can be prevented somehow? I'm OK with serving it in a corrupted state for the first time (during rebuild), but I want the file to be served correctly after page reload.

MaxListenersExceedsWarning when used with http2

I am aware of #7 which was closed a while ago now. However the issue still is occurring for me, using node 7.10.0. I think that is probably because I am using the http2 as the server and because the app that it is serving is a polymer custom components one, so calling index.html does cause an immediate request for quite a large number of additional files (approx 40 if I remember correctly) which should be being served through serve-static.

I have put a trace on the warning and confirmed it comes from the on-finished module as expressed in #7. I have also puts some instrumentation in the module that is adding and removing the listeners, so I am pretty sure that although a lot are added they are all eventually removed again, to I don't think there is an actual memory leak happening.

I report it here to see if there is anything that serve static could do to prevent the warning occuring

Serve Static Files Case sensitive

Hi i have the following server

(function() {
  'use strict';
  var express = require('express');
  var serveStatic = require('serve-static');
  var serveIndex = require('serve-index');
  var finalhandler = require('finalhandler');
  var open = require('opn');

  var app = express();
  var router = express.Router({
    caseSensitive: true
  });
  var index = serveIndex(__dirname, {
    'icons': true
  })
  var serve = serveStatic(__dirname, { // eslint-disable-line
    dotfiles: 'allow',
    setHeaders: setHeaders
  });


  function setHeaders(res, path) {
    res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
    res.header('Expires', '-1');
    res.header('Pragma', 'no-cache');
  }

  router.use(index);
  router.use(serve);
  app.use(router);

  var port = 8080;

  app.listen(port, function() {
    console.log("server is now running on http://127.0.0.1:" + port); // eslint-disable-line
    open("http://localhost:" + port);
  });
}());

but it is still serving the static files case insensitiv.

is there any way to get them served case sensitive?
is it maybe a OS problem because i am using Mac osx?

Feature request: remove extensions from path

Since send is able to handle finding files when the extensions don't exist in the path, would it be possible to expand the redirect functionality here to remove extensions?

For instance, these redirects would take place:

from to
/login.html /login
/something/index.html /something/

I think this would only happen if the extensions option is already populated with the extensions for send to look for.

Gzip support.

Would be nice to have an option to prefer sending gzipped versions of files.

Add option to remove trailing slashes, as opposed to adding them

Hello!

We're serving a website through Gatsby, which is having some trouble removing trailing slashes when serving an index.html file.

As you can see from the PR and discussion, it seems to be impossible through configuration options to remove the trailing slash, even if it's possible to stop from redirecting to a trailing slash.

Would you be interested in a PR which adds (or changes) an option to remove trailing slashes when serving index files?

Thank you very much :)

middleware fails when req.originalUrl is not present

If middleware is supposed to be generic, not express-related, it'd be nice to fix this.

req.originalUrl || req.url maybe?

  return function staticMiddleware(req, res, next) {
    if ('GET' != req.method && 'HEAD' != req.method) return next();
    var originalUrl = url.parse(req.originalUrl);
                              // ^^^ this
    var path = parse(req).pathname;
TypeError: Parameter 'url' must be a string, not undefined
    at Url.parse (url.js:107:11)
    at Object.urlParse [as parse] (url.js:101:5)
    at staticMiddleware (/home/alex/node_modules/connect/node_modules/serve-static/index.js:56:27)

Need support node >= v10.x

node: v10.15.0
npm: 6.4.1

on Travis.

Error message after npm test as below:

 1) serveStatic() fallthrough when true should fall-through when URL too long:
     Error: expected 404 "Not Found", got 400 "Bad Request"
      at Test._assertStatus (node_modules/supertest/lib/test.js:232:12)
      at Test._assertFunction (node_modules/supertest/lib/test.js:247:11)
      at Test.assert (node_modules/supertest/lib/test.js:148:18)
      at Server.assert (node_modules/supertest/lib/test.js:127:12)
      at emitCloseNT (net.js:1609:8)
      at process._tickCallback (internal/process/next_tick.js:63:19)
  2) serveStatic() fallthrough when false should 404 when URL too long:
     Error: expected 404 "Not Found", got 400 "Bad Request"
      at Test._assertStatus (node_modules/supertest/lib/test.js:232:12)
      at Test._assertFunction (node_modules/supertest/lib/test.js:247:11)
      at Test.assert (node_modules/supertest/lib/test.js:148:18)
      at Server.assert (node_modules/supertest/lib/test.js:127:12)
      at emitCloseNT (net.js:1609:8)
      at process._tickCallback (internal/process/next_tick.js:63:19)

Everything is ok when node < v10.x.

Include and exclude

Hi, i tried to use serve-static to make a simple static server. There is useful options like include or exclude i wanted but neither of them exist. So questions is coming. Is necessary to add the features include or 'exclude'? Thanks your reading.

EventEmitter memory leak

if you save this script as index.js and run it:

var connect = require('connect');
var serveStatic = require('serve-static');

var app = connect();

app.use(serveStatic('.'));
app.listen(8000);

then browse to http://localhost:8000/index.js and with the cache disabled refresh the page 9+ times you get this error:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at Socket.EventEmitter.addListener (events.js:160:15)
    at Socket.Readable.on (_stream_readable.js:688:33)
    at module.exports (/Users/Lloyd/Documents/75lb/tmp/node_modules/serve-static/node_modules/send/node_modules/finished/index.js:13:10)
    at SendStream.stream (/Users/Lloyd/Documents/75lb/tmp/node_modules/serve-static/node_modules/send/lib/send.js:526:3)
    at SendStream.send (/Users/Lloyd/Documents/75lb/tmp/node_modules/serve-static/node_modules/send/lib/send.js:470:8)
    at /Users/Lloyd/Documents/75lb/tmp/node_modules/serve-static/node_modules/send/lib/send.js:382:10
    at Object.oncomplete (fs.js:107:15)

Serve one file for any routes, catch all

I'm probably missing something simple, but I am using this module to serve a react app, and I would like any address to serve one file index.html, here is the code I am using:

var serve=serveStatic('build', {'index': ['index.html', 'index.htm']})

var server = http.createServer(function onRequest (req, res) {
    serve(req, res, finalhandler(req, res))
})

It works fine for '/' but I want '/foo/bar to also load index.html, is that possible?

Please add types for typescript

Thank you for this simple but very helpful library. I'd just like to request if you can provide types for supporting typescript. That would be superb! Thanks :)

Apply a transform stream

Hi there,

I was wondering if you envisioned the ability to apply a transform stream to a served file?

I would like to replace some tokens in a static page, eg with stream-replace. I imagined it working like this:

var replace = require('stream-replace');

var serve = serveStatic('public/ftp', {
  'index': ['index.html', 'index.htm'],
  transforms: [
    replace('{{TOKEN}}', process.env.SECRET_TOKEN) 
  ]
})

Instead of having stream.pipe(res), it would be rather like:

transforms.reduce((s, t) => s.pipe(t),stream).pipe(res);

What do you think?

Add an option to try a file based on Accept-Encoding

My webpack configuration compiles a .gz version of the scripts, though only if the compression reduces the file size, meaning that some .js scripts will have .js.gz counterpart, some will not.

I am able to tell nginx to try .gz in case of .js extension. Though, how'd I tell nginx to fallback to .js if .js.gz does not exist?

app.get('*.js', (req, res, next) => {
  // eslint-disable-next-line operator-assignment
  req.url = req.url + '.gz';

  res.set('Content-Encoding', 'gzip');

  next();
});

To complicate things further, I am using serve-static.

In nginx I'd be able to achieve this using:

try_files $uri.gz $uri @404

Repost: http://stackoverflow.com/questions/42602053/how-to-try-a-file-and-fallback-to-another-file

Feature Request: Allow multiple roots

It would be handy to enable a retry of relative paths against multiple roots before a 404.

Use case: when managing projects that rely on Bower for dependencies the paths to dependencies will differ when running from within the project itself (examples, tests, etc) vs. running 'downstream' as part of another project -- running from within the project the dependencies are nested within the project and running as a dependency downstream project dependencies will be siblings.

Related: The new "proxy" option in https://github.com/nodeapps/http-server#available-options

Somewhat similar to: https://github.com/gruntjs/grunt-contrib-less#paths and https://github.com/sass/node-sass#includepaths from the CSS compilation world --> allows a project to resolve relative @import paths against a known list of possible roots.

Angular 13 SSR serve is failing with error - TypeError: Right-hand side of 'instanceof' is not an object

Currently upgraded a legacy app from Angular version v6 to v13 & when trying to deploy the server / serve locally using 'npm run serve:ssr' facing issue in the build packages which is pointing to server static as below

TypeError: Right-hand side of 'instanceof' is not an object
at gt (/Users/dist/server.js:3673:146087)
at pt (/Users/dist/server.js:3673:145962)
at PA (/Users/dist/server.js:3673:168745)
at Cn.Vn.insertToken (/Users/dist/server.js:3673:145346)
at /Users/dist/server.js:3673:148654
at bt (/Users/dist/server.js:3673:148669)
at Wn (/Users/dist/server.js:3673:143609)
at Object.parse (/Users/dist/server.js:3673:142574)
at Object.t.createDocument (/Users/dist/server.js:3650:472)
at Object.t.createWindow (/Users/dist/server.js:3650:855)

the corresponding line are
`
/*!

  • serve-static
  • Copyright(c) 2010 Sencha Inc.
  • Copyright(c) 2011 TJ Holowaychuk
  • Copyright(c) 2014-2016 Douglas Christopher Wilson
  • MIT Licensed
    .....
    3650 : process:function(A){return n.parse("",!1,A)},document:function(){return n.document()}}},
    A.createWindow=function(n,t){var e=A.createDocument(n);

/*!
Parser-Lib
Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
... line :3673

`
detailed configuration & information :
https://stackoverflow.com/questions/75853134/angular-12-server-side-rendering-serve-throws-typeerror-right-hand-side-of-i

Extensions vs Directory

When we have a directory named like a file, extensions should win over redirect. Let's assume this listing:

/news.html
/news/latest.html
/news/index.html

and we set the extensions options to ['html'].

Right now /news is redirected to /news/ and serves /news/index.html

But it should serve /news.html – only if that file is missing, it should redirect to the directory. Whereas /news/ should always go for the directory (index).

I achieved this by changing the sendFile method like follows, but there are probably better ways to achieve the same.

SendStream.prototype.sendFile = function sendFile (path) {
  var i = 0
  var self = this
  var onExtensionsFailed

  debug('stat "%s"', path)
  fs.stat(path, function onstat (err, stat) {
    if (err && err.code === 'ENOENT' && !extname(path) && path[path.length - 1] !== sep) {
      // not found, check extensions
      return next(err)
    }
    if (err) return self.onStatError(err)
    if (stat.isDirectory()) {
      onExtensionsFailed = function() {
        self.redirect(path)
      }
      if (self._extensions.length > 0 && path[path.length - 1] !== sep) {
        return next()
      } else {
        return self.redirect(path)
      }
    }
    self.emit('file', path, stat)
    self.send(path, stat)
  })

  function next (err) {
    if (self._extensions.length <= i) {
      if (onExtensionsFailed) {
        return onExtensionsFailed()
      }
      return err
        ? self.onStatError(err)
        : self.error(404)
    }

    var p = path + '.' + self._extensions[i++]

    debug('stat "%s"', p)
    fs.stat(p, function (err, stat) {
      if (err) return next(err)
      if (stat.isDirectory()) return next()
      self.emit('file', p, stat)
      self.send(p, stat)
    })
  }
}

Does this make sense? (I am by the way trying to achieve the same kind of handling like Netlify does when serving static files).

Issue with Example Code

I'm not too sure why, but I had to change your example code a bit for it to work in conjunction with serve-index, like so:

app.use("/shared", serveStatic('shared', { index: ['.jpg', '.png'] }))

don't ignore .well-known by default

Unfortunately the RFC IETF or whoever decided it was a good idea to standardize on a directory that is, by default, hidden. Right?

So when I'm tearing my hair out wondering why my Let's Encrypt certs aren't getting validated....

  • https://example.com/.well-known/acme-challenge/xxxxxxxxxxxxxxx

It's the darn .well-known givin' me all the troubles.

I'd like to suggest that .well-known (and any other RFC / IETF spec'd dirs) not be ignored by default.

Perhaps the default should be the array ['.well-known'] and you can override that value with true, false, or [] (ignore)?

Change access to files outside root to 404 instead of 403?

A contributor was nice enough to run a full fetched grayhat attack on my website to look for security vulnerabilities and one of the critical ones that we discovered is that server-static allows you to access sub directories towards to root directory and onwards.

For example you may use serve-static to server public files such as js and css from the root onwards so domain.com/js and domain.com/css would route to <project_directory>/public/css and <project_directory>/public/js but believe it or not the user can actually take advantage of that to navigate to sub directories.

a sample request may be domain.com/a//%5c../%5c../%5c../%5c../%5c../%5c../%5c../etc/passwd/1

while of course luckily the web app runs on a dedicated user which only has access to the www directory it would return with a forbidden error I still believe that this is a critical error that needs to be solved.

If any more information is required then I'd be happy to respond and support you guys with fixing this problem by providing you with more information if required and even stack traces.

Make 'immutable' cache-control value HTTP/3 QPACK compatible

At the moment passing an immutable: true option adds an immutable value to the Cache-Control header. Ideally the header would be put as a separate Cache-Control header entry making such request QPACK-compatible.

Note: When evaluating the use of immutable and QPACK: If you're concerned that immutable changes the predefined value provided by QPACK, consider that in this case, the immutable part can be encoded separately by splitting the Cache-Control value into two lines — though this is dependent on the encoding algorithm a particular QPACK implementation uses.
(MDN reference)

Arguments to path.resolve must be strings

Getting Arguments to path.resolve must be strings. Not sure what I am missing.

path.js:313
        throw new TypeError('Arguments to path.resolve must be strings');
              ^
TypeError: Arguments to path.resolve must be strings
    at exports.resolve (path.js:313:15)
    at serveStatic (/node_modules/serve-static/index.js:36:10)
var staticServer = serveStatic('../distribute', {
    dotfiles: 'deny',
    etag: true,
    index: ['index.html'],
    lastModified: true,
    maxAge: ms('30d')
});

https.createServer(sslOptions, function(req, res) {
    var path = url.parse(req.url).pathname;

    ////
    // Match any requests to /proxy/...
    ////
    if(path.match(/proxy\/.?/)) {
        req.headers.host = config.proxy.host;
        proxy.web(req, res, config.proxy);
    }
    ////
    // Serve static content
    ////
    else {
        serveStatic(req, res, finalhandler(req, res));
    }
}).listen(config.listenPort);

setHeaders: need to access req.query

Hi,

Could setHeaders also have req in it's parameters, please?

I need to set headers depending on some req.query parameters, and I can't read those now.

Thanks

/% causes Bad Request

Hey,

The serve-static middleware, when failing to decode a path URL, fails with a bad request error.
I'd say it should merely ignore the request and pass it along to the app's final middleware.

Reproduce it by requesting /%.

Offending lines:

  var path = decode(this.path)
  if (path === -1) return this.error(400)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.