diff --git a/horizon/karma.conf.js b/horizon/karma.conf.js
index 495495dc23..f05dc11f97 100644
--- a/horizon/karma.conf.js
+++ b/horizon/karma.conf.js
@@ -62,6 +62,7 @@ module.exports = function (config) {
       // from jasmine.html
       xstaticPath + 'jquery/data/jquery.js',
       xstaticPath + 'angular/data/angular.js',
+      xstaticPath + 'angular/data/angular-route.js',
       xstaticPath + 'angular/data/angular-mocks.js',
       xstaticPath + 'angular/data/angular-cookies.js',
       xstaticPath + 'angular_bootstrap/data/angular-bootstrap.js',
diff --git a/horizon/static/framework/framework.module.js b/horizon/static/framework/framework.module.js
index fb3fb86ba9..755dde3b84 100644
--- a/horizon/static/framework/framework.module.js
+++ b/horizon/static/framework/framework.module.js
@@ -17,13 +17,15 @@
 
   angular
     .module('horizon.framework', [
+      'ngRoute',
       'horizon.framework.conf',
       'horizon.framework.util',
       'horizon.framework.widgets'
     ])
     .config(config)
     .run(run)
-    .factory('horizon.framework.redirect', httpRedirectLogin)
+    .factory('horizon.framework.redirect', redirect)
+    .config(registerNotFound)
     .constant('horizon.framework.events', {
       FORCE_LOGOUT: 'FORCE_LOGOUT'
     });
@@ -74,7 +76,7 @@
     // Global http error handler
     // if user is not authorized, log user out
     // this can happen when session expires
-    $httpProvider.interceptors.push(httpRedirectLogin);
+    $httpProvider.interceptors.push(redirect);
     $httpProvider.interceptors.push(stripAjaxHeaderForCORS);
 
     stripAjaxHeaderForCORS.$inject = [];
@@ -115,7 +117,7 @@
     }
   }
 
-  httpRedirectLogin.$inject = [
+  redirect.$inject = [
     '$q',
     '$rootScope',
     '$window',
@@ -123,7 +125,7 @@
     'horizon.framework.widgets.toast.service'
   ];
 
-  function httpRedirectLogin($q, $rootScope, $window, frameworkEvents, toastService) {
+  function redirect($q, $rootScope, $window, frameworkEvents, toastService) {
     return {
       responseError: function (error) {
         if (error.status === 401) {
@@ -135,6 +137,9 @@
           handleRedirectMessage(msg2, $rootScope, $window, frameworkEvents, toastService);
         }
         return $q.reject(error);
+      },
+      notFound: function() {
+        $window.location.href = $window.WEBROOT + 'not_found';
       }
     };
   }
@@ -149,4 +154,21 @@
     $window.location.replace($window.WEBROOT + 'auth/logout');
   }
 
+  registerNotFound.$inject = [
+    '$routeProvider'
+  ];
+
+  /**
+   * @name registerNotFound
+   * @param {Object} $routeProvider
+   * @description Routes to "not_found".
+   * @returns {undefined} Returns nothing
+   */
+  function registerNotFound($routeProvider) {
+    // if identifier not specified for "ngdetails"
+    $routeProvider.when('/ngdetails/:resourceType', {
+      redirectTo: "/not_found"
+    });
+  }
+
 })();
diff --git a/horizon/static/framework/widgets/details/routed-details-view.controller.js b/horizon/static/framework/widgets/details/routed-details-view.controller.js
index edd2fea4b9..7cedd80e20 100644
--- a/horizon/static/framework/widgets/details/routed-details-view.controller.js
+++ b/horizon/static/framework/widgets/details/routed-details-view.controller.js
@@ -22,6 +22,7 @@
 
   controller.$inject = [
     'horizon.framework.conf.resource-type-registry.service',
+    'horizon.framework.redirect',
     'horizon.framework.util.actions.action-result.service',
     'horizon.framework.util.navigations.service',
     'horizon.framework.widgets.modal-wait-spinner.service',
@@ -32,6 +33,7 @@
 
   function controller(
     registry,
+    redirect,
     resultService,
     navigationsService,
     spinnerService,
@@ -41,13 +43,17 @@
   ) {
     var ctrl = this;
 
+    if (!registry.resourceTypes[$routeParams.type]) {
+      redirect.notFound();
+    }
     ctrl.resourceType = registry.getResourceType($routeParams.type);
     ctrl.context = {};
     ctrl.context.identifier = ctrl.resourceType.parsePath($routeParams.path);
     ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier);
-    ctrl.context.loadPromise.then(loadData);
+    ctrl.context.loadPromise.then(loadData, loadDataError);
     ctrl.defaultTemplateUrl = registry.getDefaultDetailsTemplateUrl();
     ctrl.resultHandler = actionResultHandler;
+    ctrl.pageNotFound = redirect.notFound;
 
     checkRoutedByDjango(ctrl.resourceType);
 
@@ -89,6 +95,12 @@
       ctrl.itemName = ctrl.resourceType.itemName(response.data);
     }
 
+    function loadDataError(error) {
+      if (error.status === 404) {
+        redirect.notFound();
+      }
+    }
+
     function loadIndexView() {
       spinnerService.hideModalSpinner();
       ctrl.showDetails = false;
diff --git a/horizon/static/framework/widgets/details/routed-details-view.controller.spec.js b/horizon/static/framework/widgets/details/routed-details-view.controller.spec.js
index 6e9e99dcce..a765af3311 100644
--- a/horizon/static/framework/widgets/details/routed-details-view.controller.spec.js
+++ b/horizon/static/framework/widgets/details/routed-details-view.controller.spec.js
@@ -18,7 +18,7 @@
   'use strict';
 
   describe('RoutedDetailsViewController', function() {
-    var ctrl, deferred, $timeout, $q, actionResultService, navigationsService;
+    var ctrl, deferred, $timeout, $q, service, redirect, actionResultService, navigationsService;
 
     beforeEach(module('horizon.framework.widgets.details'));
     beforeEach(inject(function($injector, $controller, _$q_, _$timeout_) {
@@ -26,7 +26,8 @@
       deferred = $q.defer();
       $timeout = _$timeout_;
 
-      var service = {
+      service = {
+        resourceTypes: {'OS::Glance::Image': {}},
         getResourceType: function() {
           return {
             load: function() { return deferred.promise; },
@@ -39,6 +40,11 @@
         getDefaultDetailsTemplateUrl: angular.noop
       };
 
+      redirect = {
+        responseError: angular.noop,
+        notFound: angular.noop
+      };
+
       actionResultService = {
         getIdsOfType: function() { return []; }
       };
@@ -46,11 +52,14 @@
       navigationsService = {
         expandNavigationByUrl: function() { return ['Project', 'Compute', 'Images']; },
         setBreadcrumb: angular.noop,
-        getActivePanelUrl: function() { return 'project/fancypanel'; }
+        getActivePanelUrl: function() { return 'project/fancypanel'; },
+        nav: true,
+        isNavigationExists: function() { return navigationsService.nav; }
       };
 
       ctrl = $controller("RoutedDetailsViewController", {
         'horizon.framework.conf.resource-type-registry.service': service,
+        'horizon.framework.redirect': redirect,
         'horizon.framework.util.actions.action-result.service': actionResultService,
         'horizon.framework.util.navigations.service': navigationsService,
         'horizon.framework.widgets.modal-wait-spinner.service': {
@@ -62,8 +71,33 @@
           path: '1234'
         }
       });
+      spyOn(redirect, 'notFound');
     }));
 
+    describe('RoutedDetailsViewController', function() {
+      beforeEach(inject(function($controller) {
+        service.resourceTypes = {};
+        ctrl = $controller("RoutedDetailsViewController", {
+          'horizon.framework.conf.resource-type-registry.service': service,
+          'horizon.framework.redirect': redirect,
+          'horizon.framework.util.actions.action-result.service': actionResultService,
+          'horizon.framework.util.navigations.service': navigationsService,
+          'horizon.framework.widgets.modal-wait-spinner.service': {
+            showModalSpinner: angular.noop,
+            hideModalSpinner: angular.noop
+          },
+          '$routeParams': {
+            type: 'not exist',
+            path: 'xxxx'
+          }
+        });
+      }));
+
+      it('call redirect.notFound when resource type is not registered', function() {
+        expect(redirect.notFound).toHaveBeenCalled();
+      });
+    });
+
     it('sets resourceType', function() {
       expect(ctrl.resourceType).toBeDefined();
     });
@@ -79,6 +113,18 @@
       expect(ctrl.itemData).toEqual({some: 'data'});
     });
 
+    it('call redirect.notFound when item not found', function() {
+      deferred.reject({status: 404});
+      $timeout.flush();
+      expect(redirect.notFound).toHaveBeenCalled();
+    });
+
+    it('does not call redirect.notFound when server error occurred', function() {
+      deferred.reject({status: 500});
+      $timeout.flush();
+      expect(redirect.notFound).not.toHaveBeenCalled();
+    });
+
     it('sets itemName when item loads', function() {
       deferred.resolve({data: {some: 'data'}});
       expect(ctrl.itemData).toBeUndefined();