diff --git a/tests/test_services.py b/tests/test_services.py
index 269d1455471c70932c26ff9770d5db5d8c7b0ba6..efd71898001d796523f9d1733c6846ee5233f696 100644
--- a/tests/test_services.py
+++ b/tests/test_services.py
@@ -42,12 +42,63 @@ class TestServices(UffdTestCase):
 
 	def test_overview(self):
 		r = self.client.get(path=url_for('service.overview'))
-		dump('service_overview_public', r)
+		dump('service_overview_guest', r)
 		self.assertEqual(r.status_code, 200)
 		self.assertNotIn(b'https://example.com/', r.data)
 		self.login_as('user')
 		r = self.client.get(path=url_for('service.overview'))
-		dump('service_overview', r)
+		dump('service_overview_user', r)
 		self.assertEqual(r.status_code, 200)
 		self.assertIn(b'https://example.com/', r.data)
 
+	def test_overview_disabled(self):
+		self.app.config['SERVICES'] = []
+		# Should return login page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_disabled_guest', r)
+		self.assertEqual(r.status_code, 200)
+		self.assertIn(b'name="password"', r.data)
+		self.login_as('user')
+		# Should return access denied page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_disabled_user', r)
+		self.assertEqual(r.status_code, 403)
+		self.login_as('admin')
+		# Should return (empty) overview page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_disabled_admin', r)
+		self.assertEqual(r.status_code, 200)
+
+	def test_overview_nonpublic(self):
+		self.app.config['SERVICES_PUBLIC'] = False
+		# Should return login page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_nonpublic_guest', r)
+		self.assertEqual(r.status_code, 200)
+		self.assertIn(b'name="password"', r.data)
+		self.login_as('user')
+		# Should return overview page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_nonpublic_user', r)
+		self.assertEqual(r.status_code, 200)
+		self.login_as('admin')
+		# Should return overview page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_nonpublic_admin', r)
+		self.assertEqual(r.status_code, 200)
+
+	def test_overview_public(self):
+		# Should return overview page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_public_guest', r)
+		self.assertEqual(r.status_code, 200)
+		self.login_as('user')
+		# Should return overview page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_public_user', r)
+		self.assertEqual(r.status_code, 200)
+		self.login_as('admin')
+		# Should return overview page
+		r = self.client.get(path=url_for('service.overview'), follow_redirects=True)
+		dump('service_overview_public_admin', r)
+		self.assertEqual(r.status_code, 200)
diff --git a/tests/utils.py b/tests/utils.py
index b043f3afe37e05e6b5cdeaf81d12ad2373b722e0..c9ec3e9221fffcd31b1c77b3dd93b4a83498fbc3 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -44,6 +44,9 @@ class UffdTestCase(unittest.TestCase):
 		return Mail.query.filter_by(uid='test').one_or_none()
 
 	def login_as(self, user, ref=None):
+		# It is currently not possible to login while already logged in as another
+		# user, so make sure that we are not logged in first
+		self.client.get(path=url_for('session.logout'), follow_redirects=True)
 		loginname = None
 		password = None
 		if user == 'user':
diff --git a/uffd/service/templates/service/overview.html b/uffd/service/templates/service/overview.html
index 67795006e4485a483274a11b41441e48fa12b881..5ed19e388cc4e0b17d4655b1cfcb4305a33ff06b 100644
--- a/uffd/service/templates/service/overview.html
+++ b/uffd/service/templates/service/overview.html
@@ -4,7 +4,7 @@
 
 {% set iconstyle = 'style="width: 1.8em;"'|safe %}
 
-{% if not user %}
+{% if not request.user %}
 <div class="alert alert-warning" role="alert">{{_("Some services may not be publicly listed! Log in to see all services you have access to.")}}</div>
 {% endif %}
 
diff --git a/uffd/service/views.py b/uffd/service/views.py
index 1f5623cae17965d3845fe519280ce3c51d22d73f..cd01583316a9daa3afbbf334e0e57c77d715db35 100644
--- a/uffd/service/views.py
+++ b/uffd/service/views.py
@@ -1,3 +1,5 @@
+import functools
+
 from flask import Blueprint, render_template, request, url_for, redirect, current_app, abort
 from flask_babel import lazy_gettext
 
@@ -12,32 +14,40 @@ from uffd.database import db
 
 bp = Blueprint('service', __name__, template_folder='templates')
 
-def service_admin_acl():
+def admin_acl():
 	return request.user and request.user.is_in_group(current_app.config['ACL_ADMIN_GROUP'])
 
-def service_overview_acl():
-	return len(get_services(request.user)) > 0 or service_admin_acl()
+def overview_login_maybe_required(func):
+	@functools.wraps(func)
+	def decorator(*args, **kwargs):
+		if not current_app.config['SERVICES']:
+			return login_required(admin_acl)(func)(*args, **kwargs)
+		if not current_app.config['SERVICES_PUBLIC']:
+			return login_required()(func)(*args, **kwargs)
+		return func(*args, **kwargs)
+	return decorator
+
+def overview_navbar_visible():
+	return get_services(request.user) != [] or admin_acl()
 
 @bp.route('/services/')
-@register_navbar(lazy_gettext('Services'), icon='sitemap', blueprint=bp, visible=service_overview_acl)
+@register_navbar(lazy_gettext('Services'), icon='sitemap', blueprint=bp, visible=overview_navbar_visible)
+@overview_login_maybe_required
 def overview():
 	services = get_services(request.user)
-	if not service_overview_acl():
-		abort(404)
-	banner = current_app.config.get('SERVICES_BANNER')
-	# Set the banner to None if it is not public and no user is logged in
-	if not (current_app.config['SERVICES_BANNER_PUBLIC'] or request.user):
-		banner = None
-	return render_template('service/overview.html', user=request.user, services=services, banner=banner)
+	banner = ''
+	if request.user or current_app.config['SERVICES_BANNER_PUBLIC']:
+		banner = current_app.config['SERVICES_BANNER']
+	return render_template('service/overview.html', services=services, banner=banner)
 
 @bp.route('/service/admin')
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def index():
 	return render_template('service/index.html', services=Service.query.all())
 
 @bp.route('/service/new')
 @bp.route('/service/<int:id>')
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def show(id=None):
 	service = Service() if id is None else Service.query.get_or_404(id)
 	all_groups = Group.query.all()
@@ -46,7 +56,7 @@ def show(id=None):
 @bp.route('/service/new', methods=['POST'])
 @bp.route('/service/<int:id>', methods=['POST'])
 @csrf_protect(blueprint=bp)
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def edit_submit(id=None):
 	if id is None:
 		service = Service()
@@ -68,7 +78,7 @@ def edit_submit(id=None):
 
 @bp.route('/service/<int:id>/delete')
 @csrf_protect(blueprint=bp)
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def delete(id):
 	service = Service.query.get_or_404(id)
 	db.session.delete(service)
@@ -77,7 +87,7 @@ def delete(id):
 
 @bp.route('/service/<int:service_id>/oauth2/new')
 @bp.route('/service/<int:service_id>/oauth2/<int:db_id>')
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def oauth2_show(service_id, db_id=None):
 	service = Service.query.get_or_404(service_id)
 	client = OAuth2Client() if db_id is None else OAuth2Client.query.filter_by(service_id=service_id, db_id=db_id).first_or_404()
@@ -86,7 +96,7 @@ def oauth2_show(service_id, db_id=None):
 @bp.route('/service/<int:service_id>/oauth2/new', methods=['POST'])
 @bp.route('/service/<int:service_id>/oauth2/<int:db_id>', methods=['POST'])
 @csrf_protect(blueprint=bp)
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def oauth2_submit(service_id, db_id=None):
 	service = Service.query.get_or_404(service_id)
 	if db_id is None:
@@ -112,7 +122,7 @@ def oauth2_submit(service_id, db_id=None):
 
 @bp.route('/service/<int:service_id>/oauth2/<int:db_id>/delete')
 @csrf_protect(blueprint=bp)
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def oauth2_delete(service_id, db_id=None):
 	service = Service.query.get_or_404(service_id)
 	client = OAuth2Client.query.filter_by(service_id=service_id, db_id=db_id).first_or_404()
@@ -122,7 +132,7 @@ def oauth2_delete(service_id, db_id=None):
 
 @bp.route('/service/<int:service_id>/api/new')
 @bp.route('/service/<int:service_id>/api/<int:id>')
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def api_show(service_id, id=None):
 	service = Service.query.get_or_404(service_id)
 	client = APIClient() if id is None else APIClient.query.filter_by(service_id=service_id, id=id).first_or_404()
@@ -131,7 +141,7 @@ def api_show(service_id, id=None):
 @bp.route('/service/<int:service_id>/api/new', methods=['POST'])
 @bp.route('/service/<int:service_id>/api/<int:id>', methods=['POST'])
 @csrf_protect(blueprint=bp)
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def api_submit(service_id, id=None):
 	service = Service.query.get_or_404(service_id)
 	if id is None:
@@ -152,7 +162,7 @@ def api_submit(service_id, id=None):
 
 @bp.route('/service/<int:service_id>/api/<int:id>/delete')
 @csrf_protect(blueprint=bp)
-@login_required(service_admin_acl)
+@login_required(admin_acl)
 def api_delete(service_id, id=None):
 	service = Service.query.get_or_404(service_id)
 	client = APIClient.query.filter_by(service_id=service_id, id=id).first_or_404()