diff --git a/assets/src/bundles/save/index.js b/assets/src/bundles/save/index.js
--- a/assets/src/bundles/save/index.js
+++ b/assets/src/bundles/save/index.js
@@ -364,7 +364,7 @@
   }
 
   if (validUrl) {
-    const allowedProtocols = ['http:', 'https:', 'svn:', 'git:'];
+    const allowedProtocols = ['http:', 'https:', 'svn:', 'git:', 'rsync:', 'pserver:', 'ssh:'];
     validUrl = (
       allowedProtocols.find(protocol => protocol === originUrl.protocol) !== undefined
     );
diff --git a/cypress/integration/origin-save.spec.js b/cypress/integration/origin-save.spec.js
--- a/cypress/integration/origin-save.spec.js
+++ b/cypress/integration/origin-save.spec.js
@@ -19,8 +19,8 @@
   'csrfError': 'CSRF Failed: Referrer checking failed - no Referrer.'
 };
 
-const anonymousVisitTypes = ['git', 'hg', 'svn'];
-const allVisitTypes = ['archives', 'git', 'hg', 'svn'];
+const anonymousVisitTypes = ['git', 'hg', 'svn', 'cvs'];
+const allVisitTypes = ['archives', 'git', 'hg', 'svn', 'cvs'];
 
 function makeOriginSaveRequest(originType, originUrl) {
   cy.get('#swh-input-origin-url')
diff --git a/swh/web/api/views/origin_save.py b/swh/web/api/views/origin_save.py
--- a/swh/web/api/views/origin_save.py
+++ b/swh/web/api/views/origin_save.py
@@ -56,7 +56,7 @@
         might have been submitted for the same origin).
 
         :param string visit_type: the type of visit to perform
-            (currently the supported types are ``git``, ``hg`` and ``svn``)
+            (currently the supported types are ``git``, ``hg``, ``svn``, and ``cvs``)
         :param string origin_url: the url of the origin to save
 
         {common_headers}
diff --git a/swh/web/common/origin_save.py b/swh/web/common/origin_save.py
--- a/swh/web/common/origin_save.py
+++ b/swh/web/common/origin_save.py
@@ -122,7 +122,12 @@
 
 # map visit type to scheduler task
 # TODO: do not hardcode the task name here (T1157)
-_visit_type_task = {"git": "load-git", "hg": "load-hg", "svn": "load-svn"}
+_visit_type_task = {
+    "git": "load-git",
+    "hg": "load-hg",
+    "svn": "load-svn",
+    "cvs": "load-cvs",
+}
 
 _visit_type_task_privileged = {
     "archives": "load-archive-files",
@@ -193,7 +198,9 @@
         )
 
 
-_validate_url = URLValidator(schemes=["http", "https", "svn", "git"])
+_validate_url = URLValidator(
+    schemes=["http", "https", "svn", "git", "rsync", "pserver", "ssh"]
+)
 
 
 def _check_origin_url_valid(origin_url: str) -> None:
diff --git a/swh/web/config.py b/swh/web/config.py
--- a/swh/web/config.py
+++ b/swh/web/config.py
@@ -34,6 +34,7 @@
     "pypi",
     "svn",
     "tar",
+    "cvs",
 ]
 
 
diff --git a/swh/web/templates/misc/origin-save.html b/swh/web/templates/misc/origin-save.html
--- a/swh/web/templates/misc/origin-save.html
+++ b/swh/web/templates/misc/origin-save.html
@@ -83,6 +83,7 @@
           <li><code>git</code>, for origins using <a href="https://git-scm.com/">Git</a></li>
           <li><code>hg</code>, for origins using <a href="https://www.mercurial-scm.org/">Mercurial</a></li>
           <li><code>svn</code>, for origins using <a href="https://subversion.apache.org/">Subversion</a></li>
+          <li><code>cvs</code>, for origins using <a href="http://cvs.nongnu.org/">CVS</a></li>
         </ul>
       </li>
       <li><b>Origin url:</b> the url of the remote repository for the software origin.<br/>
diff --git a/swh/web/tests/misc/test_origin_save.py b/swh/web/tests/misc/test_origin_save.py
--- a/swh/web/tests/misc/test_origin_save.py
+++ b/swh/web/tests/misc/test_origin_save.py
@@ -15,7 +15,7 @@
 from swh.web.common.utils import reverse
 from swh.web.tests.utils import check_http_get_response
 
-VISIT_TYPES = ("git", "svn", "hg")
+VISIT_TYPES = ("git", "svn", "hg", "cvs")
 PRIVILEGED_VISIT_TYPES = tuple(list(VISIT_TYPES) + ["archives"])