I'm using React-router and it works fine while I'm clicking on link buttons, but when I refresh my webpage it does not load what I want.
For instance, I am in 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 as 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
doesn'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}/>
</Route>
);
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler/>, document.body);
});
This question is related to
javascript
reactjs
url
react-router
I found the solution for my SPA with react router (Apache). Just add in .htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
source: https://gist.github.com/alexsasharegan/173878f9d67055bfef63449fa7136042
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.
Complete Router with Example (React Router v4):
Sample working Example Check out the project below.
After download
1.) "npm install" in cmd to install project
2.) "npm start" to start your react app
Note : you need Node js software to run in your windows. Mac OS have node default.
Cheers
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: https://stackoverflow.com/a/37622953/5217568
Assume you have the following Home route definition
<Route exact path="/" render={routeProps => (
<Home routeProps={routeProps}/>
)}/>
{/*optional catch-all router */}
<Route render={routeProps => (
<div><h4>404 not found</h4></div>
)}/>
At your Home component, you can intercept the request at ComponentWillMount event,
const searchPath = this.props.routeProps.location.search;
if (searchPath){
this.props.routeProps.history.push("/" + searchPath.replace("?",""));
}
else{
/*.... originally Home event */
}
Now, instead of calling /joblist at url, you can request /?joblist, and the Component will auto redirect the request to /joblist (take note the extra question mark in the path)
If you are hosting your react app on IIS, just add a web.config file containing :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
</configuration>
This will tell IIS server to return the main page to the client instead of 404 error and no need to use hash history.
I had this same problem and this solution worked for us..
Background:
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:
package.json
"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
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
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()
ReactDOM.render(
<Provider store={store}>
<div>
<Routes history={history} />
</div>
</Provider>,
document.getElementById('mount')
)
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) {
res.render('index');
});
app.listen(8081, function () {
console.log('MD listening on port 8081!');
});
Try adding ".htaccess" file inside the public folder with the below code.
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
RewriteRule ^ /index.html [L]
I am using WebPack, I had same problem Solution=> In your server.js file
const express = require('express');
const app = express();
app.use(express.static(path.resolve(__dirname, '../dist')));
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, '../dist/index.html'));
// res.end();
});
If you are coming here and you are using apache and don´t have a .htaccess file, this is a config file that worked for me:
sites-enabled/somedomain.com.conf
<VirtualHost *:80>
ServerName somedomain.com
ServerAlias *.somedomain.com
DocumentRoot /www/somedomain.com/build
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /www/somedomain.com/build/index.html [L,NC,QSA]
</VirtualHost>
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 am using .Net Core 3.1 and just added the extension MapFallbackToController
:
Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapFallbackToController("Index", "Home");
});
If you're using firebase all you have to do is make sure you've got a rewrites property in your firebase.json file in the root of your app (in the hosting section).
For example:
{
"hosting": {
"rewrites": [{
"source":"**",
"destination": "/index.html"
}]
}
}
Hope this saves somebody else a hoard of frustration and wasted time.
Happy coding...
Further reading on the subject:
https://firebase.google.com/docs/hosting/full-config#rewrites
Firebase CLI: "Configure as a single-page app (rewrite all urls to /index.html)"
In your index.html head
, add the following:
<base href="/">
<!-- This must come before the css and javascripts -->
Then when running with webpack dev server use this command.
webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback
--history-api-fallback
is the important part
This can solve your problem
I also faced the same problem in the 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} />
</Router>
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 the addition of # tag in each and every URL. like
localhost/#/home localhost/#/aboutus
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 ls nano sample.conf Add the below content in it.
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName 0.0.0.0
ServerAlias 0.0.0.0
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
</Directory>
</VirtualHost>
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 the 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 0.0.0.0 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:
<script>
System.config({ baseURL: '/' });
</script>
This may differ from project to project.
Here is a frontend workaround I discovered that does not require modifying anything on the server.
Let's say your site is mysite.com and you have a React Route to mysite.com/about. In index.js, where you mount your top-level component, you can put another Router like:
ReactDOM.render(
<Router>
<div>
<Route exact path="/" component={Home} />
<Route exact path="/about"
render={(props) => <Home {...props} refreshRout={"/about"}/>}
/>
</div>
</Router>,
I'm assuming you have the original Router located somewhere below the top-level component in the virtual DOM. You also have to catch the url in your .urls if you are using Django like:
urlpatterns = [
path('about/', views.index),
]
This will depend on what backend you're using, however. Requesting mysite/about will get you into index.js (where you mount the top-level component) where you can use the render prop of the Route, rather than the component prop, and pass '/about' as a prop to, in this example, the Home component.
Within Home, in either componentDidMount() or the useEffect() hook, do:
useEffect() {
//check that this.props.refreshRoute actually exists before executing the
//following line
this.props.history.replace(this.props.refreshRoute);
}
I've assumed your Home component is rendering something like:
<Router>
<Route exact path="/" component={SomeComponent} />
<Route path="/about" component={AboutComponent} />
</Router>
Credit to (Pass props to a component rendered by React Router) for how to pass props to components in Routes.
We used express' 404 handling approach
// path to the static react build directory
const frontend = path.join(__dirname, 'react-app/build');
// map the requests to the static react build directory
app.use('/', express.static(frontend));
// all the unknown requests are redirected to the react SPA
app.use(function (req, res, next) {
res.sendFile(path.join(frontend, 'index.html'));
});
Works like a charm. A live demo is our site
As I am using .Net Core MVC something like this helped me:
public class HomeController : Controller
{
public IActionResult Index()
{
var url = Request.Path + Request.QueryString;
return App(url);
}
[Route("App")]
public IActionResult App(string url)
{
return View("/wwwroot/app/build/index.html");
}
}
Basically in MVC side, all the routes not matching will fall into to Home/Index
as it specified in startup.cs
. Inside Index
it is possible to get the original request url and pass it wherever needed.
startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
If you are using "create-react-app" command,
to generate a react application then the package.json needs to have one change for properly running production build React SPA in a browser. Open up package.json and add the following code segment to that,
"start": "webpack-dev-server --inline --content-base . --history-api-fallback"
Here the most important part is the "--history-api-fallback" to enable history API call back.
Sometimes you will get a 404 error if you use Spring or any other back-end API. So in such a situation, you need to have a controller in the back-end to forward any request(you desired) to the index.html file to handle by react-router. Following demonstrate example controller written using spring.
@Controller
public class ForwardingController {
@RequestMapping("/<any end point name>/{path:[^\\.]+}/**")
public String forward(HttpServletRequest httpServletRequest) {
return "forward:/";
}
}
for example, if we take a back-end API REST endpoint as "abc" (http://localhost:8080/abc/**) any request coming to that endpoint will redirect to react application (index.html file), and react-router will handle that afterwords.
If you are using Express or some other framework in the backend , you can add the similar configuration as below and check out the Webpack public path in the configuration, it should work fine even on reload if you are using BrowserRouter
expressApp.get('/*', (request, response) => {
response.sendFile(path.join(__dirname, '../public/index.html'));
});
I know this question has been answered to the death, but it doesn't solve the problem where you want to use your browser router with proxy pass, where you can't use root.
For me the solution is pretty simple.
say you have a url that's pointing to some port.
location / {
proxy_pass http://127.0.0.1:30002/;
proxy_set_header Host $host;
port_in_redirect off;
}
and now because of the browser router sub paths are broken. However you know what the sub paths are.
The solution to this? For sub path /contact
# just copy paste.
location /contact/ {
proxy_pass http://127.0.0.1:30002/;
proxy_set_header Host $host;
}
Nothing else I've tried works, but this simple fix works like a damn charm.
For those who are using IIS 10, this is what you should do to make this right. Be sure that you are using browserHistory with this. As for reference I will give the code for the routing, but this is not what matters, what matters is the next step after the component code below:
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<div>
<Root>
<Switch>
<Route exact path={"/"} component={Home} />
<Route path={"/home"} component={Home} />
<Route path={"/createnewproject"} component={CreateNewProject} />
<Route path={"/projects"} component={Projects} />
<Route path="*" component={NotFoundRoute} />
</Switch>
</Root>
</div>
</Router>
)
}
}
render (<App />, window.document.getElementById("app"));
Since the problem is IIS receives request from client browsers, it will interpret the URL as if it is asking for a page, then returns a 404 page since there is no available page. Do the following:
And it will now work fine.
I hope it helps. :-)
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 = () => (
<div>
<AddressBar />
<Router history={createHashHistory()}>
<div path="/">
<p>
all paths in preact-router are still /normal/urls.
using hash history rewrites them to /#/hash/urls
</p>
Example: <a href="/page2">page 2</a>
</div>
<div path="/page2">
<p>Page Two</p>
<a href="/">back to home</a><br/>
</div>
</Router>
</div>
);
In my case the url was not loading when I was using parameter in it.
As a quick fix I add
<base href="<yourdomain/IP>"></base>
under tag of index.html file in build folder.
And this just fixed my problem.
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'
<HashRouter>
<App/>
</HashRouter>
Reference: HashRouter
Using HashRouter
worked for me with Redux also, just simply replace:
import {
Router //replace Router
} from "react-router-dom";
ReactDOM.render(
<LocaleProvider locale={enUS}>
<Provider store={Store}>
<Router history={history}> //replace here saying Router
<Layout/>
</Router>
</Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();
to:
import {
HashRouter //replaced with HashRouter
} from "react-router-dom";
ReactDOM.render(
<LocaleProvider locale={enUS}>
<Provider store={Store}>
<HashRouter history={history}> //replaced with HashRouter
<Layout/>
</HashRouter>
</Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();
The other way of requesting data even though you are directing to urls immediately is to make every component has a method that calls to that last params like /about/test and then to your State Provider you have the function that connects to the component you want to request a data with
Its pretty simple when you got cannot get 403 error after refresh dom component. just add this one line in your web pack config, 'historyApiFallback: true '. this savez my whole day.
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 (
<Router>
// your routes here
</Router>
)
}
}
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) {
res.status(500).send(err)
}
})
})
3) bundle webpack with webpack -p
4) run nodemon server.js
or node server.js
EDIT: You may want to let nginx handle this in the server block and disregard step 2:
location / {
try_files $uri /index.html;
}
I solved this problem by changing webpack.config.js.
my new config looks like:
Before :
output: {
path: path.join(__dirname, '/build/static/js'),
filename: 'index.js'
},
devServer: {
port: 3000
}
After :
output: {
path: path.join(__dirname, '/build/static/js'),
filename: 'index.js',
publicPath: '/'
},
devServer: {
historyApiFallback: true,
port: 3000
}
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.
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.
For those of you who are here because you are trying to serve a react app from an IIS Virtual Directory (not the root of a website) then this may be for you.
When setting up your redirects '/' wont work on its own, for me it needed the virtual directory name in there too. Here is what my web config looked like:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<defaultDocument>
<files>
<remove value="default.aspx" />
<remove value="iisstart.htm" />
<remove value="index.htm" />
<remove value="Default.asp" />
<remove value="Default.htm" />
</files>
</defaultDocument>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="/YOURVIRTUALDIRECTORYNAME/" />
</rule>
</rules>
</rewrite>
<directoryBrowse enabled="false" />
<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
<remove statusCode="500" subStatusCode="100" />
<remove statusCode="500" subStatusCode="-1" />
<remove statusCode="404" subStatusCode="-1" />
<remove statusCode="403" subStatusCode="18" />
<error statusCode="403" subStatusCode="18" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
<error statusCode="404" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
<error statusCode="500" prefixLanguageFilePath="" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
<error statusCode="500" subStatusCode="100" path="/YOURVIRTUALDIRECTORYNAME/" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
</configuration>
In addition to the web.config file the react app isself needed some changes:
in package.json you need to add a 'homepage' entry:
{
"name": "sicon.react.crm",
"version": "0.1.0",
"private": true,
"homepage": "/YOURVIRTUALDIRECTORYNAME/",
"dependencies": {
...
I added the basename to my browser history object that I pass into the router to get access to history:
import {createBrowserHistory } from 'history';
export default createBrowserHistory({
//Pass the public URL as the base name for the router basename: process.env.PUBLIC_URL
});
I also added this property on my React router in App.js:
<Router history={history} basename={process.env.PUBLIC_URL}>
Finally, in index.html I added the following tab above the 'title' tag
<base href="%PUBLIC_URL%/">
it may be that some steps where note required but this seems to have done the job for me. I don't know how to set it up to run either in the root of a site or a virtual directory without a recompile though as the homepage in the package.json
can't be swapped after a build as far as I'm aware.
Add this to webpack.config.js
:
devServer: {
historyApiFallback: true
}
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" />
</httpErrors>
You can make similar configuration for any other server
you can try reading this all though it's not mine:
https://www.andreasreiterer.at/fix-browserrouter-on-apache/
Fixing the app’s routing Now here’s how to finally fix the routing. To tell Apache to redirect requests to index.html where our app lives, we have to modify the .htaccess file. If there is no such file in your app’s folder yet, just create it.
Then be sure that you put in those 4 lines that will magically make your routing work.
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
After we put that .htaccess file into the same directory as the index.html, Apache will redirect each new request directly to your app.
Bonus: Deploying the React app to a sub directory
If you’re deploying your app into a sub directory, so it’s accessible e.g. via https://myapp.com/the-app, you’ll soon notice that there is another issue. Each click to a new route will transform the URL to something like https://myapp.com/route-abc – which will break again after a reload. But there is a simple fix for that:
BrowserRouter has a prop called basename where you can specify your sub-directory path:
From now on, each Route like /contacts will result in an URL like http://myapp.com/the-app/contacts.
I like this way of handling it. Try adding: yourSPAPageRoute/* on the server side to get rid of this problem.
I went with this approach because even the native HTML5 History API doesn't support correct redirection on page refresh (As far as I know).
Note: Selected answer has already addressed this but I'm trying to be more specific.
Tested and just wanted to share this.
Hope it helps.
The Webpack Dev Server has an option to enable this. Open up package.json
and add --history-api-fallback
.
This solutions worked for me.
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.
In case you are running it on a Google Bucket, the simple solution to this is to consider 'index.html' for Error (404 not found) Page.
To do so:
I'm using React.js + Webpack mode. I added --history-api-fallback
parameter in package.json
file. Then page refreshing is working correctly.
Each when I change code, web page is refresh automatically.
"scripts": {
"start": "rimraf build && cross-env NODE_ENV='development' webpack --mode development && cross-env NODE_ENV=development webpack-dev-server --history-api-fallback",
...
}
Fixing the "cannot GET /URL" error on refresh or on calling the URL directly.
Configure your webpack.config.js to expect the given link the routes like this.
module.exports = {
entry: './app/index.js',
output: {
path: path.join(__dirname, '/bundle'),
filename: 'index_bundle.js',
publicPath: '/'
},
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:
web.xml:
<?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"
version="3.1">
<!-- WELCOME FILE LIST -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- ERROR PAGES DEFINITION -->
<error-page>
<error-code>404</error-code>
<location>/index.jsp</location>
</error-page>
</web-app>
Example:
That is it, no more magic needs:)
Adding more information to Joshua Dyck's answer.
If you are using Firebase and want to use both the root route and a sub-directory route you need to add the following code in your firebase.json
:
{
"hosting": {
"rewrites": [
{
"source": "*",
"destination": "/index.html"
},
{
"source": "/subdirectory/**",
"destination": "/subdirectory/index.html"
}
]
}
}
Example:
You are building a website for a client. You want the owner of the website to add information in https://your.domain.com/management while the users of the website will navigate to https://your.domain.com.
In this case your firebase.json
file will look like that:
{
"hosting": {
"rewrites": [
{
"source": "*",
"destination": "/index.html"
},
{
"source": "/management/**",
"destination": "/management/index.html"
}
]
}
}
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
Using express on the backend and React on the frontend ( without react-create-app) with reach/router, the correct reach/router route react component is shown and the menu link is set to the active style when hitting enter in the address bar e.g http://localhost:8050/pages. Please checkout below, or straight to my repo https://github.com/nickjohngray/staticbackeditor, all the code is there.
Webpack:
Setup proxy. This allows any calls from port 3000 ( React) to call the server including the call to get index.html or anything in the address bar when the enter key is hit. it also allows calls to the API route, to get JSON data
like await axios.post('/api/login', {email, pwd})
devServer: {
port: 3000,
open: true,
proxy: {
'/': 'http://localhost:8050',
}
}
Setup express routes
app.get('*', (req, res) => {
console.log('sending index.html')
res.sendFile(path.resolve('dist', 'index.html'))
});
This will match any request from react, it just returns the index.html page, which is in my dist folder this page, of course, has a more single-page react app. ( note any other routes should appear above this, in my case these are my API routes )
React Routes
<Router>
<Home path="/" />
<Pages path="pages"/>
<ErrorPage path="error"/>
<Products path="products"/>
<NotFound default />
</Router>
These routes are defined in my Layout component that will load the corresponding component when the path matches.
React Layout constructor
constructor(props) {
super(props);
this.props.changeURL({URL: globalHistory.location.pathname});
}
the Layout constructor is called as soon as it loads. In here I call my redux action changeURL that my menu listens to so it can highlight the correct menu item, like below:
Menu code
<nav>
{this.state.links.map( (link) =>
<Link className={this.getActiveLinkClassName(link.path) } to={link.path}>
{link.name}
</Link>)}
</nav>
You can change your .htaccess
file and insert this:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
I am using react: "^16.12.0"
and react-router: "^5.1.2"
This method is the Catch-all and is probably the easiest way to get you started.
Source: Stackoverflow.com