diff --git a/app/helpers/rss_helper.rb b/app/helpers/rss_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ea5d2932ef42c08a9f7762c70c07e5fbdcf788ca
--- /dev/null
+++ b/app/helpers/rss_helper.rb
@@ -0,0 +1,5 @@
+module RssHelper
+  def rss_url_options
+    { format: :atom, private_token: current_user.try(:private_token) }
+  end
+end
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index 0dbb0ca6958220e3fb126a7458db3a1dcf05b4c6..89d991abe548065c9bc3f9c3d535a65a6a1c8634 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -2,10 +2,9 @@
   = render "events/event_last_push", event: @last_push
 
 .nav-block
-  - if current_user
-    .controls
-      = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
-        %i.fa.fa-rss
+  .controls
+    = link_to dashboard_projects_path(rss_url_options), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
+      %i.fa.fa-rss
   = render 'shared/event_filter'
 
 .content_list
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index aa57df14c239fc621fb027b763058a26f4e31650..190ad4b40a5c698d329b2a357995ca6694894e51 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -1,6 +1,5 @@
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+  = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
 
 - page_title    "Activity"
 - header_title  "Activity", activity_dashboard_path
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 653052f7c546924d1b2a67b1639ec0296ce6a4a2..9a4e423f896c739f82610551950a29c9417b8767 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,17 +1,15 @@
 - page_title "Issues"
 - header_title  "Issues", issues_dashboard_path(assignee_id: current_user.id)
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{current_user.name} issues")
+  = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues")
 
 .top-area
   = render 'shared/issuable/nav', type: :issues
   .nav-controls
-    - if current_user
-      = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
-        = icon('rss')
-        %span.icon-label
-          Subscribe
+    = link_to params.merge(rss_url_options), class: 'btn' do
+      = icon('rss')
+      %span.icon-label
+        Subscribe
     = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
 
 = render 'shared/issuable/filter', type: :issues
diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder
index fb5be63b47236f2171f8915d8c22bd1de6ecf1c9..13f7a8ddcec67d6177ba719329ffb07dece9b881 100644
--- a/app/views/dashboard/projects/index.atom.builder
+++ b/app/views/dashboard/projects/index.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "Activity"
-  xml.link    href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: dashboard_projects_url(rss_url_options), rel: "self", type: "application/atom+xml"
   xml.link    href: dashboard_projects_url, rel: "alternate", type: "text/html"
   xml.id      dashboard_projects_url
   xml.updated @events[0].updated_at.xmlschema if @events[0]
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index b82b933c3ad78ab83336d0f6011a16d0e2d27590..f0adbad8412a567d7db4745babed1bf981959bbb 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -1,6 +1,5 @@
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+  = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
 
 - page_title    "Projects"
 - header_title  "Projects", dashboard_projects_path
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index c442cf056c3e77c59a40d7b3e3dba23c457d4342..d7851c7999033fb263b524879c427743ed7b39df 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -2,10 +2,9 @@
   = render "events/event_last_push", event: @last_push
 
 .nav-block
-  - if current_user
-    .controls
-      = link_to group_path(@group, format: :atom, private_token: current_user.private_token), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
-        %i.fa.fa-rss
+  .controls
+    = link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
+      %i.fa.fa-rss
   = render 'shared/event_filter'
 
 .content_list
diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml
index d7375b2352439bc82d00dae1e156fac6cc996d1e..3969e56f937b18d4ab08af47860754f2b25a299d 100644
--- a/app/views/groups/activity.html.haml
+++ b/app/views/groups/activity.html.haml
@@ -1,6 +1,5 @@
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
+  = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
 
 - page_title "Activity"
 = render 'groups/head'
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 939bddf3fe91d6dc8001f5aebd065d7ecae1e812..f4c17dc2d167074fa6e8fe0fcd0f66d38f893016 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,19 +1,17 @@
 - page_title "Issues"
 = render "head_issues"
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues")
+  = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
 
 - if group_issues(@group).exists?
   .top-area
     = render 'shared/issuable/nav', type: :issues
-    - if current_user
-      .nav-controls
-        = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
-          = icon('rss')
-          %span.icon-label
-            Subscribe
-        = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
+    .nav-controls
+      = link_to params.merge(rss_url_options), class: 'btn' do
+        = icon('rss')
+        %span.icon-label
+          Subscribe
+      = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
 
   = render 'shared/issuable/filter', type: :issues
 
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index b68bf444d27f5e0385dac03ba607d15ff1cd60e2..914091dfd157f0ff57614732fa1b829ed84914ad 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@group.name} activity"
-  xml.link    href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: group_url(@group, rss_url_options), rel: "self", type: "application/atom+xml"
   xml.link    href: group_url(@group), rel: "alternate", type: "text/html"
   xml.id      group_url(@group)
   xml.updated @events[0].updated_at.xmlschema if @events[0]
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 3d7b469660aa741af94da1d58723860ced3a0aa1..8f0f2708194894382603e0c4e23f35c851952965 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,8 +1,7 @@
 - @no_container = true
 
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
+  = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
 
 = render 'groups/head'
 = render 'groups/home_panel'
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 4268337fd6d1be7aff7da87f3e15020af8f3c808..aa0cb3e1a50947a4deffc104054b954d4c4dcb99 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -2,10 +2,9 @@
 
 %div{ class: container_class }
   .nav-block.activity-filter-block
-    - if current_user
-      .controls
-        = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Subscribe", class: 'btn rss-btn has-tooltip' do
-          = icon('rss')
+    .controls
+      = link_to namespace_project_path(@project.namespace, @project, rss_url_options), title: "Subscribe", class: 'btn rss-btn has-tooltip' do
+        = icon('rss')
 
     = render 'shared/event_filter'
 
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 30bb7412073922b35ea8ad2d6420abe8c01de6de..2f0b6e39800524b16a9a097665d409fccda2279a 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@project.name}:#{@ref} commits"
-  xml.link    href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), rel: "self", type: "application/atom+xml"
   xml.link    href: namespace_project_commits_url(@project.namespace, @project, @ref), rel: "alternate", type: "text/html"
   xml.id      namespace_project_commits_url(@project.namespace, @project, @ref)
   xml.updated @commits.first.committed_date.xmlschema if @commits.any?
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 08cb8a0441312dee5a81ba3543e44d0bf5f7f755..38dbf2ac10bfbf8e22a9af4675a94a7bc1a626b3 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -2,8 +2,7 @@
 
 - page_title "Commits", @ref
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
+  = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
 
 = content_for :sub_nav do
   = render "head"
@@ -27,10 +26,9 @@
       .control
         = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'commits-search-form') do
           = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
-      - if current_user && current_user.private_token
-        .control
-          = link_to namespace_project_commits_path(@project.namespace, @project, @ref, { format: :atom, private_token: current_user.private_token }), title: "Commits Feed", class: 'btn' do
-            = icon("rss")
+      .control
+        = link_to namespace_project_commits_path(@project.namespace, @project, @ref, rss_url_options), title: "Commits Feed", class: 'btn' do
+          = icon("rss")
 
   %div{ id: dom_id(@project) }
     %ol#commits-list.list-unstyled.content_list
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 8ea1a3a45e18a2df3ca8239cd86231b5e1238fcd..7b7d7b1e00ee5b983add8c67be0cf9db53047ebc 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -10,17 +10,15 @@
   = page_specific_javascript_bundle_tag('filtered_search')
 
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@project.name} issues")
+  = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@project.name} issues")
 
 - if project_issues(@project).exists?
   %div{ class: (container_class) }
     .top-area
       = render 'shared/issuable/nav', type: :issues
       .nav-controls
-        - if current_user
-          = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
-            = icon('rss')
+        = link_to params.merge(rss_url_options), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
+          = icon('rss')
         - if can? current_user, :create_issue, @project
           = link_to new_namespace_project_issue_path(@project.namespace,
                                                      @project,
diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder
index 11310d5e1e1f51cdc956bf965558eec40921460c..5c7f2e315f040ef4f0fe614ad674a707695a91ce 100644
--- a/app/views/projects/show.atom.builder
+++ b/app/views/projects/show.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@project.name} activity"
-  xml.link    href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: namespace_project_url(@project.namespace, @project, rss_url_options), rel: "self", type: "application/atom+xml"
   xml.link    href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
   xml.id      namespace_project_url(@project.namespace, @project)
   xml.updated @events[0].updated_at.xmlschema if @events[0]
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 80d4081dd7bd06fe25bd104d1fa51e178293d043..e3d8daf50663c3cfb7b2d116f6a2fbe67ede0494 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,8 +1,7 @@
 - @no_container = true
 
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
+  = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, rss_url_options), title: "#{@project.name} activity")
 
 = content_for :flash_message do
   - if current_user && can?(current_user, :download_code, @project)
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 9864be3562a8b1e8ab686a79bd9e109cb0322ded..a2a260392208937a25a8fa62d1a2fc26af4a4941 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -2,8 +2,7 @@
 
 - page_title @path.presence || "Files", @ref
 = content_for :meta_tags do
-  - if current_user
-    = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
+  = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
 = render "projects/commits/head"
 = render 'projects/last_push'
 
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index c130f3d9e17998e8ef225bcd32dc747bc106ae77..af091f9ab884b8031f9f853a4609276df4b9a51a 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -24,13 +24,12 @@
           = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
             title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
             = icon('exclamation-circle')
-      - if current_user
-        = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
-          = icon('rss')
-        - if current_user.admin?
-          = link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
-            data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
-            = icon('users')
+      = link_to user_path(@user, rss_url_options), class: 'btn btn-gray' do
+        = icon('rss')
+      - if current_user && current_user.admin?
+        = link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
+          data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+          = icon('users')
 
     .profile-header
       .avatar-holder
diff --git a/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml b/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6ee8e5724bc9d3d2d4ea5f5b913e47ada4a70545
--- /dev/null
+++ b/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml
@@ -0,0 +1,4 @@
+---
+title: Show public RSS feeds to anonymous users
+merge_request: 9596
+author: Michael Kozono
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c977f2662967bd4c548884091513d1b919850dcd
--- /dev/null
+++ b/spec/features/dashboard/activity_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+RSpec.describe 'Dashboard Activity', feature: true do
+  before do
+    login_as(create :user)
+    visit activity_dashboard_path
+  end
+  
+  it_behaves_like "it has an RSS button with current_user's private token"
+  it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 2db1cf71209ba2e1ef7ba00915ed4e2f1d49914a..f4420814c3a1bc94b951408c13e312f44d40a749 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -45,4 +45,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
     expect(page).to have_content(assigned_issue.title)
     expect(page).to have_content(other_issue.title)
   end
+
+  it_behaves_like "it has an RSS button with current_user's private token"
+  it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
 end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..63eb5c697c2f22aba752f4d9897fe0e0648d2d52
--- /dev/null
+++ b/spec/features/dashboard/projects_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+
+RSpec.describe 'Dashboard Projects', feature: true do
+  before do
+    login_as(create :user)
+    visit dashboard_projects_path
+  end
+  
+  it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+end
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3b481cba424fd68a11e88c535b45943c1d572fe0
--- /dev/null
+++ b/spec/features/groups/activity_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+feature 'Group activity page', feature: true do
+  let(:group) { create(:group) }
+  let(:path) { activity_group_path(group) }
+
+  context 'when signed in' do
+    before do
+      user = create(:group_member, :developer, user: create(:user), group: group ).user
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button with current_user's private token"
+    it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button without a private token"
+    it_behaves_like "an autodiscoverable RSS feed without a private token"
+  end
+end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 476eca17a9d8e36c3ee7bad6679ebf98febc434f..1b3747c390bca48abc335605fe9bc246e23550cc 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -5,4 +5,22 @@ feature 'Group issues page', feature: true do
   let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
 
   include_examples 'project features apply to issuables', Issue
+
+  context 'rss feed' do
+    let(:access_level) { ProjectFeature::ENABLED }
+
+    context 'when signed in' do
+      let(:user) { user_in_group }
+
+      it_behaves_like "it has an RSS button with current_user's private token"
+      it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+    end
+
+    context 'when signed out' do
+      let(:user) { nil }
+
+      it_behaves_like "it has an RSS button without a private token"
+      it_behaves_like "an autodiscoverable RSS feed without a private token"
+    end
+  end
 end
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fb39693e8ca90fe30b6306ae8a5d52635493aa79
--- /dev/null
+++ b/spec/features/groups/show_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+feature 'Group show page', feature: true do
+  let(:group) { create(:group) }
+  let(:path) { group_path(group) }
+
+  context 'when signed in' do
+    before do
+      user = create(:group_member, :developer, user: create(:user), group: group ).user
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "an autodiscoverable RSS feed without a private token"
+  end
+end
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b47c6d431eba642c321325fccb9f5cd01931feb9
--- /dev/null
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+feature 'Project Activity RSS' do
+  let(:project) { create(:empty_project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+  let(:path) { activity_namespace_project_path(project.namespace, project) }
+
+  before do
+    create(:issue, project: project)
+  end
+
+  context 'when signed in' do
+    before do
+      user = create(:user)
+      project.team << [user, :developer]
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button without a private token"
+  end
+end
diff --git a/spec/features/projects/commit/rss_spec.rb b/spec/features/projects/commit/rss_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6e0e1916f87cb3cac6c430e59b14c719ec852b1b
--- /dev/null
+++ b/spec/features/projects/commit/rss_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+feature 'Project Commits RSS' do
+  let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+  let(:path) { namespace_project_commits_path(project.namespace, project, :master) }
+
+  context 'when signed in' do
+    before do
+      user = create(:user)
+      project.team << [user, :developer]
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button with current_user's private token"
+    it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button without a private token"
+    it_behaves_like "an autodiscoverable RSS feed without a private token"
+  end
+end
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..71429f000959af4a948c7c230a77873713e41641
--- /dev/null
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+feature 'Project Issues RSS' do
+  let(:project) { create(:empty_project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+  let(:path) { namespace_project_issues_path(project.namespace, project) }
+
+  before do
+    create(:issue, project: project)
+  end
+
+  context 'when signed in' do
+    before do
+      user = create(:user)
+      project.team << [user, :developer]
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button with current_user's private token"
+    it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button without a private token"
+    it_behaves_like "an autodiscoverable RSS feed without a private token"
+  end
+end
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b1a3af612a17ad88c55b9f6ce78c882a134025ea
--- /dev/null
+++ b/spec/features/projects/main/rss_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Project RSS' do
+  let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+  let(:path) { namespace_project_path(project.namespace, project) }
+
+  context 'when signed in' do
+    before do
+      user = create(:user)
+      project.team << [user, :developer]
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "an autodiscoverable RSS feed without a private token"
+  end
+end
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9ac51997d65374543ce71c27ccf0e25b14f80aa4
--- /dev/null
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Project Tree RSS' do
+  let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+  let(:path) { namespace_project_tree_path(project.namespace, project, :master) }
+
+  context 'when signed in' do
+    before do
+      user = create(:user)
+      project.team << [user, :developer]
+      login_as(user)
+      visit path
+    end
+
+    it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "an autodiscoverable RSS feed without a private token"
+  end
+end
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..14564abb16d2b89b4411fe6e0c40353a5124b807
--- /dev/null
+++ b/spec/features/users/rss_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+feature 'User RSS' do
+  let(:path) { user_path(create(:user)) }
+
+  context 'when signed in' do
+    before do
+      login_as(create(:user))
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button with current_user's private token"
+  end
+
+  context 'when signed out' do
+    before do
+      visit path
+    end
+
+    it_behaves_like "it has an RSS button without a private token"
+  end
+end
diff --git a/spec/helpers/rss_helper_spec.rb b/spec/helpers/rss_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f3f174f3d149eda641eaac5f1a6dbbf51d3053a5
--- /dev/null
+++ b/spec/helpers/rss_helper_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe RssHelper do
+  describe '#rss_url_options' do
+    context 'when signed in' do
+      it "includes the current_user's private_token" do
+        current_user = create(:user)
+        allow(helper).to receive(:current_user).and_return(current_user)
+        expect(helper.rss_url_options).to include private_token: current_user.private_token
+      end
+    end
+
+    context 'when signed out' do
+      it "does not have a private_token" do
+        allow(helper).to receive(:current_user).and_return(nil)
+        expect(helper.rss_url_options[:private_token]).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9a3b0a731ad5f97badc16449faf324a7c238b542
--- /dev/null
+++ b/spec/support/features/rss_shared_examples.rb
@@ -0,0 +1,23 @@
+shared_examples "an autodiscoverable RSS feed with current_user's private token" do
+  it "has an RSS autodiscovery link tag with current_user's private token" do
+    expect(page).to have_css("link[type*='atom+xml'][href*='private_token=#{Thread.current[:current_user].private_token}']", visible: false)
+  end
+end
+
+shared_examples "it has an RSS button with current_user's private token" do
+  it "shows the RSS button with current_user's private token" do
+    expect(page).to have_css("a:has(.fa-rss)[href*='private_token=#{Thread.current[:current_user].private_token}']")
+  end
+end
+
+shared_examples "an autodiscoverable RSS feed without a private token" do
+  it "has an RSS autodiscovery link tag without a private token" do
+    expect(page).to have_css("link[type*='atom+xml']:not([href*='private_token'])", visible: false)
+  end
+end
+
+shared_examples "it has an RSS button without a private token" do
+  it "shows the RSS button without a private token" do
+    expect(page).to have_css("a:has(.fa-rss):not([href*='private_token'])")
+  end
+end
diff --git a/spec/support/project_features_apply_to_issuables_shared_examples.rb b/spec/support/project_features_apply_to_issuables_shared_examples.rb
index 4621d17549b9f3badc42e06235644f6628c046fd..f8b7d0527ba5f7dd459fc87b70fb621aa24aca89 100644
--- a/spec/support/project_features_apply_to_issuables_shared_examples.rb
+++ b/spec/support/project_features_apply_to_issuables_shared_examples.rb
@@ -18,7 +18,7 @@ shared_examples 'project features apply to issuables' do |klass|
 
   before do
     _ = issuable
-    login_as(user)
+    login_as(user) if user
     visit path
   end