By Dieter De Mesmaeker


2013-05-15 16:00:22 8 Comments

I want to enable HTML5 mode for my app. I have put the following code for the configuration, as shown here:

return app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {

    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix = '!';

    $routeProvider.when('/', {
        templateUrl: '/views/index.html',
        controller: 'indexCtrl'
    });
    $routeProvider.when('/about',{
        templateUrl: '/views/about.html',
        controller: 'AboutCtrl'
    });

As you can see, I used the $locationProvider.html5mode and I changed all my links at the ng-href to exclude the /#/.

The Problem

At the moment, I can go to localhost:9000/ and see the index page and navigate to the other pages like localhost:9000/about.

However, the problem occurs when I refresh the localhost:9000/about page. I get the following output: Cannot GET /about

If I look at the network calls:

Request URL:localhost:9000/about
Request Method:GET

While if I first go to localhost:9000/ and then click on a button that navigates to /about I get:

Request URL:http://localhost:9000/views/about.html

Which renders the page perfectly.

How can I enable angular to get the correct page when I refresh?

24 comments

@user3084147 2017-07-29 17:31:14

Just write out a rule in web.config

<system.webServer>
  <rewrite>
    <rules>
    <rule name="AngularJS Routes" stopProcessing="true">
      <match url=".*" />
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
      </conditions>
      <action type="Rewrite" url="/" />
    </rule>
       </rules>
    </rewrite>
</system.webServer>

In the index add this

<base href="/">

it works for me maybe you've forgotten to set Html5Mode app.config

app.config(function ($stateProvider, $urlRouterProvider, $locationProvider){
    $locationProvider.html5Mode({ enabled: true, requireBase: true });
    $locationProvider.hashPrefix('!');
}

@Ram Gollapalli 2018-02-21 07:48:19

I have resolved the issue by adding below code snippet into node.js file.

app.get("/*", function (request, response) {
    console.log('Unknown API called');
    response.redirect('/#' + request.url);
});

Note : when we refresh the page, it will look for the API instead of Angular page (Because of no # tag in URL.) . Using the above code, I am redirecting to the url with #

@Digitlimit 2017-03-07 15:43:06

I have this simple solution I have been using and its works.

In App/Exceptions/Handler.php

Add this at top:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

Then inside the render method

public function render($request, Exception $exception)
{
    .......

       if ($exception instanceof NotFoundHttpException){

        $segment = $request->segments();

        //eg. http://site.dev/member/profile
        //module => member
        // view => member.index
        //where member.index is the root of your angular app could be anything :)
        if(head($segment) != 'api' && $module = $segment[0]){
            return response(view("$module.index"), 404);
        }

        return response()->fail('not_found', $exception->getCode());

    }
    .......

     return parent::render($request, $exception);
}

@Anil Yadav 2017-02-12 19:18:09

Finally I got a way to to solve this issue by server side as it's more like an issue with AngularJs itself I am using 1.5 Angularjs and I got same issue on reload the page. But after adding below code in my server.js file it is save my day but it's not a proper solution or not a good way .

app.use(function(req, res, next){
  var d = res.status(404);
     if(d){
        res.sendfile('index.html');
     }
});

@senthil raja 2016-08-24 10:56:43

Your server side code is JAVA then Follow this below steps

step 1 : Download urlrewritefilter JAR Click Here and save to build path WEB-INF/lib

step 2 : Enable HTML5 Mode $locationProvider.html5Mode(true);

step 3 : set base URL <base href="/example.com/"/>

step 4 : copy and paste to your WEB.XML

 <filter>
     <filter-name>UrlRewriteFilter</filter-name>
 <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

step 5 : create file in WEN-INF/urlrewrite.xml

 <urlrewrite default-match-type="wildcard">


    <rule>
            <from>/</from>
            <to>/index.html</to>
        </rule>

    <!--Write every state dependent on your project url-->
    <rule>
            <from>/example</from>
            <to>/index.html</to>
        </rule>
    </urlrewrite>

@Krishna Kumar Chourasiya 2016-11-29 11:20:39

How to add rule for dynamic url like - test.com/user/101 test.com/user/102

@MrThunder 2016-08-22 09:47:17

For Grunt and Browsersync use connect-modrewrite here

var modRewrite = require('connect-modrewrite');    


browserSync: {
            dev: {
                bsFiles: {

                    src: [
                        'app/assets/css/*.css',
                        'app/*.js',
                        'app/controllers/*.js',
                        '**/*.php',
                        '*.html',
                        'app/jade/includes/*.jade',
                        'app/views/*.html',
               ],
            },
        options: {
            watchTask: true,
            debugInfo: true,
            logConnections: true,
            server: {
                baseDir :'./',
                middleware: [
                       modRewrite(['!\.html|\.js|\.jpg|\.mp4|\.mp3|\.gif|\.svg\|.css|\.png$ /index.html [L]'])
                ]
            },

            ghostMode: {
                scroll: true,
                links: true,
                forms: true
                    }
                }
            }
        },

@Omar 2017-09-12 19:50:47

there are 15 above this answer that were a waist of time. 1. npm install connect-modrewrite --save 2. require in gruntfile 3. copy the above server obj

@Jason 2016-07-26 01:52:29

IIS URL Rewrite Rule to prevent 404 error after page refresh in html5mode

For angular running under IIS on Windows

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

NodeJS / ExpressJS Routes to prevent 404 error after page refresh in html5mode

For angular running under Node/Express

var express = require('express');
var path = require('path');
var router = express.Router();

// serve angular front end files from root path
router.use('/', express.static('app', { redirect: false }));

// rewrite virtual urls to angular app to enable refreshing of internal pages
router.get('*', function (req, res, next) {
    res.sendFile(path.resolve('app/index.html'));
});

module.exports = router;

More info at: AngularJS - Enable HTML5 Mode Page Refresh Without 404 Errors in NodeJS and IIS

@bitsprint 2016-09-01 13:42:44

Thanks, the IIS rules helped me with an angular2 app deployed in azure which was failing to direct to a child route.

@Florida G. 2016-09-30 01:46:12

I cannot thank you enough for the solution for IIS (localhost in VS 2013). Banged my head long enough in frustration, finally found this answer. Worked like a charm.

@Maksood 2015-09-22 23:04:41

If you are in .NET stack with MVC with AngularJS, this is what you have to do to remove the '#' from url:

  1. Set up your base href in your _Layout page: <head> <base href="/"> </head>

  2. Then, add following in your angular app config : $locationProvider.html5Mode(true)

  3. Above will remove '#' from url but page refresh won't work e.g. if you are in "yoursite.com/about" page refresh will give you a 404. This is because MVC does not know about angular routing and by MVC pattern it will look for a MVC page for 'about' which does not exists in MVC routing path. Workaround for this is to send all MVC page request to a single MVC view and you can do that by adding a route that catches all url


routes.MapRoute(
        name: "App",
        url: "{*url}",
        defaults: new { controller = "Home", action = "Index" }
    );

@James Sharp 2013-05-15 16:37:43

From the angular docs

Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)

The reason for this is that when you first visit the page (/about), e.g. after a refresh, the browser has no way of knowing that this isn't a real URL, so it goes ahead and loads it. However if you have loaded up the root page first, and all the javascript code, then when you navigate to /about Angular can get in there before the browser tries to hit the server and handle it accordingly

@user883807 2013-07-27 20:24:10

When it says "you have to rewrite all your links to the entry point of your application (e.g. index.html)" what does this mean? Does it mean I have to go into my $routeProvider and change the paths of each templateUrl e.g. from templateUrl : '/views/about.html', to templateUrl : 'example.com/views/about.html',?

@marni 2014-02-01 15:27:26

No, your rules in $routeProvider should stay as they are. The answer refers to "server side". It refers to changing the routing on the server that provides your angular html pages. Before each request gets to your angular app, it has to pass first through server-side routing, which should rewrite all requests to point to your angular app entry point (e.g. 'index.html', or '/', depending how your angular routes work).

@Alan Quigley 2014-07-31 13:36:23

@CrazyGeek 2014-10-17 09:23:29

In case if I serve my minified index.html via some CDN than how will this routing mechanism will executes ??

@James Sharp 2014-11-04 12:18:26

You'll need to make sure all requests somehow get to index.html. Maybe the CDN will allow you to configure this or you'll have to get the CDN to pass through all requests to some origin server that does this itself (e.g. point your CDN at simple nginx server that always serves up index.html regardless of the path). However if you're frequently updating index.html then having a CDN in the way may be more trouble than its worth.

@asb14690 2015-01-21 11:16:50

what type of changes i can in server side. Is i change something in .htaccess file

@James Sharp 2015-01-22 10:18:42

@Alcalyn 2015-06-03 19:27:39

Good article for apache, there is an htaccess exemple : ngmilk.rocks/2015/03/09/…

@bjm88 2016-01-08 12:16:09

After fixing server side with node/express route config I'm still seeing a different error trying to refresh at a given url. So base is /report and if I navigate from base to /report/test it works, but refresh, server side does go to to base but I get this angular error Error: $injector:modulerr Module Error. Seems like some timing issue still, like Angular having problems initiating from non base location

@byteC0de 2018-03-30 05:45:01

how can i configure it in localhost

@Ironheartbj18 2016-02-20 02:12:44

I'm answering this question from the larger question:

When I add $locationProvider.html5Mode(true), my site will not allow pasting of urls. How do I configure my server to work when html5Mode is true?

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 are some examples:

For Express Rewrites with AngularJS, you can solve this with the following updates:

app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/app/views/index.html'));
});

and

<!-- FOR ANGULAR ROUTING -->
<base href="/">

and

app.use('/',express.static(__dirname + '/public'));

@Tigin 2017-02-18 12:25:40

Thanks its the solution. For Nodejs add to app.js to app.use('/*', index);

@Oscar Caicedo 2016-01-25 02:38:21

Gulp + browserSync:

Install connect-history-api-fallback via npm, later config your serve gulp task

var historyApiFallback = require('connect-history-api-fallback');

gulp.task('serve', function() {
  browserSync.init({
    proxy: {
            target: 'localhost:' + port,
            middleware: [ historyApiFallback() ]
        }
  });
});

@Igor Zboichik 2015-10-28 14:06:15

I had the same problem with java + angular app generated with JHipster. I solved it with Filter and list of all angular pages in properties:

application.yml:

angular-pages:
  - login
  - settings
...

AngularPageReloadFilter.java

public class AngularPageReloadFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.getRequestDispatcher("index.html").forward(request, response);
    }
}

WebConfigurer.java

private void initAngularNonRootRedirectFilter(ServletContext servletContext,
                                              EnumSet<DispatcherType> disps) {
    log.debug("Registering angular page reload Filter");
    FilterRegistration.Dynamic angularRedirectFilter =
            servletContext.addFilter("angularPageReloadFilter",
                    new AngularPageReloadFilter());
    int index = 0;
    while (env.getProperty("angular-pages[" + index + "]") != null) {
        angularRedirectFilter.addMappingForUrlPatterns(disps, true, "/" + env.getProperty("angular-pages[" + index + "]"));
        index++;
    }
    angularRedirectFilter.setAsyncSupported(true);
}

Hope, it will be helpful for somebody.

@Oscar 2015-06-08 14:17:49

Solution for BrowserSync and Gulp.

From https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643

First install connect-history-api-fallback:

npm --save-dev install connect-history-api-fallback

Then add it to your gulpfile.js:

var historyApiFallback = require('connect-history-api-fallback');

gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "app",
      middleware: [ historyApiFallback() ]
    }
  });
});

@bit-less 2016-01-05 18:15:54

Oh snap! Finally relief from Angular view development! And as a bonus, standard URLs (without a hash-bang) route too! Thank you!

@Whizkid747 2016-03-29 19:49:12

Works like a charm... Thanks a lot!

@Luke Schoen 2016-07-04 02:43:23

If you're using a specific port (i.e. the Ionic blank template runs on port 8100), just add an additional property after server i.e. server: { ... }, port: 8100, then just add the serve task to your default gulp task (i.e. gulp.task('default', ['sass', 'serve']);), and then you can run the app without the Cannot GET ... browser errors when you refresh the page by running gulp . Note that running the server with ionic serve still encounters the errors.

@atjoshi 2017-12-30 16:25:11

This works well when I use development browsersync, in prod I am running the application as express app and redirecting all the route to angular app as app.get('*', function(req, res) { req.session.valid = true; res.redirect('/'); }); The page does not load when give the path directly ?

@crlshn 2015-05-15 12:32:19

I am using apache (xampp) on my dev environment and apache on the production, add:

errorDocument 404 /index.html

to the .htaccess solve for me this issue.

@keiwt 2015-02-21 13:48:19

I solved same problem using modRewrite.  
AngularJS is reload page when after # changes.  
But HTML5 mode remove # and invalid the reload.  
So we should reload manually.
# install connect-modrewrite
    $ sudo npm install connect-modrewrite --save

# gulp/build.js
    'use strict';
    var gulp = require('gulp');
    var paths = gulp.paths;
    var util = require('util');
    var browserSync = require('browser-sync');
    var modRewrite  = require('connect-modrewrite');
    function browserSyncInit(baseDir, files, browser) {
        browser = browser === undefined ? 'default' : browser;
        var routes = null;
        if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) {
            routes = {
                '/bower_components': 'bower_components'
            };
        }

        browserSync.instance = browserSync.init(files, {
            startPath: '/',
            server: {
            baseDir: baseDir,
            middleware: [
                modRewrite([
                    '!\\.\\w+$ /index.html [L]'
                ])
            ],
            routes: routes
            },
            browser: browser
        });
    }

@Silvio Troia 2015-02-19 08:50:18

I solved to

test: {
        options: {
          port: 9000,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ],
         middleware: function (connect) {
                  return [
                      modRewrite(['^[^\\.]*$ /index.html [L]']),
                      connect.static('.tmp'),
                      connect().use(
                          '/bower_components',
                          connect.static('./bower_components')
                      ),
                      connect.static('app')
                  ];
              }
        }
      },

@lokers 2014-09-21 16:26:01

There are few things to set up so your link in the browser will look like http://yourdomain.com/path and these are your angular config + server side

1) AngularJS

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true);

2) server side, just put .htaccess inside your root folder and paste this

RewriteEngine On 
Options FollowSymLinks

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /#/$1 [L]

More interesting stuff to read about html5 mode in angularjs and the configuration required per different environment https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode Also this question might help you $location / switching between html5 and hashbang mode / link rewriting

@kTn 2016-07-05 06:14:09

Hi. I tried the above solution , made changes in my config file. But still it doesnt work. Refreshing the URL still gives the "404 NOT FOUND" error.

@Gary 2016-08-25 09:51:50

How do I redirect/rewrite in app deployed in Websphere?

@Joseph 2017-05-19 17:17:07

Will this also work if the page is https?

@lokers 2019-03-27 13:08:07

I don't see why not !

@Brett 2014-07-02 01:55:38

As others have mentioned, you need to rewrite routes on the server and set <base href="/"/>.

For gulp-connect:

npm install connect-pushstate

var gulp = require('gulp'),
  connect = require('gulp-connect'),
  pushState = require('connect-pushstate/lib/pushstate').pushState;
...
connect.server({
  ...
  middleware: function (connect, options) {
    return [
      pushState()
    ];
  }
  ...
})
....

@grant 2014-03-12 20:33:26

You need to configure your server to rewrite everything to index.html to load the app:

https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#wiki-how-to-configure-your-server-to-work-with-html5mode

@CJ Thompson 2014-10-02 21:07:39

Perfect, problem for me was using Laravel Forge to provision the server, which had the relevant nginx line as try_files $uri $uri/ /index.php?... instead of the needed index.html. Thanks for the link!

@Joe Lloyd 2015-09-19 20:19:49

Should be top answer, Gives every situation no matter your server. Worked perfectly with NodeJS

@Cody 2014-01-29 22:00:22

We had a server redirect in Express:

app.get('*', function(req, res){
    res.render('index');
});

and we were still getting page-refresh issues, even after we added the <base href="/" />.

Solution: make sure you're using real links in you page to navigate; don't type in the route in the URL or you'll get a page-refresh. (silly mistake, I know)

:-P

@vijay 2017-02-28 13:34:49

but due to this EVERY http request will have index file,Like eg : /getJsonFromServer - an http request that returns data but due to this configuration it will also return index page

@MichaƂ Lach 2014-01-14 10:27:55

I have found even better Grunt plugin, that works if you have your index.html and Gruntfile.js in the same directory;

https://npmjs.org/package/grunt-connect-pushstate

After that in your Gruntfile:

 var pushState = require('grunt-connect-pushstate/lib/utils').pushState;


    connect: {
    server: {
      options: {
        port: 1337,
        base: '',
        logger: 'dev',
        hostname: '*',
        open: true,
        middleware: function (connect, options) {
          return [
            // Rewrite requests to root so they may be handled by router
            pushState(),
            // Serve static files
            connect.static(options.base)
          ];
        }
      },
    }
},

@Evan Plaice 2015-03-02 01:05:41

The grunt-connect-pushstate module is deprecated

@Taye 2013-08-13 16:35:31

I had a similar problem and I solved it by:

  • Using <base href="/index.html"> in the index page

  • Using a catch all route middleware in my node/Express server as follows (put it after the router):

app.use(function(req, res) {
    res.sendfile(__dirname + '/Public/index.html');
});

I think that should get you up and running.

If you use an apache server, you might want to mod_rewrite your links. It is not difficult to do. Just a few changes in the config files.

All that is assuming you have html5mode enabled on angularjs. Now. note that in angular 1.2, declaring a base url is not recommended anymore actually.

@Gilad Peleg 2013-11-11 19:13:14

This solution worked for me with setting: <base href="/"> and adding the Express routing you suggested. Mucho thanks.

@Cody 2014-01-29 21:40:33

Tahir Chad, did you still need to use a serverside url-rewrite after implementing the <base href="/index.html">?

@Taye 2014-02-08 00:13:50

Yes, you can see url rewriting as a way to set the base url of your application/website in stone (domain included). The 'base url' html statement is just a way to ensure that all relative links are correctly derived from the same base. Base url is not absolutely necessary if your links are absolute.

@Sam 2015-03-30 14:21:36

Where should I make the express change in cordova app?

@Noah 2015-05-30 08:18:16

When I use this, I just see the html code itself on the page, not rendered as an actual page.

@Ahmed Haque 2015-11-11 18:17:02

This answer deserves triple stars! Thanks for providing such a straightforward answer. Everyone else just repeats the same line about "needing to do server redirecting".

@Stanislasdrg 2016-10-24 10:57:30

Nice answer, it appears to be the only one suitable for an Angular2 Chrome extension, using a wildcard to catch missing routes.

@vijay 2017-02-28 13:36:28

but due to this EVERY http request will have index file,Like eg : /getJsonFromServer - an http request that returns json but due to this configuration it will also return index page

@ta4ka 2017-03-15 15:29:25

I suggest you put <base href="/index.html" /> right after <head> not on end of </head>. Because I had css that needed to be served from node.js and it didn't work when it was before the base tag

@Murat Çorlu 2013-06-18 09:08:46

I wrote a simple connect middleware for simulating url-rewriting on grunt projects. https://gist.github.com/muratcorlu/5803655

You can use like that:

module.exports = function(grunt) {
  var urlRewrite = require('grunt-connect-rewrite');

  // Project configuration.
  grunt.initConfig({
    connect: {
      server: {
        options: {
          port: 9001,
          base: 'build',
          middleware: function(connect, options) {
            // Return array of whatever middlewares you want
            return [
              // redirect all urls to index.html in build folder
              urlRewrite('build', 'index.html'),

              // Serve static files.
              connect.static(options.base),

              // Make empty directories browsable.
              connect.directory(options.base)
            ];
          }
        }
      }
    }
  })
};

@user883807 2013-07-27 20:26:23

Is there a manual way to accomplish URL re-writing in the $routeProvider?

@user393219 2014-12-12 19:14:38

I get urlRewrite is not defined warning when trying to run it, and I'm having a hard time finding the right connect with rewrite that I can use.

@edonbajrami 2015-07-18 11:57:01

Shows me an error "Object build has no method 'initConfig' Use --force to continue."

@lucuma 2013-05-15 16:35:39

I believe your issue is with regards to the server. The angular documentation with regards to HTML5 mode (at the link in your question) states:

Server side Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html)

I believe you'll need to setup a url rewrite from /about to /.

@Dieter De Mesmaeker 2013-05-15 16:52:39

Thank you for your answer. So when I am on the about page and I refresh, the server should rewrite the url to / ? The problem is that I work with a rails backend that is on a different domain. All communication is by API calls. How would I do those URL rewrites?

@lucuma 2013-05-15 16:54:20

The thing is your app is availabe at your server route of / so when you refresh a url that is /about the browser will request from your server whatever page is at /about (in this case you have no pages there so you need to redirect those back).

@Dieter De Mesmaeker 2013-05-15 17:00:07

Aha ok, thank you for your help!

Related Questions

Sponsored Content

7 Answered Questions

[SOLVED] AngularJS passing data to $http.get request

7 Answered Questions

2 Answered Questions

angularjs view loads only on hard refresh and not on redirect

1 Answered Questions

Page not found while reload page during # removal

1 Answered Questions

[SOLVED] Base tag breaks script links in Angular HTML5 mode

1 Answered Questions

1 Answered Questions

[SOLVED] Codeigniter - AngularJS routing causes 404

3 Answered Questions

[SOLVED] Page doesn't redirect to the specified page on invalid url

1 Answered Questions

1 Answered Questions

[SOLVED] Securing XHR Request to AngularJS templateURLs

Sponsored Content