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
Show file tree
Hide file tree
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
Expand Up @@ -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 | |
Expand All @@ -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

Expand Down
3 changes: 3 additions & 0 deletions bin/http-server
Original file line number Diff line number Diff line change
Expand Up @@ -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',
'',
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions lib/core/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"brotli": false,
"defaultExt": ".html",
"handleError": true,
"spa": false,
"contentType": "application/octet-stream",
"weakEtags": true,
"weakCompare": true,
Expand Down
6 changes: 6 additions & 0 deletions lib/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions lib/core/opts.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -193,6 +198,7 @@ module.exports = (opts) => {
gzip,
brotli,
handleError,
spa,
headers,
contentType,
mimeTypes,
Expand Down
2 changes: 2 additions & 0 deletions lib/http-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down