diff --git a/models/repo/attachment.go b/models/repo/attachment.go
index 93b83aae8a..df3b9cd213 100644
--- a/models/repo/attachment.go
+++ b/models/repo/attachment.go
@@ -65,13 +65,6 @@ func (a *Attachment) DownloadURL() string {
 	return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
 }
 
-//    _____   __    __                .__                           __
-//   /  _  \_/  |__/  |______    ____ |  |__   _____   ____   _____/  |_
-//  /  /_\  \   __\   __\__  \ _/ ___\|  |  \ /     \_/ __ \ /    \   __\
-// /    |    \  |  |  |  / __ \\  \___|   Y  \  Y Y  \  ___/|   |  \  |
-// \____|__  /__|  |__| (____  /\___  >___|  /__|_|  /\___  >___|  /__|
-//         \/                \/     \/     \/      \/     \/     \/
-
 // ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error.
 type ErrAttachmentNotExist struct {
 	ID   int64
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 0e28bde683..eee97cbc20 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -1034,7 +1034,7 @@ func Routes() *web.Route {
 						m.Group("/assets", func() {
 							m.Combo("").Get(repo.ListReleaseAttachments).
 								Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment)
-							m.Combo("/{asset}").Get(repo.GetReleaseAttachment).
+							m.Combo("/{attachment_id}").Get(repo.GetReleaseAttachment).
 								Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
 								Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment)
 						})
@@ -1179,7 +1179,7 @@ func Routes() *web.Route {
 								m.Combo("").
 									Get(repo.ListIssueCommentAttachments).
 									Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment)
-								m.Combo("/{asset}").
+								m.Combo("/{attachment_id}").
 									Get(repo.GetIssueCommentAttachment).
 									Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment).
 									Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment)
@@ -1231,7 +1231,7 @@ func Routes() *web.Route {
 							m.Combo("").
 								Get(repo.ListIssueAttachments).
 								Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment)
-							m.Combo("/{asset}").
+							m.Combo("/{attachment_id}").
 								Get(repo.GetIssueAttachment).
 								Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment).
 								Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment)
diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go
index 92e1138688..779da9fd7f 100644
--- a/routers/api/v1/repo/issue_attachment.go
+++ b/routers/api/v1/repo/issue_attachment.go
@@ -64,7 +64,7 @@ func GetIssueAttachment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAttachment(attach))
+	ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
 }
 
 // ListIssueAttachments lists all attachments of the issue
@@ -194,7 +194,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment))
+	ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
 }
 
 // EditIssueAttachment updates the given attachment
@@ -254,7 +254,7 @@ func EditIssueAttachment(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "UpdateAttachment", err)
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment))
+	ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
 }
 
 // DeleteIssueAttachment delete a given attachment
@@ -332,7 +332,7 @@ func getIssueAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment
 }
 
 func getIssueAttachmentSafeRead(ctx *context.APIContext, issue *issues_model.Issue) *repo_model.Attachment {
-	attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset"))
+	attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id"))
 	if err != nil {
 		ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
 		return nil
diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go
index 5616e255ad..c2392126db 100644
--- a/routers/api/v1/repo/issue_comment.go
+++ b/routers/api/v1/repo/issue_comment.go
@@ -103,7 +103,7 @@ func ListIssueComments(ctx *context.APIContext) {
 	apiComments := make([]*api.Comment, len(comments))
 	for i, comment := range comments {
 		comment.Issue = issue
-		apiComments[i] = convert.ToComment(ctx, comments[i])
+		apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
 	}
 
 	ctx.SetTotalCountHeader(totalCount)
@@ -191,7 +191,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
 	for _, comment := range comments {
 		if comment.Type != issues_model.CommentTypeCode && isXRefCommentAccessible(ctx, ctx.Doer, comment, issue.RepoID) {
 			comment.Issue = issue
-			apiComments = append(apiComments, convert.ToTimelineComment(ctx, comment, ctx.Doer))
+			apiComments = append(apiComments, convert.ToTimelineComment(ctx, issue.Repo, comment, ctx.Doer))
 		}
 	}
 
@@ -308,7 +308,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
 		return
 	}
 	for i := range comments {
-		apiComments[i] = convert.ToComment(ctx, comments[i])
+		apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
 	}
 
 	ctx.SetTotalCountHeader(totalCount)
@@ -368,7 +368,7 @@ func CreateIssueComment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToComment(ctx, comment))
+	ctx.JSON(http.StatusCreated, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
 }
 
 // GetIssueComment Get a comment by ID
@@ -436,7 +436,7 @@ func GetIssueComment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment))
+	ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
 }
 
 // EditIssueComment modify a comment of an issue
@@ -561,7 +561,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment))
+	ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
 }
 
 // DeleteIssueComment delete a comment from an issue
diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go
index 6fe4dbc977..121e3f10e0 100644
--- a/routers/api/v1/repo/issue_comment_attachment.go
+++ b/routers/api/v1/repo/issue_comment_attachment.go
@@ -68,7 +68,7 @@ func GetIssueCommentAttachment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAttachment(attachment))
+	ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
 }
 
 // ListIssueCommentAttachments lists all attachments of the comment
@@ -110,7 +110,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAttachments(comment.Attachments))
+	ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx.Repo.Repository, comment.Attachments))
 }
 
 // CreateIssueCommentAttachment creates an attachment and saves the given file
@@ -201,7 +201,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment))
+	ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
 }
 
 // EditIssueCommentAttachment updates the given attachment
@@ -259,7 +259,7 @@ func EditIssueCommentAttachment(ctx *context.APIContext) {
 	if err := repo_model.UpdateAttachment(ctx, attach); err != nil {
 		ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
 	}
-	ctx.JSON(http.StatusCreated, convert.ToAttachment(attach))
+	ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
 }
 
 // DeleteIssueCommentAttachment delete a given attachment
@@ -352,7 +352,7 @@ func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues
 }
 
 func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_model.Comment) *repo_model.Attachment {
-	attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset"))
+	attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id"))
 	if err != nil {
 		ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
 		return nil
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index e9693dd053..af7199d1d6 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -64,7 +64,7 @@ func GetRelease(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release))
+	ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
 }
 
 // GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
@@ -105,7 +105,7 @@ func GetLatestRelease(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release))
+	ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
 }
 
 // ListReleases list a repository's releases
@@ -174,7 +174,7 @@ func ListReleases(ctx *context.APIContext) {
 			ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 			return
 		}
-		rels[i] = convert.ToRelease(ctx, release)
+		rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)
 	}
 
 	filteredCount, err := repo_model.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
@@ -272,7 +272,7 @@ func CreateRelease(ctx *context.APIContext) {
 			return
 		}
 	}
-	ctx.JSON(http.StatusCreated, convert.ToRelease(ctx, rel))
+	ctx.JSON(http.StatusCreated, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel))
 }
 
 // EditRelease edit a release
@@ -357,7 +357,7 @@ func EditRelease(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToRelease(ctx, rel))
+	ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel))
 }
 
 // DeleteRelease delete a release from a repository
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
index 305b2808df..a7d73acceb 100644
--- a/routers/api/v1/repo/release_attachment.go
+++ b/routers/api/v1/repo/release_attachment.go
@@ -52,7 +52,7 @@ func GetReleaseAttachment(ctx *context.APIContext) {
 	//     "$ref": "#/responses/Attachment"
 
 	releaseID := ctx.ParamsInt64(":id")
-	attachID := ctx.ParamsInt64(":asset")
+	attachID := ctx.ParamsInt64(":attachment_id")
 	attach, err := repo_model.GetAttachmentByID(ctx, attachID)
 	if err != nil {
 		if repo_model.IsErrAttachmentNotExist(err) {
@@ -68,7 +68,7 @@ func GetReleaseAttachment(ctx *context.APIContext) {
 		return
 	}
 	// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
-	ctx.JSON(http.StatusOK, convert.ToAttachment(attach))
+	ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
 }
 
 // ListReleaseAttachments lists all attachments of the release
@@ -117,7 +117,7 @@ func ListReleaseAttachments(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release).Attachments)
+	ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release).Attachments)
 }
 
 // CreateReleaseAttachment creates an attachment and saves the given file
@@ -209,7 +209,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(http.StatusCreated, convert.ToAttachment(attach))
+	ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
 }
 
 // EditReleaseAttachment updates the given attachment
@@ -256,7 +256,7 @@ func EditReleaseAttachment(ctx *context.APIContext) {
 
 	// Check if release exists an load release
 	releaseID := ctx.ParamsInt64(":id")
-	attachID := ctx.ParamsInt64(":asset")
+	attachID := ctx.ParamsInt64(":attachment_id")
 	attach, err := repo_model.GetAttachmentByID(ctx, attachID)
 	if err != nil {
 		if repo_model.IsErrAttachmentNotExist(err) {
@@ -279,7 +279,7 @@ func EditReleaseAttachment(ctx *context.APIContext) {
 	if err := repo_model.UpdateAttachment(ctx, attach); err != nil {
 		ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
 	}
-	ctx.JSON(http.StatusCreated, convert.ToAttachment(attach))
+	ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
 }
 
 // DeleteReleaseAttachment delete a given attachment
@@ -318,7 +318,7 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
 
 	// Check if release exists an load release
 	releaseID := ctx.ParamsInt64(":id")
-	attachID := ctx.ParamsInt64(":asset")
+	attachID := ctx.ParamsInt64(":attachment_id")
 	attach, err := repo_model.GetAttachmentByID(ctx, attachID)
 	if err != nil {
 		if repo_model.IsErrAttachmentNotExist(err) {
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index 051ee8f22e..a03edfafcf 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -63,7 +63,7 @@ func GetReleaseByTag(ctx *context.APIContext) {
 		ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
 		return
 	}
-	ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release))
+	ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
 }
 
 // DeleteReleaseByTag delete a release from a repository by tag name
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 317f762fb1..31bcbd7c21 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -2058,7 +2058,7 @@ func GetIssueInfo(ctx *context.Context) {
 		}
 	}
 
-	ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
+	ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue))
 }
 
 // UpdateIssueTitle change issue's title
@@ -2563,7 +2563,7 @@ func SearchIssues(ctx *context.Context) {
 	}
 
 	ctx.SetTotalCountHeader(filteredCount)
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
 }
 
 func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
@@ -2724,7 +2724,7 @@ func ListIssues(ctx *context.Context) {
 	}
 
 	ctx.SetTotalCountHeader(filteredCount)
-	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
+	ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
 }
 
 func BatchDeleteIssues(ctx *context.Context) {
@@ -3290,7 +3290,7 @@ func GetIssueAttachments(ctx *context.Context) {
 	}
 	attachments := make([]*api.Attachment, len(issue.Attachments))
 	for i := 0; i < len(issue.Attachments); i++ {
-		attachments[i] = convert.ToAttachment(issue.Attachments[i])
+		attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i])
 	}
 	ctx.JSON(http.StatusOK, attachments)
 }
@@ -3314,7 +3314,7 @@ func GetCommentAttachments(ctx *context.Context) {
 		return
 	}
 	for i := 0; i < len(comment.Attachments); i++ {
-		attachments = append(attachments, convert.ToAttachment(comment.Attachments[i]))
+		attachments = append(attachments, convert.ToAttachment(ctx.Repo.Repository, comment.Attachments[i]))
 	}
 	ctx.JSON(http.StatusOK, attachments)
 }
diff --git a/services/actions/notifier.go b/services/actions/notifier.go
index 507eeaacf6..cfe2e284da 100644
--- a/services/actions/notifier.go
+++ b/services/actions/notifier.go
@@ -171,7 +171,7 @@ func (n *actionsNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us
 			WithPayload(&api.IssueCommentPayload{
 				Action:     api.HookIssueCommentCreated,
 				Issue:      convert.ToAPIIssue(ctx, issue),
-				Comment:    convert.ToComment(ctx, comment),
+				Comment:    convert.ToAPIComment(ctx, repo, comment),
 				Repository: convert.ToRepo(ctx, repo, permission),
 				Sender:     convert.ToUser(ctx, doer, nil),
 				IsPull:     true,
@@ -185,7 +185,7 @@ func (n *actionsNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us
 		WithPayload(&api.IssueCommentPayload{
 			Action:     api.HookIssueCommentCreated,
 			Issue:      convert.ToAPIIssue(ctx, issue),
-			Comment:    convert.ToComment(ctx, comment),
+			Comment:    convert.ToAPIComment(ctx, repo, comment),
 			Repository: convert.ToRepo(ctx, repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 			IsPull:     false,
diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go
index c4c2a0df29..90ad3001ba 100644
--- a/services/actions/notifier_helper.go
+++ b/services/actions/notifier_helper.go
@@ -260,7 +260,7 @@ func notifyRelease(ctx context.Context, doer *user_model.User, rel *repo_model.R
 		WithRef(git.RefNameFromTag(rel.TagName).String()).
 		WithPayload(&api.ReleasePayload{
 			Action:     action,
-			Release:    convert.ToRelease(ctx, rel),
+			Release:    convert.ToAPIRelease(ctx, rel.Repo, rel),
 			Repository: convert.ToRepo(ctx, rel.Repo, permission),
 			Sender:     convert.ToUser(ctx, doer, nil),
 		}).
diff --git a/services/convert/activity.go b/services/convert/activity.go
index 71a2722a49..01fef73e58 100644
--- a/services/convert/activity.go
+++ b/services/convert/activity.go
@@ -37,7 +37,7 @@ func ToActivity(ctx context.Context, ac *activities_model.Action, doer *user_mod
 
 	if ac.Comment != nil {
 		result.CommentID = ac.CommentID
-		result.Comment = ToComment(ctx, ac.Comment)
+		result.Comment = ToAPIComment(ctx, ac.Repo, ac.Comment)
 	}
 
 	return result
diff --git a/services/convert/attachment.go b/services/convert/attachment.go
index ddba181a12..ab36a1c577 100644
--- a/services/convert/attachment.go
+++ b/services/convert/attachment.go
@@ -4,12 +4,38 @@
 package convert
 
 import (
+	"strconv"
+
 	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
-// ToAttachment converts models.Attachment to api.Attachment
-func ToAttachment(a *repo_model.Attachment) *api.Attachment {
+func WebAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string {
+	return attach.DownloadURL()
+}
+
+func APIAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string {
+	if attach.CustomDownloadURL != "" {
+		return attach.CustomDownloadURL
+	}
+
+	// /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}
+	return setting.AppURL + "api/repos/" + repo.FullName() + "/releases/" + strconv.FormatInt(attach.ReleaseID, 10) + "/assets/" + strconv.FormatInt(attach.ID, 10)
+}
+
+// ToAttachment converts models.Attachment to api.Attachment for API usage
+func ToAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
+	return toAttachment(repo, a, WebAssetDownloadURL)
+}
+
+// ToAPIAttachment converts models.Attachment to api.Attachment for API usage
+func ToAPIAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
+	return toAttachment(repo, a, APIAssetDownloadURL)
+}
+
+// toAttachment converts models.Attachment to api.Attachment for API usage
+func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment {
 	return &api.Attachment{
 		ID:            a.ID,
 		Name:          a.Name,
@@ -17,14 +43,18 @@ func ToAttachment(a *repo_model.Attachment) *api.Attachment {
 		DownloadCount: a.DownloadCount,
 		Size:          a.Size,
 		UUID:          a.UUID,
-		DownloadURL:   a.DownloadURL(),
+		DownloadURL:   getDownloadURL(repo, a), // for web request json and api request json, return different download urls
 	}
 }
 
-func ToAttachments(attachments []*repo_model.Attachment) []*api.Attachment {
+func ToAPIAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment {
+	return toAttachments(repo, attachments, APIAssetDownloadURL)
+}
+
+func toAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment {
 	converted := make([]*api.Attachment, 0, len(attachments))
 	for _, attachment := range attachments {
-		converted = append(converted, ToAttachment(attachment))
+		converted = append(converted, toAttachment(repo, attachment, getDownloadURL))
 	}
 	return converted
 }
diff --git a/services/convert/issue.go b/services/convert/issue.go
index bcb1618e8f..d81840f025 100644
--- a/services/convert/issue.go
+++ b/services/convert/issue.go
@@ -19,11 +19,19 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 )
 
+func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
+	return toIssue(ctx, issue, WebAssetDownloadURL)
+}
+
 // ToAPIIssue converts an Issue to API format
 // it assumes some fields assigned with values:
 // Required - Poster, Labels,
 // Optional - Milestone, Assignee, PullRequest
 func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
+	return toIssue(ctx, issue, APIAssetDownloadURL)
+}
+
+func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
 	if err := issue.LoadLabels(ctx); err != nil {
 		return &api.Issue{}
 	}
@@ -40,7 +48,7 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
 		Poster:      ToUser(ctx, issue.Poster, nil),
 		Title:       issue.Title,
 		Body:        issue.Content,
-		Attachments: ToAttachments(issue.Attachments),
+		Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL),
 		Ref:         issue.Ref,
 		State:       issue.State(),
 		IsLocked:    issue.IsLocked,
@@ -105,6 +113,15 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
 	return apiIssue
 }
 
+// ToIssueList converts an IssueList to API format
+func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
+	result := make([]*api.Issue, len(il))
+	for i := range il {
+		result[i] = ToIssue(ctx, il[i])
+	}
+	return result
+}
+
 // ToAPIIssueList converts an IssueList to API format
 func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
 	result := make([]*api.Issue, len(il))
diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go
index db48faa69e..b0145c38e6 100644
--- a/services/convert/issue_comment.go
+++ b/services/convert/issue_comment.go
@@ -14,8 +14,8 @@ import (
 	"code.gitea.io/gitea/modules/util"
 )
 
-// ToComment converts a issues_model.Comment to the api.Comment format
-func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment {
+// ToAPIComment converts a issues_model.Comment to the api.Comment format for API usage
+func ToAPIComment(ctx context.Context, repo *repo_model.Repository, c *issues_model.Comment) *api.Comment {
 	return &api.Comment{
 		ID:          c.ID,
 		Poster:      ToUser(ctx, c.Poster, nil),
@@ -23,14 +23,14 @@ func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment {
 		IssueURL:    c.IssueURL(),
 		PRURL:       c.PRURL(),
 		Body:        c.Content,
-		Attachments: ToAttachments(c.Attachments),
+		Attachments: ToAPIAttachments(repo, c.Attachments),
 		Created:     c.CreatedUnix.AsTime(),
 		Updated:     c.UpdatedUnix.AsTime(),
 	}
 }
 
 // ToTimelineComment converts a issues_model.Comment to the api.TimelineComment format
-func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_model.User) *api.TimelineComment {
+func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issues_model.Comment, doer *user_model.User) *api.TimelineComment {
 	err := c.LoadMilestone(ctx)
 	if err != nil {
 		log.Error("LoadMilestone: %v", err)
@@ -143,7 +143,7 @@ func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_
 			log.Error("LoadPoster: %v", err)
 			return nil
 		}
-		comment.RefComment = ToComment(ctx, com)
+		comment.RefComment = ToAPIComment(ctx, repo, com)
 	}
 
 	if c.Label != nil {
diff --git a/services/convert/release.go b/services/convert/release.go
index ca28aa0d6b..d8aa46d432 100644
--- a/services/convert/release.go
+++ b/services/convert/release.go
@@ -10,8 +10,8 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 )
 
-// ToRelease convert a repo_model.Release to api.Release
-func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release {
+// ToAPIRelease convert a repo_model.Release to api.Release
+func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_model.Release) *api.Release {
 	return &api.Release{
 		ID:           r.ID,
 		TagName:      r.TagName,
@@ -27,6 +27,6 @@ func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release {
 		CreatedAt:    r.CreatedUnix.AsTime(),
 		PublishedAt:  r.CreatedUnix.AsTime(),
 		Publisher:    ToUser(ctx, r.Publisher, nil),
-		Attachments:  ToAttachments(r.Attachments),
+		Attachments:  ToAPIAttachments(repo, r.Attachments),
 	}
 }
diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go
index 3332d5d4aa..23080a5a35 100644
--- a/services/webhook/notifier.go
+++ b/services/webhook/notifier.go
@@ -386,7 +386,7 @@ func (m *webhookNotifier) NotifyUpdateComment(ctx context.Context, doer *user_mo
 	if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
 		Action:  api.HookIssueCommentEdited,
 		Issue:   convert.ToAPIIssue(ctx, c.Issue),
-		Comment: convert.ToComment(ctx, c),
+		Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c),
 		Changes: &api.ChangesPayload{
 			Body: &api.ChangesFromPayload{
 				From: oldContent,
@@ -414,7 +414,7 @@ func (m *webhookNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us
 	if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
 		Action:     api.HookIssueCommentCreated,
 		Issue:      convert.ToAPIIssue(ctx, issue),
-		Comment:    convert.ToComment(ctx, comment),
+		Comment:    convert.ToAPIComment(ctx, repo, comment),
 		Repository: convert.ToRepo(ctx, repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
 		IsPull:     issue.IsPull,
@@ -451,7 +451,7 @@ func (m *webhookNotifier) NotifyDeleteComment(ctx context.Context, doer *user_mo
 	if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
 		Action:     api.HookIssueCommentDeleted,
 		Issue:      convert.ToAPIIssue(ctx, comment.Issue),
-		Comment:    convert.ToComment(ctx, comment),
+		Comment:    convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
 		Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
 		IsPull:     comment.Issue.IsPull,
@@ -808,7 +808,7 @@ func sendReleaseHook(ctx context.Context, doer *user_model.User, rel *repo_model
 	permission, _ := access_model.GetUserRepoPermission(ctx, rel.Repo, doer)
 	if err := PrepareWebhooks(ctx, EventSource{Repository: rel.Repo}, webhook_module.HookEventRelease, &api.ReleasePayload{
 		Action:     action,
-		Release:    convert.ToRelease(ctx, rel),
+		Release:    convert.ToAPIRelease(ctx, rel.Repo, rel),
 		Repository: convert.ToRepo(ctx, rel.Repo, permission),
 		Sender:     convert.ToUser(ctx, doer, nil),
 	}); err != nil {
diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go
index 8aa73dd368..e211376c3c 100644
--- a/tests/integration/api_comment_attachment_test.go
+++ b/tests/integration/api_comment_attachment_test.go
@@ -45,11 +45,12 @@ func TestAPIGetCommentAttachment(t *testing.T) {
 	var apiAttachment api.Attachment
 	DecodeJSON(t, resp, &apiAttachment)
 
-	expect := convert.ToAttachment(attachment)
+	expect := convert.ToAPIAttachment(repo, attachment)
 	assert.Equal(t, expect.ID, apiAttachment.ID)
 	assert.Equal(t, expect.Name, apiAttachment.Name)
 	assert.Equal(t, expect.UUID, apiAttachment.UUID)
 	assert.Equal(t, expect.Created.Unix(), apiAttachment.Created.Unix())
+	assert.Equal(t, expect.DownloadURL, apiAttachment.DownloadURL)
 }
 
 func TestAPIListCommentAttachments(t *testing.T) {
diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go
index c773afab3d..ee648210e5 100644
--- a/tests/integration/api_comment_test.go
+++ b/tests/integration/api_comment_test.go
@@ -128,7 +128,7 @@ func TestAPIGetComment(t *testing.T) {
 	DecodeJSON(t, resp, &apiComment)
 
 	assert.NoError(t, comment.LoadPoster(db.DefaultContext))
-	expect := convert.ToComment(db.DefaultContext, comment)
+	expect := convert.ToAPIComment(db.DefaultContext, repo, comment)
 
 	assert.Equal(t, expect.ID, apiComment.ID)
 	assert.Equal(t, expect.Poster.FullName, apiComment.Poster.FullName)