diff --git a/modules/base/markdown.go b/modules/base/markdown.go
index d55e98dd54..53eee4b3e4 100644
--- a/modules/base/markdown.go
+++ b/modules/base/markdown.go
@@ -14,6 +14,7 @@ import (
 	"regexp"
 	"strings"
 
+	"github.com/Unknwon/com"
 	"github.com/russross/blackfriday"
 	"golang.org/x/net/html"
 
@@ -99,13 +100,26 @@ func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte,
 	options.Renderer.Link(out, link, title, content)
 }
 
+var (
+	svgSuffix         = []byte(".svg")
+	svgSuffixWithMark = []byte(".svg?")
+)
+
 func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
 	prefix := strings.Replace(options.urlPrefix, "/src/", "/raw/", 1)
-	if len(link) > 0 && !isLink(link) {
-		if link[0] != '/' {
-			prefix += "/"
+	if len(link) > 0 {
+		if isLink(link) {
+			// External link with .svg suffix usually means CI status.
+			if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) {
+				options.Renderer.Image(out, link, title, alt)
+				return
+			}
+		} else {
+			if link[0] != '/' {
+				prefix += "/"
+			}
+			link = []byte(prefix + string(link))
 		}
-		link = []byte(prefix + string(link))
 	}
 
 	out.WriteString(`<a href="`)
@@ -236,12 +250,16 @@ var (
 	rightAngleBracket = []byte(">")
 )
 
+var noEndTags = []string{"img", "input", "br", "hr"}
+
 // PostProcessMarkdown treats different types of HTML differently,
 // and only renders special links for plain text blocks.
 func PostProcessMarkdown(rawHtml []byte, urlPrefix string) []byte {
-	var startTag string
+	startTags := make([]string, 0, 5)
 	var buf bytes.Buffer
 	tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml))
+
+OUTER_LOOP:
 	for html.ErrorToken != tokenizer.Next() {
 		token := tokenizer.Token()
 		switch token.Type {
@@ -249,26 +267,32 @@ func PostProcessMarkdown(rawHtml []byte, urlPrefix string) []byte {
 			buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix))
 
 		case html.StartTagToken:
-			startTag = token.Data
 			buf.WriteString(token.String())
 			tagName := token.Data
 			// If this is an excluded tag, we skip processing all output until a close tag is encountered.
 			if strings.EqualFold("a", tagName) || strings.EqualFold("code", tagName) || strings.EqualFold("pre", tagName) {
 				for html.ErrorToken != tokenizer.Next() {
 					token = tokenizer.Token()
+
 					// Copy the token to the output verbatim
 					buf.WriteString(token.String())
 					// If this is the close tag, we are done
-					if html.EndTagToken == token.Type && strings.EqualFold(tagName, token.Data) {
+					if token.Type == html.EndTagToken && strings.EqualFold(tagName, token.Data) {
 						break
 					}
 				}
+				continue OUTER_LOOP
+			}
+
+			if !com.IsSliceContainsStr(noEndTags, token.Data) {
+				startTags = append(startTags, token.Data)
 			}
 
 		case html.EndTagToken:
 			buf.Write(leftAngleBracket)
-			buf.WriteString(startTag)
+			buf.WriteString(startTags[len(startTags)-1])
 			buf.Write(rightAngleBracket)
+			startTags = startTags[:len(startTags)-1]
 		default:
 			buf.WriteString(token.String())
 		}