Removing the Angular # From App URLs: An .htaccess Fix

September, 2015

Tools: MAMP, angular.js, .htaccess

Intro

Man have I been struggling with a routing issue in Angular.

As you may know if you've played around with Angular before, apps are routed from view to view using the hash symbol (#). This means that instead of serving a view at:


        http://localhost/your-app-name/view
    

by default Angular actually serves it at:


        http://localhost/your-app-name/#/view
    

See that "#"? That's annoying. That needs to go.

What Everyone Will Tell You

So a quick Google search turns up two items:

  1. Set $locationProvider html5 mode to true in your route config section
    $locationProvider.html5Mode(true);
  2. Define a base url tag in the head of your main page
    <base href="/base/" />

Great advice. All's well and dandy except for when you refresh your app and you get a Page Not Found error.

Can't have that.

The .htaccess Solution

After approximately 9 gray hairs' worth of search and experimenting, here's what ultimately removed the # from my URLs while still allowing routing and page refreshing.

Create a .htaccess in root directory. Note that I am working in localhost with MAMP, so I've my project lives in a sub-folder.


            RewriteEngine on
            RewriteCond %{REQUEST_FILENAME} -s [OR]
            RewriteCond %{REQUEST_FILENAME} -l [OR]
            RewriteCond %{REQUEST_FILENAME} -d
            RewriteRule ^.*$ - [NC,L]

            RewriteRule ^(.*) /sub-folder/index.html [NC,L]
    

When I move this project to a live server in the root of a domain, my .htaccess will become:


            RewriteEngine on
            RewriteCond %{REQUEST_FILENAME} -s [OR]
            RewriteCond %{REQUEST_FILENAME} -l [OR]
            RewriteCond %{REQUEST_FILENAME} -d
            RewriteRule ^.*$ - [NC,L]

            RewriteRule ^(.*) /index.html [NC,L]
    

Ok, so the purpose of this file is to intercept page refreshes and force them to go through the main index.html file. Page refresh problem solved.

Meanwhile, the route config section of your app should look like this, setting $locationProvider.html5Mode(true). I've included two fake routes, help and about, as an illustration.


    myApp.config(['$routeProvider', '$locationProvider', 
      function($routeProvider, $locationProvider) { 
      $routeProvider.
        when('/help', {
          templateUrl: 'app/templates/help.html',
          controller: 'HelpController',
          controllerAs: 'help'
        }).
        when('/about', {
          templateUrl: 'app/templates/about.html',
        }).
        otherwise({
          redirectTo: '/'
        });

        $locationProvider.html5Mode(true);
    }]);
    

Last, I have set a base url in the first line of my html head tag in index.html:


        <base href="/sub-folder/" />
    

Again, because I'm working in sub-folder in localhost, this base URL will have to change when I move the project to a live server. It will become:


        <base href="/" />
    

Is there a better way of doing this? Highly likely. If so, gimme a shout: amy at amypeniston dot com.