diff --git a/models/attachment.go b/models/attachment.go
index 4a5101b385..808bc243dc 100644
--- a/models/attachment.go
+++ b/models/attachment.go
@@ -151,6 +151,11 @@ func GetAttachmentByUUID(uuid string) (*Attachment, error) {
 	return getAttachmentByUUID(x, uuid)
 }
 
+// GetAttachmentByReleaseIDFileName returns attachment by given releaseId and fileName.
+func GetAttachmentByReleaseIDFileName(releaseID int64, fileName string) (*Attachment, error) {
+	return getAttachmentByReleaseIDFileName(x, releaseID, fileName)
+}
+
 func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
 	attachments := make([]*Attachment, 0, 10)
 	return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
@@ -171,6 +176,18 @@ func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error)
 	return attachments, x.Where("comment_id=?", commentID).Find(&attachments)
 }
 
+// getAttachmentByReleaseIDFileName return a file based on the the following infos:
+func getAttachmentByReleaseIDFileName(e Engine, releaseID int64, fileName string) (*Attachment, error) {
+	attach := &Attachment{ReleaseID: releaseID, Name: fileName}
+	has, err := e.Get(attach)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, err
+	}
+	return attach, nil
+}
+
 // DeleteAttachment deletes the given attachment and optionally the associated file.
 func DeleteAttachment(a *Attachment, remove bool) error {
 	_, err := DeleteAttachments([]*Attachment{a}, remove)
diff --git a/models/release.go b/models/release.go
index c1c8b933d0..0c78707d6b 100644
--- a/models/release.go
+++ b/models/release.go
@@ -14,6 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
+
 	api "code.gitea.io/sdk/gitea"
 	"github.com/go-xorm/builder"
 )
@@ -283,6 +284,15 @@ func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions, page, pageSize
 	return rels, err
 }
 
+// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
+func GetReleasesByRepoIDAndNames(repoID int64, tagNames []string) (rels []*Release, err error) {
+	err = x.
+		Desc("created_unix").
+		In("tag_name", tagNames).
+		Find(&rels, Release{RepoID: repoID})
+	return rels, err
+}
+
 // GetReleaseCountByRepoID returns the count of releases of repository
 func GetReleaseCountByRepoID(repoID int64, opts FindReleasesOptions) (int64, error) {
 	return x.Where(opts.toConds(repoID)).Count(&Release{})
diff --git a/routers/repo/repo.go b/routers/repo/repo.go
index 236d66bd1f..6071b7a54a 100644
--- a/routers/repo/repo.go
+++ b/routers/repo/repo.go
@@ -310,6 +310,38 @@ func Action(ctx *context.Context) {
 	ctx.RedirectToFirst(ctx.Query("redirect_to"), ctx.Repo.RepoLink)
 }
 
+// RedirectDownload return a file based on the following infos:
+func RedirectDownload(ctx *context.Context) {
+	var (
+		vTag     = ctx.Params("vTag")
+		fileName = ctx.Params("fileName")
+	)
+	tagNames := []string{vTag}
+	curRepo := ctx.Repo.Repository
+	releases, err := models.GetReleasesByRepoIDAndNames(curRepo.ID, tagNames)
+	if err != nil {
+		if models.IsErrAttachmentNotExist(err) {
+			ctx.Error(404)
+			return
+		}
+		ctx.ServerError("RedirectDownload", err)
+		return
+	}
+	if len(releases) == 1 {
+		release := releases[0]
+		att, err := models.GetAttachmentByReleaseIDFileName(release.ID, fileName)
+		if err != nil {
+			ctx.Error(404)
+			return
+		}
+		if att != nil {
+			ctx.Redirect(setting.AppSubURL + "/attachments/" + att.UUID)
+			return
+		}
+	}
+	ctx.Error(404)
+}
+
 // Download download an archive of a repository
 func Download(ctx *context.Context) {
 	var (
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 5d9a6b31f0..b776d0c201 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -475,6 +475,9 @@ func RegisterRoutes(m *macaron.Macaron) {
 		}, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader)
 	}, reqSignIn)
 
+	// ***** Release Attachment Download without Signin
+	m.Get("/:username/:reponame/releases/download/:vTag/:fileName", ignSignIn, context.RepoAssignment(), repo.MustBeNotBare, repo.RedirectDownload)
+
 	m.Group("/:username/:reponame", func() {
 		m.Group("/settings", func() {
 			m.Combo("").Get(repo.Settings).
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl
index 7ee58c6b10..9df2f6cb81 100644
--- a/templates/repo/release/list.tmpl
+++ b/templates/repo/release/list.tmpl
@@ -14,7 +14,7 @@
 			{{end}}
 		</h2>
 		<ul id="release-list">
-			{{range .Releases}}
+			{{range $release := .Releases}}
 				<li class="ui grid">
 					<div class="ui four wide column meta">
 						{{if .IsTag}}
@@ -75,14 +75,14 @@
 									</li>
 									{{end}}
 									{{if .Attachments}}
-									{{range .Attachments}}
-									<li>
-										<a target="_blank" rel="noopener noreferrer" href="{{AppSubUrl}}/attachments/{{.UUID}}">
-											<strong><span class="ui image octicon octicon-package" title='{{.Name}}'></span> {{.Name}}</strong>
-											<span class="ui text grey right">{{.Size | FileSize}}</span>
-										</a>
-									</li>
-									{{end}}
+										{{range $attachment := .Attachments}}
+										<li>
+											<a target="_blank" rel="noopener noreferrer" href="{{$.RepoLink}}/releases/download/{{$release.TagName}}/{{$attachment.Name}}">
+												<strong><span class="ui image octicon octicon-package" title='{{$attachment.Name}}'></span> {{$attachment.Name}}</strong>
+												<span class="ui text grey right">{{$attachment.Size | FileSize}}</span>
+											</a>
+										</li>
+										{{end}}
 									{{end}}
 								</ul>
 							</div>