Search code examples
reactjswebpackwebpack-dev-servercreate-react-appwebpack-dev-middleware

Matching root route in webpack-dev-server's historyApiFallback`


Sample repo demonstrating the issue is here.

I'm trying to set up webpack dev server so that:

  1. Requests to / are served by public/landing.html (a static landing page)
  2. Requests to anything else are served by my React app

Using create-react-app, and based on webpack-dev-server's options, I've set up my webpackDevServer.config.js as follows:

historyApiFallback: { // Paths with dots should still use the history fallback. // See https://github.com/facebookincubator/create-react-app/issues/387. disableDotRule: true, rewrites: [ // shows views/landing.html as the landing page { from: /^\/$/, to: 'landing.html' }, // shows views/subpage.html for all routes starting with /subpage { from: /^\/subpage/, to: 'subpage.html' }, // shows views/404.html on all other pages { from: /./, to: '404.html' }, ], },

And when I start webpack here's what I see:

  • Requests to /subpage are routed correctly to subpage.html
  • Requests to /foo are routed correctly to 404.html. Eventually, these would be handled by my React app.
  • Requests to / are routed incorrectly to my React app.

How can I get landing.html to respond to requests at /?


Solution

  • I ended up opening a bug request with webpack-dev-middleware and discovered it was not a bug but a failure of configuration.

    Specifically, the issue is using HtmlWebpackPlugin alongside historyApiFallback. I believe plugins are processed before the regex matching, and HtmlWebpackPlugin's default file output is index.html; this means that out of the box, / will always be routed to the HtmlWebpackPlugin output file.

    The solution to this is to set a custom filename in HtmlWebpackPlugin, which allows you to control the matching again. Here's a sample repo demonstrating the fix and here's the webpack config:

    module.exports = {
      context: __dirname,
      entry: [
        './app.js',
      ],
      output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
      },
      devServer: {
        publicPath: "/",
        // contentBase: "./public",
        // hot: true,
        historyApiFallback: {
          // disableDotRule: true,
          rewrites: [
            { from: /^\/$/, to: '/public/index.html' },
            { from: /^\/foo/, to: '/public/foo.html' },
            { from: /(.*)/, to: '/test.html' },
          ],
        }
      },
      plugins: [
        new HtmlWebpackPlugin({
          title: "Html Webpack html",
          hash: true,
          filename: 'test.html',
          template: 'public/plugin.html',
        }),
      ],
    };