diff --git a/CTFd/themes/admin/templates/config.html b/CTFd/themes/admin/templates/config.html
index 3fae030e..1e097069 100644
--- a/CTFd/themes/admin/templates/config.html
+++ b/CTFd/themes/admin/templates/config.html
@@ -23,6 +23,9 @@
diff --git a/CTFd/utils/initialization/__init__.py b/CTFd/utils/initialization/__init__.py
index 8e0c376c..7379b36e 100644
--- a/CTFd/utils/initialization/__init__.py
+++ b/CTFd/utils/initialization/__init__.py
@@ -204,6 +204,7 @@ def init_request_processors(app):
"views.themes",
"views.files",
"views.healthcheck",
+ "views.robots",
):
return
else:
diff --git a/CTFd/views.py b/CTFd/views.py
index d94cecf3..8876bab8 100644
--- a/CTFd/views.py
+++ b/CTFd/views.py
@@ -2,7 +2,15 @@ import os
from flask import Blueprint, abort
from flask import current_app as app
-from flask import redirect, render_template, request, send_file, session, url_for
+from flask import (
+ make_response,
+ redirect,
+ render_template,
+ request,
+ send_file,
+ session,
+ url_for,
+)
from flask.helpers import safe_join
from jinja2.exceptions import TemplateNotFound
from sqlalchemy.exc import IntegrityError
@@ -514,3 +522,11 @@ def healthcheck():
if check_config() is False:
return "ERR", 500
return "OK", 200
+
+
+@views.route("/robots.txt")
+def robots():
+ text = get_config("robots_txt", "User-agent: *\nDisallow: /admin\n")
+ r = make_response(text, 200)
+ r.mimetype = "text/plain"
+ return r
diff --git a/tests/test_views.py b/tests/test_views.py
index 3b814940..55da66bf 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -451,3 +451,19 @@ def test_user_can_access_files_if_view_after_ctf():
rmdir(directory)
destroy_ctfd(app)
+
+
+def test_robots_txt():
+ """Does the robots.txt page work"""
+ app = create_ctfd()
+ with app.app_context():
+ with app.test_client() as client:
+ r = client.get("/robots.txt")
+ assert r.status_code == 200
+ assert r.get_data(as_text=True) == "User-agent: *\nDisallow: /admin\n"
+ set_config("robots_txt", "testing")
+ with app.test_client() as client:
+ r = client.get("/robots.txt")
+ assert r.status_code == 200
+ assert r.get_data(as_text=True) == "testing"
+ destroy_ctfd(app)