diff --git a/modules/markdown/markdown.go b/modules/markdown/markdown.go
index 1f890cc822..9773e8c2f8 100644
--- a/modules/markdown/markdown.go
+++ b/modules/markdown/markdown.go
@@ -14,13 +14,14 @@ import (
 	"regexp"
 	"strings"
 
+	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/setting"
+
 	"github.com/Unknwon/com"
 	"github.com/russross/blackfriday"
 	"golang.org/x/net/html"
-
-	"code.gitea.io/gitea/modules/base"
-	"code.gitea.io/gitea/modules/markup"
-	"code.gitea.io/gitea/modules/setting"
 )
 
 // Issue name styles
@@ -213,36 +214,17 @@ func cutoutVerbosePrefix(prefix string) string {
 }
 
 // URLJoin joins url components, like path.Join, but preserving contents
-func URLJoin(elem ...string) string {
-	res := ""
-	last := len(elem) - 1
-	for i, item := range elem {
-		res += item
-		if i != last && !strings.HasSuffix(res, "/") {
-			res += "/"
-		}
+func URLJoin(base string, elems ...string) string {
+	u, err := url.Parse(base)
+	if err != nil {
+		log.Error(4, "URLJoin: Invalid base URL %s", base)
+		return ""
 	}
-	cwdIndex := strings.Index(res, "/./")
-	for cwdIndex != -1 {
-		res = strings.Replace(res, "/./", "/", 1)
-		cwdIndex = strings.Index(res, "/./")
-	}
-	upIndex := strings.Index(res, "/..")
-	for upIndex != -1 {
-		res = strings.Replace(res, "/..", "", 1)
-		prevStart := -1
-		for i := upIndex - 1; i >= 0; i-- {
-			if res[i] == '/' {
-				prevStart = i
-				break
-			}
-		}
-		if prevStart != -1 {
-			res = res[:prevStart] + res[upIndex:]
-		}
-		upIndex = strings.Index(res, "/..")
-	}
-	return res
+	joinArgs := make([]string, 0, len(elems)+1)
+	joinArgs = append(joinArgs, u.Path)
+	joinArgs = append(joinArgs, elems...)
+	u.Path = path.Join(joinArgs...)
+	return u.String()
 }
 
 // RenderIssueIndexPattern renders issue indexes to corresponding links.
diff --git a/modules/markdown/markdown_test.go b/modules/markdown/markdown_test.go
index e6bc3683c6..c622009e82 100644
--- a/modules/markdown/markdown_test.go
+++ b/modules/markdown/markdown_test.go
@@ -59,6 +59,31 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, metas map
 		string(RenderIssueIndexPattern([]byte(input), AppSubURL, metas)))
 }
 
+func TestURLJoin(t *testing.T) {
+	type test struct {
+		Expected string
+		Base     string
+		Elements []string
+	}
+	newTest := func(expected, base string, elements ...string) test {
+		return test{Expected: expected, Base: base, Elements: elements}
+	}
+	for _, test := range []test{
+		newTest("https://try.gitea.io/a/b/c",
+			"https://try.gitea.io", "a/b", "c"),
+		newTest("https://try.gitea.io/a/b/c",
+			"https://try.gitea.io/", "/a/b/", "/c/"),
+		newTest("https://try.gitea.io/a/c",
+			"https://try.gitea.io/", "/a/./b/", "../c/"),
+		newTest("a/b/c",
+			"a", "b/c/"),
+		newTest("a/b/d",
+			"a/", "b/c/", "/../d/"),
+	} {
+		assert.Equal(t, test.Expected, URLJoin(test.Base, test.Elements...))
+	}
+}
+
 func TestRender_IssueIndexPattern(t *testing.T) {
 	// numeric: render inputs without valid mentions
 	test := func(s string) {
@@ -641,8 +666,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 		`<p>Wiki! Enjoy :)</p>
 
 <ul>
-<li><a href="` + baseURLContent + `Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
-<li><a href="` + baseURLContent + `Tips" rel="nofollow">Tips</a></li>
+<li><a href="` + baseURLContent + `/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
+<li><a href="` + baseURLContent + `/Tips" rel="nofollow">Tips</a></li>
 </ul>
 
 <p>Ideas and codes</p>
@@ -650,8 +675,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 <ul>
 <li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>)<a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">#786</a></li>
 <li>Node graph editors<a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">#306</a></li>
-<li><a href="` + baseURLContent + `memory_editor_example" rel="nofollow">Memory Editor</a></li>
-<li><a href="` + baseURLContent + `plot_var_example" rel="nofollow">Plot var helper</a></li>
+<li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li>
+<li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>
 </ul>
 `,
 		`<h2>What is Wine Staging?</h2>
@@ -665,15 +690,15 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 <table>
 <thead>
 <tr>
-<th><a href="` + baseURLImages + `images/icon-install.png" rel="nofollow"><img src="` + baseURLImages + `images/icon-install.png" alt="images/icon-install.png" title="icon-install.png"/></a></th>
-<th><a href="` + baseURLContent + `Installation" rel="nofollow">Installation</a></th>
+<th><a href="` + baseURLImages + `/images/icon-install.png" rel="nofollow"><img src="` + baseURLImages + `/images/icon-install.png" alt="images/icon-install.png" title="icon-install.png"/></a></th>
+<th><a href="` + baseURLContent + `/Installation" rel="nofollow">Installation</a></th>
 </tr>
 </thead>
 
 <tbody>
 <tr>
-<td><a href="` + baseURLImages + `images/icon-usage.png" rel="nofollow"><img src="` + baseURLImages + `images/icon-usage.png" alt="images/icon-usage.png" title="icon-usage.png"/></a></td>
-<td><a href="` + baseURLContent + `Usage" rel="nofollow">Usage</a></td>
+<td><a href="` + baseURLImages + `/images/icon-usage.png" rel="nofollow"><img src="` + baseURLImages + `/images/icon-usage.png" alt="images/icon-usage.png" title="icon-usage.png"/></a></td>
+<td><a href="` + baseURLContent + `/Usage" rel="nofollow">Usage</a></td>
 </tr>
 </tbody>
 </table>
@@ -682,9 +707,9 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 
 <ol>
 <li><a href="https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop" rel="nofollow">Package your libGDX application</a>
-<a href="` + baseURLImages + `images/1.png" rel="nofollow"><img src="` + baseURLImages + `images/1.png" alt="images/1.png" title="1.png"/></a></li>
+<a href="` + baseURLImages + `/images/1.png" rel="nofollow"><img src="` + baseURLImages + `/images/1.png" alt="images/1.png" title="1.png"/></a></li>
 <li>Perform a test run by hitting the Run! button.
-<a href="` + baseURLImages + `images/2.png" rel="nofollow"><img src="` + baseURLImages + `images/2.png" alt="images/2.png" title="2.png"/></a></li>
+<a href="` + baseURLImages + `/images/2.png" rel="nofollow"><img src="` + baseURLImages + `/images/2.png" alt="images/2.png" title="2.png"/></a></li>
 </ol>
 `,
 	}
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl
index af87e9c43d..cb928b2830 100644
--- a/templates/repo/wiki/new.tmpl
+++ b/templates/repo/wiki/new.tmpl
@@ -18,7 +18,7 @@
 				<input name="title" value="{{.title}}" autofocus required>
 			</div>
 			<div class="field">
-				<textarea id="edit_area" name="content" data-id="wiki-{{.old_title}}" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea required>
+				<textarea id="edit_area" name="content" data-id="wiki-{{.old_title}}" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/wiki">{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea required>
 			</div>
 			<div class="field">
 				<input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}">