diff --git a/app/assets/javascripts/discourse.js.es6 b/app/assets/javascripts/discourse.js.es6
index 41eba5d39b9..6e24d1e3d1d 100644
--- a/app/assets/javascripts/discourse.js.es6
+++ b/app/assets/javascripts/discourse.js.es6
@@ -41,7 +41,7 @@ const Discourse = Ember.Application.extend({
 
   Resolver: buildResolver("discourse"),
 
-  @observes("_docTitle", "hasFocus", "notifyCount")
+  @observes("_docTitle", "hasFocus", "contextCount", "notificationCount")
   _titleChanged() {
     let title = this.get("_docTitle") || Discourse.SiteSettings.title;
 
@@ -51,15 +51,18 @@ const Discourse = Ember.Application.extend({
       $("title").text(title);
     }
 
-    const notifyCount = this.get("notifyCount");
-    if (notifyCount > 0 && !Discourse.User.currentProp("dynamic_favicon")) {
-      title = `(${notifyCount}) ${title}`;
+    var displayCount = Discourse.User.current()
+      ? this.get("notificationCount")
+      : this.get("contextCount");
+
+    if (displayCount > 0 && !Discourse.User.currentProp("dynamic_favicon")) {
+      title = `(${displayCount}) ${title}`;
     }
 
     document.title = title;
   },
 
-  @observes("notifyCount")
+  @observes("contextCount", "notificationCount")
   faviconChanged() {
     if (Discourse.User.currentProp("dynamic_favicon")) {
       let url = Discourse.SiteSettings.site_favicon_url;
@@ -71,7 +74,11 @@ const Discourse = Ember.Application.extend({
         url = Discourse.getURL("/favicon/proxied?" + encodeURIComponent(url));
       }
 
-      new window.Favcount(url).set(this.get("notifyCount"));
+      var displayCount = Discourse.User.current()
+        ? this.get("notificationCount")
+        : this.get("contextCount");
+
+      new window.Favcount(url).set(displayCount);
     }
   },
 
@@ -83,23 +90,33 @@ const Discourse = Ember.Application.extend({
     });
   },
 
-  notifyTitle(count) {
-    this.set("notifyCount", count);
+  updateContextCount(count) {
+    this.set("contextCount", count);
   },
 
-  notifyBackgroundCountIncrement() {
+  updateNotificationCount(count) {
+    if (!this.get("hasFocus")) {
+      this.set("notificationCount", count);
+    }
+  },
+
+  incrementBackgroundContextCount() {
     if (!this.get("hasFocus")) {
       this.set("backgroundNotify", true);
-      this.set("notifyCount", (this.get("notifyCount") || 0) + 1);
+      this.set("contextCount", (this.get("contextCount") || 0) + 1);
     }
   },
 
   @observes("hasFocus")
-  resetBackgroundNotifyCount() {
+  resetCounts() {
     if (this.get("hasFocus") && this.get("backgroundNotify")) {
-      this.set("notifyCount", 0);
+      this.set("contextCount", 0);
     }
     this.set("backgroundNotify", false);
+
+    if (this.get("hasFocus")) {
+      this.set("notificationCount", 0);
+    }
   },
 
   authenticationComplete(options) {
diff --git a/app/assets/javascripts/discourse/components/discovery-topics-list.js.es6 b/app/assets/javascripts/discourse/components/discovery-topics-list.js.es6
index 465a8d10a08..fdc448f3eef 100644
--- a/app/assets/javascripts/discourse/components/discovery-topics-list.js.es6
+++ b/app/assets/javascripts/discourse/components/discovery-topics-list.js.es6
@@ -24,7 +24,7 @@ const DiscoveryTopicsListComponent = Ember.Component.extend(
 
     @observes("incomingCount")
     _updateTitle() {
-      Discourse.notifyTitle(this.get("incomingCount"));
+      Discourse.updateContextCount(this.get("incomingCount"));
     },
 
     saveScrollPosition() {
@@ -38,7 +38,7 @@ const DiscoveryTopicsListComponent = Ember.Component.extend(
 
     actions: {
       loadMore() {
-        Discourse.notifyTitle(0);
+        Discourse.updateContextCount(0);
         this.get("model")
           .loadMore()
           .then(hasMoreResults => {
diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6
index c8ae6d66fcf..a51c2dda9a6 100644
--- a/app/assets/javascripts/discourse/controllers/topic.js.es6
+++ b/app/assets/javascripts/discourse/controllers/topic.js.es6
@@ -1226,7 +1226,7 @@ export default Ember.Controller.extend(bufferedProperty("model"), {
           case "created": {
             postStream.triggerNewPostInStream(data.id).then(() => refresh());
             if (this.get("currentUser.id") !== data.user_id) {
-              Discourse.notifyBackgroundCountIncrement();
+              Discourse.incrementBackgroundContextCount();
             }
             break;
           }
diff --git a/app/assets/javascripts/discourse/initializers/title-notifications.js.es6 b/app/assets/javascripts/discourse/initializers/title-notifications.js.es6
new file mode 100644
index 00000000000..d84cfc1cc4b
--- /dev/null
+++ b/app/assets/javascripts/discourse/initializers/title-notifications.js.es6
@@ -0,0 +1,18 @@
+export default {
+  name: "title-notifications",
+  after: "message-bus",
+
+  initialize(container) {
+    const appEvents = container.lookup("app-events:main");
+    const user = container.lookup("current-user:main");
+
+    if (!user) return; // must be logged in
+
+    appEvents.on("notifications:changed", () => {
+      let notifications =
+        user.get("unread_notifications") + user.get("unread_private_messages");
+
+      Discourse.updateNotificationCount(notifications);
+    });
+  }
+};
diff --git a/app/assets/javascripts/discourse/lib/clean-dom.js.es6 b/app/assets/javascripts/discourse/lib/clean-dom.js.es6
index c848ace5553..59157189f63 100644
--- a/app/assets/javascripts/discourse/lib/clean-dom.js.es6
+++ b/app/assets/javascripts/discourse/lib/clean-dom.js.es6
@@ -22,7 +22,7 @@ function _clean() {
     .not(".no-blur")
     .blur();
 
-  Discourse.set("notifyCount", 0);
+  Discourse.set("contextCount", 0);
   Discourse.__container__.lookup("route:application").send("closeModal");
   const hideDropDownFunction = $("html").data("hide-dropdown");
   if (hideDropDownFunction) {
diff --git a/test/javascripts/lib/discourse-test.js.es6 b/test/javascripts/lib/discourse-test.js.es6
index 94498a16462..014ec78c717 100644
--- a/test/javascripts/lib/discourse-test.js.es6
+++ b/test/javascripts/lib/discourse-test.js.es6
@@ -1,3 +1,5 @@
+import { logIn, replaceCurrentUser } from "helpers/qunit-helpers";
+
 QUnit.module("lib:discourse");
 
 QUnit.test("getURL on subfolder install", assert => {
@@ -24,3 +26,64 @@ QUnit.test("getURLWithCDN on subfolder install with S3", assert => {
   Discourse.S3CDN = null;
   Discourse.S3BaseUrl = null;
 });
+
+QUnit.test("title counts are updated correctly", assert => {
+  Discourse.set("hasFocus", true);
+  Discourse.set("contextCount", 0);
+  Discourse.set("notificationCount", 0);
+
+  Discourse.set("_docTitle", "Test Title");
+
+  assert.equal(document.title, "Test Title", "title is correct");
+
+  Discourse.updateNotificationCount(5);
+  assert.equal(document.title, "Test Title", "title doesn't change with focus");
+
+  Discourse.incrementBackgroundContextCount();
+  assert.equal(document.title, "Test Title", "title doesn't change with focus");
+
+  Discourse.set("hasFocus", false);
+
+  Discourse.updateNotificationCount(5);
+  assert.equal(
+    document.title,
+    "Test Title",
+    "notification count ignored for anon"
+  );
+
+  Discourse.incrementBackgroundContextCount();
+  assert.equal(
+    document.title,
+    "(1) Test Title",
+    "title changes when incremented for anon"
+  );
+
+  logIn();
+  replaceCurrentUser({ dynamic_favicon: false });
+
+  Discourse.set("hasFocus", true);
+  Discourse.set("hasFocus", false);
+
+  Discourse.incrementBackgroundContextCount();
+  assert.equal(
+    document.title,
+    "Test Title",
+    "title doesn't change when incremented for logged in"
+  );
+
+  Discourse.updateNotificationCount(3);
+  assert.equal(
+    document.title,
+    "(3) Test Title",
+    "title includes notification count for logged in user"
+  );
+
+  Discourse.set("hasFocus", false);
+  Discourse.set("hasFocus", true);
+
+  assert.equal(
+    document.title,
+    "Test Title",
+    "counter dissappears after focus, and doesn't reappear until another notification arrives"
+  );
+});