Home » Reactjs » React-router urls don't work when refreshing or writting manually

React-router urls don't work when refreshing or writting manually

Posted by: admin November 29, 2017 Leave a comment


I’m using React-router and it works fine while I’m clicking into link buttons, but when I refresh my webpage it does not load what I want.

For instance, I am into localhost/joblist and everything is fine because I arrived here pressing a link. But If I refresh the webpage I get: Cannot GET /joblist

By default It didn’t work like this. Initially I had my URL: localhost/#/ and localhost/#/joblist and they worked perfectly fine. But I don’t like this kind of url, so trying to erase that ‘#’ I wrote:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);

This problem does not happen with localhost/, this one always returns what I want.

EDIT: This app is single-page, so /joblist don’t need to ask anything to any server.

EDIT2: My entire router.

var routes = (
    <Route name="app" path="/" handler={App}>
        <Route name="joblist" path="/joblist" handler={JobList}/>
        <DefaultRoute handler={Dashboard}/>
        <NotFoundRoute handler={NotFound}/>

Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);

Looking at the comments on the accepted answer and the generic nature of this question (‘don’t work’), I thought this might be a good place for some general explanations about the issues involved here. So this answer is intended as background info / elaboration on the specific use case of the OP. Please bear with me.

Server-side vs Client-side

The first big thing to understand about this is that there are now 2 places where the URL is interpreted, whereas there used to be only 1 in ‘the old days’. In the past, when life was simple, some user sent a request for http://example.com/about to the server, which inspected the path part of the URL, determined the user was requesting the about page and then sent back that page.

With client-side routing, which is what React-Router provides, things are less simple. At first, the client does not have any JS code loaded yet. So the very first request will always be to the server. That will then return a page that contains the needed script tags to load React and React Router etc. Only when those scripts have loaded does phase 2 start. In phase 2, when the user clicks on the ‘About us’ navigation link for example, the URL is changed locally only to http://example.com/about (made possible by the History API), but no request to the server is made. Instead, React Router does it’s thing on the client side, determines which React view to render and renders it. Assuming your about page does not need to make any REST calls, it’s done already. You have transitioned from Home to About Us without any server request having fired.

So basically when you click a link, some Javascript runs that manipulates the URL in the address bar, without causing a page refresh, which in turn causes React Router to perform a page transition on the client side.

But now consider what happens if you copy-paste the URL in the address bar and e-mail it to a friend. Your friend has not loaded your website yet. In other words, she is still in phase 1. No React Router is running on her machine yet. So her browser will make a server request to http://example.com/about.

And this is where your trouble starts. Until now, you could get away with just placing a static HTML at the webroot of your server. But that would give 404 errors for all other URLs when requested from the server. Those same URLs work fine on the client side, because there React Router is doing the routing for you, but they fail on the server side unless you make your server understand them.

Combining server- and client-side routing

If you want the http://example.com/about URL to work on both the server- and the client-side, you need to set up routes for it on both the server- and the client side. Makes sense right?

And this is where your choices begin. Solutions range from bypassing the problem altogether, via a catch-all route that returns the bootstrap HTML, to the full-on isomorphic approach where both the server and the client run the same JS code.


Bypassing the problem altogether: Hash History

With Hash History i.s.o Browser History, your URL for the about page would look something like this:
The part after the hash (#) symbol is not sent to the server. So the server only sees http://example.com/ and sends the index page as expected. React-Router will pick up the #/about part and show the correct page.


  • ‘ugly’ URLs
  • Server-side rendering is not possible with this approach. As far as SEO is concerned, your website consists of a single page with hardly any content on it.



With this approach you do use Browser History, but just set up a catch-all on the server that sends /* to index.html, effectively giving you much the same situation as with Hash History. You do have clean URLs however and you could improve upon this scheme later without having to invalidate all your user’s favorites.


  • More complex to set up
  • Still no good SEO



In the hybrid approach you expand upon the catch-all scenario by adding specific scripts for specific routes. You could make some simple PHP scripts to return the most important pages of your site with content included, so Googlebot can at least see what’s on your page.


  • Even more complex to set up
  • Only good SEO for those routes you give the special treatment
  • Duplicating code for rendering content on server and client



What if we use Node JS as our server so we can run the same JS code on both ends? Now, we have all our routes defined in a single react-router config and we don’t need to duplicate our rendering code. This is ‘the holy grail’ so to speak. The server sends the exact same markup as we would end up with if the page transition had happened on the client. This solution is optimal in terms of SEO.


  • Server must (be able to) run JS. I’ve experimented with Java i.c.w. Nashorn but it’s not working for me. In practice it mostly means you must use a Node JS based server.
  • Many tricky environmental issues (using window on server-side etc)
  • Steep learning curve


Which should I use?

Choose the one that you can get away with. Personally I think the catch-all is simple enough to set up that that would be my minimum. This setup allows you to improve on things over time. If you are already using Node JS as your server platform, I’d definitely investigate doing an isomorphic app. Yes it’s tough at first but once you get the hang of it it’s actually a very elegant solution to the problem.

So basically, for me, that would be the deciding factor. If my server runs on Node JS, I’d go isomorphic, otherwise I would go for the Catch-all solution and just expand on it (Hybrid solution) as time progresses and SEO requirements demand it.

If you’d like to learn more on isomorphic (also called ‘universal’) rendering with React, there are some good tutorials on the subject:

Also, to get you started, I recommend looking at some starter kits. Pick one that matches your choices for the technology stack (remember, React is just the V in MVC, you need more stuff to build a full app). Start with looking at the one published by Facebook itself:

Or pick one of the many by the community. There is a nice site now that tries to index all of them:

I started with these:

Currently I am using a home-brew version of universal rendering that was inspired by the two starter kits above, but they are out of date now.

Good luck with your quest!


The answers here are all extremely helpful, what worked for me was configuring my Webpack server to expect the routes.

  devServer: {
    historyApiFallback: true,
    contentBase: './',
    hot: true

The historyApiFallback is what fixed this issue for me. Now routing works correctly and I can refresh the page or type in the URL directly. No need to worry about work arounds on your node server. This answer obviously only works if you’re using webpack.

EDIT: see my answer here for a more detailed reason why this is necessary:


The router can be called in two different ways, depending on whether the navigation occurs on the client or on the server. You have it configured for client-side operation. The key parameter is the second one to the run method, the location.

When you use the React Router Link component, it blocks browser navigation and calls transitionTo to do a client-side navigation. You are using HistoryLocation, so it uses the HTML5 history API to complete the illusion of navigation by simulating the new URL in the address bar. If you’re using older browsers, this won’t work. You would need to use the HashLocation component.

When you hit refresh, you bypass all of the React and React Router code. The server gets the request for /joblist and it must return something. On the server you need to pass the path that was requested to the run method in order for it to render the correct view. You can use the same route map, but you’ll probably need a different call to Router.run. As Charles points out, you can use URL rewriting to handle this. Another option is to use a node.js server to handle all requests and pass the path value as the location argument.

In express, for example, it might look like this:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });

Note that the request path is being passed to run. To do this, you’ll need to have a server-side view engine that you can pass the rendered HTML to. There are a number of other considerations using renderToString and in running React on the server. Once the page is rendered on the server, when your app loads in the client, it will render again, updating the server-side rendered HTML as needed.


You can change your htaccess and insert this:

RewriteBase /
RewriteRule ^index\.html$ - [L]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

i am using react: "^15.3.2" ,  react-router: "^3.0.0", history: "^4.3.0",


For React Router V4 Users:

If you try to solve this problem by Hash History technique mentioned in other answers, note that

<Router history={hashHistory} >

does not work in V4, please use HashRouter instead:

import { HashRouter } from 'react-router-dom'




The Webpack Dev Server has an option to enable this. Open up package.json and add --history-api-fallback.
This solutions worked for me.



This can solve your problem

I also faced the same problem in ReactJS application in Production mode.
Here is the 2 solution to the problem.

1.Change the routing history to “hashHistory” instead of browserHistory in the place of

 <Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />

Now build the app using the command

sudo npm run build

Then place the build folder in your var/www/ folder, Now the application is working fine with addition of # tag in each and every url. like


Solution 2 : Without # tag using browserHistory,

Set your history = {browserHistory} in your Router,Now build it using sudo npm run build.

You need to create the “conf” file to solve the 404 not found page,
the conf file should be like this.

open your terminal type the below commands

cd /etc/apache2/sites-available
nano sample.conf
Add the below content in it.

 <VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted

Now you need to enable the sample.conf file by using the following command

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

then it will ask you to reload the apache server,using
sudo service apache2 reload or restart

then open your localhost/build folder and add the .htaccess file with content of below.

   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-l
   RewriteRule ^.*$ / [L,QSA]

Now the app is working normally.

Note: change ip to your local ip address.

If any doubts regarding this feel free to raise a comment.

I hope it is helpful to others.


If you do have a fallback to your index.html, make sure that in your index.html file you have this:

  System.config({ baseURL: '/' });

This may differ from project to project.


I’m not using server side rendering yet but I hit the same problem as the OP where Link seemed to work fine most of the time but failed when I had a parameter. I’ll document my solution here to see if it helps anyone.

My main jsx contains this:

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

This works fine for the first matching link but when the :id changes in <Link> expressions nested on that model’s detail page, the url changes in the browser bar but the content of the page did not initially change to reflect the linked model.

The trouble was that I had used the props.params.id to set the model in componentDidMount. The component is just mounted once so this means that the first model is the one that sticks on the page and the subsequent Links change the props but leave the page looking unchanged.

Setting the model in the component state in both componentDidMount and in componentWillReceiveProps (where it is based on the next props) solves the problem and the page content changes to reflect the desired model.


If you are using Create React App:

There’s a great walk though of this issue with solutions for many major hosting platforms that you can find HERE on the Create React App page. For example, I use React Router v4 and Netlify for my frontend code. All it took was adding 1 file to my public folder (“_redirects”) and one line of code in that file:

/*  /index.html  200

Now my website properly renders paths like mysite.com/pricing when entered into the browser or when someone hits refresh.


I used create-react-app to make a website just now and had the same issue presented here. I use BrowserRouting from the react-router-dom package. I am running on a Nginx server and what solved it for me was adding the following to /etc/nginx/yourconfig.conf

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;

Which corresponds to adding the following to the .htaccess in case you are running Appache

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

This also seems to be the solution suggested by Facebook themselves and can be found here


This topic is a little bit old and solved but I would like to suggest you a simply, clear and better solution. It works if you use web server.

Each web server has an ability to redirect the user to an error page in case of http 404. To solve this issue you need to redirect user to the index page.

If you use Java base server (tomcat or any java application server) the solution could be the following:


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

    <!-- WELCOME FILE LIST -->




  • GET http://example.com/about
  • Web server throws http 404 because this page does not exist on the server side
  • the error page configuration tells to the server that send the index.jsp page back to the user
  • then JS will do the rest of the job on the clien side because the url on the client side is still http://example.com/about.

That is it, no more magic needs:)


Production stack: React, React Router v4, BrowswerRouter, Express, Nginx

1) User BrowserRouter for pretty urls

// app.js

import { BrowserRouter as Router } from 'react-router-dom'

const App = () {
  render() {
    return (
           // your routes here

2) Add index.html to all unknown requests by using /*

// server.js

app.get('/*', function(req, res) {   
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {

3) bundle webpack with webpack -p

4) run nodemon server.js or node server.js


If you’re hosting a react app via AWS Static S3 Hosting & CloudFront

This problem presented itself by CloudFront responding with a 403 Access Denied message because it expected /some/other/path to exist in my S3 folder, but that path only exists internally in React’s routing with react-router.

The solution was to set up a distribution Error Pages rule. Go to the CloudFront settings and choose your distribution. Next go to the “Error Pages” tab. Click “Create Custom Error Response” and add an entry for 403 since that’s the error status code we get. Set the Response Page Path to /index.html and the status code to 200. The end result astonishes me with its simplicity. The index page is served, but the URL is preserved in the browser, so once the react app loads, it detects the URL path and navigates to the desired route.

Error Pages 403 Rule


If you are hosting in IIS ; Adding this to my webconfig solved my problem

    <httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
        <remove statusCode="500" subStatusCode="100" />
        <remove statusCode="500" subStatusCode="-1" />
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
        <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
        <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />

You can make similar configuration for any other server


I had this same problem and this solution worked for us..


We are hosting multiple apps on the same server. When we would refresh the server would not understand where to look for our index in the dist folder for that particular app. The above link will take you to what worked for us… Hope this helps, as we have spent quite a hours on figuring out a solution for our needs.

We are using:


"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"

my webpack.config.js


/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
  template: __dirname + '/app/views/index.html',
  filename: 'index.html',
  inject: 'body'

module.exports = {
  entry: [
    'babel-polyfill', './app/index.js'
  output: {
    path: __dirname + '/dist/your_app_name_here',
    filename: 'index_bundle.js'
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'babel-loader',
      query : {
          presets : ["env", "react", "stage-1"]
      exclude: /node_modules/
  plugins: [HTMLWebpackPluginConfig]

my index.js


import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'

const store = configureStore();

const browserHistory = useRouterHistory(createHistory) ({
  basename: '/your_app_name_here'
const history = syncHistoryWithStore(browserHistory, store)

persistStore(store, {blacklist: ['routing']}, () => {
  console.log('rehydration complete')
// persistStore(store).purge()

    <Provider store={store}>
        <Routes history={history} />

my app.js

var express = require('express');
var app = express();

app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

app.get('/*', function (req, res) {

app.listen(8081, function () {
  console.log('MD listening on port 8081!');


add this to webpack.congif.js

devServer: {
      historyApiFallback: true


Solution for Preact with preact-router

Works with refresh and direct access

For those discovering this via Google, here’s a demo of preact-router + hash history:

const { h, Component, render } = preact; /** @jsx h */
const { Router } = preactRouter;
const { createHashHistory } = History;
const App = () => (
        <AddressBar />

        <Router history={createHashHistory()}>
            <div path="/">
                    all paths in preact-router are still /normal/urls.
                    using hash history rewrites them to /#/hash/urls
                Example: <a href="/page2">page 2</a>
            <div path="/page2">
                <p>Page Two</p>
                <a href="/">back to home</a><br/>



If you are hosting your react app on IIS, just add a web.config file containing :

<?xml version="1.0" encoding="utf-8"?>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />

This will tell IIS server to return the main page to the client instead of 404 error and no need to use hash history.


In case, anyone is here looking for solution on React JS SPA with Laravel.
The accepted answer is the best explanation of why such problems happen. As already explained you have to configure both client side and server side.
In your blade template, include the js bundled file, make sure to use URL facade like this

<script src="{{ URL::to('js/user/spa.js') }}"></script>

In your routes, make sure add this to the main endpoint where the blade template is. For example,

Route::get('/setting-alerts', function () {
   return view('user.set-alerts');

The above is the main endpoint for the blade template. Now add an optional route too,

Route::get('/setting-alerts/{spa?}', function () {
  return view('user.set-alerts');

The problem that happens is that first the blade template is loaded, then the react router. So, when you’re loading '/setting-alerts', it loads the html and the js. But when you load '/setting-alerts/about', it first loads on the server side. Since on the server side, there is nothing on this location, it returns not found. When you have that optional router, it loads that same page and react router is also loaded, then react loader decides which component to show.
Hope this helps.


Many good answers are here and no need to write more answers here.

but, I’m providing another good link here



Practical workaround:

  1. Duplicate and rename original html file as joblist.html
  2. Use joblist.html as route path

No server-side changes required.