I have a vue app with history mode running in tomcat.
When visiting www.mywebapp.com/faq or mywebapp.com/other or ../post/99 etc it first seems to be working fine. Refreshing the page seems to be working fine aswell.
However when looking closer in the browser console i see that i got an error message.
"Failed to load resource: the server responded with a status of 404 ()"
The page is returning a 404 error for a split second before it redirects me and then the page loads.
On very slow internet connections the 404 error is there for a longer time before it loads.
Anyone know what i am missing here? I have added the following code to try to handle it but without success.
My router looks like this:
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{
name: 'home',
path: '/',
component: Home
},
....,
{
path: '*',
component: ErrorLanding,
name: 'NotFound'
},
],
scrollBehavior(to, from, savedPosition) {
return {x:0, y: 0}
}
});
router.beforeEach((to, from, next) => {
const publicPages = ['/', '/login', '/register',
'/help', '/ticket', '/about' ];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
const redirect = to.path;
if (authRequired && loggedIn === null) {
if(to.meta.authRequired === false) {
next();
}
else
next({ name: 'Login', query: { redirect: redirect } });
} else {
next();
}
});
my vue.config.js looks like this:
module.exports = {
devServer: {
port: 80,
proxy: "http://localhost/*"
},
publicPath: process.env.NODE_ENV === 'production'
? '/'
: '/'
}
and finally, i have a web.xml file in dist/ROOT/WEB-INF/ with the following code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="true">
<display-name>my app</display-name>
<description>
this is my app description
</description>
<error-page>
<error-code>404</error-code>
<location>/</location>
</error-page>
</web-app>
update
When i add this line of code to server.xml
<Valve
className="org.apache.catalina.valves.rewrite.RewriteValve"/>
and create a rewrite.config file that i add to Catalina/localhost/
with this info:
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
It is working, and i dont get any error message any more.
But now i have a new issue. My arrays that are being populated via Vuex are returning infinite loops. How can this be?
Update
I have tried to add this to the rewrite.config file:
RewriteRule ^/api1/ - [L,NC]
RewriteRule ^/api2/ - [L,NC]
RewriteRule ^/api3/ - [L,NC]
RewriteRule ^/api4/ - [L,NC]
RewriteRule ^/api5/ - [L,NC]
RewriteRule ^/api6/ - [L,NC]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
update SOLVED
Below had to be added to the rewrite.config file.
RewriteRule ^/myapi1/.* - [L,NC]
RewriteRule ^/myapi2/.* - [L,NC]
RewriteRule ^/myapi3/.* - [L,NC]
RewriteRule ^/myapi4/.* - [L,NC]
RewriteRule ^/myapi5/.* - [L,NC]
RewriteRule ^/host-manager/.* - [L,NC]
RewriteRule ^/manager/.* - [L,NC]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
Big thanks to #Michal Levy for help!!
Problem is result of using history mode of Vue Router and is very well described in the docs
When using history mode, the URL will look "normal," e.g. http://oursite.com/user/id. Here comes a problem, though: Since our app is a single page client side app, without a proper server configuration, the users will get a 404 error if they access http://oursite.com/user/id directly in their browser. To fix the issue, all you need to do is add a simple catch-all fallback route to your server. If the URL doesn't match any static assets, it should serve the same index.html page that your app lives in.
There are also lot of examples how to do it for lot of servers - not for Tomcat tho...
To configure "SPA fallback" (common name for this) in Tomcat you must:
Configure the RewriteValve in server.xml
Edit the ~/conf/server.xml to add the below Valve inside the Host section as below:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
</Host>
Write the rewrite rule in rewrite.config
Create directory structure — ~/conf/Catalina/localhost/ and create the rewrite.config file inside it with the below content:
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
Source
Note: Beware that above rewrite rule causes your server to return index.html for any request that does not match "regular file" (see Rewrite Valve Docs). So if your server also serves some kind of API (REST for example), you need to modify rewrite rules to not to rewrite requests to API.
For example if your API is served on /api/..., your rewrite rules should look like this:
# match any URL starting with `/api/`, do not rewrite and don't process other rules...
RewriteRule ^/api/ - [L]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
#Michal Levys answer is correct, just some small adjustments had to be made for my case.
To solve this issue below had to be added to rewrite.config.
RewriteRule ^/myapi1/.* - [L,NC]
RewriteRule ^/myapi2/.* - [L,NC]
RewriteRule ^/myapi3/.* - [L,NC]
RewriteRule ^/myapi4/.* - [L,NC]
RewriteRule ^/myapi5/.* - [L,NC]
RewriteRule ^/host-manager/.* - [L,NC]
RewriteRule ^/manager/.* - [L,NC]
RewriteCond %{REQUEST_PATH} !-f
RewriteRule ^/(.*) /index.html
Suppose my application path is:-
http://www.example.com/index.php?r=dashboard/event/view
I make a new controller on which I want to change the URL to pretty URL.
Default URL for the new controller
http://www.example.com/index.php?r=newcontroller/view?id=23&name=urlpretty
I want to make above URL to pretty URL such as
http://www.example.com/23/sampleprettyurl
But I do not want to change the complete application URL path to pretty URL. My other controller path/URL should work normally.
Thanks in advance
I don't think is doable, controller name must be in the URL, get what you need to configure the module name, then the urlManager:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => false,
'rules' => [
'newcontroller/<id>/<name>' => 'moduleAlias/newcontroller',
],
],
Then in the action you can get the value by Yii::$app->request->get('id');
in config web.php
component => [
/* pretty url */
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
/* ./pretty url */
]
and .htaccess on /web
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
I'm working with Vue Js 2 and the server it's Nginx.
I have the next code:
const routes = [
{
path: '/design-dashboard',
component: DesignDashboard,
meta: {
forAuth : true
}
},
{
path: '/dashboard/new/create',
component: ProductLineEdit,
meta: {
forAuth : true
}
}
];
export default new VueRouter({
mode: 'history',
routes
});
The problem is when the url has more than 1 sections o slashes '/', for example it works with "/design-dashboard", and don't works with "/dashboard/new/create" only works throught a vue router link or a router.push() method, but when I reload or try to access directly with the url in the browser doesn't work.
I also added this htaccess code:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
As I said before everything works fine with only one section or sliash '/ like for example "/design-dashboard"
Any ideas? Thanks in advance.
It seems that you are trying to configure the nginx server using htaccess which is intended for apache.
Try to configure nginx, with configs like this:
location / {
try_files $uri $uri/ /index.html;
}
I solved this adding the .htaccess and transform to a nginx code and adding an slash '/' in src attribute in script tag
<script src="/dist/app.js">
I just enable Vue router history mode. And it work fine when I visit to vue routing via v-href or href. But, when I try to refresh that page or go directly from browser address bar, it just return 404. Is there any option to accept refresh/revisit to that url?
The following is my Vue router configuration
var router = new VueRouter({
hashbang: false,
history: true,
mode: 'html5',
linkActiveClass: "active",
root: '/user'
});
I think you are missing that SPA is not server side rendering. At least for the majority. So, when you access /anything your web server won't redirect it to index.html. However, if you click on any vuejs router link, it will work due to the fact that the javascript got in action, but not the server side.
To solve this, use .htaccess and redirect all requests to index.html like so
<ifModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</ifModule>
Hope it helps someone!
By refreshing the page you are making a request to the server with the current url and the server returns 404. You have to handle this on your web framework or web server level.
This article contains example server configuration:
https://v3.router.vuejs.org/guide/essentials/history-mode.html
That is too simple, you just have to add this config on your server:
Apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
Then if you are on Nginx:
location / {
try_files $uri $uri/ /index.html;
}
And that's all
For more information, visit this Link
Even better (on Ubuntu) if you have root access would be to modify /etc/apache2/apache2.conf, since even Apache themselves discourage the use of .htaccess under such circumstances.
Note that first, you have to do
sudo a2enmod rewrite
Then, add the following block to /etc/apache2/apache2.conf:
<Directory /var/www/html/>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</Directory>
After this, do
sudo systemctl restart apache2
For me, this works flawlessly, as it not just redirects routes created by Vue back to /, but actually refreshes the website to the same route, so you're still on the same view after refreshing.
If you don't really care about the hash in the url, you can just set the mode to hash in you router configuration file: mode: 'hash'
That works fine without the need to set up the server side.
const router = new VueRouter({
routes: Routes,
mode: 'hash'
});
Hash mode comes by default in vue, so it should work even if you leave blank instead of mode: 'hash'.
If someone is dealing with this issue in .NET Core as the backend of the app, a nice approach is a simple fallback handler in the Startup.cs of our .NET Core application:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
....Your configuration
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//handle client side routes
app.Run(async (context) =>
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "index.html"));
});
}
}
For more details: http://blog.manuelmejiajr.com/2017/10/letting-net-core-handle-client-routes.html
Just put the code in the .htaccess file in the same folder and that's it. No need to reboot or Apache restart.
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
For Netlify, add the following to a public/_redirects file...
/* /index.html 200
See https://www.netlify.com/docs/redirects/#history-pushstate-and-single-page-apps
if any one of is facing the issue even after trying above solution, please find below method.
If you have vue.config.js which has
module.exports = {
publicPath: process.env.PUBLIC_URL || "", // <-- this is wrong
...
};
either remove the vue.config.js or try removing the publicPath from the exports object.
Also you can try below method if you dont want to remove the publicPath
module.exports = {
publicPath: process.env.PUBLIC_URL || "/", default)
transpileDependencies: ["vuetify"],
};
In my case it was happening in a SPA hosted in azure blob storage as static website under an azure CDN.
The solution of the following question solved the issue for me (fix applied in CDN endpoint): Hosting SPA with Static Website option on Azure Blob Storage (clean URLs and Deep links)
In my case, I had handled it through virtual host:
Apache
goto /etc/apache2/sites-available and clone 000-default.conf with you domain name i.e. app.abc.com.conf and open it by sudo nano app.abc.com.conf and then place below snippet:
Vue Doc Reference: Click here
StackOverflow Reference: Click here
<VirtualHost *:80>
ServerName app.abc.com
DocumentRoot /var/www/html/project_name/dist/
ErrorLog ${APACHE_LOG_DIR}/app.abc.com.error.log
CustomLog ${APACHE_LOG_DIR}/app.abc.com.access.log combined
<Directory /var/www/html/project_name/dist>
FallbackResource /index.html
</Directory>
</VirtualHost>
Niginx Vue Cli (SPA) virtual host:
Vue Doc Reference: Click here
server {
listen 80;
root /var/www/html/project_name/dist;
server_name app.abc.com;
index index.html index.htm index.php;
location / {
try_files $uri $uri/ /index.html;
}
}
Note: Tested on ubuntu server
For someone seeing this using an Express backend, there is the middleware connect-history-api-fallback that is implemented like this
const express = require('express');
const history = require('connect-history-api-fallback');
const app = express();
app.use(history({
index: '/' //whatever your home/index path is default is /index.html
}));
Or with Native Node
const http = require('http')
const fs = require('fs')
const httpPort = 80
http.createServer((req, res) => {
fs.readFile('index.htm', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.htm" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
})
res.end(content)
})
}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
Both are suggestions in the documentation.
Anyone else facing the same problem, I just realized that
"book/:bookId/read" // this will not work after page reload
and this are different
"/book/:bookId/read" // this is what works even after page reload
This is of course after following what other fellas have suggested up there more importantly the catch all route in your app server side.
I actually don't know why this worked, but any one with any ideas can let us know.
I'm using Razor Pages with .net core 2.2 and I got this working:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
routes.MapRoute(
name: "spa-fallback",
template: "{*url:regex(^((?!.css|.map|.js|sockjs).)*$)}",
defaults: new { controller = "Index", action = "Index" });
});
Coming in late here, but.....
If anyone's interested in a workaround for this, I simply did this:
Redirect requests to known client-side URLs to the index (but only known client-side URLs, that way, you can keep 404s for actual page-not-found URLs). How you do this is dependent upon your server. I was using flask at the time, and just added more routes.
Add an attribute to the root vue element
<div id="content" redirect_to="{{redirect_to}}"></div>
Where my view function was replacing the parameter with the actual route requested by the URL
In the root vue component, check for this attribute and retrieve its value, as so:
if (this.$el.attributes['redirect_to'].value !== "") path =
this.$el.attributes['redirect_to'].value
Make a call to $router.push with the value of the attribute
this.$router.push(path).catch(err => {})
(The catch is needed otherwise the router will throw a NavigationDuplicated error.)
I did this because the entire point of urls in an SPA is because they point to specific resources and I wanted my users to be able to use the URLs to get back to those same resources.
Hopefully this either helps someone or points them in the right direction to make it work for their specific needs.
I am also facing the same problem. After adding this code in routes -> web.php the issue is resolved.
Route::get('/{vue_capture?}', function () {
return view('admin.dashboard');
})->where('vue_capture', '[\/\w\.-]*');
Redirect this route to your parent blade view.
Just as explained by #SERPRO, Vuejs, Angular, Reactjs, etc are all build on the concept of Single Page Application (SPA), so what I did on my Apache server was simply tell the server to load index.html for any path value after the origin. below are the changes that worked for me.
on my virtual host file, I added a simple rewrite rule to always load index.html and not to redirect.
RewriteEngine On
RewriteRule "^/auth/account/confirm" "/index.html"
And so if a user enters http://my-origin/account/confirm, the server will load the index.html with the app.vue file bound to it, and the app.vue file router will now pick the path account/confirm and determine the appropriate component to render
Then run
apachectl restart
service httpd restart
In my case the 404 error not only happend on a page reload but also when using the navigation buttons of the browser.
The problem were special characters like ä, ö, etc. in the path. First, the routing worked very well with those but when using the browsers navigation buttons or on a page reload the page can't be found anymore. After I replaced special characters with normal letters the routing worked again.
Maybe this helps somebody else.
In my case, I used Vue's own configuration to tackle this issue. The idea here is to utilize pages option. So in the file vue.config.js, we will add the following sample of code:
module.exports = {
pages: {
// This is our main page which refers to the main Javascript file
index: "src/main.js",
// Next, we list all other pages in the application,
// and let all of them refer to the main Javascript file as well.
other_page: "src/main.js",
},
};
With this implementation, if we stay at other_page and reload, the application would still work without any 404 error displayed.
My case is Apache server. There are 2 ways to configure:
I) Put a .htaccess file in the root vue source, (usually it is /var/www/html/)
The code and explanation for .htaccess are well explained by #Tiago Matos already.
The only one thing he didn't mention is to let Apache know a user can configure the Apache in .htaccess file under the root vue source (here is /var/www/html/), to do this:
Open httpd config file to edit:
sudo vi /etc/httpd/conf/httpd.conf
Change AllowOverride to All
<Directory /var/www/html>
AllowOverride All
Restart Apache to apply a new configuration
sudo systemctl restart httpd
II) Edit directly configuration of Apache: /etc/apache2/apache2.conf as #Tobias Feil mentioned. (Although not tested yet)