Reloading the page gives wrong GET request with AngularJS HTML5 mode

AngularjsHtmlAngular Routing

Angularjs Problem Overview


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?

Angularjs Solutions


Solution 1 - Angularjs

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

Solution 2 - Angularjs

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 https://stackoverflow.com/questions/16677528/location-switching-between-html5-and-hashbang-mode-link-rewriting

Solution 3 - Angularjs

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.

Solution 4 - Angularjs

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

Solution 5 - Angularjs

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

Solution 6 - Angularjs

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

Solution 7 - Angularjs

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

Solution 8 - Angularjs

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

Solution 9 - Angularjs

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

Solution 10 - Angularjs

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.

Solution 11 - Angularjs

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

Solution 12 - Angularjs

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

Solution 13 - Angularjs

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'));

Solution 14 - Angularjs

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

Solution 15 - Angularjs

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

Solution 16 - Angularjs

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

Solution 17 - Angularjs

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 #

Solution 18 - Angularjs

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

Solution 19 - Angularjs

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

Solution 20 - Angularjs

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.

Solution 21 - Angularjs

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

Solution 22 - Angularjs

Your server side code is JAVA then Follow this below steps

step 1 : Download urlrewritefilter JAR [Click Here][1] [1]: https://mvnrepository.com/artifact/org.tuckey/urlrewritefilter/4.0.3 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>
      

Solution 23 - Angularjs

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

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
QuestionDieter De MesmaekerView Question on Stackoverflow
Solution 1 - AngularjsJames SharpView Answer on Stackoverflow
Solution 2 - AngularjslokersView Answer on Stackoverflow
Solution 3 - AngularjsTayeView Answer on Stackoverflow
Solution 4 - AngularjsOscarView Answer on Stackoverflow
Solution 5 - AngularjsgrantView Answer on Stackoverflow
Solution 6 - AngularjsMurat ÇorluView Answer on Stackoverflow
Solution 7 - AngularjsMaksoodView Answer on Stackoverflow
Solution 8 - AngularjsJason WatmoreView Answer on Stackoverflow
Solution 9 - AngularjsBrettView Answer on Stackoverflow
Solution 10 - AngularjscrlshnView Answer on Stackoverflow
Solution 11 - AngularjsMrThunderView Answer on Stackoverflow
Solution 12 - AngularjsSilvio TroiaView Answer on Stackoverflow
Solution 13 - AngularjsIronheartbj18View Answer on Stackoverflow
Solution 14 - AngularjslucumaView Answer on Stackoverflow
Solution 15 - AngularjsCodyView Answer on Stackoverflow
Solution 16 - AngularjsAnil YadavView Answer on Stackoverflow
Solution 17 - AngularjsRam GollapalliView Answer on Stackoverflow
Solution 18 - AngularjsMichaƂ LachView Answer on Stackoverflow
Solution 19 - AngularjskeiwtView Answer on Stackoverflow
Solution 20 - AngularjsIgor ZboichikView Answer on Stackoverflow
Solution 21 - AngularjsOscar CaicedoView Answer on Stackoverflow
Solution 22 - Angularjssenthil rajaView Answer on Stackoverflow
Solution 23 - AngularjsEmeka MbahView Answer on Stackoverflow