diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index d2fa7c7d1f..b0142b8509 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -14,8 +14,6 @@ import (
 	"strings"
 	"unicode/utf8"
 
-	"gitea.com/go-chi/binding"
-
 	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
@@ -27,6 +25,8 @@ import (
 	gitea_context "code.gitea.io/gitea/services/context"
 	"code.gitea.io/gitea/services/forms"
 	"code.gitea.io/gitea/services/webhook/shared"
+
+	"gitea.com/go-chi/binding"
 )
 
 type discordHandler struct{}
@@ -37,8 +37,8 @@ func (discordHandler) Icon(size int) template.HTML   { return shared.ImgIcon("di
 type discordForm struct {
 	forms.WebhookCoreForm
 	PayloadURL string `binding:"Required;ValidUrl"`
-	Username   string
-	IconURL    string
+	Username   string `binding:"Required;MaxSize(80)"`
+	IconURL    string `binding:"ValidUrl"`
 }
 
 var _ binding.Validator = &discordForm{}
@@ -75,7 +75,7 @@ func (discordHandler) UnmarshalForm(bind func(any)) forms.WebhookForm {
 type (
 	// DiscordEmbedFooter for Embed Footer Structure.
 	DiscordEmbedFooter struct {
-		Text string `json:"text"`
+		Text string `json:"text,omitempty"`
 	}
 
 	// DiscordEmbedAuthor for Embed Author Structure
@@ -99,16 +99,16 @@ type (
 		Color       int                 `json:"color"`
 		Footer      DiscordEmbedFooter  `json:"footer"`
 		Author      DiscordEmbedAuthor  `json:"author"`
-		Fields      []DiscordEmbedField `json:"fields"`
+		Fields      []DiscordEmbedField `json:"fields,omitempty"`
 	}
 
 	// DiscordPayload represents
 	DiscordPayload struct {
-		Wait      bool           `json:"wait"`
-		Content   string         `json:"content"`
+		Wait      bool           `json:"-"`
+		Content   string         `json:"-"`
 		Username  string         `json:"username"`
 		AvatarURL string         `json:"avatar_url,omitempty"`
-		TTS       bool           `json:"tts"`
+		TTS       bool           `json:"-"`
 		Embeds    []DiscordEmbed `json:"embeds"`
 	}
 
@@ -341,6 +341,12 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string,
 }
 
 func (d discordConvertor) createPayload(s *api.User, title, text, url string, color int) DiscordPayload {
+	if len([]rune(title)) > 256 {
+		title = fmt.Sprintf("%.253s...", title)
+	}
+	if len([]rune(text)) > 4096 {
+		text = fmt.Sprintf("%.4093s...", text)
+	}
 	return DiscordPayload{
 		Username:  d.Username,
 		AvatarURL: d.AvatarURL,
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index cc5ec8a3e0..680f7806a9 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -120,6 +120,49 @@ func TestDiscordPayload(t *testing.T) {
 		assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
 		assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL)
 		assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL)
+
+		j, err := json.Marshal(pl)
+		require.NoError(t, err)
+
+		unsetFields := struct {
+			Content *string `json:"content"`
+			TTS     *bool   `json:"tts"`
+			Wait    *bool   `json:"wait"`
+			Fields  []any   `json:"fields"`
+			Footer  struct {
+				Text *string `json:"text"`
+			} `json:"footer"`
+		}{}
+
+		err = json.Unmarshal(j, &unsetFields)
+		require.NoError(t, err)
+		assert.Nil(t, unsetFields.Content)
+		assert.Nil(t, unsetFields.TTS)
+		assert.Nil(t, unsetFields.Wait)
+		assert.Nil(t, unsetFields.Fields)
+		assert.Nil(t, unsetFields.Footer.Text)
+	})
+
+	t.Run("Issue with long title", func(t *testing.T) {
+		p := issueTestPayloadWithLongTitle()
+
+		p.Action = api.HookIssueOpened
+		pl, err := dc.Issue(p)
+		require.NoError(t, err)
+
+		assert.Len(t, pl.Embeds, 1)
+		assert.Len(t, pl.Embeds[0].Title, 256)
+	})
+
+	t.Run("Issue with long body", func(t *testing.T) {
+		p := issueTestPayloadWithLongBody()
+
+		p.Action = api.HookIssueOpened
+		pl, err := dc.Issue(p)
+		require.NoError(t, err)
+
+		assert.Len(t, pl.Embeds, 1)
+		assert.Len(t, pl.Embeds[0].Description, 4096)
 	})
 
 	t.Run("IssueComment", func(t *testing.T) {
diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go
index 2d991f8300..6dcd787fab 100644
--- a/services/webhook/general_test.go
+++ b/services/webhook/general_test.go
@@ -4,6 +4,7 @@
 package webhook
 
 import (
+	"strings"
 	"testing"
 
 	api "code.gitea.io/gitea/modules/structs"
@@ -113,6 +114,18 @@ func pushTestPayloadWithCommitMessage(message string) *api.PushPayload {
 }
 
 func issueTestPayload() *api.IssuePayload {
+	return issuePayloadWithTitleAndBody("crash", "issue body")
+}
+
+func issueTestPayloadWithLongBody() *api.IssuePayload {
+	return issuePayloadWithTitleAndBody("crash", strings.Repeat("issue body", 4097))
+}
+
+func issueTestPayloadWithLongTitle() *api.IssuePayload {
+	return issuePayloadWithTitleAndBody(strings.Repeat("a", 257), "issue body")
+}
+
+func issuePayloadWithTitleAndBody(title, body string) *api.IssuePayload {
 	return &api.IssuePayload{
 		Index: 2,
 		Sender: &api.User{
@@ -129,8 +142,8 @@ func issueTestPayload() *api.IssuePayload {
 			Index:   2,
 			URL:     "http://localhost:3000/api/v1/repos/test/repo/issues/2",
 			HTMLURL: "http://localhost:3000/test/repo/issues/2",
-			Title:   "crash",
-			Body:    "issue body",
+			Title:   title,
+			Body:    body,
 			Poster: &api.User{
 				UserName:  "user1",
 				AvatarURL: "http://localhost:3000/user1/avatar",
diff --git a/templates/webhook/new/discord.tmpl b/templates/webhook/new/discord.tmpl
index 48ac84fc6b..455a96c1f4 100644
--- a/templates/webhook/new/discord.tmpl
+++ b/templates/webhook/new/discord.tmpl
@@ -5,9 +5,9 @@
 		<label for="payload_url">{{ctx.Locale.Tr "repo.settings.payload_url"}}</label>
 		<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
 	</div>
-	<div class="field">
+	<div class="required field {{if .Err_PayloadURL}}error{{end}}">
 		<label for="username">{{ctx.Locale.Tr "repo.settings.discord_username"}}</label>
-		<input id="username" name="username" value="{{.HookMetadata.Username}}" placeholder="Forgejo">
+		<input id="username" name="username" value="{{.HookMetadata.Username}}" autofocus required placeholder="Forgejo">
 	</div>
 	<div class="field">
 		<label for="icon_url">{{ctx.Locale.Tr "repo.settings.discord_icon_url"}}</label>
diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go
index b98fce0f4a..8f65b5c34f 100644
--- a/tests/integration/repo_webhook_test.go
+++ b/tests/integration/repo_webhook_test.go
@@ -172,6 +172,7 @@ func TestWebhookForms(t *testing.T) {
 	}))
 
 	t.Run("discord/required", testWebhookForms("discord", session, map[string]string{
+		"username":    "john",
 		"payload_url": "https://discord.example.com",
 	}))
 	t.Run("discord/optional", testWebhookForms("discord", session, map[string]string{