204

I am using Angular 2.0.0-alpha.30 version. When redirect to a different route, then refresh the browser , its showing Cannot GET /route.

Can you help me with figuring why this error happened.

Kamil Naja
  • 6,267
  • 6
  • 33
  • 47
Vinz and Tonz
  • 3,487
  • 5
  • 21
  • 32

33 Answers33

150

The error you are seeing is because you are requesting http://localhost/route which doesn't exist. According to Simon.

When using html5 routing you need to map all routes in your app(currently 404) to index.html in your server side. Here are some options for you:

  1. using live-server: https://www.npmjs.com/package/live-server

    $live-server --entry-file=index.html`
    
  2. using nginx: http://nginx.org/en/docs/beginners_guide.html

    error_page 404 /index.html
    
  3. Tomcat - configuration of web.xml. From Kunin's comment

    <error-page>
          <error-code>404</error-code>
          <location>/index.html</location>
    </error-page>
    
Franklin Yu
  • 8,920
  • 6
  • 43
  • 57
Quy Tang
  • 3,929
  • 1
  • 31
  • 45
  • 1
    Since one needs server side support for html5 routing this works perfectly well. Thanx – Prabhat Feb 21 '16 at 13:19
  • Can I use nginx as a proxy to `pub serve`? That way I don't need to `pub build` a lot during development. I tried but [`proxy_pass`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) doesn't work very well with [`error_page`](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page). – Franklin Yu Dec 19 '16 at 14:37
  • @FranklinYu I haven't done it before. Do you get it resolved? – Quy Tang Dec 29 '16 at 02:48
  • Not yet. I thought the argument `uri` of `error_page` can be something like `localhost:8000/index.html`, but it didn't work. The example on the documentation hint that I can "pass error processing into a named location", but it didn't seem to work for me either. – Franklin Yu Dec 29 '16 at 03:04
  • 1
    Using nginx. , error_page 404 /index.html in the location of the app worked for me. Thank you. – Richard K. Campion May 15 '17 at 19:18
  • Using nginx. This works but then still displays a 404 in my browser console. Any workaround? – Bob Jun 07 '17 at 21:03
  • @RichardK.Campion https://stackoverflow.com/a/19489441/2463695. You can click at `edited` to view the old answer version – Quy Tang Jun 13 '17 at 03:42
  • @flash that's why I wrote: error_page 404 =200 /index.html. – Quy Tang Jun 13 '17 at 03:42
  • How can I fix that, if I'm using `ng build` to generate my app? – Braulio Soncco Aug 28 '17 at 01:13
  • 2
    Tomcat - configuration of web.xml 404 /index.html – A Kunin Dec 07 '17 at 20:42
  • does anyone know how to do this with npm's http-server: https://github.com/indexzero/http-server/issues/427 – Alexander Mills Feb 06 '18 at 00:03
  • How to do it in IIS server? And if we redirect all the 404 error pages to index.html how should we handle the real 404 error page issue? – Temp O'rary Nov 08 '18 at 09:40
  • @TempO'rary you can handle it in client-side routing or you can define a regex rule in your server routing engine for known routes. For IIS server, unfortunately, I haven't used it before. – Quy Tang Nov 09 '18 at 08:20
  • My Angular app resides directly on IIS. How should I manage this issue in such a circumstance? – Temp O'rary Nov 09 '18 at 11:04
  • 1
    @AKunin this tomcat config doesn't work for tomcat 9 ? I tried same in tomcat 9 but its not working – user1608841 Nov 13 '18 at 10:16
  • @user1608841 I didn't test it with Tomcat 9. I am still on Tomcat 8.5 - setting is working – A Kunin Nov 14 '18 at 23:31
  • If I deploy my Angular application to AWS S3 or Azure Blob storage or CDN, what can I do to resolve this issue? – Duc Nguyen Apr 13 '20 at 09:58
69

Disclaimer: this fix works with Alpha44

I had the same issue and solved it by implementing the HashLocationStrategy listed in the Angular.io API Preview.

https://angular.io/docs/ts/latest/api/common/index/HashLocationStrategy-class.html

Start by importing the necessary directives

import {provide} from 'angular2/angular2';
import {
  ROUTER_PROVIDERS,
  LocationStrategy,
  HashLocationStrategy
} from 'angular2/router';

And finally, bootstrap it all together like so

bootstrap(AppCmp, [
  ROUTER_PROVIDERS,
  provide(LocationStrategy, {useClass: HashLocationStrategy})
]);

Your route will appear as http://localhost/#/route and when you refresh, it will reload at it's proper place.

starball
  • 20,030
  • 7
  • 43
  • 238
SimonHawesome
  • 1,292
  • 2
  • 14
  • 19
  • 1
    this code will work fine but there is someProblem in this technique by using this `#` will added automaticaly in the URL. is there any solution to remove # from the URL by using this ? – Pardeep Jain Nov 24 '15 at 05:04
  • Agreed, I tried implementing the "PathLocationStrategy" listed [here](https://angular.io/docs/ts/latest/api/router/PathLocationStrategy-class.html), but couldn't get it to work. A URL without the `#` would be optimal. – SimonHawesome Nov 25 '15 at 14:24
  • Yes me too tried the same with the `PathLocationStrategy` but this is not working for me. either i used wrong method or either i dont know how to use PathLocationStrategy. – Pardeep Jain Nov 25 '15 at 17:39
  • 1
    This doesn't work for me in RC1. Importing LocationStrategy, HashLocationStrategy from '@angular/router' fails. – infojolt Jun 04 '16 at 16:47
  • @psych As indicated in the disclaimer, the solution I provided works for Alpha 44. This issue has since been fixed as of (RC.3). See Günter Zöchbauer's answer, apart of this thread. – SimonHawesome Aug 24 '16 at 14:09
  • We use https://github.com/mgechev/angular2-seed and the current version (based on angular2 rc.5) incorporates this. However it was not in the version based on rc.4. – HankCa Aug 26 '16 at 00:16
  • 1
    Sorry for coming back to this questions after more than 1 year :) But does HashLocationStrategy help stay on the same Router path? In my case when I refresh it takes me to http://localhost/#/ i.e the default route for the home page. How do I go to the current route that I was originally on when I 'Refreshed'? – rajugaadu Jul 24 '17 at 18:32
49

Angular by default uses HTML5 pushstate (PathLocationStrategy in Angular slang).
You either need a server that processes all requests like it were requesting index.html or you switch to HashLocationStrategy (with # in the URL for routes) https://angular.io/docs/ts/latest/api/common/index/HashLocationStrategy-class.html

See also https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/

To switch to HashLocationStrategy use

update for >= RC.5 and 2.0.0 final

import {HashLocationStrategy, LocationStrategy} from '@angular/common';

@NgModule({
  declarations: [AppCmp], 
  bootstrap: [AppCmp],
  imports: [BrowserModule, routes],
  providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]
]);

or shorter with useHash

imports: [RouterModule.forRoot(ROUTER_CONFIG, {useHash: true}), ...

ensure you have all required imports

For the new (RC.3) router

<base href="."> 

can cause 404 as well.

It requires instead

<base href="/">

update for >= RC.x

bootstrap(AppCmp, [
  ROUTER_PROVIDERS,
  provide(LocationStrategy, {useClass: HashLocationStrategy})
  // or since RC.2
  {provide: LocationStrategy, useClass: HashLocationStrategy} 
]);

import {provide} from '@angular/core';
import {  
  PlatformLocation,  
  Location,  
  LocationStrategy,  
  HashLocationStrategy,  
  PathLocationStrategy,  
  APP_BASE_HREF}  
from '@angular/common';  

update for >= beta.16 Imports have changed

import {BrowserPlatformLocation} from '@angular/platform-browser';

import {provide} from 'angular2/core';
import {
  // PlatformLocation,
  // Location,
  LocationStrategy,
  HashLocationStrategy,
  // PathLocationStrategy,
  APP_BASE_HREF}
from 'angular2/router';
import {BrowserPlatformLocation} from 'angular2/src/router/location/browser_platform_location';

< beta.16

import {provide} from 'angular2/core';
import {
  HashLocationStrategy
  LocationStrategy,
  ROUTER_PROVIDERS,
} from 'angular2/router';

See also https://github.com/angular/angular/blob/master/CHANGELOG.md#200-beta16-2016-04-26 breaking-changes

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
30

I think the error you are seeing is because your are requesting http://localhost/route which doesn't exist. You need to make sure that your server will map all requests to your main index.html page.

As Angular 2 uses html5 routing by default rather than using hashes at the end of the url, refreshing the page looks like a request for a different resource.

Simon
  • 1,751
  • 2
  • 14
  • 11
  • 7
    so how do we fix that? how do we map all requests to main index by default? is it server-reliant? – Ayyash Feb 17 '16 at 10:57
  • 1
    Yes, you would do this on the server with whatever web framework you are using. The alternative is to use the HashLocationStrategy as described below. – Simon Feb 18 '16 at 12:25
  • You will be reloading the angular SPA in every non present route. Is it how it should be handled? – Gary Mar 15 '16 at 15:12
  • It only reloads if the user clicks refresh on the browser, not when user changes routes through the UI. Not really any way around a reload when the user refreshes. – Wallace Howery Jan 03 '18 at 21:45
23

This is a common situation in all router versions if you are using the default HTML location strategy.

What happens is that the URL on the browser bar is a normal full HTML url, like for example: http://localhost/route.

So when we hit Enter in the browser bar, there is an actual HTTP request sent to the server to get a file named route.

The server does not have such file, and neither something like express is configured on the server to handle the request and provide a response, so the server return 404 Not Found, because it could not find the route file.

What we would like is for the server to return the index.html file containing the single page application. Then the router should kick in and process the /route url and display the component mapped to it.

So to fix the issue we need to configure the server to return index.html (assuming that is the name of your single page application file) in case the request could not be handled, as opposed to a 404 Not Found.

The way to do this will depend on the server side technology being used. If its Java for example you might have to write a servlet, in Rails it will be different, etc.

To give a concrete example, if for example you are using NodeJs, you would have to write a middleware like this:

function sendSpaFileIfUnmatched(req,res) {
    res.sendFile("index.html", { root: '.' });
}

And then register it at the very end of the middleware chain:

app.use(sendSpaFileIfUnmatched);

This will serve index.html instead of returning a 404, the router will kick in and everything will work as expected.

Angular University
  • 42,341
  • 15
  • 74
  • 81
15

Make sure this is placed in the head element of your index.html:

<base href="/">

The Example in the Angular2 Routing & Navigation documentation uses the following code in the head instead (they explain why in the live example note of the documentation):

<script>document.write('<base href="' + document.location + '" />');</script>

When you refresh a page this will dynamically set the base href to your current document.location. I could see this causing some confusion for people skimming the documentation and trying to replicate the plunker.

user2263572
  • 5,435
  • 5
  • 35
  • 57
  • Thanks for sharing this. Let's just be clear, it only worked for me when I put directly after loading all css in the , and before loading ANY Js files . I hope it helps anyone – wmehanna Jun 09 '16 at 14:50
  • i have in index.html then removed '.' from the href and it worked.. – Saurabh Solanki Apr 20 '19 at 05:22
12

If you want to be able to enter urls in browser without configuring your AppServer to handle all requests to index.html, you must use HashLocationStrategy.

The easiest way to configure is using:

RouterModule.forRoot(routes, { useHash: true })

Instead of:

RouterModule.forRoot(routes)

With HashLocationStrategy your urls gonna be like:

http://server:port/#/path
felipecrp
  • 999
  • 10
  • 16
7

I had the same problem with using webpack-dev-server. I had to add the devServer option to my webpack.

Solution:

// in webpack
devServer: {
    historyApiFallback: true,
    stats: 'minimal'
}
angmerica
  • 787
  • 8
  • 13
  • Kind of voodoo magic :p For more info, see : https://webpack.github.io/docs/webpack-dev-server.html#the-historyapifallback-option – Jissay Oct 21 '16 at 15:01
7

If you are using Apache or Nginx as a server you have to create a .htaccess (if not created before) and "On" RewriteEngine

RewriteEngine On  
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
RewriteRule ^ /index.html
Franklin Yu
  • 8,920
  • 6
  • 43
  • 57
Rakesh Roy
  • 930
  • 2
  • 12
  • 22
5

My server is Apache, what I did to fix 404 when refreshing or deep linking is very simple. Just add one line in apache vhost config:

ErrorDocument 404 /index.html

So that any 404 error will be redirected to index.html, which is what angular2 routing wants.

The whole vhost file example:

<VirtualHost *:80>
  ServerName fenz.niwa.local
  DirectoryIndex index.html
  ErrorDocument 404 /index.html

  DocumentRoot "/Users/zhoum/Documents/workspace/fire/fire_service/dist"
  ErrorLog /Users/zhoum/Documents/workspace/fire/fire_service/logs/fenz.error.log
  CustomLog /Users/zhoum/Documents/workspace/fire/fire_service/logs/fenz.access.log combined

  <Directory "/Users/zhoum/Documents/workspace/fire/fire_service/dist">
    AllowOverride All
    Options Indexes FollowSymLinks
    #Order allow,deny
    #Allow from All
    Require all granted
  </Directory>

  Header set Access-Control-Allow-Origin "*"
  Header set Access-Control-Allow-Methods "GET, POST"
  Header set Access-Control-Allow-Credentials "true"
  Header set Access-Control-Allow-Headers "Accept-Encoding"
</VirtualHost>

No matter what server you are using, I think the whole point is finding out the ways to config the server to redirect 404 to your index.html.

Franklin Yu
  • 8,920
  • 6
  • 43
  • 57
Willie Z
  • 476
  • 7
  • 12
4

Server configuration is not a solution for an SPA is what even I think. You dont want to reload an angular SPA again if a wrong route comes in, do you? So I will not depend on a server route and redirect to other route but yes I will let index.html handle all the requests for angular routes of the angular app path.

Try this instead of otherwise or wrong routes. It works for me, not sure but seems like work in progress. Stumbled this myself when facing an issue.

@RouteConfig([
  { path: '/**', redirectTo: ['MycmpnameCmp'] }, 
   ...
  }
])

https://github.com/angular/angular/issues/4055

However remember to configure your server folders and access right in case you have HTML or web scripts which are not SPA. Else you will face issues. For me when facing issue like you it was a mix of server configuration and above.

Gary
  • 2,293
  • 2
  • 25
  • 47
4

for angular 5 quick fix, edit app.module.ts and add {useHash:true} after the appRoutes.

@NgModule(
{
  imports:[RouterModule.forRoot(appRoutes,{useHash:true})]
})
Adesh Shah
  • 442
  • 4
  • 12
3

You can use this solution for mean application, I used ejs as view engine:

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);
app.use(function (req, res, next) {
    return res.render('index.html');
});

and also set in angular-cli.json

"apps": [
    {
      "root": "src",
      "outDir": "views",

it will work fine instead of

app.get('*', function (req, res, next) {
    res.sendFile('dist/index.html', { root: __dirname });
 });

its creating issue with get db calls and returning index.html

Tena
  • 600
  • 4
  • 14
3

Angular apps are perfect candidates for serving with a simple static HTML server. You don't need a server-side engine to dynamically compose application pages because Angular does that on the client-side.

If the app uses the Angular router, you must configure the server to return the application's host page (index.html) when asked for a file that it does not have.

A routed application should support "deep links". A deep link is a URL that specifies a path to a component inside the app. For example, http://www.example.com/heroes/42 is a deep link to the hero detail page that displays the hero with id: 42.

There is no issue when the user navigates to that URL from within a running client. The Angular router interprets the URL and routes to that page and hero.

But clicking a link in an email, entering it in the browser address bar, or merely refreshing the browser while on the hero detail page — all of these actions are handled by the browser itself, outside the running application. The browser makes a direct request to the server for that URL, bypassing the router.

A static server routinely returns index.html when it receives a request for http://www.example.com/. But it rejects http://www.example.com/heroes/42 and returns a 404 - Not Found error unless it is configured to return index.html instead

If this issue occurred in production, follow below steps

1) Add a Web.Config file in the src folder of your angular application. Place below code in it.

<configuration>
<system.webServer>
<rewrite>
    <rules>
    <rule name="Angular Routes" stopProcessing="true">
        <match url=".*" />
        <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        </conditions>
        <action type="Rewrite" url="/" />
    </rule>
    </rules>
</rewrite>
</system.webServer>
</configuration>

2) Add a reference to it in angular-cli.json. In angular-cli.json put Web.config in assets block as shown below.

"assets": [
    "assets",
    "favicon.ico",
    "Web.config"
  ],

3) Now you can build the solution for production using

ng build --prod

This will create a dist folder. The files inside dist folder are ready for deployment by any mode.

Malatesh Patil
  • 4,505
  • 1
  • 14
  • 14
  • Best solution, can find the details from angular: https://angular.io/guide/deployment#fallback While creating web.config file do not forget to add tag. Thanks for the answer Malatesh. – Palash Roy Jun 25 '19 at 04:50
  • Not working, I used 1. HashLocationStrategy 2. htaacess 3. web.config. Please suggest any other solutions. – Mayank Sudden Oct 10 '21 at 19:30
2

For those of us struggling through life in IIS: use the following PowerShell code to fix this issue based on the official Angular 2 docs (that someone posted in this thread? http://blog.angular-university.io/angular2-router/)

Import-WebAdministration
# Grab the 404 handler and update it to redirect to index.html.
$redirect = Get-WebConfiguration -filter "/system.WebServer/httperrors/error[@statusCode='404']" -PSPath IIS:\Sites\LIS 
$redirect.path = "/index.html"
$redirect.responseMode = 1
# shove the updated config back into IIS
Set-WebConfiguration -filter "/system.WebServer/httperrors/error[@statusCode='404']" -PSPath IIS:\Sites\LIS -value $redirect

This redirects the 404 to the /index.html file as per the suggestion in the Angular 2 docs (link above).

Jim Hume
  • 51
  • 1
  • 6
2

You can try out below. It works for me!

main.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

...
export class MainComponent implements OnInit {
    constructor(private router: Router) {
        let path: string = window.location.hash;
        if (path && path.length > 0) {
            this.router.navigate([path.substr(2)]);
        }
    }

    public ngOnInit() { }
}

You can further enhance path.substr(2) to split into router parameters. I'm using angular 2.4.9

kaifeong
  • 21
  • 1
  • This was really helpful for handling browser refreshes! For angular 5, I had to modify it a bit to use window.location.pathname, and compare that value to expected paths. – Wallace Howery Jan 03 '18 at 22:05
2

i Just adding .htaccess in root.

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
    RewriteRule ^ - [L]
    RewriteRule ^ ./index.html
</IfModule>

here i just add dot '.'(parent directory) in /index.html to ./index.html
make sure your index.html file in base path is main directory path and set in build of project.

hadiya vipul
  • 106
  • 1
  • 2
1

I wanted to preserve the URL path of sub pages in HTML5 mode without a redirect back to index and none of the solutions out there told me how to do this, so this is how I accomplished it:

Create simple virtual directories in IIS for all your routes and point them to the app root.

Wrap your system.webServer in your Web.config.xml with this location tag, otherwise you will get duplicate errors from it loading Web.config a second time with the virtual directory:

<configuration>
    <location path="." inheritInChildApplications="false">
    <system.webServer>
        <defaultDocument enabled="true">
            <files>
                <add value="index.html" />
            </files>
        </defaultDocument>
    </system.webServer>
  </location>
</configuration>
Devin McQueeney
  • 1,277
  • 2
  • 14
  • 31
1

I checked in angular 2 seed how it works.

You can use express-history-api-fallback to redirect automatically when a page is reload.

I think it's the most elegant way to resolve this problem IMO.

Lievno
  • 1,013
  • 9
  • 14
1

If you want to use PathLocationStrategy :

  • Wildfly configuration :
    • Create undertow-handlers.conf file to be placed in the WEB-INF
    • Content : (exclude your rest endpoints !)
      • regex['(./overview/?.?$)'] and not regex['(./endpoints.)'] -> rewrite['/index.html']
      • regex['(./deployments/?.?$)'] and not regex['(./endpoints.)'] -> rewrite['/index.html']

Single page application with Java EE/Wildfly: server-side configuration

Community
  • 1
  • 1
nkeymeulen
  • 11
  • 1
1

2017-July-11: Since this is linked from a question having this problem but using Angular 2 with Electron, I'll add my solution here.

All I had to do was remove <base href="./"> from my index.html and Electron started reloading the page again successfully.

TimTheEnchanter
  • 3,370
  • 1
  • 26
  • 47
1

Add imports:

import { HashLocationStrategy, LocationStrategy } from '@angular/common';

And in NgModule provider, add:

providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]

In main index.html File of the App change the base href to ./index.html from /

The App when deployed in any server will give a real url for the page which can be accessed from any external application.

AmmoPT
  • 958
  • 1
  • 9
  • 16
Debargho24
  • 51
  • 4
1

Just adding .htaccess in root resolved 404 while refreshing the page in angular 4 apache2.

<IfModule mod_rewrite.c>
    RewriteEngine on

    # Don't rewrite files or directories
    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]

    # Rewrite everything else to index.html
    # to allow html5 state links
    RewriteRule ^ index.html [L]
</IfModule>
a_k_v
  • 1,558
  • 7
  • 18
1

I think you are getting 404 because your are requesting http://localhost/route which doesn't exist on tomcat server. As Angular 2 uses html 5 routing by default rather than using hashes at the end of the URL, refreshing the page looks like a request for a different resource.

When using angular routing on tomcat you need to make sure that your server will map all routes in your app to your main index.html while refreshing the page. There are multiple way to resolve this issue. Whichever one suits you you can go for that.

1) Put below code in web.xml of your deployment folder :

<error-page>
     <error-code>404</error-code>
     <location>/index.html</location>
</error-page>

2) You can also try using HashLocationStrategy with # in the URL for routes :

Try using:

RouterModule.forRoot(routes, { useHash: true })

Instead of:

RouterModule.forRoot(routes)

With HashLocationStrategy your urls gonna be like:

http://localhost/#/route

3) Tomcat URL Rewrite Valve : Re-write the url's using a server level configuration to redirect to index.html if the resource is not found.

3.1) Inside META-INF folder create a file context.xml and copy the below context inside it.

<? xml version='1.0' encoding='utf-8'?>
<Context>
  <Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />
</Context>

3.2) Inside WEB-INF, create file rewrite.config(this file contain the rule for URL Rewriting and used by tomcat for URL rewriting). Inside rewrite.config, copy the below content:

  RewriteCond %{SERVLET_PATH} !-f
  RewriteRule ^/(.*)$ /index.html [L]
Karan Khanna
  • 272
  • 2
  • 12
0

This is not the right answer but On-refresh you can redirect all dead calls to Homepage by sacrificing 404 page it's a temporary hack just replay following on 404.html file

<!doctype html>
<html>
    <head>
        <script type="text/javascript">
            window.location.href = "http://" + document.location.host;
        </script>
    </head>
</html>
don505
  • 152
  • 9
0

The best solution of resolve the "router-not-working-on-reloading-the-browser" is that we should use spa-fall back. If you are using angular2 application with asp.net core then we need to defined it on "StartUp.cs" page. under MVC routs. I am attaching the code.

 app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
            routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
        });
0

The answer is quite tricky. If you use a plain old Apache Server (or IIS), you get the problem because the Angular pages do not exist for real. They are "computed" from the Angular route.

There are several ways to fix the issue. One is to use the HashLocationStrategy offered by Angular. But a sharp sign is added in the URL. This is mainly for compatibility with Angular 1 (I assume). The fact is the part after the sharp is not part of the URL (then the server resolves the part before the "#" sign). That can be perfect.

Here an enhanced method (based on the 404 trick). I assume you have a "distributed" version of your angular application (ng build --prod if you use Angular-CLI) and you access the pages directly with your server and PHP is enabled.

If your website is based on pages (Wordpress for example) and you have only one folder dedicated to Angular (named "dist" in my example), you can do something weird but, at the end, simple. I assume you have stored your Angular pages in "/dist" (with the according <BASE HREF="/dist/">). Now use a 404 redirection and the help of PHP.

In your Apache configuration (or in the .htaccess file of your angular application directory), you must add ErrorDocument 404 /404.php

The 404.php will start with the following code:

<?php
$angular='/dist/';
if( substr($_SERVER['REQUEST_URI'], 0, strlen($angular)) == $angular ){
    $index = $_SERVER['DOCUMENT_ROOT'] . $angular . "index.html";
    http_response_code(200);
    include $index;
    die;
}

// NOT ANGULAR...
echo "<h1>Not found.</h1>"

where $angular is the value stored in the HREF of your angular index.html.

The principle is quite simple, if Apache does not find the page, a 404 redirection is made to the PHP script. We just check if the page is inside the angular application directory. If it is the case, we just load the index.html directly (without redirecting): this is necessary to keep the URL unchanged. We also change the HTTP code from 404 to 200 (just better for crawlers).

What if the page does not exist in the angular application? Well, we use the "catch all" of the angular router (see Angular router documentation).

This method works with an embedded Angular application inside a basic website (I think it will be the case in future).

NOTES:

  • Trying to do the same with the mod_redirect (by rewriting the URLs) is not at all a good solution because files (like assets) have to be really loaded then it is much more risky than just using the "not found" solution.
  • Just redirecting using ErrorDocument 404 /dist/index.html works but Apache is still responding with a 404 error code (which is bad for crawlers).
William R
  • 37
  • 12
0

This is not a permanent Fix for the problem but more like a workaround or hack

I had this very same issue when deploying my Angular App to gh-pages. First i was greeted with 404 messages when Refreshing my pages on gh-pages.

Then as what @gunter pointed out i started using HashLocationStrategy which was provided with Angular 2 .

But that came with it's own set of problems the # in the url it was really bad made the url look wierd like this https://rahulrsingh09.github.io/AngularConcepts/#/faq.

I started researching out about this problem and came across a blog. I tried giving it a shot and it worked .

Here is what i did as mentioned in that blog.

You’ll need to start by adding a 404.html file to your gh-pages repo that contains an empty HTML document inside it – but your document must total more than 512 bytes (explained below). Next put the following markup in your 404.html page’s head element:

<script>
  sessionStorage.redirect = location.href;
</script>
<meta http-equiv="refresh" content="0;URL='/REPO_NAME_HERE'"></meta>

This code sets the attempted entrance URL to a variable on the standard sessionStorage object and immediately redirects to your project’s index.html page using a meta refresh tag. If you’re doing a Github Organization site, don’t put a repo name in the content attribute replacer text, just do this: content="0;URL='/'"

In order to capture and restore the URL the user initially navigated to, you’ll need to add the following script tag to the head of your index.html page before any other JavaScript acts on the page’s current state:

<script>
  (function(){
    var redirect = sessionStorage.redirect;
    delete sessionStorage.redirect;
    if (redirect && redirect != location.href) {
      history.replaceState(null, null, redirect);
    }
  })();
</script>

This bit of JavaScript retrieves the URL we cached in sessionStorage over on the 404.html page and replaces the current history entry with it.

Reference backalleycoder Thanks to @Daniel for this workaround.

Now the above URL changes to https://rahulrsingh09.github.io/AngularConcepts/faq

Rahul Singh
  • 19,030
  • 11
  • 64
  • 86
0

I fixed this (using Java / Spring backend) by adding a handler that matches everything defined in my Angular routes, that sends back index.html instead of a 404. This then effectively (re)bootstraps the application and loads the correct page. I also have a 404 handler for anything that isn't caught by this.

@Controller         ////don't use RestController or it will just send back the string "index.html"
public class Redirect {

    private static final Logger logger = LoggerFactory.getLogger(Redirect.class);

    @RequestMapping(value = {"comma", "sep", "list", "of", "routes"})
    public String redirectToIndex(HttpServletRequest request) {
        logger.warn("Redirect api called for URL {}. Sending index.html back instead. This will happen on a page refresh or reload when the page is on an Angular route", request.getRequestURL());
        return "/index.html";
    }
}
Lee Willis
  • 1,552
  • 9
  • 14
0

If you are using Apache or Nginx as a server you need to create a .htaccess

<IfModule mime_module>
      AddHandler application/x-httpd-ea-php72 .php .php7 .phtml
    </IfModule>
    # php -- END cPanel-generated handler, do not edit

    <IfModule mod_rewrite.c>
        RewriteEngine on

        # Don't rewrite files or directories
        RewriteCond %{REQUEST_FILENAME} -f [OR]
        RewriteCond %{REQUEST_FILENAME} -d
        RewriteRule ^ - [L]

        # Rewrite everything else to index.html
        # to allow html5 state links
        RewriteRule ^ index.html [L]
    </IfModule>
0

You can use location strategy, Add useHash: true in routing file.

imports: [RouterModule.forRoot(routes, { useHash: true })]

0

In my case I am running an Angular app on an Azure hosted Linux box, the previously mentioned hash location strategy worked, but having the # in the URL wasn't what was wanted. Adding --spa to the configuration startup command allowed manually typing URLs. So my command is now this

pm2 serve /home/site/wwwroot/ --no-daemon --spa

More information on Azure deployment with PM2

WhatsThePoint
  • 3,395
  • 8
  • 31
  • 53
-3

Simon's answer was correct for me. I added this code:

app.get('*', function(req, res, next) {
  res.sendfile("dist/index.html");
});
GeoPro44
  • 41
  • 3