diff --git a/modules/markup/html.go b/modules/markup/html.go
index f279e23bff..4d6574a596 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -830,7 +830,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 		reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
 		if exttrack && !ref.IsPull {
 			ctx.Metas["index"] = ref.Issue
-			link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue")
+			link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue ref-external-issue")
 		} else {
 			// Path determines the type of link that will be rendered. It's unknown at this point whether
 			// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 330750a47a..dbad350de2 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -96,12 +96,14 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
 	// numeric: render inputs with valid mentions
 	test := func(s, expectedFmt, marker string, indices ...int) {
 		var path, prefix string
+		isExternal := false
 		if marker == "!" {
 			path = "pulls"
 			prefix = "http://localhost:3000/someUser/someRepo/pulls/"
 		} else {
 			path = "issues"
 			prefix = "https://someurl.com/someUser/someRepo/"
+			isExternal = true
 		}
 
 		links := make([]interface{}, len(indices))
@@ -111,8 +113,13 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
 		expectedNil := fmt.Sprintf(expectedFmt, links...)
 		testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{Metas: localMetas})
 
+		class := "ref-issue"
+		if isExternal {
+			class += " ref-external-issue"
+		}
+
 		for i, index := range indices {
-			links[i] = numericIssueLink(prefix, "ref-issue", index, marker)
+			links[i] = numericIssueLink(prefix, class, index, marker)
 		}
 		expectedNum := fmt.Sprintf(expectedFmt, links...)
 		testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{Metas: numericMetas})
@@ -178,7 +185,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
 	test := func(s, expectedFmt string, names ...string) {
 		links := make([]interface{}, len(names))
 		for i, name := range names {
-			links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue", name)
+			links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
 		}
 		expected := fmt.Sprintf(expectedFmt, links...)
 		testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: alphanumericMetas})
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 9342d65de5..c8f9de33b5 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -66,7 +66,7 @@ func createDefaultPolicy() *bluemonday.Policy {
 	}
 
 	// Allow classes for anchors
-	policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")
+	policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue( ref-external-issue)?`)).OnElements("a")
 
 	// Allow classes for task lists
 	policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")
diff --git a/web_src/js/features/contextpopup.js b/web_src/js/features/contextpopup.js
index 8583c6253c..b97f77275a 100644
--- a/web_src/js/features/contextpopup.js
+++ b/web_src/js/features/contextpopup.js
@@ -7,6 +7,9 @@ export default function initContextPopups() {
   if (!refIssues.length) return;
 
   refIssues.each(function () {
+    if ($(this).hasClass('ref-external-issue')) {
+      return;
+    }
     const [index, _issues, repo, owner] = $(this).attr('href').replace(/[#?].*$/, '').split('/').reverse();
 
     const el = document.createElement('div');