How do I implement `connect-history-api-fallback` so that URL paths redirect to index.html?

Note: I figured out a solution to this question as I was typing it up, but I thought I’d go ahead and post this here in case some other confused newbie like myself finds it helpful.

I’m working on a webpack-simple-templated SPA project, using the basic router function from the Simple Routing From Scratch example in the Vue Guide. I’m deploying the project using Zeit’s now, with the package.json script "start": "serve".

(I’m quite obviously new to node and Vue; I used serve because I think it may have been mentioned in some of Zeit’s documentation, but I understand it’s an extremely basic server that doesn’t include many configuration options.)

URL routes work perfectly in my local dev environment (using webpack-dev-server as configured by default via webpack-simple, which includes the devServer option historyApiFallback: true. But on the now.sh server, URL routes don’t get redirected back to index.html – instead, it’s displaying a “Not Found” error message instead of rendering the route. The problem of course is that the now server isn’t configured to redirect all URLs back to /index.html.

From the example server configurations in the HTML5 History Mode section of the docs for vue-router (which I’m not using but figured would be relevant), it looks like I should use the connect-history-api-fallback middleware to get the now server to redirect the URL routes properly, which means I should switch to using express instead of serve to serve the app in production. I’m struggling to figure out how to set up the start script to serve the app using express, and how to implement the middleware into my main.js file.

(I could of course obviate this issue by using hash-style URLs instead, but I’d prefer to use “normal” URLs.)

Any ideas for how I should set up express and get connect-history-api-fallback to handle the redirects to /index.html correctly?

1 Like

I answered my own question after realizing I needed to create a separate JS file (e.g. server.js) that node can use to load and serve the SPA with express. (Yeah, this is probably super-obvious to most folks, but I wasn’t picking up on this in the express tutorials I was reading!)

Here’s my step-by-step:

  1. Install express (of course it’s already installed, but really just adding this as a dependency) from my project’s root directory:
    npm install --save express

  2. Install connect-history-fallback-api as a dependency:
    npm install --save connect-history-api-fallback

  3. Create a server.js file in the project’s root. (See script below.)

  4. Update package.json start script so that now will use express instead of serve:
    "start": "node server.js"

  5. Run now!

// server.js

const express = require('express')
const path = require('path')
const history = require('connect-history-api-fallback')
// ^ middleware to redirect all URLs to index.html

const app = express()
const staticFileMiddleware = express.static(path.join(__dirname))

app.use(staticFileMiddleware)
app.use(history())
app.use(staticFileMiddleware)
// ^ `app.use(staticFileMiddleware)` is included twice as per https://github.com/bripkens/connect-history-api-fallback/blob/master/examples/static-files-and-index-rewrite/README.md#configuring-the-middleware

app.get('/', function (req, res) {
  res.render(path.join(__dirname + '/index.html'))
})

app.listen(5000, function () {
  console.log( 'Express serving on 5000!' )
})

Now when I deploy using now, the redirects work properly! (If I’m wrong about any of the details, or if any of this is confusing, please let me know!)

2 Likes

Thanks alot. I had the same problem, but I found answer sooner. thanks. :smiley:

1 Like

This method is not working while I am deploying application to heroku server, can you please help me out here.

Sorry, I’m not at all familiar with Heroku and could only just barely get this working on a now.sh deployment. Perhaps someone else in the forum might be able to help! (In case someone else can help, though, you may want to provide details to explain what you mean by “this method is not working”, and be as specific as you can be, with code and error messages, etc.) Cheers!

Its okay man, I got the solution for this.

var express = require(‘express’);
var app = express();
var history = require(‘connect-history-api-fallback’);

app.use(history({
// verbose: true
}));

app.use(express.static(__dirname));

app.set(‘port’, (process.env.PORT || 8080));

app.listen(app.get(‘port’), () => {
console.log(ENV: ${process.env.NODE_ENV});
console.log(Derp is running at localhost: ${app.get('port')});
});

if we use this in our dist/server.js file it will work on heroku too. Tested.

3 Likes

Great — glad you figured it out. :+1:

I have a question, currently I’m facing the same problem and first of all why you used an external module instead of using the res.redirect() function that came by default with express? (works well only if you have one route in express) And second, if I am managing sessions how this module helps me? Because I could have multiples routes in my vue app but what if a user close the window? Does he should get redirect to the index.html? Or how this module redirects him to where he was? E.g /myaccount/subscriptions/add?? I think these problems needs to be handle in the browser when you need to persist the data, I am thinking in the web storage api

> Cita

1 Like

Thank you!!

Great — glad you figured it out. :+1:

Thank you for the solution. It helped me lot.

1 Like

I would like to add for rewriting all non-index routes (as iHaventSleep was asking about above), just needed to make these small changes to code above to make sure the built dist (npm run build) file runs in production:

const staticFileMiddleware = express.static(path.join(__dirname, '/dist'))

and

 res.render(path.join(__dirname, '/dist/index.html'))

That runs all my routes created in the vue-router through the index, just remember to add a catch all error to routes for pages/routes that don’t exist