1

Recently I came across the same issue as the post "historyApiFallback doesn't work in Webpack dev server".


I will first quote the accepted answer in that post.

Answer:

I meet the same question today. let config in webpack.config.js: output.publicPath be equal to devServer.historyApiFallback.index and point out html file route.my webpack-dev-server version is 1.10.1 and work well. http://webpack.github.io/docs/webpack-dev-server.html#the-historyapifallback-option doesn't work, you must point out html file route.

module.exports = {
    entry: "./src/app/index.js",
    output: {
        path: path.resolve(__dirname, 'build'),
        publicPath: 'build',
        filename: 'bundle-main.js'
    },
    devServer: {
        historyApiFallback:{
            index:'build/index.html'
        },
    },
};


I tried to use this answer to fix the problem(set output.publicPath: 'dist' and devServer.historyApiFallback:{index:'dist/index.html'}) but somehow it didn't work.

After some search I found this page. According to the description in the page:

This section is for everyone who ran into this problem in development using webpack-dev-server.. Just as above, what we need to do it tell Webpack Dev Sever to redirect all server requests to /index.html. There are just two properties in your webpack config you need to set to do this, publicPath and historyApiFallback.

module.exports = {
  entry: './app/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js',
    publicPath: '/'
  },
  module: {
    rules: [
      { test: /\.(js)$/, use: 'babel-loader' },
      { test: /\.css$/, use: [ 'style-loader', 'css-loader' ]}
    ]
  },
  devServer: {
    historyApiFallback: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'app/index.html'
    })
  ]
};

According to the config I modified my devServer.historyApiFallback to be true, and output.publicPath to be /.

My webpack config:

const webpack = require("webpack")
const path = require("path")
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: "development",
  entry: {
    app: "./src/base/index.js"
  },
  output: {
    filename: "[name].bundle.js",
    publicPath: '/',
    path: path.resolve(__dirname, "dist")
  },
  devtool: 'inline-source-map',
  devServer: {
    hot: true,
    port: 3000,
    historyApiFallback: true
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(scss|css)$/,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader"
        ]
      },
      {
        test: /\.(pdf|jpg|png|gif|svg|ico)$/,
        use: [
          {
            loader: 'url-loader'
          },
        ]
      },
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/base/index.html'
    }),
    new webpack.HotModuleReplacementPlugin()
  ],
}

Everything seemed working now.

But I have the puzzle that I don't know why it's working.


To be specific
  1. devServer.historyApiFallback: true is clear according to webpack doc, so I'm not doubt about that part.

  2. output.publicPath:/ is pretty vague for me though.

Question:

  1. If I tried to use something like output.publicPath:/public, it will not work. So why I must use output.publicPath:/ here?
  2. How output.publicPath:/ can tell webpack-devserver to find the right place and server the right index.html(which is generated by the devserver I believe)?

Sry if it's a bit tedious. I just want to provide some detail.

Sean
  • 380
  • 1
  • 4
  • 7

2 Answers2

2

But I have the puzzle that I don't know why it's working.

Setting 1
In webpack.config.js the setting

output: {
  ...
  publicPath: '/static1/',
    },

tells webpack to embed '/static1/' into the bundle's path in the generated .html file:

<script src="/static1/<name>.bundle.js" type="text/javascript"></script>

You can open the generated .html file(s) on disk and see the above tag with '/static1/' prepended to the bundle.

Setting 2
This setting:

devServer: {
   publicPath: '/static/',      // different from 'static1' we used above

tells webpack-dev-server to create a route handler for the path /static and serve resourses e.g. /static/<name>.bundle.js. The webpack-dev-server is based on Express which uses route handlers e.g. app.use(/mypath, ...); to serve requests.

If now you point a browser to localhost:8080, you will see blank screen. Righ-click on it to see Page Source. You will see the above <script>tag that makes the browser issue GET request for the bundle using the path /static1/xxx that doesn't work because you didn't tell webpack-dev-server to create a route handler for this path. Now type in the browser navigation bar http://localhost:8080/static/<name>.bundle.js and you will see the internals of your bundle.

Eliminate the discrepancy between static1 and static and the page will render. In your case it works because one setting is set explicitly to '/' and the second one defaults to the same value.

Setting 3
historyApiFallback has a more narrow scope than other two settings because it is used with SPAs only. During the initial rendering a user sees the landing page of the SPA e.g. /mysample.html. This is the file with our <script> tag shown above. It should be used without any path like /static prepended to it:

historyApiFallback: {
  ...
  index: mysample.html,

because Setting1 and Setting2 apply to bundles, not to bundle-containing .html pages.

winwiz1
  • 2,906
  • 11
  • 24
0

Faced the same problem, it was succeeded to solve, having specified a route, instead of a path to index.html

devServer: {
  publicPath: `/myApp`,
  historyApiFallback: {
    rewrites: [
      { from: /\/myApp/, to: `/myApp` }
    ]
  }
}
Nermo
  • 61
  • 6