diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml
index 166ee0da9b..eac1c53763 100644
--- a/models/fixtures/issue.yml
+++ b/models/fixtures/issue.yml
@@ -321,3 +321,20 @@
   created_unix: 946684830
   updated_unix: 978307200
   is_locked: false
+
+-
+  id: 20
+  repo_id: 10
+  index: 2
+  poster_id: 8
+  original_author_id: 0
+  name: issue for pr
+  content: content
+  milestone_id: 0
+  priority: 0
+  is_closed: false
+  is_pull: false
+  num_comments: 0
+  created_unix: 946684830
+  updated_unix: 978307200
+  is_locked: false
diff --git a/models/fixtures/issue_index.yml b/models/fixtures/issue_index.yml
index de6e955804..ceae892d99 100644
--- a/models/fixtures/issue_index.yml
+++ b/models/fixtures/issue_index.yml
@@ -9,7 +9,7 @@
   max_index: 2
 -
   group_id: 10
-  max_index: 1
+  max_index: 2
 -
   group_id: 32
   max_index: 2
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index d30633543b..7d2ee661c3 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -283,7 +283,7 @@
   num_watches: 0
   num_stars: 0
   num_forks: 1
-  num_issues: 0
+  num_issues: 1
   num_closed_issues: 0
   num_pulls: 1
   num_closed_pulls: 0
diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go
index 26bc87f511..f8e651402c 100644
--- a/models/issues/issue_test.go
+++ b/models/issues/issue_test.go
@@ -405,7 +405,7 @@ func TestCountIssues(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	count, err := issues_model.CountIssues(db.DefaultContext, &issues_model.IssuesOptions{})
 	assert.NoError(t, err)
-	assert.EqualValues(t, 19, count)
+	assert.EqualValues(t, 20, count)
 }
 
 func TestIssueLoadAttributes(t *testing.T) {
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 8267dd53e2..5d6391fcc0 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2838,7 +2838,7 @@ users.cannot_delete_self = "You cannot delete yourself"
 users.still_own_repo = This user still owns one or more repositories. Delete or transfer these repositories first.
 users.still_has_org = This user is a member of an organization. Remove the user from any organizations first.
 users.purge = Purge User
-users.purge_help = Forcibly delete user and any repositories, organizations, and packages owned by the user. All comments will be deleted too.
+users.purge_help = Forcibly delete user and any repositories, organizations, and packages owned by the user. All comments and issues posted by this user will also be deleted.
 users.still_own_packages = This user still owns one or more packages, delete these packages first.
 users.deletion_success = The user account has been deleted.
 users.reset_2fa = Reset 2FA
diff --git a/services/user/delete.go b/services/user/delete.go
index 0f33d712a4..8abba6cb0b 100644
--- a/services/user/delete.go
+++ b/services/user/delete.go
@@ -22,6 +22,7 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
+	issue_service "code.gitea.io/gitea/services/issue"
 
 	"xorm.io/builder"
 )
@@ -127,6 +128,31 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
 		}
 	}
 
+	// ***** START: Issues *****
+	if purge {
+		const batchSize = 50
+
+		for {
+			issues := make([]*issues_model.Issue, 0, batchSize)
+			if err = e.Where("poster_id=?", u.ID).Limit(batchSize, 0).Find(&issues); err != nil {
+				return err
+			}
+			if len(issues) == 0 {
+				break
+			}
+
+			for _, issue := range issues {
+				// NOTE: Don't open git repositories just to remove the reference data,
+				// `git gc` is able to remove that reference which is run as a cron job
+				// by default. Also use the deleted user as doer to delete the issue.
+				if err = issue_service.DeleteIssue(ctx, u, nil, issue); err != nil {
+					return err
+				}
+			}
+		}
+	}
+	// ***** END: Issues *****
+
 	// ***** START: Branch Protections *****
 	{
 		const batchSize = 50
diff --git a/tests/integration/admin_user_test.go b/tests/integration/admin_user_test.go
index 669060c787..682d632a0f 100644
--- a/tests/integration/admin_user_test.go
+++ b/tests/integration/admin_user_test.go
@@ -75,9 +75,10 @@ func TestAdminDeleteUser(t *testing.T) {
 	csrf := GetCSRF(t, session, "/admin/users/8/edit")
 	req := NewRequestWithValues(t, "POST", "/admin/users/8/delete", map[string]string{
 		"_csrf": csrf,
+		"purge": "true",
 	})
 	session.MakeRequest(t, req, http.StatusSeeOther)
 
-	assertUserDeleted(t, 8)
+	assertUserDeleted(t, 8, true)
 	unittest.CheckConsistencyFor(t, &user_model.User{})
 }
diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go
index 61408e2c50..e2aa98ad41 100644
--- a/tests/integration/api_issue_test.go
+++ b/tests/integration/api_issue_test.go
@@ -370,7 +370,7 @@ func TestAPISearchIssues(t *testing.T) {
 	token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadIssue)
 
 	// as this API was used in the frontend, it uses UI page size
-	expectedIssueCount := 17 // from the fixtures
+	expectedIssueCount := 18 // from the fixtures
 	if expectedIssueCount > setting.UI.IssuePagingNum {
 		expectedIssueCount = setting.UI.IssuePagingNum
 	}
@@ -394,7 +394,7 @@ func TestAPISearchIssues(t *testing.T) {
 	req = NewRequest(t, "GET", link.String())
 	resp = MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
-	assert.Len(t, apiIssues, 10)
+	assert.Len(t, apiIssues, 11)
 	query.Del("since")
 	query.Del("before")
 
@@ -410,15 +410,15 @@ func TestAPISearchIssues(t *testing.T) {
 	req = NewRequest(t, "GET", link.String())
 	resp = MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
-	assert.EqualValues(t, "19", resp.Header().Get("X-Total-Count"))
-	assert.Len(t, apiIssues, 19)
+	assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count"))
+	assert.Len(t, apiIssues, 20)
 
 	query.Add("limit", "10")
 	link.RawQuery = query.Encode()
 	req = NewRequest(t, "GET", link.String())
 	resp = MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
-	assert.EqualValues(t, "19", resp.Header().Get("X-Total-Count"))
+	assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count"))
 	assert.Len(t, apiIssues, 10)
 
 	query = url.Values{"assigned": {"true"}, "state": {"all"}, "token": {token}}
@@ -468,7 +468,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
 	// as this API was used in the frontend, it uses UI page size
-	expectedIssueCount := 17 // from the fixtures
+	expectedIssueCount := 18 // from the fixtures
 	if expectedIssueCount > setting.UI.IssuePagingNum {
 		expectedIssueCount = setting.UI.IssuePagingNum
 	}
diff --git a/tests/integration/api_nodeinfo_test.go b/tests/integration/api_nodeinfo_test.go
index c99e9ef59b..fb35d72ac2 100644
--- a/tests/integration/api_nodeinfo_test.go
+++ b/tests/integration/api_nodeinfo_test.go
@@ -33,7 +33,7 @@ func TestNodeinfo(t *testing.T) {
 		assert.True(t, nodeinfo.OpenRegistrations)
 		assert.Equal(t, "gitea", nodeinfo.Software.Name)
 		assert.Equal(t, 25, nodeinfo.Usage.Users.Total)
-		assert.Equal(t, 19, nodeinfo.Usage.LocalPosts)
+		assert.Equal(t, 20, nodeinfo.Usage.LocalPosts)
 		assert.Equal(t, 3, nodeinfo.Usage.LocalComments)
 	})
 }
diff --git a/tests/integration/delete_user_test.go b/tests/integration/delete_user_test.go
index 806b87dc4c..fa407a75ad 100644
--- a/tests/integration/delete_user_test.go
+++ b/tests/integration/delete_user_test.go
@@ -17,7 +17,7 @@ import (
 	"code.gitea.io/gitea/tests"
 )
 
-func assertUserDeleted(t *testing.T, userID int64) {
+func assertUserDeleted(t *testing.T, userID int64, purged bool) {
 	unittest.AssertNotExistsBean(t, &user_model.User{ID: userID})
 	unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: userID})
 	unittest.AssertNotExistsBean(t, &user_model.Follow{FollowID: userID})
@@ -27,6 +27,9 @@ func assertUserDeleted(t *testing.T, userID int64) {
 	unittest.AssertNotExistsBean(t, &issues_model.IssueUser{UID: userID})
 	unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID})
 	unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID})
+	if purged {
+		unittest.AssertNotExistsBean(t, &issues_model.Issue{PosterID: userID})
+	}
 }
 
 func TestUserDeleteAccount(t *testing.T) {
@@ -40,7 +43,7 @@ func TestUserDeleteAccount(t *testing.T) {
 	})
 	session.MakeRequest(t, req, http.StatusSeeOther)
 
-	assertUserDeleted(t, 8)
+	assertUserDeleted(t, 8, false)
 	unittest.CheckConsistencyFor(t, &user_model.User{})
 }
 
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 4cae00f0bc..853e565b0f 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -356,7 +356,7 @@ func TestSearchIssues(t *testing.T) {
 
 	session := loginUser(t, "user2")
 
-	expectedIssueCount := 17 // from the fixtures
+	expectedIssueCount := 18 // from the fixtures
 	if expectedIssueCount > setting.UI.IssuePagingNum {
 		expectedIssueCount = setting.UI.IssuePagingNum
 	}
@@ -377,7 +377,7 @@ func TestSearchIssues(t *testing.T) {
 	req = NewRequest(t, "GET", link.String())
 	resp = session.MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
-	assert.Len(t, apiIssues, 10)
+	assert.Len(t, apiIssues, 11)
 	query.Del("since")
 	query.Del("before")
 
@@ -393,15 +393,15 @@ func TestSearchIssues(t *testing.T) {
 	req = NewRequest(t, "GET", link.String())
 	resp = session.MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
-	assert.EqualValues(t, "19", resp.Header().Get("X-Total-Count"))
-	assert.Len(t, apiIssues, 19)
+	assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count"))
+	assert.Len(t, apiIssues, 20)
 
 	query.Add("limit", "5")
 	link.RawQuery = query.Encode()
 	req = NewRequest(t, "GET", link.String())
 	resp = session.MakeRequest(t, req, http.StatusOK)
 	DecodeJSON(t, resp, &apiIssues)
-	assert.EqualValues(t, "19", resp.Header().Get("X-Total-Count"))
+	assert.EqualValues(t, "20", resp.Header().Get("X-Total-Count"))
 	assert.Len(t, apiIssues, 5)
 
 	query = url.Values{"assigned": {"true"}, "state": {"all"}}
@@ -450,7 +450,7 @@ func TestSearchIssues(t *testing.T) {
 func TestSearchIssuesWithLabels(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
-	expectedIssueCount := 17 // from the fixtures
+	expectedIssueCount := 18 // from the fixtures
 	if expectedIssueCount > setting.UI.IssuePagingNum {
 		expectedIssueCount = setting.UI.IssuePagingNum
 	}