diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 90309c9df9..90564c63b1 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1374,6 +1374,9 @@ issues.lock.reason = Reason for locking
 issues.lock.title = Lock conversation on this issue.
 issues.unlock.title = Unlock conversation on this issue.
 issues.comment_on_locked = You cannot comment on a locked issue.
+issues.delete = Delete
+issues.delete.title = Delete this issue?
+issues.delete.text = Do you really want to delete this issue? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived)
 issues.tracker = Time Tracker
 issues.start_tracking_short = Start Timer
 issues.start_tracking = Start Time Tracking
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index d0c3d3325a..a81b1f1962 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -852,6 +852,21 @@ func NewIssueChooseTemplate(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplIssueChoose)
 }
 
+// DeleteIssue deletes an issue
+func DeleteIssue(ctx *context.Context) {
+	issue := GetActionIssue(ctx)
+	if ctx.Written() {
+		return
+	}
+
+	if err := issue_service.DeleteIssue(ctx.User, ctx.Repo.GitRepo, issue); err != nil {
+		ctx.ServerError("DeleteIssueByID", err)
+		return
+	}
+
+	ctx.Redirect(fmt.Sprintf("%s/issues", ctx.Repo.Repository.HTMLURL()), http.StatusSeeOther)
+}
+
 // ValidateRepoMetas check and returns repository's meta information
 func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull bool) ([]int64, []int64, int64, int64) {
 	var (
diff --git a/routers/web/web.go b/routers/web/web.go
index d8c197fb96..9faa61e560 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -757,6 +757,7 @@ func RegisterRoutes(m *web.Route) {
 				m.Post("/reactions/{action}", bindIgnErr(forms.ReactionForm{}), repo.ChangeIssueReaction)
 				m.Post("/lock", reqRepoIssueWriter, bindIgnErr(forms.IssueLockForm{}), repo.LockIssue)
 				m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue)
+				m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
 			}, context.RepoMustNotBeArchived())
 			m.Group("/{index}", func() {
 				m.Get("/attachments", repo.GetIssueAttachments)
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index 6198b6a621..65c1cf75e8 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -645,6 +645,27 @@
 					</form>
 				</div>
 			</div>
+			<button class="fluid ui show-modal button negative mt-3" data-modal="#delete">
+				{{svg "octicon-trash"}}
+				{{.i18n.Tr "repo.issues.delete"}}
+			</button>
+			<div class="ui basic modal" id="delete">
+				<div class="ui icon header">
+					{{.i18n.Tr "repo.issues.delete.title"}}
+				</div>
+				<div class="content center">
+					<p>
+						{{.i18n.Tr "repo.issues.delete.text"}}
+					</p>
+				</div>
+				<form action="{{.Issue.Link}}/delete" method="post">
+					{{.CsrfTokenHtml}}
+					<div class="center actions">
+						<div class="ui basic cancel inverted button">{{.i18n.Tr "settings.cancel"}}</div>
+						<button class="ui basic red inverted button">{{.i18n.Tr "modal.yes"}}</button>
+					</div>
+				</form>
+			</div>
 		{{end}}
 	</div>
 </div>