diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js
index f86413482..0e28271e4 100644
--- a/public/js/icinga/events.js
+++ b/public/js/icinga/events.js
@@ -188,6 +188,7 @@
             var $li;
             var $target;
             var isMenuLink = $a.closest('#menu').length > 0;
+            var formerUrl;
 
             // TODO: Let remote links pass through. Right now they only work
             //       combined with target="_blank" or target="_self"
@@ -201,8 +202,8 @@
             event.stopPropagation();
             event.preventDefault();
 
-            // If link is hash tag...
-            if (href === '#') {
+            // If link has hash tag...
+            if (href.match(/#/)) {
                 // ...it may be a menu section without a dedicated link.
                 // Switch the active menu item:
                 if (isMenuLink) {
@@ -210,14 +211,25 @@
                     $('#menu .active').removeClass('active');
                     $li.addClass('active');
                 }
+                if (href === '#') {
+                    return false;
+                }
 
-                // Stop here for all hash tags
-                // TODO: handle anchors
-                return false;
+                $target = self.getLinkTargetFor($a);
+
+                formerUrl = $target.data('icingaUrl');
+                if (typeof formerUrl !== 'undefined' && formerUrl.split(/#/)[0] === href.split(/#/)[0]) {
+                    icinga.ui.scrollContainerToAnchor($target, href.split(/#/)[1]);
+                    $target.data('icingaUrl', href);
+                    if (formerUrl !== href) {
+                        icinga.history.pushCurrentState();
+                    }
+                    return false;
+                }
+            } else {
+                $target = self.getLinkTargetFor($a);
             }
 
-            $target = self.getLinkTargetFor($a);
-
             // Load link URL
             icinga.loader.loadUrl(href, $target);
 
diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js
index 155ec0032..3423cf038 100644
--- a/public/js/icinga/loader.js
+++ b/public/js/icinga/loader.js
@@ -388,6 +388,9 @@
             */
 
             this.renderContentToContainer($resp, req.$target);
+            if (url.match(/#/)) {
+                this.icinga.ui.scrollContainerToAnchor(req.$target, url.split(/#/)[1]);
+            }
             if (newBody) {
                 this.icinga.ui.fixDebugVisibility().triggerWindowResize();
             }
@@ -515,6 +518,7 @@
                     $container.html(content);
                 }
             }
+
             if (scrollPos !== false) {
                 $container.scrollTop(scrollPos);
             }
diff --git a/public/js/icinga/ui.js b/public/js/icinga/ui.js
index ff43be353..c2801028a 100644
--- a/public/js/icinga/ui.js
+++ b/public/js/icinga/ui.js
@@ -120,6 +120,18 @@
             $col.data('icingaModule', backup['data']['data-icinga-module']);
         },
 
+        scrollContainerToAnchor: function ($container, anchorName) {
+            // TODO: Generic issue -> we probably should escape attribute value selectors!?
+            var $anchor = $("a[name='" + anchorName + "']", $container);
+            if ($anchor.length) {
+                $container.scrollTop(0);
+                $container.scrollTop($anchor.first().position().top);
+                this.icinga.logger.debug('Scrolling ', $container, ' to ', anchorName);
+            } else {
+                this.icinga.logger.info('Anchor "' + anchorName + '" not found in ', $container);
+            }
+        },
+
         triggerWindowResize: function () {
             this.onWindowResize({data: {self: this}});
         },