Skip to content

Add --spa option #772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ This will install `http-server` globally so that it may be run from the command
|`-c` |Set cache time (in seconds) for cache-control max-age header, e.g. `-c10` for 10 seconds. To disable caching, use `-c-1`.|`3600` |
|`-U` or `--utc` |Use UTC time format in log messages.| |
|`--log-ip` |Enable logging of the client's IP address |`false` |
|`--spa` |Fallback to index.html (for single page applications)` |`false`|
|`-P` or `--proxy` |Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com | |
|`--proxy-options` |Pass proxy [options](https://github.com/http-party/node-http-proxy#options) using nested dotted objects. e.g.: --proxy-options.secure false |
|`--username` |Username for basic authentication | |
@@ -73,17 +74,10 @@ This will install `http-server` globally so that it may be run from the command
## 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.
- `404.html` will be served if a file is not found.

## Catch-all redirect
For Single-Page App (SPA) hosting, use the `--spa` option. That will serve the entry page by `index.html`.

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

3 changes: 3 additions & 0 deletions bin/http-server
Original file line number Diff line number Diff line change
@@ -44,6 +44,8 @@ if (argv.h || argv.help) {
' -U --utc Use UTC time format in log messages.',
' --log-ip Enable logging of the client\'s IP address',
'',
' --spa Fallback to index.html (for single page applications)',
'',
' -P --proxy Fallback proxy if the request cannot be resolved. e.g.: http://someurl.com',
' --proxy-options Pass options to proxy using nested dotted objects. e.g.: --proxy-options.secure false',
'',
@@ -148,6 +150,7 @@ function listen(port) {
robots: argv.r || argv.robots,
ext: argv.e || argv.ext,
logFn: logger.request,
spa: argv.spa,
proxy: proxy,
proxyOptions: proxyOptions,
showDotfiles: argv.dotfiles,
1 change: 1 addition & 0 deletions lib/core/defaults.json
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
"brotli": false,
"defaultExt": ".html",
"handleError": true,
"spa": false,
"contentType": "application/octet-stream",
"weakEtags": true,
"weakCompare": true,
6 changes: 6 additions & 0 deletions lib/core/index.js
Original file line number Diff line number Diff line change
@@ -108,6 +108,7 @@ module.exports = function createMiddleware(_dir, _options) {
const baseDir = opts.baseDir;
let defaultExt = opts.defaultExt;
const handleError = opts.handleError;
const spa = opts.spa;
const headers = opts.headers;
const weakEtags = opts.weakEtags;
const handleOptionsMethod = opts.handleOptionsMethod;
@@ -359,6 +360,11 @@ module.exports = function createMiddleware(_dir, _options) {
url: `${parsed.pathname}.${defaultExt}${(parsed.search) ? parsed.search : ''}`,
headers: req.headers,
}, res, next);
} else if (spa) {
middleware({
url: `/${path.join(baseDir, `index.${defaultExt}`)}`,
headers: req.headers,
}, res, next);
} else {
// Try to serve default ./404.html
const rawUrl = (handleError ? `/${path.join(baseDir, `404.${defaultExt}`)}` : req.url);
6 changes: 6 additions & 0 deletions lib/core/opts.js
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ module.exports = (opts) => {
let brotli = defaults.brotli;
let defaultExt = defaults.defaultExt;
let handleError = defaults.handleError;
let spa = defaults.spa;
const headers = {};
let contentType = defaults.contentType;
let mimeTypes;
@@ -117,6 +118,10 @@ module.exports = (opts) => {
return false;
});

if (typeof opts.spa !== 'undefined' && opts.spa !== null) {
spa = opts.spa;
}

aliases.cors.forEach((k) => {
if (isDeclared(k) && opts[k]) {
handleOptionsMethod = true;
@@ -193,6 +198,7 @@ module.exports = (opts) => {
gzip,
brotli,
handleError,
spa,
headers,
contentType,
mimeTypes,
2 changes: 2 additions & 0 deletions lib/http-server.js
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ function HttpServer(options) {
this.autoIndex = options.autoIndex !== 'false';
this.showDotfiles = options.showDotfiles;
this.gzip = options.gzip === true;
this.spa = options.spa === true;
this.brotli = options.brotli === true;
if (options.ext) {
this.ext = options.ext === true
@@ -133,6 +134,7 @@ function HttpServer(options) {
autoIndex: this.autoIndex,
defaultExt: this.ext,
gzip: this.gzip,
spa: this.spa,
brotli: this.brotli,
contentType: this.contentType,
mimetypes: options.mimetypes,