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

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?

This question is related to angular angular2-routing

The answer is


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>

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 { }

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 {}

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>

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();
    }
});

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}
]);