diff --git a/app/assets/javascripts/discourse/app/controllers/tags-intersection.js b/app/assets/javascripts/discourse/app/controllers/tags-intersection.js
index 9f076ccb317..24764ec9982 100644
--- a/app/assets/javascripts/discourse/app/controllers/tags-intersection.js
+++ b/app/assets/javascripts/discourse/app/controllers/tags-intersection.js
@@ -2,12 +2,5 @@ import { queryParams } from "discourse/controllers/discovery-sortable";
 import TagShowController from "discourse/controllers/tag-show";
 
 export default class TagsIntersectionController extends TagShowController {
-  constructor() {
-    super(...arguments);
-
-    this.set("queryParams", [
-      ...Object.keys(queryParams),
-      { categoryParam: "category" },
-    ]);
-  }
+  queryParams = [...Object.keys(queryParams), { categoryParam: "category" }];
 }
diff --git a/app/assets/javascripts/discourse/app/routes/tags-intersection.js b/app/assets/javascripts/discourse/app/routes/tags-intersection.js
index fdaeb9897ae..8c9705a439f 100644
--- a/app/assets/javascripts/discourse/app/routes/tags-intersection.js
+++ b/app/assets/javascripts/discourse/app/routes/tags-intersection.js
@@ -1,4 +1,3 @@
-import { queryParams } from "discourse/controllers/discovery-sortable";
 import { buildTagRoute } from "discourse/routes/tag-show";
 
 // The tags-intersection route is exactly the same as the tags-show route, but the wildcard at the
@@ -6,15 +5,6 @@ import { buildTagRoute } from "discourse/routes/tag-show";
 // breaking all other tags-show routes. Ember thinks the query params are addition tags and should
 // be handled by the intersection logic. Defining tags-intersection as something separate avoids
 // that confusion.
-export default buildTagRoute().extend({
-  controllerName: "tags.intersection",
-
-  init() {
-    this._super(...arguments);
-
-    // The only difference is support for `category` query param.
-    // Other routes include category in the route path.
-    this.set("queryParams", { ...queryParams });
-    this.queryParams["categoryParam"] = { replace: true, refreshModel: true };
-  },
-});
+export default class extends buildTagRoute() {
+  controllerName = "tags.intersection";
+}
diff --git a/spec/system/tags_intersection_spec.rb b/spec/system/tags_intersection_spec.rb
new file mode 100644
index 00000000000..c62c51a1052
--- /dev/null
+++ b/spec/system/tags_intersection_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+describe "Tags intersection", type: :system do
+  let(:discovery) { PageObjects::Pages::Discovery.new }
+
+  fab!(:category) { Fabricate(:category, name: "fruits") }
+  fab!(:some_topic) { Fabricate(:topic, category: category) }
+
+  fab!(:tag) { Fabricate(:tag, name: "sour") }
+  fab!(:tag2) { Fabricate(:tag, name: "tangy") }
+  fab!(:some_topic) { Fabricate(:topic, tags: [tag, tag2]) }
+
+  fab!(:the_topic) { Fabricate(:topic, category: category, tags: [tag, tag2]) }
+
+  it "filters by category" do
+    visit("/tags/intersection/sour/tangy?category=fruits")
+
+    expect(page).to have_current_path("/tags/intersection/sour/tangy?category=fruits")
+    expect(discovery.topic_list).to have_topic(the_topic)
+    expect(discovery.topic_list).to have_topics(count: 1)
+
+    visit("/")
+
+    # Confirm that frontend transitions work as well,
+    # even though UI doesn't support that
+    page.execute_script <<~JS
+      require("discourse/lib/url").default.routeTo("/tags/intersection/sour/tangy?category=fruits")
+    JS
+
+    expect(page).to have_current_path("/tags/intersection/sour/tangy?category=fruits")
+    expect(discovery.topic_list).to have_topic(the_topic)
+    expect(discovery.topic_list).to have_topics(count: 1)
+  end
+end