66

I am going to develop a simple Angular 2 application. I have created a project with routing, using Angular CLI and added several components to the app using 'ng generate component ' command. Then I specified routing in the app-routing.module.ts as following.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { UserComponent } from './user/user.component';
import { ErrorComponent } from './error/error.component';
import { SpecialpageComponent } from './specialpage/specialpage.component';

const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'about',
    component: AboutComponent
  },
    {
    path: 'user',
    component: UserComponent
  },
  {
    path: 'specialpage',
    component: SpecialpageComponent
  },
  {
    path: '**',
    component: ErrorComponent
  }

];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule { }

app.module.ts is as following.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ErrorComponent } from './error/error.component';
import { UserComponent } from './user/user.component';
import { SpecialpageComponent } from './specialpage/specialpage.component';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent,
    ErrorComponent,
    UserComponent,
    SpecialpageComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

I have not added any modifications for the other components. Then I deployed the application using 'ng serve' command and the app works fine with the links. Eg: http://localhost:4200/about

enter image description here

But when I deploy the project in http-server, the links do not work as expected. I deployed the app using 'http-server ./dist' command and the app gets deployed fine, but the links do not work. When I go to 'http://localhost:4200/about', it gives 404 error.

enter image description here

Am I doing anything wrong? Why 'ng-serve' works and 'http-server' does not work?

Any help would be appreciated. Thanks in advance.

I have uploaded my project to github.

13
  • 6
    Try imports: [RouterModule.forRoot(routes, {useHash: true})],. If it works this way, you need to enable HTML5 pushState on your production server. Commented Apr 21, 2017 at 6:11
  • @GünterZöchbauer, I tried, but no luck
    – kinath_ru
    Commented Apr 21, 2017 at 7:03
  • 1
    Yes, do not use useHash:true :D. As I said in my initial comment. You have to configure the server to support HTML5 pushState. useHash:true was only to debug the cause of the issue. Commented Apr 21, 2017 at 8:08
  • 1
    @GünterZöchbauer, thank you for your invaluable information regarding this matter. I will try to find a solution for this. Thanks again
    – kinath_ru
    Commented Apr 21, 2017 at 11:30
  • 2
    Did you find out the solution. i have same issue.
    – Ziggler
    Commented Feb 13, 2018 at 17:08

19 Answers 19

54

This problem is solved by implementing HashLocationStrategy which adds # to all your routes. You achieve this by adding HashLocationStrategy to AppModule providers.

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

and add the corresponding import

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

This will solve your problem.

And if you don't want to use the HashLocationStrategy, you can use the PathLocationStrategy, and so your Angular app will not show Hash in the URL.

8
  • 1
    I have same issue and I also have this in my app.modules.ts. I am not getting any 404 error. After app component init method nothing is loading as if my routes are not working. ng serve works fine but my localhost not..
    – Ziggler
    Commented Feb 13, 2018 at 17:03
  • Thank you! helped me. But is it ok to always show a hashtag in the deployed websites urls? Hmm Commented May 7, 2018 at 21:19
  • @ninjaxlite I Updated my answer if you don't want to show Hashtag in URLs
    – Manu
    Commented May 9, 2018 at 5:01
  • 2
    I feel like this shouldn't have to be a thing. Fix this Google. Change the construct of the entire internet to appease me. Commented Aug 16, 2018 at 22:33
  • This. I've tried all kinds of things to get it to run both on my nginx and IIS - to no avail. Until I came across this one. Wouldn't we all want angular to use the HashLocationStrategy by default? Or, even farther, it shouldn't even be an option NOT to use it?
    – Ya.
    Commented Sep 3, 2018 at 20:26
24

This is because http-server does not support fallback like lite-server or web pack-dev server. This is why it shows 404 not found. There are two solution to resolve this issue:

  1. you can use HashLocationStrategy like mentioned above.
  2. If you are deploying it to Apache or IIS server then you can simply add configurations as mentioned here!

Note: For development you can use lite-server.

Hope this will help you.

15

I've resolved this problem with adding this in AppModule Providers:

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

and importing

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

Then I created .htaccess:

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html

Everything works great without # in URLs :)

3
  • Where i have to add .htaccess file?
    – jay khatri
    Commented Nov 14, 2019 at 12:10
  • 1
    @jaykhatri In main directory (there where Angular app is) on your web server. It's probably public_html. Commented Nov 14, 2019 at 18:34
  • 1
    Created the .htaaccess file with contents from your post. Uploaded to the root of the website. it works!. Thanks i didnt add the PathLocationStrategy . i am using angular 13.3.3 Commented Aug 14, 2024 at 0:15
10

It will be solved in this way:

Case-1: For version less than Angular 5.1

1) Use HashLocationStrategy like mentioned below: In AppModule do the following.

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

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

Case-2: For version equal or more than Angular 5.1

2.1) Angular has introduced this property onSameUrlNavigation for overcoming refresh issue on the server.

2.2) Add onSameUrlNavigation to the RouterModule.forRoot in imports array .

a) In Application main routing module, i,e app.routing.module.ts / app.routing.ts add the property

See below:

@ngModule({

    imports: 
    [
       RouterModule.forRoot(routes, {onSameUrlNavigation: ‘reload’})
    ],

    exports: [RouterModule],
 });

Hope it help somebody.

0
6

It will happen because it goes to find a page about which is not there inside at all hence 404. Possible options:

  1. If you want to go with http-server then use a proxy which will redirect everything to http://localhost:your-port.

    Option: -P or --proxy Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com

  2. Don't use express at all. Create your own http-server using express & Redirect everything to index.html

    Assuming public is your folder where u keep all transpiled things.

    var express = require('express');
    var app = express();
    
    var path = __dirname + '/public';
    var port = 8080;
    
    app.use(express.static(path));
    app.get('*', function(req, res) {
        res.sendFile(path + '/index.html');
    });
    app.listen(port);
    
2
  • I'm sorry I did not get you quite well. I'm really a newbie to Angular. I'm not doing anything complex here. An regarding your first option, I tried to host my server and I accessed the URL without a port. Only the root URL works. Cannot navigate into other links.
    – kinath_ru
    Commented Apr 21, 2017 at 7:05
  • http-server --port 4200 -P http://someurl.com raise an infinite loop in my case Commented Mar 22, 2022 at 18:33
5

If you, like me, want to make no code changes at all, simply use this instead:

https://www.npmjs.com/package/angular-http-server

3

For Apache server:

in the Production servers

Add or create ".htaccess" file into the project root, add a rewrite rule to the .htaccess file as shown

RewriteEngine On
  # If an existing asset or directory is requested go to it as it is
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
  RewriteRule ^ - [L]
  # If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
2

I also faced this issue and found a solution that does not require HashLocationStrategy solution.

Issue is, a server like tomcat looks for a actual folder (for eg : /about ) which does not exist in angular app, as its a single page application. So every request needs to be redirected to index.html.

Easiest way to fix this issue is to add the following java class and override addResourceHandlers() method like below:

@Configuration
class WebMVCCustomConfiguration implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**").addResourceLocations("classpath:static").resourceChain(true)
            .addResolver(new PathResourceResolver() {
                @Override
                protected Resource getResource(String resourcePath, Resource location) {
                    String path = "static/";
                    path += (resourcePath.contains(".") && !resourcePath.equals("index.html")) ? resourcePath : "index.html";

                    Resource resource = new ClassPathResource(path);
                    return resource.exists() ? resource : null;
                }
            });
    }
  }

This will fix all the issues and this solution does not exist anywhere.

1
  • Good to know that there is solution like this, but I think is better to you use mechanisms provided by angular like that one which is mentioned above -> HashLocationStrategy. Cheers :) Commented Mar 16, 2021 at 10:26
2

As per the documentation at https://angular.io/guide/deployment (Apache, you could also look for nginx) You need to point the server to index.html using the .htaccess file as

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
1

For Apache Server

  • Create a file name .htaccess
  • Edit that file and write index.html instead of index.php
  • For those who dont have any code, can write the code below in your .htaccess file : RewriteEngine On # If an existing asset or directory is requested go to it as it is RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR] RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d RewriteRule ^ - [L] # If the requested resource doesn't exist, use index.html RewriteRule ^ /index.html

  • This code might not work for SSL certificate - contact your hosting provider OR visit https://httpd.apache.org/docs/current/howto/htaccess.html to refer to docs.

1

You should try specifying the url in the build from the point the app should initialize:

Ng build --prod --base-href="http://your.url.com/subdirectory/etc"
1

The exact reason why this particular issue is being faced is because of your server settings.

So when you implement the application there are certain steps that you will have to take. One of the essential steps is to mark a particular segment of your path say: http://domain-name/main where main being the path segment must be used as the identifier in your server settings say your server.js or app.js when it comes to a node backend or a webconfig file for IIS and Tomcat deployment.

The reason for marking a particular segment is so that when the server receives any request with that particular segment + additional you reroute it to your application in www or public folder for the angular router to kick in.

This process is known as url rewriting. Hence if the web server or the app server depending on the application size is not under your control then please use hashLocationStratergy else it is always neat to use pathLocationStartergy as it is helpful when it comes to history tracking and other aesthetic and performance benefits.

To read more on this you can visit these links:

For IIS: https://blog.angularindepth.com/deploy-an-angular-application-to-iis-60a0897742e7

0

Add a dot in your base href strings.

Correct

<base href="./home">

Wrong

<base href="/home">

http://www.wisdomofjim.com/blog/solved-problems-with-resource-loading-failures-when-deploying-angular-2-webapps-live

0

If you use href, change them to routerlink.

-1
export const routes: Routes =[
   **{ path: '', redirectTo: '/', pathMatch: 'full'},**
     { path: 'about', component: AboutComponent},
     { path: 'user',  component: UserComponent},
     { path: 'specialpage',  component: SpecialpageComponent},
     { path: '**',  component: ErrorComponent}
    ];

Look at the content of blockquote. And then you give the name of "HttpModule" at providers:[] in app.module.ts

Thanks.

1
-1

You can do it while registering your RouterModule.forRoot, you can pass a second object with propert {useHash: true} like the below :

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

@NgModule({
   declarations: [AppComponent],
   imports: [BrowserModule],
   RouterModule.forRoot(appRoutes , {useHash: true})],
   providers: [],
   bootstrap: [AppComponent],
})
export class AppModule {}
3
  • i have the same problem but the solution does not work Commented Jan 6, 2018 at 17:35
  • I have same problem. ng serve works good but when I deploy to my local, localhost/ doesnot work
    – Ziggler
    Commented Feb 13, 2018 at 17:09
  • 1
    don't forget to use the hash in your url when you test it, like: 127.0.0.1/#/home
    – Aurelien
    Commented May 29, 2018 at 15:41
-1

Change your index.html

<base href="/">
 to 
<base href="./">

Because we are using Tomcat we have mentioned this(.) for Routing is based on this(/) So same like http-server

I mean running normally ng serve you have seen only http://localhost:4200/

but run in server you have put your build into(dist) in webapps

so its come like

http://localhost:8080/dist/

so you need to add <base href="./">

i think this is the problem for you may be solved.

-1

in the project folder create .htaccess file an then write

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]

follow this link: https://angular.io/guide/deployment

-1

I used lite-server.

npm i -D  lite-server

then in my package.json file:

"scripts": {
   "ci:serve:employee-onboarding": "ng build --prod --project=employee-onboarding && lite-server -c lite-server-config.json"
}

to run it: npm run ci:serve:employee-onboarding

my lite-server-config.json file looks like this

{
  "port": 4201,
  "server": { "baseDir": "dist/apps/employee-onboarding" }
}

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.