Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: http-party/http-server
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d5b599f2932a6dd96e4c0efc87edfb8c139c5832
Choose a base ref
...
head repository: http-party/http-server
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 46cfd108d70ac8a5c95e7d0515d6feaebf49eefe
Choose a head ref
Loading
16 changes: 16 additions & 0 deletions .github/ISSUE_TEMPLATE
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
**Steps to reproduce the issue, if applicable. Include the actual command and output and/or stack trace.**

<!-- Paste the command here: -->

**What did you expect to happen?**

<!-- Paste the error and / or stack trace here: -->

**If the issue is a feature request, what is the motivation / use case for it?**

**Tell us about your environment**
- **exact http-server version:**
- **Node version:**
- **Platform:**

**Other information (related issues, suggestions for a fix, etc):**
19 changes: 19 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
**Please ensure that your pull request fulfills these requirements:**
- [ ] The pull request is being made against the `master` branch
- [ ] Tests for the changes have been added (for bug fixes / features)

**What is the purpose of this pull request? (bug fix, enhancement, new feature,...)**

<!--
Link to the issue this pull request fixes here, if applicable: "Fixes #xxx" or "Resolves #xxx"
-->

**What changes did you make?**

**Provide some example code that this change will affect, if applicable:**

<!-- Paste the example code here: -->

**Is there anything you'd like reviewers to focus on?**

**Please provide testing instructions, if applicable:**
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules/*
npm-debug.log
node_modules/
npm-debug.log*
.dir-locals.el
.DS_Store
16 changes: 13 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
sudo: false
language: node_js
node_js:
- "4"
- "5"
- "6"
- "7"
- "8"
- "10"
- "12"
- "13"
- "14"

git:
autocrlf: input

os:
- linux
- windows
- osx

before_install:
- travis_retry npm install
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2011 Charlie Robbins, Marak Squires, and the Contributors.
Copyright (c) 2011-2020 Charlie Robbins, Marak Squires, and the Contributors.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
109 changes: 90 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
[![build status](https://img.shields.io/travis/indexzero/http-server.svg?style=flat-square)](https://travis-ci.org/indexzero/http-server)
[![dependencies status](https://img.shields.io/david/indexzero/http-server.svg?style=flat-square)](https://david-dm.org/indexzero/http-server)
[![npm](https://img.shields.io/npm/v/http-server.svg?style=flat-square)](https://www.npmjs.com/package/http-server)
[![license](https://img.shields.io/github/license/indexzero/http-server.svg?style=flat-square)](https://github.com/indexzero/http-server)
[![build status](https://img.shields.io/travis/http-party/http-server.svg?style=flat-square)](https://travis-ci.org/http-party/http-server)
[![npm](https://img.shields.io/npm/v/http-server.svg?style=flat-square)](https://www.npmjs.com/package/http-server) [![homebrew](https://img.shields.io/homebrew/v/http-server?style=flat-square)](https://formulae.brew.sh/formula/http-server) [![npm downloads](https://img.shields.io/npm/dm/http-server?color=blue&label=npm%20downloads&style=flat-square)](https://www.npmjs.com/package/http-server)
[![license](https://img.shields.io/github/license/http-party/http-server.svg?style=flat-square)](https://github.com/http-party/http-server)

# http-server: a command-line http server

`http-server` is a simple, zero-configuration command-line http server. It is powerful enough for production usage, but it's simple and hackable enough to be used for testing, local development, and learning.

![](https://github.com/nodeapps/http-server/raw/master/screenshots/public.png)
![Example of running http-server](https://github.com/http-party/http-server/raw/master/screenshots/public.png)

# Installing globally:
## Installation:

Installation via `npm`:
#### Globally via `npm`

npm install http-server -g
npm install --global http-server

This will install `http-server` globally so that it may be run from the command line.
This will install `http-server` globally so that it may be run from the command line anywhere.

#### Globally via Homebrew

brew install http-server

#### Running on-demand:

Using `npx` you can run the script without installing it first:

npx http-server [path] [options]
#### As a dependency in your `npm` package:

npm install http-server

## Usage:

@@ -25,42 +38,100 @@ This will install `http-server` globally so that it may be run from the command

*Now you can visit http://localhost:8080 to view your server*

**Note:** Caching is on by default. Add `-c-1` as an option to disable caching.

## Available Options:

`-p` Port to use (defaults to 8080)
`-p` or `--port` Port to use (defaults to 8080)

`-a` Address to use (defaults to 0.0.0.0)

`-d` Show directory listings (defaults to 'True')
`-d` Show directory listings (defaults to `true`)

`-i` Display autoIndex (defaults to 'True')
`-i` Display autoIndex (defaults to `true`)

`-g` or `--gzip` When enabled (defaults to 'False') it will serve `./public/some-file.js.gz` in place of `./public/some-file.js` when a gzipped version of the file exists and the request accepts gzip encoding.
`-g` or `--gzip` When enabled (defaults to `false`) it will serve `./public/some-file.js.gz` in place of `./public/some-file.js` when a gzipped version of the file exists and the request accepts gzip encoding. If brotli is also enabled, it will try to serve brotli first.

`-e` or `--ext` Default file extension if none supplied (defaults to 'html')
`-b` or `--brotli` When enabled (defaults to `false`) it will serve `./public/some-file.js.br` in place of `./public/some-file.js` when a brotli compressed version of the file exists and the request accepts `br` encoding. If gzip is also enabled, it will try to serve brotli first.

`-e` or `--ext` Default file extension if none supplied (defaults to `html`)

`-s` or `--silent` Suppress log messages from output

`--cors` Enable CORS via the `Access-Control-Allow-Origin` header

`-o` Open browser window after starting the server
`-o [path]` Open browser window after starting the server. Optionally provide a URL path to open. e.g.: -o /other/dir/

`-c` Set cache time (in seconds) for cache-control max-age header, e.g. -c10 for 10 seconds (defaults to '3600'). To disable caching, use -c-1.
`-c` Set cache time (in seconds) for cache-control max-age header, e.g. `-c10` for 10 seconds (defaults to `3600`). To disable caching, use `-c-1`.

`-U` or `--utc` Use UTC time format in log messages.

`--log-ip` Enable logging of the client's IP address (default: `false`).

`-P` or `--proxy` Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com

`--username` Username for basic authentication [none]

`--password` Password for basic authentication [none]

`-S` or `--ssl` Enable https.

`-C` or `--cert` Path to ssl cert file (default: cert.pem).
`-C` or `--cert` Path to ssl cert file (default: `cert.pem`).

`-K` or `--key` Path to ssl key file (default: key.pem).
`-K` or `--key` Path to ssl key file (default: `key.pem`).

`-r` or `--robots` Provide a /robots.txt (whose content defaults to 'User-agent: *\nDisallow: /')
`-r` or `--robots` Provide a /robots.txt (whose content defaults to `User-agent: *\nDisallow: /`)

`--no-dotfiles` Do not show dotfiles

`-h` or `--help` Print this list and exit.

`-v` or `--version` Print the version and exit.

## Magic Files

- `index.html` will be served as the default file to any directory requests.
- `404.html` will be served if a file is not found. This can be used for Single-Page App (SPA) hosting to serve the entry page.

## Catch-all redirect

To implement a catch-all redirect, use the index page itself as the proxy with:

```
http-server --proxy http://localhost:8080?
```

Note the `?` at the end of the proxy URL. Thanks to [@houston3](https://github.com/houston3) for this clever hack!

## TLS/SSL

First, you need to make sure that [openssl](https://github.com/openssl/openssl) is installed correctly, and you have `key.pem` and `cert.pem` files. You can generate them using this command:

``` sh
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
```

You will be prompted with a few questions after entering the command. Use `127.0.0.1` as value for `Common name` if you want to be able to install the certificate in your OS's root certificate store or browser so that it is trusted.

This generates a cert-key pair and it will be valid for 3650 days (about 10 years).

Then you need to run the server with `-S` for enabling SSL and `-C` for your certificate file.

``` sh
http-server -S -C cert.pem
```

This is what should be output if successful:

``` sh
Starting up http-server, serving ./ through https
Available on:
https:127.0.0.1:8080
https:192.168.1.101:8080
https:192.168.1.104:8080
Hit CTRL-C to stop the server
```

# Development

Checkout this repository locally, then:
83 changes: 62 additions & 21 deletions bin/http-server
Original file line number Diff line number Diff line change
@@ -7,67 +7,83 @@ var colors = require('colors/safe'),
httpServer = require('../lib/http-server'),
portfinder = require('portfinder'),
opener = require('opener'),
argv = require('optimist')
.boolean('cors')
.argv;

fs = require('fs'),
argv = require('minimist')(process.argv.slice(2));
var ifaces = os.networkInterfaces();

process.title = 'http-server';

if (argv.h || argv.help) {
console.log([
'usage: http-server [path] [options]',
'',
'options:',
' -p Port to use [8080]',
' -p --port Port to use [8080]',
' -a Address to use [0.0.0.0]',
' -d Show directory listings [true]',
' -i Display autoIndex [true]',
' -g --gzip Serve gzip files when possible [false]',
' -b --brotli Serve brotli files when possible [false]',
' If both brotli and gzip are enabled, brotli takes precedence',
' -e --ext Default file extension if none supplied [none]',
' -s --silent Suppress log messages from output',
' --cors[=headers] Enable CORS via the "Access-Control-Allow-Origin" header',
' Optionally provide CORS headers list separated by commas',
' -o [path] Open browser window after starting the server',
' -o [path] Open browser window after starting the server.',
' Optionally provide a URL path to open the browser window to.',
' -c Cache time (max-age) in seconds [3600], e.g. -c10 for 10 seconds.',
' To disable caching, use -c-1.',
' -t Connections timeout in seconds [120], e.g. -t60 for 1 minute.',
' To disable timeout, use -t0',
' -U --utc Use UTC time format in log messages.',
' --log-ip Enable logging of the client\'s IP address',
'',
' -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com',
'',
' -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com',
' --username Username for basic authentication [none]',
' Can also be specified with the env variable NODE_HTTP_SERVER_USERNAME',
' --password Password for basic authentication [none]',
' Can also be specified with the env variable NODE_HTTP_SERVER_PASSWORD',
'',
' -S --ssl Enable https.',
' -C --cert Path to ssl cert file (default: cert.pem).',
' -K --key Path to ssl key file (default: key.pem).',
'',
' -r --robots Respond to /robots.txt [User-agent: *\\nDisallow: /]',
' --no-dotfiles Do not show dotfiles',
' -h --help Print this list and exit.'
' -r --robots Respond to /robots.txt [User-agent: *\\nDisallow: /]',
' --no-dotfiles Do not show dotfiles',
' -h --help Print this list and exit.',
' -v --version Print the version and exit.'
].join('\n'));
process.exit();
}

var port = argv.p || parseInt(process.env.PORT, 10),
var port = argv.p || argv.port || parseInt(process.env.PORT, 10),
host = argv.a || '0.0.0.0',
ssl = !!argv.S || !!argv.ssl,
ssl = argv.S || argv.ssl,
proxy = argv.P || argv.proxy,
utc = argv.U || argv.utc,
version = argv.v || argv.version,
logger;

if (!argv.s && !argv.silent) {
logger = {
info: console.log,
request: function (req, res, error) {
var date = utc ? new Date().toUTCString() : new Date();
var ip = argv['log-ip']
? req.headers['x-forwarded-for'] || '' + req.connection.remoteAddress
: '';
if (error) {
logger.info(
'[%s] "%s %s" Error (%s): "%s"',
date, colors.red(req.method), colors.red(req.url),
'[%s] %s "%s %s" Error (%s): "%s"',
date, ip, colors.red(req.method), colors.red(req.url),
colors.red(error.status.toString()), colors.red(error.message)
);
}
else {
logger.info(
'[%s] "%s %s" "%s"',
date, colors.cyan(req.method), colors.cyan(req.url),
'[%s] %s "%s %s" "%s"',
date, ip, colors.cyan(req.method), colors.cyan(req.url),
req.headers['user-agent']
);
}
@@ -81,6 +97,11 @@ else if (colors) {
};
}

if (version) {
logger.info('v' + require('../package.json').version);
process.exit();
}

if (!port) {
portfinder.basePort = 8080;
portfinder.getPort(function (err, port) {
@@ -96,14 +117,18 @@ function listen(port) {
var options = {
root: argv._[0],
cache: argv.c,
timeout: argv.t,
showDir: argv.d,
autoIndex: argv.i,
gzip: argv.g || argv.gzip,
brotli: argv.b || argv.brotli,
robots: argv.r || argv.robots,
ext: argv.e || argv.ext,
logFn: logger.request,
proxy: proxy,
showDotfiles: argv.dotfiles
showDotfiles: argv.dotfiles,
username: argv.username || process.env.NODE_HTTP_SERVER_USERNAME,
password: argv.password || process.env.NODE_HTTP_SERVER_PASSWORD
};

if (argv.cors) {
@@ -118,6 +143,20 @@ function listen(port) {
cert: argv.C || argv.cert || 'cert.pem',
key: argv.K || argv.key || 'key.pem'
};
try {
fs.lstatSync(options.https.cert);
}
catch (err) {
logger.info(colors.red('Error: Could not find certificate ' + options.https.cert));
process.exit(1);
}
try {
fs.lstatSync(options.https.key);
}
catch (err) {
logger.info(colors.red('Error: Could not find private key ' + options.https.key));
process.exit(1);
}
}

var server = httpServer.createServer(options);
@@ -150,10 +189,12 @@ function listen(port) {

logger.info('Hit CTRL-C to stop the server');
if (argv.o) {
opener(
protocol + canonicalHost + ':' + port,
{ command: argv.o !== true ? argv.o : null }
);
var openUrl = protocol + canonicalHost + ':' + port;
if (typeof argv.o === 'string') {
openUrl += argv.o[0] === '/' ? argv.o : '/' + argv.o;
}
logger.info('open: ' + openUrl);
opener(openUrl);
}
});
}
Loading