Angular 2: 404 error occur when I refresh through the browser

AngularAngular2 Routing

Angular Problem Overview


I'm new to Angular 2. I have stored my single-page application in my server within a folder named as "myapp". I have changed the URL in the base to http://example.com/myapp/`.

My project has two pages. So I implement Angular 2 routing. I set the default page as login. When I type http://example.com/myapp/ in my browser it will redirect automatically to http://example.com/myapp/login. But if refresh that page I get a 404 error, saying that http://example.com/myapp/login is not found.

But if I run my project using the lite server everything is working. In this case the base URL in index.html will be "/". How do fix it?

Angular Solutions


Solution 1 - Angular

Update for Angular 2 final version

In app.module.ts:

  • Add imports:

      import { HashLocationStrategy, LocationStrategy } from '@angular/common';
    
  • And in NgModule provider, add:

      {provide: LocationStrategy, useClass: HashLocationStrategy}
    

Example (app.module.ts):

import { NgModule }       from '@angular/core';
import { BrowserModule  } from '@angular/platform-browser';
import { AppComponent }   from './app.component';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';

@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule],
    providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}],
    bootstrap: [AppComponent],
})
export class AppModule {}

Alternative

Use RouterModule.forRoot with the {useHash: true} argument.

Example:(from angular docs)

import { NgModule } from '@angular/core';
...

const routes: Routes = [//routes in here];

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(routes, { useHash: true })
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Solution 2 - Angular

For people (like me) who really want PathLocationStrategy (i.e. html5Mode) instead of HashLocationStrategy, see How to: Configure your server to work with html5Mode from a third-party wiki:

> When you have html5Mode enabled, the # character will no longer be used in your URLs. The # symbol is useful because it requires no server side configuration. Without #, the URL looks much nicer, but it also requires server side rewrites.

Here I only copy three examples from the wiki, in case the Wiki get lost. Other examples can be found by searching keyword "URL rewrite" (e.g. this answer for Firebase).

Apache

<VirtualHost *:80>
    ServerName my-app

    DocumentRoot /path/to/app

    <Directory /path/to/app>
        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]
    </Directory>
</VirtualHost>

Documentation for rewrite module

nginx
server {
    server_name my-app;

    root /path/to/app;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Documentation for try_files

IIS

<system.webServer>
  <rewrite>
    <rules> 
      <rule name="Main Rule" 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>

Solution 3 - Angular

In fact, it's normal that you have a 404 error when refreshing your application since the actual address within the browser is updating (and without # / hashbang approach). By default, HTML5 history is used for reusing in Angular2.

To fix the 404 error, you need to update your server to serve the index.html file for each route path you defined.

If you want to switch to the HashBang approach, you need to use this configuration:

import {bootstrap} from 'angular2/platform/browser';
import {provide} from 'angular2/core';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {LocationStrategy, HashLocationStrategy} from '@angular/common';

import {MyApp} from './myapp';

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

In this case, when you refresh the page, it will be displayed again (but you will have a # in your address).

This link could help you as well: https://stackoverflow.com/questions/34415725/when-i-refresh-my-website-i-get-a-404-this-is-with-angular2-and-firebase/.

Hope it helps you, Thierry

Solution 4 - Angular

I had the same problem. My Angular application is running on a Windows server.

I solved this problem by making a web.config file in the root directory.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="AngularJS" 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>

Solution 5 - Angular

Perhaps you can do it while registering your root with RouterModule. You can pass a second object with property useHash:true like the below:

import { NgModule }       from '@angular/core';
import { BrowserModule  } from '@angular/platform-browser';
import { AppComponent }   from './app.component';
import { ROUTES }   from './app.routes';

@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule],
    RouterModule.forRoot(ROUTES ,{ useHash: true }),],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}

Solution 6 - Angular

For people reading this that use Angular 2 rc4 or later, it appears LocationStrategy has been moved from router to common. You'll have to import it from there.

Also note the curly brackets around the 'provide' line.

main.ts

// Imports for loading & configuring the in-memory web api
import { XHRBackend } from '@angular/http';

// The usual bootstrapping imports
import { bootstrap }      from '@angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '@angular/http';

import { AppComponent }         from './app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
import { Location, LocationStrategy, HashLocationStrategy} from '@angular/common';

bootstrap(AppComponent, [
    APP_ROUTER_PROVIDERS,
    HTTP_PROVIDERS,
    {provide: LocationStrategy, useClass: HashLocationStrategy}
]);

Solution 7 - Angular

If you're running Angular 2 through ASP.NET Core 1 in Visual Studio 2015, you might find this solution from Jürgen Gutsch helpful. He describes it in a blog post. It was the best solution for me. Place the C# code provided below in your Startup.cs public void Configure() just before app.UseStaticFiles();

app.Use( async ( context, next ) => {
    await next();

    if( context.Response.StatusCode == 404 && !Path.HasExtension( context.Request.Path.Value ) ) {
        context.Request.Path = "/index.html";
        await next();
    }
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDANView Question on Stackoverflow
Solution 1 - AngularAsaf HananelView Answer on Stackoverflow
Solution 2 - AngularFranklin YuView Answer on Stackoverflow
Solution 3 - AngularThierry TemplierView Answer on Stackoverflow
Solution 4 - AngularBennie-beView Answer on Stackoverflow
Solution 5 - AngularNiyazView Answer on Stackoverflow
Solution 6 - AngularKDCView Answer on Stackoverflow
Solution 7 - AngularStackOverflowUserView Answer on Stackoverflow