diff --git a/cli/onionshare_cli/resources/templates/500.html b/cli/onionshare_cli/resources/templates/500.html
new file mode 100644
index 00000000..9f6727d2
--- /dev/null
+++ b/cli/onionshare_cli/resources/templates/500.html
@@ -0,0 +1,21 @@
+
+
+
+
+  OnionShare: An error occurred
+  
+   
+  
+  
+
+
+
+  
+    
+      

+      
+    
+  
 
+
+
+
diff --git a/cli/onionshare_cli/web/chat_mode.py b/cli/onionshare_cli/web/chat_mode.py
index e9b573dd..f6dc2d1a 100644
--- a/cli/onionshare_cli/web/chat_mode.py
+++ b/cli/onionshare_cli/web/chat_mode.py
@@ -39,6 +39,12 @@ class ChatModeWeb:
         # This tracks the history id
         self.cur_history_id = 0
 
+        # Whether or not we can send REQUEST_INDIVIDUAL_FILE_STARTED
+        # and maybe other events when requests come in to this mode
+        # Chat mode has no concept of individual file requests that
+        # turn into history widgets in the GUI, so set it to False
+        self.supports_file_requests = False
+
         self.define_routes()
 
     def define_routes(self):
@@ -46,7 +52,7 @@ class ChatModeWeb:
         The web app routes for chatting
         """
 
-        @self.web.app.route("/")
+        @self.web.app.route("/", methods=["GET"], provide_automatic_options=False)
         def index():
             history_id = self.cur_history_id
             self.cur_history_id += 1
@@ -72,7 +78,7 @@ class ChatModeWeb:
             )
             return self.web.add_security_headers(r)
 
-        @self.web.app.route("/update-session-username", methods=["POST"])
+        @self.web.app.route("/update-session-username", methods=["POST"], provide_automatic_options=False)
         def update_session_username():
             history_id = self.cur_history_id
             data = request.get_json()
diff --git a/cli/onionshare_cli/web/receive_mode.py b/cli/onionshare_cli/web/receive_mode.py
index f5aae296..76abb0a8 100644
--- a/cli/onionshare_cli/web/receive_mode.py
+++ b/cli/onionshare_cli/web/receive_mode.py
@@ -64,6 +64,10 @@ class ReceiveModeWeb:
         # This tracks the history id
         self.cur_history_id = 0
 
+        # Whether or not we can send REQUEST_INDIVIDUAL_FILE_STARTED
+        # and maybe other events when requests come in to this mode
+        self.supports_file_requests = True
+
         self.define_routes()
 
     def define_routes(self):
@@ -71,7 +75,7 @@ class ReceiveModeWeb:
         The web app routes for receiving files
         """
 
-        @self.web.app.route("/")
+        @self.web.app.route("/", methods=["GET"], provide_automatic_options=False)
         def index():
             history_id = self.cur_history_id
             self.cur_history_id += 1
@@ -93,7 +97,7 @@ class ReceiveModeWeb:
             )
             return self.web.add_security_headers(r)
 
-        @self.web.app.route("/upload", methods=["POST"])
+        @self.web.app.route("/upload", methods=["POST"], provide_automatic_options=False)
         def upload(ajax=False):
             """
             Handle the upload files POST request, though at this point, the files have
@@ -225,7 +229,7 @@ class ReceiveModeWeb:
                     )
                     return self.web.add_security_headers(r)
 
-        @self.web.app.route("/upload-ajax", methods=["POST"])
+        @self.web.app.route("/upload-ajax", methods=["POST"], provide_automatic_options=False)
         def upload_ajax_public():
             if not self.can_upload:
                 return self.web.error403()
diff --git a/cli/onionshare_cli/web/send_base_mode.py b/cli/onionshare_cli/web/send_base_mode.py
index 742f6f75..e448d2dd 100644
--- a/cli/onionshare_cli/web/send_base_mode.py
+++ b/cli/onionshare_cli/web/send_base_mode.py
@@ -52,6 +52,10 @@ class SendBaseModeWeb:
         # This tracks the history id
         self.cur_history_id = 0
 
+        # Whether or not we can send REQUEST_INDIVIDUAL_FILE_STARTED
+        # and maybe other events when requests come in to this mode
+        self.supports_file_requests = True
+
         self.define_routes()
         self.init()
 
@@ -208,10 +212,6 @@ class SendBaseModeWeb:
         history_id = self.cur_history_id
         self.cur_history_id += 1
 
-        # Only GET requests are allowed, any other method should fail
-        if request.method != "GET":
-            return self.web.error405(history_id)
-
         self.web.add_request(
             self.web.REQUEST_INDIVIDUAL_FILE_STARTED,
             path,
diff --git a/cli/onionshare_cli/web/share_mode.py b/cli/onionshare_cli/web/share_mode.py
index 95aec1ba..51ddd674 100644
--- a/cli/onionshare_cli/web/share_mode.py
+++ b/cli/onionshare_cli/web/share_mode.py
@@ -134,8 +134,8 @@ class ShareModeWeb(SendBaseModeWeb):
         The web app routes for sharing files
         """
 
-        @self.web.app.route("/", defaults={"path": ""})
-        @self.web.app.route("/")
+        @self.web.app.route("/", defaults={"path": ""}, methods=["GET"], provide_automatic_options=False)
+        @self.web.app.route("/", methods=["GET"], provide_automatic_options=False)
         def index(path):
             """
             Render the template for the onionshare landing page.
@@ -160,7 +160,7 @@ class ShareModeWeb(SendBaseModeWeb):
 
             return self.render_logic(path)
 
-        @self.web.app.route("/download")
+        @self.web.app.route("/download", methods=["GET"], provide_automatic_options=False)
         def download():
             """
             Download the zip file.
diff --git a/cli/onionshare_cli/web/web.py b/cli/onionshare_cli/web/web.py
index d88a7e4e..56e307b4 100644
--- a/cli/onionshare_cli/web/web.py
+++ b/cli/onionshare_cli/web/web.py
@@ -229,6 +229,20 @@ class Web:
             mode.cur_history_id += 1
             return self.error404(history_id)
 
+        @self.app.errorhandler(405)
+        def method_not_allowed(e):
+            mode = self.get_mode()
+            history_id = mode.cur_history_id
+            mode.cur_history_id += 1
+            return self.error405(history_id)
+
+        @self.app.errorhandler(500)
+        def method_not_allowed(e):
+            mode = self.get_mode()
+            history_id = mode.cur_history_id
+            mode.cur_history_id += 1
+            return self.error500(history_id)
+
         @self.app.route("//shutdown")
         def shutdown(password_candidate):
             """
@@ -280,11 +294,13 @@ class Web:
         return self.add_security_headers(r)
 
     def error404(self, history_id):
-        self.add_request(
-            self.REQUEST_INDIVIDUAL_FILE_STARTED,
-            request.path,
-            {"id": history_id, "status_code": 404},
-        )
+        mode = self.get_mode()
+        if mode.supports_file_requests:
+            self.add_request(
+                self.REQUEST_INDIVIDUAL_FILE_STARTED,
+                request.path,
+                {"id": history_id, "status_code": 404},
+            )
 
         self.add_request(Web.REQUEST_OTHER, request.path)
         r = make_response(
@@ -293,11 +309,13 @@ class Web:
         return self.add_security_headers(r)
 
     def error405(self, history_id):
-        self.add_request(
-            self.REQUEST_INDIVIDUAL_FILE_STARTED,
-            request.path,
-            {"id": history_id, "status_code": 405},
-        )
+        mode = self.get_mode()
+        if mode.supports_file_requests:
+            self.add_request(
+                self.REQUEST_INDIVIDUAL_FILE_STARTED,
+                request.path,
+                {"id": history_id, "status_code": 405},
+            )
 
         self.add_request(Web.REQUEST_OTHER, request.path)
         r = make_response(
@@ -305,6 +323,21 @@ class Web:
         )
         return self.add_security_headers(r)
 
+    def error500(self, history_id):
+        mode = self.get_mode()
+        if mode.supports_file_requests:
+            self.add_request(
+                self.REQUEST_INDIVIDUAL_FILE_STARTED,
+                request.path,
+                {"id": history_id, "status_code": 500},
+            )
+
+        self.add_request(Web.REQUEST_OTHER, request.path)
+        r = make_response(
+            render_template("500.html", static_url_path=self.static_url_path), 500
+        )
+        return self.add_security_headers(r)
+
     def add_security_headers(self, r):
         """
         Add security headers to a request
diff --git a/cli/onionshare_cli/web/website_mode.py b/cli/onionshare_cli/web/website_mode.py
index 6badd399..5ab1b184 100644
--- a/cli/onionshare_cli/web/website_mode.py
+++ b/cli/onionshare_cli/web/website_mode.py
@@ -37,8 +37,8 @@ class WebsiteModeWeb(SendBaseModeWeb):
         The web app routes for sharing a website
         """
 
-        @self.web.app.route("/", defaults={"path": ""})
-        @self.web.app.route("/")
+        @self.web.app.route("/", defaults={"path": ""}, methods=["GET"], provide_automatic_options=False)
+        @self.web.app.route("/", methods=["GET"], provide_automatic_options=False)
         def path_public(path):
             return path_logic(path)
 
diff --git a/desktop/tests/gui_base_test.py b/desktop/tests/gui_base_test.py
index 3a38ff8e..acaa9739 100644
--- a/desktop/tests/gui_base_test.py
+++ b/desktop/tests/gui_base_test.py
@@ -465,6 +465,20 @@ class GuiBaseTest(unittest.TestCase):
         # We should have timed out now
         self.assertEqual(tab.get_mode().server_status.status, 0)
 
+    def hit_405(self, url, expected_resp, data = {}, methods = [] ):
+        """Test various HTTP methods and the response"""
+        for method in methods:
+            if method == "put":
+                r = requests.put(url, data = data)
+            if method == "post":
+                r = requests.post(url, data = data)
+            if method == "delete":
+                r = requests.delete(url)
+            if method == "options":
+                r = requests.options(url)
+            self.assertTrue(expected_resp in r.text)
+            self.assertFalse('Werkzeug' in r.headers)
+
     # Grouped tests follow from here
 
     def run_all_common_setup_tests(self):
diff --git a/desktop/tests/test_gui_chat.py b/desktop/tests/test_gui_chat.py
index 08c619c6..15ecaa44 100644
--- a/desktop/tests/test_gui_chat.py
+++ b/desktop/tests/test_gui_chat.py
@@ -30,7 +30,7 @@ class TestChat(GuiBaseTest):
     def change_username(self, tab):
         """Test that we can change our username"""
         url = f"http://127.0.0.1:{tab.app.port}/update-session-username"
-        data = {"username":"oniontest"}
+        data = {"username": "oniontest"}
         if tab.settings.get("general", "public"):
             r = requests.post(url, json=data)
         else:
@@ -47,28 +47,7 @@ class TestChat(GuiBaseTest):
         self.assertTrue(jsonResponse["success"])
         self.assertEqual(jsonResponse["username"], "oniontest")
 
-    def change_username_too_long(self, tab):
-        """Test that we can't set our username to something 128 chars or longer"""
-        url = f"http://127.0.0.1:{tab.app.port}/update-session-username"
-        bad_username = "sduBB9yEMkyQpwkMM4A9nUbQwNUbPU2PQuJYN26zCQ4inELpB76J5i5oRUnD3ESVaE9NNE8puAtBj2DiqDaZdVqhV8MonyxSSGHRv87YgM5dzwBYPBxttoQSKZAUkFjo"
-        data = {"username":bad_username}
-        if tab.settings.get("general", "public"):
-            r = requests.post(url, json=data)
-        else:
-            r = requests.post(
-                url,
-                json=data,
-                auth=requests.auth.HTTPBasicAuth(
-                    "onionshare", tab.get_mode().server_status.web.password
-                ),
-            )
-
-        QtTest.QTest.qWait(500, self.gui.qtapp)
-        jsonResponse = r.json()
-        self.assertFalse(jsonResponse["success"])
-        self.assertNotEqual(jsonResponse["username"], bad_username)
-
-    def run_all_chat_mode_tests(self, tab):
+    def run_all_chat_mode_started_tests(self, tab):
         """Tests in chat mode after starting a chat"""
         self.server_working_on_start_button_pressed(tab)
         self.server_status_indicator_says_starting(tab)
@@ -79,9 +58,9 @@ class TestChat(GuiBaseTest):
         self.have_copy_url_button(tab)
         self.have_show_qr_code_button(tab)
         self.server_status_indicator_says_started(tab)
-        self.view_chat(tab)
-        self.change_username(tab)
-        self.change_username_too_long(tab)
+
+    def run_all_chat_mode_stopping_tests(self, tab):
+        """Tests stopping a chat"""
         self.server_is_stopped(tab)
         self.web_server_is_stopped(tab)
         self.server_status_indicator_says_closed(tab)
@@ -93,5 +72,27 @@ class TestChat(GuiBaseTest):
         Test chat mode
         """
         tab = self.new_chat_tab()
-        self.run_all_chat_mode_tests(tab)
+        self.run_all_chat_mode_started_tests(tab)
+        self.view_chat(tab)
+        self.change_username(tab)
+        self.run_all_chat_mode_stopping_tests(tab)
+        self.close_all_tabs()
+
+    def test_405_page_returned_for_invalid_methods(self):
+        """
+        Our custom 405 page should return for invalid methods
+        """
+        tab = self.new_chat_tab()
+
+        tab.get_mode().mode_settings_widget.public_checkbox.click()
+
+        self.run_all_chat_mode_started_tests(tab)
+        url = f"http://127.0.0.1:{tab.app.port}/"
+        self.hit_405(
+            url,
+            expected_resp="OnionShare: 405 Method Not Allowed",
+            data={"foo": "bar"},
+            methods=["put", "post", "delete", "options"],
+        )
+        self.run_all_chat_mode_stopping_tests(tab)
         self.close_all_tabs()
diff --git a/desktop/tests/test_gui_receive.py b/desktop/tests/test_gui_receive.py
index 6e14ae67..b523b0fa 100644
--- a/desktop/tests/test_gui_receive.py
+++ b/desktop/tests/test_gui_receive.py
@@ -286,3 +286,22 @@ class TestReceive(GuiBaseTest):
         self.run_all_upload_non_writable_dir_tests(tab)
 
         self.close_all_tabs()
+
+    def test_405_page_returned_for_invalid_methods(self):
+        """
+        Our custom 405 page should return for invalid methods
+        """
+        tab = self.new_receive_tab()
+
+        tab.get_mode().mode_settings_widget.public_checkbox.click()
+
+        self.run_all_common_setup_tests()
+        self.run_all_receive_mode_setup_tests(tab)
+        self.upload_file(tab, self.tmpfile_test, "test.txt")
+        url = f"http://127.0.0.1:{tab.app.port}/"
+        self.hit_405(url, expected_resp="OnionShare: 405 Method Not Allowed", data = {'foo':'bar'}, methods = ["put", "post", "delete", "options"])
+
+        self.server_is_stopped(tab)
+        self.web_server_is_stopped(tab)
+        self.server_status_indicator_says_closed(tab)
+        self.close_all_tabs()
diff --git a/desktop/tests/test_gui_share.py b/desktop/tests/test_gui_share.py
index 380d63f6..531e456f 100644
--- a/desktop/tests/test_gui_share.py
+++ b/desktop/tests/test_gui_share.py
@@ -608,3 +608,20 @@ class TestShare(GuiBaseTest):
         self.hit_401(tab)
 
         self.close_all_tabs()
+
+    def test_405_page_returned_for_invalid_methods(self):
+        """
+        Our custom 405 page should return for invalid methods
+        """
+        tab = self.new_share_tab()
+
+        tab.get_mode().autostop_sharing_checkbox.click()
+        tab.get_mode().mode_settings_widget.public_checkbox.click()
+
+        self.run_all_common_setup_tests()
+        self.run_all_share_mode_setup_tests(tab)
+        self.run_all_share_mode_started_tests(tab)
+        url = f"http://127.0.0.1:{tab.app.port}/"
+        self.hit_405(url, expected_resp="OnionShare: 405 Method Not Allowed", data = {'foo':'bar'}, methods = ["put", "post", "delete", "options"])
+        self.history_widgets_present(tab)
+        self.close_all_tabs()
diff --git a/desktop/tests/test_gui_website.py b/desktop/tests/test_gui_website.py
index a838cb96..f526756a 100644
--- a/desktop/tests/test_gui_website.py
+++ b/desktop/tests/test_gui_website.py
@@ -99,3 +99,19 @@ class TestWebsite(GuiBaseTest):
         tab.get_mode().disable_csp_checkbox.click()
         self.run_all_website_mode_download_tests(tab)
         self.close_all_tabs()
+
+    def test_405_page_returned_for_invalid_methods(self):
+        """
+        Our custom 405 page should return for invalid methods
+        """
+        tab = self.new_website_tab()
+
+        tab.get_mode().mode_settings_widget.public_checkbox.click()
+
+        self.run_all_common_setup_tests()
+        self.run_all_website_mode_setup_tests(tab)
+        self.run_all_website_mode_started_tests(tab)
+        url = f"http://127.0.0.1:{tab.app.port}/"
+        self.hit_405(url, expected_resp="OnionShare: 405 Method Not Allowed", data = {'foo':'bar'}, methods = ["put", "post", "delete", "options"])
+
+        self.close_all_tabs()