← Back to Articles         
How do you configure Angular routing on a production web server?

How do you configure Angular routing on a production web server?

Published on 02 Oct 2026     10 min read Angular
Deployment

How do you configure Angular routing on a production web server?

Angular normally uses client-side routing. In production, the web server must be configured so that Angular routes such as:

/products
/articles
/admin/dashboard

return Angular's index.html instead of producing a server-side 404 Not Found.

The core rule is:

If the requested URL is not a real static file or backend/API endpoint, return index.html and let Angular Router handle the route.


1. What is the routing problem in production?

Suppose your Angular routes are:

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./home').then(m => m.Home)
  },
  {
    path: 'products',
    loadComponent: () =>
      import('./products').then(m => m.Products)
  },
  {
    path: 'contact',
    loadComponent: () =>
      import('./contact').then(m => m.Contact)
  }
];

Angular understands:

/
/products
/contact

When navigating inside Angular, everything works:

Home
 ↓
click Products
 ↓
Angular Router
 ↓
/products
 ↓
Products component

No full page request may be required.


2. The refresh/direct-navigation problem

Now directly enter:

https://www.example.com/products

or refresh the browser while on /products.

The browser sends:

GET /products

to the production web server.

The server may think:

Does /products exist as a physical file/folder?

        ↓

       No

        ↓

404 Not Found

But /products isn't supposed to be a physical file.

It is an Angular route.

Therefore, the server needs to return:

index.html

Then Angular starts and Angular Router reads /products.


3. Correct production flow

The desired flow is:

Browser
   |
   | GET /products
   ↓
Web Server
   |
   | Is /products a physical file?
   | No
   ↓
Return index.html
   ↓
Angular starts
   ↓
Angular Router examines URL
   ↓
/products
   ↓
Products component

This is generally called an SPA fallback or URL rewrite/fallback.


4. Full Simple Angular Example

app.routes.ts

import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./home').then(m => m.Home)
  },
  {
    path: 'products',
    loadComponent: () =>
      import('./products').then(m => m.Products)
  },
  {
    path: 'contact',
    loadComponent: () =>
      import('./contact').then(m => m.Contact)
  },
  {
    path: '**',
    loadComponent: () =>
      import('./not-found').then(m => m.NotFound)
  }
];

app.config.ts

import {
  ApplicationConfig
} from '@angular/core';

import {
  provideRouter
} from '@angular/router';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes)
  ]
};

main.ts

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

import { App } from './app/app';
import { appConfig } from './app/app.config';

bootstrapApplication(App, appConfig)
  .catch(err => console.error(err));

5. Build Angular

Create the production build:

ng build --configuration production

You may get output similar to:

dist/
└── my-app/
    └── browser/
        ├── index.html
        ├── main-XXXX.js
        ├── styles-XXXX.css
        └── chunk-XXXX.js

Deploy these browser files to your web server.

But deployment alone isn't enough.

You also need the SPA fallback configuration.


6. ASP.NET Core hosting

If ASP.NET Core is serving your Angular static files, this is straightforward.

Suppose:

wwwroot/
├── index.html
├── main-XXXX.js
├── styles-XXXX.css
└── chunk-XXXX.js

Configure:

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseAuthorization();

app.MapControllers();

app.MapFallbackToFile("index.html");

app.Run();

The critical line is:

app.MapFallbackToFile("index.html");

7. What does MapFallbackToFile() do?

Suppose the browser requests:

/products

ASP.NET Core checks the mapped endpoints.

There isn't an API/controller endpoint for:

/products

The fallback returns:

index.html

Then Angular Router processes:

/products

Flow:

GET /products
      ↓
ASP.NET Core
      ↓
No matching endpoint
      ↓
Fallback
      ↓
index.html
      ↓
Angular starts
      ↓
Angular Router
      ↓
Products

8. What about ASP.NET Core API routes?

Suppose your API is:

/api/products

Controller:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok(new[]
        {
            new { Id = 1, Name = "Laptop" },
            new { Id = 2, Name = "Mouse" }
        });
    }
}

Then:

GET /api/products

matches:

ProductsController

It doesn't need Angular's fallback.

Conceptually:

Request
   |
   ├── /api/products
   │       ↓
   │   ASP.NET Core Controller
   │
   ├── /main-ABC.js
   │       ↓
   │   Static File
   │
   └── /products
           ↓
       index.html
           ↓
       Angular Router

This separation is important.


9. IIS hosting

If Angular is hosted directly in IIS rather than being served by ASP.NET Core, IIS needs a URL rewrite/fallback rule.

A common web.config pattern is:

<?xml version="1.0" encoding="utf-8"?>

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

The concept is:

Request /products
       ↓
Is it a physical file?
       ↓ No
Is it a physical directory?
       ↓ No
Rewrite to Angular entry page
       ↓
Angular Router
       ↓
/products

This typically requires the IIS URL Rewrite functionality to be available.


10. Why check IsFile?

Suppose Angular requests:

/main-ABC123.js

That is a real file.

You don't want:

/main-ABC123.js
       ↓
index.html

You want:

/main-ABC123.js
       ↓
main-ABC123.js

Therefore:

<add input="{REQUEST_FILENAME}"
     matchType="IsFile"
     negate="true" />

essentially means:

Apply the SPA rewrite only when the requested path is not an existing file.


11. Why check IsDirectory?

Similarly, existing directories shouldn't unnecessarily be rewritten.

<add input="{REQUEST_FILENAME}"
     matchType="IsDirectory"
     negate="true" />

means:

Apply the fallback when the requested path isn't an existing directory.


12. Nginx hosting

For Nginx, a common SPA configuration uses try_files.

Conceptually:

server {

    listen 80;

    server_name www.example.com;

    root /var/www/angular/browser;

    index index.html;

    location / {

        try_files $uri $uri/ /index.html;

    }

}

The important line is:

try_files $uri $uri/ /index.html;

It means:

Request /products
       ↓
Does /products exist?
       ↓ No
Return /index.html
       ↓
Angular Router

But:

Request /main-ABC.js
       ↓
Does file exist?
       ↓ Yes
Return main-ABC.js

13. Apache hosting

For Apache, SPA fallback is commonly implemented using rewrite rules in .htaccess or the virtual-host configuration.

Conceptually:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^ index.html [L]

Meaning:

Not physical file
AND
Not physical directory
       ↓
index.html

14. Angular route vs server route

This distinction is extremely important.

Suppose:

/products

This may be an Angular route.

/api/products

This may be an ASP.NET Core API route.

Therefore:

https://www.example.com/products
                         ↑
                    Angular Router


https://www.example.com/api/products
                         ↑
                  ASP.NET Core

The production server needs to distinguish these correctly.


15. Angular wildcard route vs server fallback

These are related but not the same thing.

Angular:

{
  path: '**',
  loadComponent: () =>
    import('./not-found').then(m => m.NotFound)
}

handles unknown routes after Angular has loaded.

For example:

/abcdef
   ↓
Web Server
   ↓
index.html
   ↓
Angular
   ↓
**
   ↓
NotFound component

But the server must first return index.html.

Therefore:

Server SPA fallback
        ↓
Gets request into Angular

Angular wildcard **
        ↓
Handles unknown Angular route

You may need both.


16. What is <base href="/">?

Angular's index.html normally contains:

<base href="/">

This tells Angular/browser URL resolution that the application is hosted from the root path.

For example:

https://www.example.com/

Routes:

/products
/articles
/contact

work relative to:

/

17. What if Angular is hosted in a subfolder?

Suppose your application is deployed at:

https://www.example.com/portfolio/

rather than:

https://www.example.com/

Then the application's base path needs to correspond to that deployment.

For example:

<base href="/portfolio/">

You can specify a base href when building:

ng build --configuration production --base-href /portfolio/

Then routes can look like:

https://www.example.com/portfolio/
https://www.example.com/portfolio/products
https://www.example.com/portfolio/articles

The server fallback also needs to point to the correct application location.


18. Hash-based routing alternative

Angular can also use hash-based routing.

For example:

https://www.example.com/#/products

Instead of:

https://www.example.com/products

With standalone router configuration, this can be enabled using:

import {
  provideRouter,
  withHashLocation
} from '@angular/router';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(
      routes,
      withHashLocation()
    )
  ]
};

Now:

https://www.example.com/#/products

The part after # isn't sent to the server as the request path in the same way.

Therefore, server-side SPA fallback configuration is less of an issue.

However, normal path-based URLs are commonly preferred:

/products

rather than:

/#/products

when the web server can be configured properly.


19. Development vs Production

During development:

ng serve

Angular's development server handles SPA routing for you.

So:

http://localhost:4200/products

generally works.

That can create confusion:

Development
/products
    ↓
Works


Production
/products
    ↓
Refresh
    ↓
404

The Angular route itself isn't necessarily wrong.

The missing piece may be the production server's SPA fallback configuration.


20. Full Production Flow

Suppose the user enters:

https://www.example.com/articles/10

The complete flow is:

Browser
   ↓
GET /articles/10
   ↓
Production Web Server
   ↓
Does physical file exist?
   ↓
No
   ↓
SPA fallback
   ↓
index.html
   ↓
Angular bootstraps
   ↓
Angular Router reads
/articles/10
   ↓
Article Component
   ↓
Angular calls
/api/articles/10
   ↓
ASP.NET Core Web API
   ↓
Database
   ↓
JSON
   ↓
Angular displays article

Notice the two different routes:

/articles/10
      ↑
Angular route


/api/articles/10
      ↑
ASP.NET Core route

Production Server Configuration Summary

Hosting Typical SPA fallback
ASP.NET Core MapFallbackToFile("index.html")
IIS URL Rewrite rule
Nginx try_files ... /index.html
Apache Rewrite to index.html
Static/cloud hosting Configure platform-specific SPA fallback/rewrite

Regardless of server technology, the principle is the same:

Unknown client route
       ↓
index.html
       ↓
Angular Router

Key Points

  1. Angular Router is primarily client-side routing.
  2. Directly opening or refreshing /products sends /products to the production server.
  3. The production server must provide an SPA fallback.
  4. The fallback usually returns:
index.html
  1. With ASP.NET Core:
app.MapFallbackToFile("index.html");
  1. With Nginx, the common concept is:
try_files $uri $uri/ /index.html;
  1. With IIS, configure an appropriate URL Rewrite rule.
  2. Existing static files must not be rewritten to index.html.
  3. Backend routes such as /api/products should continue to reach ASP.NET Core.
  4. Angular's ** wildcard route does not replace the server-side SPA fallback.
  5. <base href="/"> is important for root deployment.
  6. Subfolder deployments require the correct base path and corresponding server configuration.

Interview Questions and Answers

1. How do you configure Angular routing on a production server?

Configure the web server with an SPA fallback so that routes that aren't physical files or backend endpoints return Angular's index.html. Angular Router then handles the client-side URL.


2. Why does an Angular route work when clicking a link but fail after refresh?

Client-side navigation is handled by Angular Router. A refresh sends the URL directly to the web server, which may return 404 unless an SPA fallback is configured.


3. How do you handle Angular routing when ASP.NET Core hosts Angular?

Use static-file handling and an SPA fallback such as:

app.UseStaticFiles();

app.MapControllers();

app.MapFallbackToFile("index.html");

4. What does MapFallbackToFile("index.html") do?

It allows unmatched client-side application routes to return index.html, after which Angular Router processes the requested route.


5. What is the difference between Angular wildcard routing and server fallback?

Angular:

{ path: '**', component: NotFound }

handles unknown routes after Angular starts.

The server fallback gets the browser request to Angular in the first place:

Server → index.html → Angular → **

6. What does <base href="/"> mean?

It specifies the base URL from which Angular resolves routes and resources when the application is hosted at the domain root.


7. What if Angular is hosted under /portfolio/?

Configure the application for that base path, for example:

ng build --configuration production --base-href /portfolio/

and ensure the server's SPA fallback also handles routes beneath /portfolio/.


8. Why does Angular routing work with ng serve but not necessarily after deployment?

The Angular development server already handles SPA fallback behavior. The production web server must be configured separately.


9. What is hash routing?

Hash routing uses URLs such as:

/#/products

The route after # is handled client-side, reducing the need for server rewrite handling, although normal path-based routing is commonly preferred when server fallback is available.


10. Short interview answer

Angular uses client-side routing, so in production I configure the web server with an SPA fallback. If a request such as /products doesn't match a physical file or backend endpoint, the server returns index.html, and Angular Router handles /products. With ASP.NET Core I can use MapFallbackToFile("index.html"); IIS, Nginx, Apache, and cloud static hosts use equivalent rewrite/fallback rules.

← Back to Articles