diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md
index 22e2d900..64dc8a9b 100644
--- a/docs/about/release-notes.md
+++ b/docs/about/release-notes.md
@@ -23,6 +23,7 @@ The current and past members of the MkDocs team.
 
 ## Development Version
 
+* Bugfix: Support `repo_url` with missing ending slash. (#1321).
 * Bugfix: Add length support to `mkdocs.toc.TableOfContext` (#1325).
 * Bugfix: Add some theme specific settings to the search plugin for third party
   themes (#1316).
diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py
index 230a49e8..01fbd5ab 100644
--- a/mkdocs/config/config_options.py
+++ b/mkdocs/config/config_options.py
@@ -276,7 +276,7 @@ class RepoURL(URL):
         if edit_uri:
             if not edit_uri.startswith(('?', '#')) \
                     and not config['repo_url'].endswith('/'):
-                edit_uri = '/' + edit_uri
+                config['repo_url'] += '/'
             if not edit_uri.endswith('/'):
                 edit_uri += '/'
 

Broken 'Edit on GitHub' link
We recently updated to 0.17.0 and it seems to have broken our 'Edit on GitHub' link, you can see this in action [here](https://opensciencegrid.github.io/docs/). The link given is https://github.com/edit/master/docs/index.md when it should be https://github.com/opensciencegrid/docs/edit/master/docs/index.md. [repo_url](https://github.com/opensciencegrid/docs/blob/master/mkdocs.yml#L3) appears to be set properly.
diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py
index 7bdb49a0..87394fe5 100644
--- a/mkdocs/tests/config/config_options_tests.py
+++ b/mkdocs/tests/config/config_options_tests.py
@@ -196,7 +196,7 @@ class RepoURLTest(unittest.TestCase):
         option = config_options.RepoURL()
         config = {'repo_url': "https://github.com/mkdocs/mkdocs"}
         option.post_validation(config, 'repo_url')
-        self.assertEqual(config['edit_uri'], '/edit/master/docs/')
+        self.assertEqual(config['edit_uri'], 'edit/master/docs/')
 
     def test_edit_uri_bitbucket(self):
 
@@ -218,7 +218,7 @@ class RepoURLTest(unittest.TestCase):
         config = {'repo_url': "https://github.com/mkdocs/mkdocs",
                   'repo_name': 'mkdocs'}
         option.post_validation(config, 'repo_url')
-        self.assertEqual(config.get('edit_uri'), '/edit/master/docs/')
+        self.assertEqual(config.get('edit_uri'), 'edit/master/docs/')
 
 
 class DirTest(unittest.TestCase):
diff --git a/mkdocs/tests/nav_tests.py b/mkdocs/tests/nav_tests.py
index 98fa515e..d8648b1c 100644
--- a/mkdocs/tests/nav_tests.py
+++ b/mkdocs/tests/nav_tests.py
@@ -466,9 +466,6 @@ class SiteNavigationTests(unittest.TestCase):
         self.assertEqual(str(site_navigation).strip(), expected)
 
     def test_edit_uri(self):
-        """
-        Ensure that set_edit_url creates well formed URLs for edit_uri
-        """
 
         pages = [
             'index.md',
@@ -500,6 +497,47 @@ class SiteNavigationTests(unittest.TestCase):
         for idx, page in enumerate(site_navigation.walk_pages()):
             self.assertEqual(page.edit_url, expected_results[idx])
 
+    def test_edit_uri_sub_dir(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub/internal.md',
+            'sub1/sub2/internal.md',
+        ]
+
+        # Basic test
+        repo_url = 'http://example.com/foo/'
+        edit_uri = 'edit/master/docs/'
+
+        site_navigation = nav.SiteNavigation(load_config(
+            pages=pages,
+            repo_url=repo_url,
+            edit_uri=edit_uri,
+            site_dir='site',
+            site_url='',
+            use_directory_urls=True
+        ))
+
+        expected_results = (
+            repo_url + edit_uri + pages[0],
+            repo_url + edit_uri + pages[1],
+            repo_url + edit_uri + pages[2],
+            repo_url + edit_uri + pages[3],
+        )
+
+        for idx, page in enumerate(site_navigation.walk_pages()):
+            self.assertEqual(page.edit_url, expected_results[idx])
+
+    def test_edit_uri_missing_slash(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub/internal.md',
+            'sub1/sub2/internal.md',
+        ]
+
         # Ensure the '/' is added to the repo_url and edit_uri
         repo_url = 'http://example.com'
         edit_uri = 'edit/master/docs'
@@ -513,9 +551,57 @@ class SiteNavigationTests(unittest.TestCase):
             use_directory_urls=True
         ))
 
+        expected_results = (
+            repo_url + '/' + edit_uri + '/' + pages[0],
+            repo_url + '/' + edit_uri + '/' + pages[1],
+            repo_url + '/' + edit_uri + '/' + pages[2],
+            repo_url + '/' + edit_uri + '/' + pages[3],
+        )
+
         for idx, page in enumerate(site_navigation.walk_pages()):
             self.assertEqual(page.edit_url, expected_results[idx])
 
+    def test_edit_uri_sub_dir_missing_slash(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub/internal.md',
+            'sub1/sub2/internal.md',
+        ]
+
+        # Basic test
+        repo_url = 'http://example.com/foo'
+        edit_uri = 'edit/master/docs'
+
+        site_navigation = nav.SiteNavigation(load_config(
+            pages=pages,
+            repo_url=repo_url,
+            edit_uri=edit_uri,
+            site_dir='site',
+            site_url='',
+            use_directory_urls=True
+        ))
+
+        expected_results = (
+            repo_url + '/' + edit_uri + '/' + pages[0],
+            repo_url + '/' + edit_uri + '/' + pages[1],
+            repo_url + '/' + edit_uri + '/' + pages[2],
+            repo_url + '/' + edit_uri + '/' + pages[3],
+        )
+
+        for idx, page in enumerate(site_navigation.walk_pages()):
+            self.assertEqual(page.edit_url, expected_results[idx])
+
+    def test_edit_uri_query_string(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub/internal.md',
+            'sub1/sub2/internal.md',
+        ]
+
         # Ensure query strings are supported
         repo_url = 'http://example.com'
         edit_uri = '?query=edit/master/docs/'
@@ -539,6 +625,15 @@ class SiteNavigationTests(unittest.TestCase):
         for idx, page in enumerate(site_navigation.walk_pages()):
             self.assertEqual(page.edit_url, expected_results[idx])
 
+    def test_edit_uri_fragment(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub/internal.md',
+            'sub1/sub2/internal.md',
+        ]
+
         # Ensure fragment strings are supported
         repo_url = 'http://example.com'
         edit_uri = '#fragment/edit/master/docs/'
@@ -563,9 +658,6 @@ class SiteNavigationTests(unittest.TestCase):
             self.assertEqual(page.edit_url, expected_results[idx])
 
     def test_edit_uri_windows(self):
-        """
-        Ensure that set_edit_url creates well formed URLs for edit_uri with a windows path
-        """
 
         pages = [
             'index.md',
@@ -597,6 +689,47 @@ class SiteNavigationTests(unittest.TestCase):
         for idx, page in enumerate(site_navigation.walk_pages()):
             self.assertEqual(page.edit_url, expected_results[idx])
 
+    def test_edit_uri_sub_dir_windows(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub\\internal.md',
+            'sub1\\sub2\\internal.md',
+        ]
+
+        # Basic test
+        repo_url = 'http://example.com/foo/'
+        edit_uri = 'edit/master/docs/'
+
+        site_navigation = nav.SiteNavigation(load_config(
+            pages=pages,
+            repo_url=repo_url,
+            edit_uri=edit_uri,
+            site_dir='site',
+            site_url='',
+            use_directory_urls=True
+        ))
+
+        expected_results = (
+            repo_url + edit_uri + pages[0],
+            repo_url + edit_uri + pages[1],
+            repo_url + edit_uri + pages[2].replace('\\', '/'),
+            repo_url + edit_uri + pages[3].replace('\\', '/'),
+        )
+
+        for idx, page in enumerate(site_navigation.walk_pages()):
+            self.assertEqual(page.edit_url, expected_results[idx])
+
+    def test_edit_uri_missing_slash_windows(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub\\internal.md',
+            'sub1\\sub2\\internal.md',
+        ]
+
         # Ensure the '/' is added to the repo_url and edit_uri
         repo_url = 'http://example.com'
         edit_uri = 'edit/master/docs'
@@ -610,9 +743,57 @@ class SiteNavigationTests(unittest.TestCase):
             use_directory_urls=True
         ))
 
+        expected_results = (
+            repo_url + '/' + edit_uri + '/' + pages[0],
+            repo_url + '/' + edit_uri + '/' + pages[1],
+            repo_url + '/' + edit_uri + '/' + pages[2].replace('\\', '/'),
+            repo_url + '/' + edit_uri + '/' + pages[3].replace('\\', '/'),
+        )
+
         for idx, page in enumerate(site_navigation.walk_pages()):
             self.assertEqual(page.edit_url, expected_results[idx])
 
+    def test_edit_uri_sub_dir_missing_slash_windows(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub\\internal.md',
+            'sub1\\sub2\\internal.md',
+        ]
+
+        # Ensure the '/' is added to the repo_url and edit_uri
+        repo_url = 'http://example.com/foo'
+        edit_uri = 'edit/master/docs'
+
+        site_navigation = nav.SiteNavigation(load_config(
+            pages=pages,
+            repo_url=repo_url,
+            edit_uri=edit_uri,
+            site_dir='site',
+            site_url='',
+            use_directory_urls=True
+        ))
+
+        expected_results = (
+            repo_url + '/' + edit_uri + '/' + pages[0],
+            repo_url + '/' + edit_uri + '/' + pages[1],
+            repo_url + '/' + edit_uri + '/' + pages[2].replace('\\', '/'),
+            repo_url + '/' + edit_uri + '/' + pages[3].replace('\\', '/'),
+        )
+
+        for idx, page in enumerate(site_navigation.walk_pages()):
+            self.assertEqual(page.edit_url, expected_results[idx])
+
+    def test_edit_uri_query_string_windows(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub\\internal.md',
+            'sub1\\sub2\\internal.md',
+        ]
+
         # Ensure query strings are supported
         repo_url = 'http://example.com'
         edit_uri = '?query=edit/master/docs/'
@@ -636,6 +817,15 @@ class SiteNavigationTests(unittest.TestCase):
         for idx, page in enumerate(site_navigation.walk_pages()):
             self.assertEqual(page.edit_url, expected_results[idx])
 
+    def test_edit_uri_fragment_windows(self):
+
+        pages = [
+            'index.md',
+            'internal.md',
+            'sub\\internal.md',
+            'sub1\\sub2\\internal.md',
+        ]
+
         # Ensure fragment strings are supported
         repo_url = 'http://example.com'
         edit_uri = '#fragment/edit/master/docs/'

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-mock mock
apt-get update apt-get install -y gcc
3.9
requirements/project.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
click==8.1.8
exceptiongroup==1.2.2
importlib_metadata==8.6.1
iniconfig==2.1.0
Jinja2==3.1.6
livereload==2.7.1
Markdown==3.7
MarkupSafe==3.0.2
-e git+https://github.com/mkdocs/mkdocs.git@fd0428543225f6fe5795b294235b12c540092aad#egg=mkdocs
mock==5.2.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-mock==3.14.0
PyYAML==6.0.2
tomli==2.2.1
tornado==6.4.2
zipp==3.21.0

name: mkdocs
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - click==8.1.8
      - exceptiongroup==1.2.2
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - jinja2==3.1.6
      - livereload==2.7.1
      - markdown==3.7
      - markupsafe==3.0.2
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-mock==3.14.0
      - pyyaml==6.0.2
      - tomli==2.2.1
      - tornado==6.4.2
      - zipp==3.21.0
prefix: /opt/conda/envs/mkdocs

mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_github mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_custom_and_empty_edit_uri
mkdocs/tests/nav_tests.py::SiteNavigationTests::test_ancestors mkdocs/tests/nav_tests.py::SiteNavigationTests::test_base_url mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_fragment mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_fragment_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_missing_slash mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_missing_slash_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_query_string mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_query_string_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_sub_dir mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_sub_dir_missing_slash mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_sub_dir_missing_slash_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_sub_dir_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_edit_uri_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_empty_toc_item mkdocs/tests/nav_tests.py::SiteNavigationTests::test_force_abs_urls mkdocs/tests/nav_tests.py::SiteNavigationTests::test_force_abs_urls_with_base mkdocs/tests/nav_tests.py::SiteNavigationTests::test_generate_site_navigation mkdocs/tests/nav_tests.py::SiteNavigationTests::test_generate_site_navigation_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_indented_toc mkdocs/tests/nav_tests.py::SiteNavigationTests::test_nested_ungrouped mkdocs/tests/nav_tests.py::SiteNavigationTests::test_nested_ungrouped_no_titles mkdocs/tests/nav_tests.py::SiteNavigationTests::test_nested_ungrouped_no_titles_windows mkdocs/tests/nav_tests.py::SiteNavigationTests::test_nesting mkdocs/tests/nav_tests.py::SiteNavigationTests::test_pages_config mkdocs/tests/nav_tests.py::SiteNavigationTests::test_relative_md_links_have_slash mkdocs/tests/nav_tests.py::SiteNavigationTests::test_simple_toc mkdocs/tests/nav_tests.py::SiteNavigationTests::test_walk_empty_toc mkdocs/tests/nav_tests.py::SiteNavigationTests::test_walk_indented_toc mkdocs/tests/nav_tests.py::SiteNavigationTests::test_walk_simple_toc
mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_default mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_empty mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_replace_default mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_required mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_required_no_default mkdocs/tests/config/config_options_tests.py::TypeTest::test_length mkdocs/tests/config/config_options_tests.py::TypeTest::test_multiple_types mkdocs/tests/config/config_options_tests.py::TypeTest::test_single_type mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_default_address mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_format mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_missing_port mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_port mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_type mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_named_address mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_valid_IPv6_address mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_valid_address mkdocs/tests/config/config_options_tests.py::URLTest::test_invalid mkdocs/tests/config/config_options_tests.py::URLTest::test_invalid_url mkdocs/tests/config/config_options_tests.py::URLTest::test_valid_url mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_bitbucket mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_custom mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_bitbucket mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_custom mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_github mkdocs/tests/config/config_options_tests.py::DirTest::test_doc_dir_is_config_dir mkdocs/tests/config/config_options_tests.py::DirTest::test_file mkdocs/tests/config/config_options_tests.py::DirTest::test_incorrect_type_attribute_error mkdocs/tests/config/config_options_tests.py::DirTest::test_incorrect_type_type_error mkdocs/tests/config/config_options_tests.py::DirTest::test_missing_dir mkdocs/tests/config/config_options_tests.py::DirTest::test_missing_dir_but_required mkdocs/tests/config/config_options_tests.py::DirTest::test_valid_dir mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_common_prefix mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_doc_dir_in_site_dir mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_site_dir_in_docs_dir mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_as_complex_config mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_as_simple_config mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_as_string mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_config_missing_name mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_default mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_invalid_type mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_name_is_none mkdocs/tests/config/config_options_tests.py::ThemeTest::test_uninstalled_theme_as_config mkdocs/tests/config/config_options_tests.py::ThemeTest::test_uninstalled_theme_as_string mkdocs/tests/config/config_options_tests.py::PagesTest::test_invalid_config mkdocs/tests/config/config_options_tests.py::PagesTest::test_invalid_type mkdocs/tests/config/config_options_tests.py::PagesTest::test_old_format mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided_dict mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided_empty mkdocs/tests/config/config_options_tests.py::PrivateTest::test_defined mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_builtins mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_builtins_config mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_configkey mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_duplicates mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_config_item mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_config_option mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_dict_item mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_list_dicts mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_mixed_list mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_none mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_not_list mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_simple_list mkdocs/tests/nav_tests.py::SiteNavigationTests::test_invalid_pages_config
docs/about/release-notes.md mkdocs/config/config_options.py
docs/about/release-notes.md mkdocs/config/config_options.py