diff --git a/lib/plausible/auth/auth.ex b/lib/plausible/auth/auth.ex
index ac3c78643b8661a20a046813a7a6183147dade6c..3dd8250485e9a27e7abb1d6115be0889d3e6f833 100644
--- a/lib/plausible/auth/auth.ex
+++ b/lib/plausible/auth/auth.ex
@@ -1,7 +1,6 @@
 defmodule Plausible.Auth do
   use Plausible.Repo
   alias Plausible.Auth
-  alias Plausible.Stats.Clickhouse, as: Stats
 
   def issue_email_verification(user) do
     Repo.update_all(from(c in "email_verification_codes", where: c.user_id == ^user.id),
@@ -68,7 +67,7 @@ defmodule Plausible.Auth do
   end
 
   def has_active_sites?(user, roles \\ [:owner, :admin, :viewer]) do
-    domains =
+    sites =
       Repo.all(
         from u in Plausible.Auth.User,
           where: u.id == ^user.id,
@@ -77,10 +76,10 @@ defmodule Plausible.Auth do
           where: sm.role in ^roles,
           join: s in Plausible.Site,
           on: s.id == sm.site_id,
-          select: s.domain
+          select: s
       )
 
-    Stats.has_pageviews?(domains)
+    Enum.any?(sites, &Plausible.Sites.has_stats?/1)
   end
 
   def user_owns_sites?(user) do
diff --git a/lib/plausible/site/schema.ex b/lib/plausible/site/schema.ex
index ade0ffc2c87e5c6acc7e5b8408937fb78f8030a4..625c5bcd6dfab3fcf66e5dc610a2493b4a265879 100644
--- a/lib/plausible/site/schema.ex
+++ b/lib/plausible/site/schema.ex
@@ -10,6 +10,7 @@ defmodule Plausible.Site do
     field :timezone, :string, default: "Etc/UTC"
     field :public, :boolean
     field :locked, :boolean
+    field :has_stats, :boolean
 
     many_to_many :members, User, join_through: Plausible.Site.Membership
     has_many :memberships, Plausible.Site.Membership
@@ -42,6 +43,10 @@ defmodule Plausible.Site do
     change(site, public: false)
   end
 
+  def set_has_stats(site, has_stats_val) do
+    change(site, has_stats: has_stats_val)
+  end
+
   defp clean_domain(changeset) do
     clean_domain =
       (get_field(changeset, :domain) || "")
diff --git a/lib/plausible/sites.ex b/lib/plausible/sites.ex
index f50f034dcdca475e124643c7153ebc061d2b7e13..e073fc7f1a22f198643f9ce12156728e877a22b4 100644
--- a/lib/plausible/sites.ex
+++ b/lib/plausible/sites.ex
@@ -26,6 +26,23 @@ defmodule Plausible.Sites do
     end
   end
 
+  def has_stats?(site) do
+    if site.has_stats do
+      true
+    else
+      has_stats = Plausible.Stats.Clickhouse.has_pageviews?(site)
+
+      if has_stats do
+        Plausible.Site.set_has_stats(site, true)
+        |> Repo.update()
+
+        true
+      else
+        false
+      end
+    end
+  end
+
   def create_shared_link(site, name, password \\ nil) do
     changes =
       SharedLink.changeset(
diff --git a/lib/plausible/stats/clickhouse.ex b/lib/plausible/stats/clickhouse.ex
index 60607f6a839a9e7570eddbdcb1aef4e01d7cca53..ac5b2a678f7317a9fb24fd91f95e8e14d46851c6 100644
--- a/lib/plausible/stats/clickhouse.ex
+++ b/lib/plausible/stats/clickhouse.ex
@@ -892,7 +892,9 @@ defmodule Plausible.Stats.Clickhouse do
   end
 
   def has_pageviews?(site) do
-    ClickhouseRepo.exists?(from e in "events", where: e.domain == ^site.domain)
+    ClickhouseRepo.exists?(
+      from e in "events", where: e.domain == ^site.domain and e.name == "pageview"
+    )
   end
 
   def all_props(site, %Query{filters: %{"props" => meta}} = query) when is_map(meta) do
diff --git a/lib/plausible_web/controllers/stats_controller.ex b/lib/plausible_web/controllers/stats_controller.ex
index cdd2023f442987d088dc49139d1ddb611ce22d53..00c0e9ae7df4d7e064ccdd9cd9b5a756eaee7c71 100644
--- a/lib/plausible_web/controllers/stats_controller.ex
+++ b/lib/plausible_web/controllers/stats_controller.ex
@@ -1,16 +1,15 @@
 defmodule PlausibleWeb.StatsController do
   use PlausibleWeb, :controller
   use Plausible.Repo
-  alias Plausible.Stats.Clickhouse, as: Stats
   alias Plausible.Stats.Query
 
   plug PlausibleWeb.AuthorizeSiteAccess when action in [:stats, :csv_export]
 
   def stats(%{assigns: %{site: site}} = conn, _params) do
-    has_pageviews = Stats.has_pageviews?(site)
+    has_stats = Plausible.Sites.has_stats?(site)
 
     cond do
-      !site.locked && has_pageviews ->
+      !site.locked && has_stats ->
         demo = site.domain == PlausibleWeb.Endpoint.host()
         offer_email_report = get_session(conn, site.domain <> "_offer_email_report")
 
@@ -26,7 +25,7 @@ defmodule PlausibleWeb.StatsController do
           demo: demo
         )
 
-      !site.locked && !has_pageviews ->
+      !site.locked && !has_stats ->
         conn
         |> assign(:skip_plausible_tracking, true)
         |> render("waiting_first_pageview.html", site: site)
diff --git a/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex b/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex
index b262ca0b7dc33c22f997fc0c66f511bcbfb4dce2..e66ebf684f444d7499d687cbbfbd7fc75ac0f2f2 100644
--- a/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex
+++ b/lib/plausible_web/templates/stats/waiting_first_pageview.html.eex
@@ -10,7 +10,7 @@
       })
   }
 
-  setInterval(updateStatus, 1500)
+  setInterval(updateStatus, 5000)
 </script>
 
 <div class="w-full max-w-md mx-auto mt-8">
diff --git a/lib/workers/send_site_setup_emails.ex b/lib/workers/send_site_setup_emails.ex
index 74c95546c0fb23fb80148167d1702a481b2e97a1..d4250aac41cbd461dc7feefefe65e1c4627cfdb4 100644
--- a/lib/workers/send_site_setup_emails.ex
+++ b/lib/workers/send_site_setup_emails.ex
@@ -2,7 +2,6 @@ defmodule Plausible.Workers.SendSiteSetupEmails do
   use Plausible.Repo
   use Oban.Worker, queue: :site_setup_emails
   require Logger
-  alias Plausible.Stats.Clickhouse, as: Stats
 
   @impl Oban.Worker
   def perform(_job) do
@@ -46,7 +45,7 @@ defmodule Plausible.Workers.SendSiteSetupEmails do
         Plausible.Sites.owner_for(site)
         |> Repo.preload(:subscription)
 
-      setup_completed = Stats.has_pageviews?(site)
+      setup_completed = Plausible.Sites.has_stats?(site)
       hours_passed = Timex.diff(Timex.now(), site.inserted_at, :hours)
 
       if !setup_completed && hours_passed > 47 do
@@ -69,7 +68,7 @@ defmodule Plausible.Workers.SendSiteSetupEmails do
         Plausible.Sites.owner_for(site)
         |> Repo.preload(:subscription)
 
-      if Stats.has_pageviews?(site) do
+      if Plausible.Sites.has_stats?(site) do
         send_setup_success_email(owner, site)
       end
     end
diff --git a/priv/repo/migrations/20210906102736_memoize_setup_complete.exs b/priv/repo/migrations/20210906102736_memoize_setup_complete.exs
new file mode 100644
index 0000000000000000000000000000000000000000..a1b5ebc0ee601accdd02d3260433e22c97753041
--- /dev/null
+++ b/priv/repo/migrations/20210906102736_memoize_setup_complete.exs
@@ -0,0 +1,9 @@
+defmodule Plausible.Repo.Migrations.MemoizeSetupComplete do
+  use Ecto.Migration
+
+  def change do
+    alter table(:sites) do
+      add :has_stats, :boolean, null: false, default: false
+    end
+  end
+end
diff --git a/test/plausible/sites_test.exs b/test/plausible/sites_test.exs
index b4f4be59df88bb86a829f8cabe431dcf717272d8..077c694d4719797dec89b8e6f19105ad2debd6e8 100644
--- a/test/plausible/sites_test.exs
+++ b/test/plausible/sites_test.exs
@@ -1,5 +1,6 @@
 defmodule Plausible.SitesTest do
   use Plausible.DataCase
+  import Plausible.TestUtils
   alias Plausible.Sites
 
   describe "is_member?" do
@@ -17,4 +18,35 @@ defmodule Plausible.SitesTest do
       refute Sites.is_member?(user.id, site)
     end
   end
+
+  describe "has_stats?" do
+    test "is false if site has no stats" do
+      site = insert(:site)
+
+      refute Sites.has_stats?(site)
+    end
+
+    test "is true if site has stats" do
+      site = insert(:site)
+
+      populate_stats(site, [
+        build(:pageview)
+      ])
+
+      assert Sites.has_stats?(site)
+    end
+
+    test "memoizes has_stats value" do
+      site = insert(:site)
+
+      populate_stats(site, [
+        build(:pageview)
+      ])
+
+      refute site.has_stats
+
+      assert Sites.has_stats?(site)
+      assert Repo.reload!(site).has_stats
+    end
+  end
 end