diff --git a/models/issue.go b/models/issue.go
index 63074cd40c..bde5758b02 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -472,6 +472,18 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
 	}
 }
 
+// ReplyReference returns tokenized address to use for email reply headers
+func (issue *Issue) ReplyReference() string {
+	var path string
+	if issue.IsPull {
+		path = "pulls"
+	} else {
+		path = "issues"
+	}
+
+	return fmt.Sprintf("%s/%s/%d@%s", issue.Repo.FullName(), path, issue.Index, setting.Domain)
+}
+
 func (issue *Issue) addLabel(e *xorm.Session, label *Label, doer *User) error {
 	return newIssueLabel(e, issue, label, doer)
 }
diff --git a/models/mail.go b/models/mail.go
index 2bb07607a4..cd4e4bc804 100644
--- a/models/mail.go
+++ b/models/mail.go
@@ -156,7 +156,13 @@ func composeTplData(subject, body, link string) map[string]interface{} {
 }
 
 func composeIssueCommentMessage(issue *Issue, doer *User, content string, comment *Comment, tplName base.TplName, tos []string, info string) *mailer.Message {
-	subject := issue.mailSubject()
+	var subject string
+	if comment != nil {
+		subject = "Re: " + issue.mailSubject()
+	} else {
+		subject = issue.mailSubject()
+	}
+
 	err := issue.LoadRepo()
 	if err != nil {
 		log.Error("LoadRepo: %v", err)
@@ -179,6 +185,15 @@ func composeIssueCommentMessage(issue *Issue, doer *User, content string, commen
 
 	msg := mailer.NewMessageFrom(tos, doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String())
 	msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
+
+	// Set Message-ID on first message so replies know what to reference
+	if comment == nil {
+		msg.SetHeader("Message-ID", "<"+issue.ReplyReference()+">")
+	} else {
+		msg.SetHeader("In-Reply-To", "<"+issue.ReplyReference()+">")
+		msg.SetHeader("References", "<"+issue.ReplyReference()+">")
+	}
+
 	return msg
 }
 
diff --git a/models/mail_test.go b/models/mail_test.go
new file mode 100644
index 0000000000..51c52427f0
--- /dev/null
+++ b/models/mail_test.go
@@ -0,0 +1,87 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+	"html/template"
+	"testing"
+
+	"code.gitea.io/gitea/modules/setting"
+
+	"github.com/stretchr/testify/assert"
+)
+
+const tmpl = `
+<!DOCTYPE html>
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<title>{{.Subject}}</title>
+</head>
+
+<body>
+	<p>{{.Body}}</p>
+	<p>
+		---
+		<br>
+		<a href="{{.Link}}">View it on Gitea</a>.
+	</p>
+</body>
+</html>
+`
+
+func TestComposeIssueCommentMessage(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	var MailService setting.Mailer
+
+	MailService.From = "test@gitea.com"
+	setting.MailService = &MailService
+
+	doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, Owner: doer}).(*Repository)
+	issue := AssertExistsAndLoadBean(t, &Issue{ID: 1, Repo: repo, Poster: doer}).(*Issue)
+	comment := AssertExistsAndLoadBean(t, &Comment{ID: 2, Issue: issue}).(*Comment)
+
+	email := template.Must(template.New("issue/comment").Parse(tmpl))
+	InitMailRender(email)
+
+	tos := []string{"test@gitea.com", "test2@gitea.com"}
+	msg := composeIssueCommentMessage(issue, doer, "test body", comment, mailIssueComment, tos, "issue comment")
+
+	subject := msg.GetHeader("Subject")
+	inreplyTo := msg.GetHeader("In-Reply-To")
+	references := msg.GetHeader("References")
+
+	assert.Equal(t, subject[0], "Re: "+issue.mailSubject(), "Comment reply subject should contain Re:")
+	assert.Equal(t, inreplyTo[0], "<user2/repo1/issues/1@localhost>", "In-Reply-To header doesn't match")
+	assert.Equal(t, references[0], "<user2/repo1/issues/1@localhost>", "References header doesn't match")
+
+}
+
+func TestComposeIssueMessage(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+	var MailService setting.Mailer
+
+	MailService.From = "test@gitea.com"
+	setting.MailService = &MailService
+
+	doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, Owner: doer}).(*Repository)
+	issue := AssertExistsAndLoadBean(t, &Issue{ID: 1, Repo: repo, Poster: doer}).(*Issue)
+
+	email := template.Must(template.New("issue/comment").Parse(tmpl))
+	InitMailRender(email)
+
+	tos := []string{"test@gitea.com", "test2@gitea.com"}
+	msg := composeIssueCommentMessage(issue, doer, "test body", nil, mailIssueComment, tos, "issue create")
+
+	subject := msg.GetHeader("Subject")
+	messageID := msg.GetHeader("Message-ID")
+
+	assert.Equal(t, subject[0], issue.mailSubject(), "Subject not equal to issue.mailSubject()")
+	assert.Nil(t, msg.GetHeader("In-Reply-To"))
+	assert.Nil(t, msg.GetHeader("References"))
+	assert.Equal(t, messageID[0], "<user2/repo1/issues/1@localhost>", "Message-ID header doesn't match")
+}