diff --git a/lib/modules/platform/azure/__snapshots__/index.spec.ts.snap b/lib/modules/platform/azure/__snapshots__/index.spec.ts.snap
index 32ee7b4ee7d0f99d2d842af4fa1a5175dba3a7b3..667c22b640b052d112a3a50f7ad79a9d36fd196c 100644
--- a/lib/modules/platform/azure/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/azure/__snapshots__/index.spec.ts.snap
@@ -2,7 +2,9 @@
 
 exports[`modules/platform/azure/index createPr() should create and return a PR object 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #456",
   "number": 456,
@@ -16,7 +18,9 @@ Object {
 
 exports[`modules/platform/azure/index createPr() should create and return a PR object from base branch 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #456",
   "number": 456,
@@ -33,7 +37,9 @@ Object {
   "autoCompleteSetBy": Object {
     "id": 123,
   },
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "completionOptions": Object {
     "deleteSourceBranch": true,
     "mergeCommitMessage": "The Title",
@@ -56,7 +62,9 @@ Object {
 
 exports[`modules/platform/azure/index createPr() should create and return an approved PR object 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "createdBy": Object {
     "id": 123,
@@ -133,7 +141,9 @@ content",
 
 exports[`modules/platform/azure/index findPr(branchName, prTitle, state) returns pr if found it all state 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #1",
   "number": 1,
@@ -150,7 +160,9 @@ Object {
 
 exports[`modules/platform/azure/index findPr(branchName, prTitle, state) returns pr if found it close 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #1",
   "number": 1,
@@ -167,7 +179,9 @@ Object {
 
 exports[`modules/platform/azure/index findPr(branchName, prTitle, state) returns pr if found it open 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #1",
   "number": 1,
@@ -184,7 +198,9 @@ Object {
 
 exports[`modules/platform/azure/index findPr(branchName, prTitle, state) returns pr if found not open 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #1",
   "number": 1,
@@ -217,7 +233,9 @@ Array [
 
 exports[`modules/platform/azure/index getPr(prNo) should return a pr in the right format 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #1234",
   "hasReviewers": false,
diff --git a/lib/modules/platform/azure/__snapshots__/util.spec.ts.snap b/lib/modules/platform/azure/__snapshots__/util.spec.ts.snap
index a2c0f8bc33f6ca49c09f4a3139e88370378f006f..8ccfec1443f366d143228cf53a30be24b2999425 100644
--- a/lib/modules/platform/azure/__snapshots__/util.spec.ts.snap
+++ b/lib/modules/platform/azure/__snapshots__/util.spec.ts.snap
@@ -16,7 +16,9 @@ Object {
 
 exports[`modules/platform/azure/util getRenovatePRFormat should be formated (closed v2) 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #undefined",
   "number": undefined,
@@ -30,7 +32,9 @@ Object {
 
 exports[`modules/platform/azure/util getRenovatePRFormat should be formated (closed) 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #undefined",
   "number": undefined,
@@ -44,7 +48,9 @@ Object {
 
 exports[`modules/platform/azure/util getRenovatePRFormat should be formated (not closed) 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #undefined",
   "number": undefined,
diff --git a/lib/modules/platform/azure/util.ts b/lib/modules/platform/azure/util.ts
index 915b0cb1c1b4a5a16f7cc527497ec96a6982a0e6..0b7aa652faa983a2481b83da605cb6c6f53087c7 100644
--- a/lib/modules/platform/azure/util.ts
+++ b/lib/modules/platform/azure/util.ts
@@ -9,6 +9,7 @@ import { HostRule, PrState } from '../../../types';
 import type { GitOptions } from '../../../types/git';
 import { addSecretForSanitizing } from '../../../util/sanitize';
 import { toBase64 } from '../../../util/string';
+import { getPrBodyStruct } from '../pr-body';
 import type { AzurePr } from './types';
 
 export function getNewBranchName(branchName?: string): string | undefined {
@@ -97,7 +98,7 @@ export function getRenovatePRFormat(azurePr: GitPullRequest): AzurePr {
   const targetBranch = getBranchNameWithoutRefsheadsPrefix(
     azurePr.targetRefName
   );
-  const body = azurePr.description;
+  const bodyStruct = getPrBodyStruct(azurePr.description);
 
   const createdAt = azurePr.creationDate?.toISOString();
   // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
@@ -111,7 +112,7 @@ export function getRenovatePRFormat(azurePr: GitPullRequest): AzurePr {
     state,
     number,
     displayNumber,
-    body,
+    bodyStruct,
     sourceRefName,
     targetBranch,
     createdAt,
diff --git a/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap b/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap
index ef262cf28d8d5d30c9375fc36e036332691bb77f..741dd2b173996d9d7a0f3731875195003fbea979 100644
--- a/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap
@@ -12,8 +12,9 @@ exports[`modules/platform/bitbucket-server/index endpoint with no path deleteLAb
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path findPr() has pr 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "number": 5,
   "sourceBranch": "userName1/pullRequest5",
@@ -26,8 +27,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path getBranchPr() has pr 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -45,8 +47,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path getPr() canRebase 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -64,8 +67,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path getPr() canRebase 2`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -83,8 +87,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path getPr() canRebase 3`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -102,8 +107,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path getPr() gets a PR 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -121,7 +127,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with no path getPr() gets a closed PR 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #undefined",
   "hasReviewers": false,
@@ -138,8 +146,9 @@ Object {
 exports[`modules/platform/bitbucket-server/index endpoint with no path getPrList() has pr 1`] = `
 Array [
   Object {
-    "body": "* Line 1
-* Line 2",
+    "bodyStruct": Object {
+      "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+    },
     "createdAt": 1547853840016,
     "number": 5,
     "sourceBranch": "userName1/pullRequest5",
@@ -215,8 +224,9 @@ exports[`modules/platform/bitbucket-server/index endpoint with path deleteLAbel(
 
 exports[`modules/platform/bitbucket-server/index endpoint with path findPr() has pr 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "number": 5,
   "sourceBranch": "userName1/pullRequest5",
@@ -229,8 +239,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with path getBranchPr() has pr 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -248,8 +259,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with path getPr() canRebase 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -267,8 +279,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with path getPr() canRebase 2`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -286,8 +299,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with path getPr() canRebase 3`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -305,8 +319,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with path getPr() gets a PR 1`] = `
 Object {
-  "body": "* Line 1
-* Line 2",
+  "bodyStruct": Object {
+    "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+  },
   "createdAt": 1547853840016,
   "displayNumber": "Pull Request #5",
   "hasReviewers": true,
@@ -324,7 +339,9 @@ Object {
 
 exports[`modules/platform/bitbucket-server/index endpoint with path getPr() gets a closed PR 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": undefined,
   "displayNumber": "Pull Request #undefined",
   "hasReviewers": false,
@@ -341,8 +358,9 @@ Object {
 exports[`modules/platform/bitbucket-server/index endpoint with path getPrList() has pr 1`] = `
 Array [
   Object {
-    "body": "* Line 1
-* Line 2",
+    "bodyStruct": Object {
+      "hash": "7980dafc4eb6f0c79278fd929d3e8e5954b32b68ae118a22565c7c369fc2f591",
+    },
     "createdAt": 1547853840016,
     "number": 5,
     "sourceBranch": "userName1/pullRequest5",
diff --git a/lib/modules/platform/bitbucket-server/utils.ts b/lib/modules/platform/bitbucket-server/utils.ts
index 8db62147876af15c65a45cd12f63fd30d1581cb5..5f8f88f2d49d80c8027c2b90b79f50cf4d7d2d81 100644
--- a/lib/modules/platform/bitbucket-server/utils.ts
+++ b/lib/modules/platform/bitbucket-server/utils.ts
@@ -10,6 +10,7 @@ import type {
   HttpPostOptions,
   HttpResponse,
 } from '../../../util/http/types';
+import { getPrBodyStruct } from '../pr-body';
 import type { BbsPr, BbsRestPr, BbsRestRepo, BitbucketError } from './types';
 
 export const BITBUCKET_INVALID_REVIEWERS_EXCEPTION =
@@ -28,7 +29,7 @@ export function prInfo(pr: BbsRestPr): BbsPr {
   return {
     version: pr.version,
     number: pr.id,
-    body: pr.description,
+    bodyStruct: getPrBodyStruct(pr.description),
     sourceBranch: pr.fromRef.displayId,
     targetBranch: pr.toRef.displayId,
     title: pr.title,
diff --git a/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap b/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap
index f61be46c071ded940547d5a14af3d9a394c19660..a541332388fcfae9146203f6e5dc5eaaaf44aed5 100644
--- a/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/bitbucket/__snapshots__/index.spec.ts.snap
@@ -15,7 +15,9 @@ Object {
 
 exports[`modules/platform/bitbucket/index findPr() finds pr 1`] = `
 Object {
-  "body": "summary",
+  "bodyStruct": Object {
+    "hash": "761b7ad8ad439b2855fcbb611331c646ef0870b0631247bba3f3025cb6df5a53",
+  },
   "createdAt": "2018-07-02T07:02:25.275030+00:00",
   "displayNumber": "Pull Request #5",
   "number": 5,
@@ -28,7 +30,9 @@ Object {
 
 exports[`modules/platform/bitbucket/index getBranchPr() bitbucket finds PR for branch 1`] = `
 Object {
-  "body": "summary",
+  "bodyStruct": Object {
+    "hash": "761b7ad8ad439b2855fcbb611331c646ef0870b0631247bba3f3025cb6df5a53",
+  },
   "createdAt": "2018-07-02T07:02:25.275030+00:00",
   "displayNumber": "Pull Request #5",
   "hasReviewers": false,
@@ -61,7 +65,9 @@ Array [
 
 exports[`modules/platform/bitbucket/index getPr() canRebase 1`] = `
 Object {
-  "body": "summary",
+  "bodyStruct": Object {
+    "hash": "761b7ad8ad439b2855fcbb611331c646ef0870b0631247bba3f3025cb6df5a53",
+  },
   "createdAt": "2018-07-02T07:02:25.275030+00:00",
   "displayNumber": "Pull Request #3",
   "hasReviewers": false,
@@ -75,7 +81,9 @@ Object {
 
 exports[`modules/platform/bitbucket/index getPr() canRebase 2`] = `
 Object {
-  "body": "summary",
+  "bodyStruct": Object {
+    "hash": "761b7ad8ad439b2855fcbb611331c646ef0870b0631247bba3f3025cb6df5a53",
+  },
   "createdAt": "2018-07-02T07:02:25.275030+00:00",
   "displayNumber": "Pull Request #5",
   "hasReviewers": false,
@@ -89,7 +97,9 @@ Object {
 
 exports[`modules/platform/bitbucket/index getPr() canRebase 3`] = `
 Object {
-  "body": "summary",
+  "bodyStruct": Object {
+    "hash": "761b7ad8ad439b2855fcbb611331c646ef0870b0631247bba3f3025cb6df5a53",
+  },
   "createdAt": "2018-07-02T07:02:25.275030+00:00",
   "displayNumber": "Pull Request #5",
   "hasReviewers": false,
@@ -103,7 +113,9 @@ Object {
 
 exports[`modules/platform/bitbucket/index getPr() exists 1`] = `
 Object {
-  "body": "summary",
+  "bodyStruct": Object {
+    "hash": "761b7ad8ad439b2855fcbb611331c646ef0870b0631247bba3f3025cb6df5a53",
+  },
   "createdAt": "2018-07-02T07:02:25.275030+00:00",
   "displayNumber": "Pull Request #5",
   "hasReviewers": false,
@@ -118,7 +130,9 @@ Object {
 exports[`modules/platform/bitbucket/index getPrList() filters PR list by author 1`] = `
 Array [
   Object {
-    "body": undefined,
+    "bodyStruct": Object {
+      "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+    },
     "createdAt": undefined,
     "displayNumber": "Pull Request #1",
     "number": 1,
diff --git a/lib/modules/platform/bitbucket/utils.ts b/lib/modules/platform/bitbucket/utils.ts
index 9ba9a497d9ef08b77523bc3c6bdde2b5ad3f34e6..c72b76a54cf14f0c5a16155e18530b4b7b21a4e3 100644
--- a/lib/modules/platform/bitbucket/utils.ts
+++ b/lib/modules/platform/bitbucket/utils.ts
@@ -7,6 +7,7 @@ import type {
   HttpPostOptions,
   HttpResponse,
 } from '../../../util/http/types';
+import { getPrBodyStruct } from '../pr-body';
 import type { Pr } from '../types';
 import type { BitbucketMergeStrategy, MergeRequestBody } from './types';
 
@@ -180,7 +181,7 @@ export function prInfo(pr: PrResponse): Pr {
   return {
     number: pr.id,
     displayNumber: `Pull Request #${pr.id}`,
-    body: pr.summary?.raw,
+    bodyStruct: getPrBodyStruct(pr.summary?.raw),
     sourceBranch: pr.source?.branch?.name,
     targetBranch: pr.destination?.branch?.name,
     title: pr.title,
diff --git a/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap b/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap
index 620a5d636b16914fa3a1a4ac7ac471a34964b76e..844b609082e0fad9b317ac264ea0ba32edd51cdb 100644
--- a/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap
@@ -2,7 +2,9 @@
 
 exports[`modules/platform/gitea/index createPr should use base branch by default 1`] = `
 Object {
-  "body": "pr-body",
+  "bodyStruct": Object {
+    "hash": "9d586a6aedc4e7cb205276933c9e474cd3c2b341d3340458c31eb750795f197d",
+  },
   "cannotMergeReason": undefined,
   "createdAt": "2014-04-01T05:14:20Z",
   "displayNumber": "Pull Request #42",
@@ -19,7 +21,9 @@ Object {
 
 exports[`modules/platform/gitea/index createPr should use default branch if requested 1`] = `
 Object {
-  "body": "pr-body",
+  "bodyStruct": Object {
+    "hash": "9d586a6aedc4e7cb205276933c9e474cd3c2b341d3340458c31eb750795f197d",
+  },
   "cannotMergeReason": undefined,
   "createdAt": "2014-04-01T05:14:20Z",
   "displayNumber": "Pull Request #42",
@@ -36,7 +40,9 @@ Object {
 
 exports[`modules/platform/gitea/index getPr should fallback to direct fetching if cache fails 1`] = `
 Object {
-  "body": "some random pull request",
+  "bodyStruct": Object {
+    "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
+  },
   "cannotMergeReason": "pr.mergeable=\\"false\\"",
   "createdAt": "2015-03-22T20:36:16Z",
   "displayNumber": "Pull Request #1",
@@ -53,7 +59,9 @@ Object {
 
 exports[`modules/platform/gitea/index getPr should return enriched pull request which exists if open 1`] = `
 Object {
-  "body": "some random pull request",
+  "bodyStruct": Object {
+    "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
+  },
   "cannotMergeReason": undefined,
   "createdAt": "2015-03-22T20:36:16Z",
   "displayNumber": "Pull Request #1",
@@ -78,7 +86,9 @@ Object {
 exports[`modules/platform/gitea/index getPrList should filter list by creator 2`] = `
 Array [
   Object {
-    "body": "some random pull request",
+    "bodyStruct": Object {
+      "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
+    },
     "cannotMergeReason": undefined,
     "createdAt": "2015-03-22T20:36:16Z",
     "displayNumber": "Pull Request #1",
@@ -92,7 +102,9 @@ Array [
     "title": "Some PR",
   },
   Object {
-    "body": "other random pull request",
+    "bodyStruct": Object {
+      "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4",
+    },
     "cannotMergeReason": undefined,
     "createdAt": "2011-08-18T22:30:38Z",
     "displayNumber": "Pull Request #2",
@@ -111,7 +123,9 @@ Array [
 exports[`modules/platform/gitea/index getPrList should return list of pull requests 1`] = `
 Array [
   Object {
-    "body": "some random pull request",
+    "bodyStruct": Object {
+      "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
+    },
     "cannotMergeReason": undefined,
     "createdAt": "2015-03-22T20:36:16Z",
     "displayNumber": "Pull Request #1",
@@ -125,7 +139,9 @@ Array [
     "title": "Some PR",
   },
   Object {
-    "body": "other random pull request",
+    "bodyStruct": Object {
+      "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4",
+    },
     "cannotMergeReason": undefined,
     "createdAt": "2011-08-18T22:30:38Z",
     "displayNumber": "Pull Request #2",
diff --git a/lib/modules/platform/gitea/index.ts b/lib/modules/platform/gitea/index.ts
index 54a82bfff5f7ab379cf342e138822b28f3570c60..8b62ab12dc9cc9229ff204b75b421e9790ec1caa 100644
--- a/lib/modules/platform/gitea/index.ts
+++ b/lib/modules/platform/gitea/index.ts
@@ -18,6 +18,7 @@ import * as hostRules from '../../../util/host-rules';
 import { setBaseUrl } from '../../../util/http/gitea';
 import { sanitize } from '../../../util/sanitize';
 import { ensureTrailingSlash } from '../../../util/url';
+import { getPrBodyStruct, hashBody } from '../pr-body';
 import type {
   BranchStatusConfig,
   CreatePRConfig,
@@ -96,7 +97,7 @@ function toRenovatePR(data: helper.PR): Pr | null {
     displayNumber: `Pull Request #${data.number}`,
     state: data.state,
     title: data.title,
-    body: data.body,
+    bodyStruct: getPrBodyStruct(data.body),
     sha: data.head.sha,
     sourceBranch: data.head.label,
     targetBranch: data.base.ref,
@@ -524,8 +525,8 @@ const platform: Platform = {
         });
 
         // If a valid PR was found, return and gracefully recover from the error. Otherwise, abort and throw error.
-        if (pr) {
-          if (pr.title !== title || pr.body !== body) {
+        if (pr?.bodyStruct) {
+          if (pr.title !== title || pr.bodyStruct.hash !== hashBody(body)) {
             logger.debug(
               `Recovered from 409 Conflict, but PR for ${sourceBranch} is outdated. Updating...`
             );
@@ -535,7 +536,7 @@ const platform: Platform = {
               prBody: body,
             });
             pr.title = title;
-            pr.body = body;
+            pr.bodyStruct = getPrBodyStruct(body);
           } else {
             logger.debug(
               `Recovered from 409 Conflict and PR for ${sourceBranch} is up-to-date`
diff --git a/lib/modules/platform/github/__snapshots__/index.spec.ts.snap b/lib/modules/platform/github/__snapshots__/index.spec.ts.snap
index c200deb87d269df4da92229f7d68e6b4a0ecb368..ed91cf8007a916621b09238c66114e044c5a6de0 100644
--- a/lib/modules/platform/github/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/github/__snapshots__/index.spec.ts.snap
@@ -2,7 +2,9 @@
 
 exports[`modules/platform/github/index getBranchPr(branchName) should cache and return the PR object 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Pull Request #91",
   "number": 91,
   "sourceBranch": "somebranch",
@@ -14,7 +16,9 @@ Object {
 
 exports[`modules/platform/github/index getBranchPr(branchName) should cache and return the PR object in fork mode 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Pull Request #90",
   "number": 90,
   "sourceBranch": "somebranch",
@@ -26,7 +30,9 @@ Object {
 
 exports[`modules/platform/github/index getBranchPr(branchName) should reopen and cache autoclosed PR 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Pull Request #91",
   "number": 91,
   "sourceBranch": "somebranch",
@@ -38,7 +44,9 @@ Object {
 
 exports[`modules/platform/github/index getPr(prNo) should return PR 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Pull Request #2500",
   "number": 2500,
   "sourceBranch": "renovate/jest-monorepo",
@@ -50,7 +58,9 @@ Object {
 
 exports[`modules/platform/github/index getPr(prNo) should return a PR object - 0 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "createdAt": "01-01-2022",
   "displayNumber": "Pull Request #1234",
   "hasAssignees": true,
@@ -68,7 +78,9 @@ Object {
 
 exports[`modules/platform/github/index getPr(prNo) should return a PR object - 1 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Pull Request #1234",
   "hasAssignees": true,
   "hasReviewers": true,
@@ -81,7 +93,9 @@ Object {
 
 exports[`modules/platform/github/index getPr(prNo) should return a PR object - 2 1`] = `
 Object {
-  "body": "dummy body",
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Pull Request #1234",
   "number": 1234,
   "sourceBranch": "some/branch",
diff --git a/lib/modules/platform/github/common.ts b/lib/modules/platform/github/common.ts
index b7d2c52ed63a987b27c1c10fabd87cf2ae00fc31..121b52274b8740cad0e19078298c69a2263519c9 100644
--- a/lib/modules/platform/github/common.ts
+++ b/lib/modules/platform/github/common.ts
@@ -1,5 +1,6 @@
 import is from '@sindresorhus/is';
 import { PrState } from '../../../types';
+import { getPrBodyStruct } from '../pr-body';
 import type { Pr } from '../types';
 import type { GhRestPr } from './types';
 
@@ -11,6 +12,7 @@ export function coerceRestPr(pr: GhRestPr | null | undefined): Pr | null {
     return null;
   }
 
+  const bodyStruct = pr.bodyStruct ?? getPrBodyStruct(pr.body);
   const result: Pr = {
     displayNumber: `Pull Request #${pr.number}`,
     number: pr.number,
@@ -20,7 +22,7 @@ export function coerceRestPr(pr: GhRestPr | null | undefined): Pr | null {
       pr.state === PrState.Closed && is.string(pr.merged_at)
         ? PrState.Merged
         : pr.state,
-    body: pr.body ?? 'dummy body',
+    bodyStruct,
   };
 
   if (pr.head?.sha) {
diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts
index 4167cc6c1031a2aa961561c8b18d69249e779ab8..376bb2c31c5239352d583a8cfcff1e9f5c6cab86 100644
--- a/lib/modules/platform/github/index.spec.ts
+++ b/lib/modules/platform/github/index.spec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
 import { DateTime } from 'luxon';
 import * as httpMock from '../../../../test/http-mock';
 import { logger, mocked } from '../../../../test/util';
@@ -12,6 +11,7 @@ import * as _git from '../../../util/git';
 import * as _hostRules from '../../../util/host-rules';
 import { setBaseUrl } from '../../../util/http/github';
 import { toBase64 } from '../../../util/string';
+import { hashBody } from '../pr-body';
 import type { CreatePRConfig, UpdatePrConfig } from '../types';
 import type { ApiPageCache, GhRestPr } from './types';
 import * as github from '.';
@@ -559,25 +559,31 @@ describe('modules/platform/github/index', () => {
     const t3 = t.plus({ minutes: 3 }).toISO();
     const t4 = t.plus({ minutes: 4 }).toISO();
 
-    const pr1 = {
+    const pr1: GhRestPr = {
       number: 1,
-      head: { ref: 'branch-1', repo: { full_name: 'some/repo' } },
+      head: { ref: 'branch-1', sha: '111', repo: { full_name: 'some/repo' } },
+      base: { repo: { pushed_at: '' } },
       state: PrState.Open,
       title: 'PR #1',
+      created_at: t1,
       updated_at: t1,
+      mergeable_state: 'clean',
+      node_id: '12345',
     };
 
-    const pr2 = {
+    const pr2: GhRestPr = {
+      ...pr1,
       number: 2,
-      head: { ref: 'branch-2', repo: { full_name: 'some/repo' } },
+      head: { ref: 'branch-2', sha: '222', repo: { full_name: 'some/repo' } },
       state: PrState.Open,
       title: 'PR #2',
       updated_at: t2,
     };
 
-    const pr3 = {
+    const pr3: GhRestPr = {
+      ...pr1,
       number: 3,
-      head: { ref: 'branch-3', repo: { full_name: 'some/repo' } },
+      head: { ref: 'branch-3', sha: '333', repo: { full_name: 'some/repo' } },
       state: PrState.Open,
       title: 'PR #3',
       updated_at: t3,
@@ -663,65 +669,110 @@ describe('modules/platform/github/index', () => {
       ]);
     });
 
-    it('removes url data from response', async () => {
-      const scope = httpMock.scope(githubApiHost);
-      initRepoMock(scope, 'some/repo');
-      scope.get(pagePath(1)).reply(200, [
-        {
-          ...pr1,
-          url: 'https://example.com',
-          example_url: 'https://example.com',
-          _links: { foo: { href: 'https:/example.com' } },
-          repo: { example_url: 'https://example.com' },
-        },
-      ]);
-      await github.initRepo({ repository: 'some/repo' } as never);
+    describe('Url cleanup', () => {
+      type GhRestPrWithUrls = GhRestPr & {
+        url: string;
+        example_url: string;
+        repo: {
+          example_url: string;
+        };
+      };
 
-      await github.getPrList();
-      const cache = repository.getCache().platform!.github!
-        .prCache as ApiPageCache<GhRestPr>;
-      const item = cache.items['1'];
+      type PrCache = ApiPageCache<GhRestPrWithUrls>;
 
-      expect(item['_links']).toBeUndefined();
-      // TODO: fix types #7154
-      expect((item as any)['url']).toBeUndefined();
-      expect((item as any)['example_url']).toBeUndefined();
-      expect((item as any)['repo']['example_url']).toBeUndefined();
+      const prWithUrls = (): GhRestPrWithUrls => ({
+        ...pr1,
+        url: 'https://example.com',
+        example_url: 'https://example.com',
+        _links: { foo: { href: 'https://example.com' } },
+        repo: { example_url: 'https://example.com' },
+      });
+
+      it('removes url data from response', async () => {
+        const scope = httpMock.scope(githubApiHost);
+        initRepoMock(scope, 'some/repo');
+        scope.get(pagePath(1)).reply(200, [prWithUrls()]);
+        await github.initRepo({ repository: 'some/repo' } as never);
+
+        await github.getPrList();
+
+        const repoCache = repository.getCache();
+        const prCache = repoCache.platform?.github?.prCache as PrCache;
+        expect(prCache).toMatchObject({ items: {} });
+
+        const item = prCache.items[1];
+        expect(item).toBeDefined();
+        expect(item._links).toBeUndefined();
+        expect(item.url).toBeUndefined();
+        expect(item.example_url).toBeUndefined();
+        expect(item.repo.example_url).toBeUndefined();
+      });
+
+      it('removes url data from existing cache', async () => {
+        const scope = httpMock.scope(githubApiHost);
+        initRepoMock(scope, 'some/repo');
+        scope.get(pagePath(1, 20)).reply(200, []);
+        await github.initRepo({ repository: 'some/repo' } as never);
+        const repoCache = repository.getCache();
+        const prCache: PrCache = { items: { 1: prWithUrls() } };
+        repoCache.platform = { github: { prCache } };
+
+        await github.getPrList();
+
+        const item = prCache.items[1];
+        expect(item._links).toBeUndefined();
+        expect(item.url).toBeUndefined();
+        expect(item.example_url).toBeUndefined();
+        expect(item.repo.example_url).toBeUndefined();
+      });
     });
 
-    it('removes url data from existing cache', async () => {
-      const scope = httpMock.scope(githubApiHost);
-      initRepoMock(scope, 'some/repo');
-      scope.get(pagePath(1, 20)).reply(200, [
-        {
-          ...pr1,
-          url: 'https://example.com',
-          example_url: 'https://example.com',
-          _links: { foo: { href: 'https:/example.com' } },
-          repo: { example_url: 'https://example.com' },
-        },
-      ]);
-      await github.initRepo({ repository: 'some/repo' } as never);
-      const repoCache = repository.getCache();
-      const item = {
-        head: { ref: 'branch-1', repo: { full_name: 'some/repo' } },
-        number: 1,
-        repo: { example_url: 'https://example.com' },
-        state: 'open',
-        title: 'PR #1',
-        updated_at: '2000-01-01T02:01:00.000+02:00',
-        _links: { foo: { href: 'https:/example.com' } },
-        url: 'https://example.com',
-      };
-      repoCache.platform = { github: { prCache: { items: { '1': item } } } };
+    describe('Body compaction', () => {
+      type PrCache = ApiPageCache<GhRestPr>;
 
-      await github.getPrList();
+      const prWithBody = (body: string): GhRestPr => ({
+        ...pr1,
+        body,
+      });
 
-      expect(item['_links']).toBeUndefined();
-      expect(item['url']).toBeUndefined();
-      // TODO: fix types #7154
-      expect((item as any)['example_url']).toBeUndefined();
-      expect(item['repo']['example_url']).toBeUndefined();
+      it('compacts body from response', async () => {
+        const scope = httpMock.scope(githubApiHost);
+        initRepoMock(scope, 'some/repo');
+        scope.get(pagePath(1)).reply(200, [prWithBody('foo')]);
+        await github.initRepo({ repository: 'some/repo' } as never);
+
+        await github.getPrList();
+
+        const repoCache = repository.getCache();
+        const prCache = repoCache.platform?.github?.prCache as PrCache;
+        expect(prCache).toMatchObject({ items: {} });
+
+        const item = prCache.items[1];
+        expect(item).toBeDefined();
+        expect(item.body).toBeUndefined();
+        expect(item.bodyStruct).toEqual({ hash: hashBody('foo') });
+      });
+
+      it('removes url data from existing cache', async () => {
+        const scope = httpMock.scope(githubApiHost);
+        initRepoMock(scope, 'some/repo');
+        scope.get(pagePath(1)).reply(200, [prWithBody('foo')]);
+        await github.initRepo({ repository: 'some/repo' } as never);
+        const repoCache = repository.getCache();
+        const prCache: PrCache = {
+          items: { 1: prWithBody('bar'), 2: prWithBody('baz') },
+        };
+        repoCache.platform = { github: { prCache } };
+
+        await github.getPrList();
+
+        expect(prCache.items[2]).toBeUndefined();
+
+        const item = prCache.items[1];
+        expect(item).toBeDefined();
+        expect(item.body).toBeUndefined();
+        expect(item.bodyStruct).toEqual({ hash: hashBody('foo') });
+      });
     });
   });
 
diff --git a/lib/modules/platform/github/pr.ts b/lib/modules/platform/github/pr.ts
index 3629bdf9ffccfcc1b799f9828d7d62826d895637..416afd3a9136401a12ab0a652f32be1adf78fb9b 100644
--- a/lib/modules/platform/github/pr.ts
+++ b/lib/modules/platform/github/pr.ts
@@ -5,6 +5,7 @@ import { ExternalHostError } from '../../../types/errors/external-host-error';
 import { getCache } from '../../../util/cache/repository';
 import type { GithubHttp, GithubHttpOptions } from '../../../util/http/github';
 import { parseLinkHeader } from '../../../util/url';
+import { getPrBodyStruct } from '../pr-body';
 import type { Pr } from '../types';
 import { ApiCache } from './api-cache';
 import { coerceRestPr } from './common';
@@ -22,11 +23,23 @@ function removeUrlFields(input: unknown): void {
   }
 }
 
+function compactPrBodyStructure(input: unknown): void {
+  if (is.plainObject(input)) {
+    if (!input.bodyStruct && is.string(input.body)) {
+      input.bodyStruct = getPrBodyStruct(input.body);
+      delete input.body;
+    }
+  }
+}
+
 function massageGhRestPr(ghPr: GhRestPr): GhRestPr {
   removeUrlFields(ghPr);
-  delete ghPr?.head?.repo?.pushed_at;
-  delete ghPr?.base?.repo?.pushed_at;
-  delete ghPr?._links;
+  delete ghPr.head?.repo?.pushed_at;
+  delete ghPr.base?.repo?.pushed_at;
+  delete ghPr._links;
+
+  compactPrBodyStructure(ghPr);
+
   return ghPr;
 }
 
@@ -39,7 +52,11 @@ function getPrApiCache(): ApiCache<GhRestPr> {
     .prCache as ApiPageCache<GhRestPr>;
 
   const items = Object.values(apiPageCache.items);
-  if (items?.[0]?._links) {
+
+  const firstItem = items?.[0];
+  if (firstItem?.body) {
+    apiPageCache.items = {};
+  } else if (firstItem?._links) {
     for (const ghPr of items) {
       massageGhRestPr(ghPr);
     }
@@ -145,7 +162,8 @@ export async function getPrCache(
     throw new ExternalHostError(err, PlatformId.Github);
   }
 
-  for (const ghPr of prApiCache.getItems()) {
+  const cacheItems = prApiCache.getItems();
+  for (const ghPr of cacheItems) {
     const pr = coerceRestPr(ghPr);
     if (pr) {
       prCache[ghPr.number] = pr;
diff --git a/lib/modules/platform/github/types.ts b/lib/modules/platform/github/types.ts
index 0c3427a64b71ef796d8af6f49f5c602e9d101321..dcb6c6ee006e6e6d8cf656c7929f42ff36e94dc8 100644
--- a/lib/modules/platform/github/types.ts
+++ b/lib/modules/platform/github/types.ts
@@ -1,4 +1,4 @@
-import type { Pr } from '../types';
+import type { Pr, PrBodyStruct } from '../types';
 
 // https://developer.github.com/v3/repos/statuses
 // https://developer.github.com/v3/checks/runs/
@@ -37,11 +37,12 @@ export interface GhRestPr {
   mergeable_state: string;
   number: number;
   title: string;
-  body: string;
+  body?: string;
+  bodyStruct?: PrBodyStruct;
   state: string;
-  merged_at: string;
+  merged_at?: string;
   created_at: string;
-  closed_at: string;
+  closed_at?: string;
   updated_at: string;
   user?: { login?: string };
   node_id: string;
diff --git a/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap
index 209ce225289bad061c5a996a3fe55e0e9d11514c..a19ca9e09da9c2ac8aab274679d4225038ec47c4 100644
--- a/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap
+++ b/lib/modules/platform/gitlab/__snapshots__/index.spec.ts.snap
@@ -70,7 +70,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getBranchPr(branchName) should return the PR object 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Merge Request #91",
   "hasAssignees": false,
   "hasReviewers": false,
@@ -86,7 +88,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getBranchPr(branchName) should strip deprecated draft prefix from title 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Merge Request #91",
   "hasAssignees": false,
   "hasReviewers": false,
@@ -103,7 +107,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getBranchPr(branchName) should strip draft prefix from title 1`] = `
 Object {
-  "body": undefined,
+  "bodyStruct": Object {
+    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+  },
   "displayNumber": "Merge Request #91",
   "hasAssignees": false,
   "hasReviewers": false,
@@ -120,7 +126,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getPr(prNo) removes deprecated draft prefix from returned title 1`] = `
 Object {
-  "body": "a merge request",
+  "bodyStruct": Object {
+    "hash": "23f41dbec0785a6c77457dd6ebf99ae5970c5fffc9f7a8ad7f66c1b8eeba5b90",
+  },
   "displayNumber": "Merge Request #12345",
   "hasAssignees": false,
   "hasReviewers": false,
@@ -137,7 +145,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getPr(prNo) removes draft prefix from returned title 1`] = `
 Object {
-  "body": "a merge request",
+  "bodyStruct": Object {
+    "hash": "23f41dbec0785a6c77457dd6ebf99ae5970c5fffc9f7a8ad7f66c1b8eeba5b90",
+  },
   "displayNumber": "Merge Request #12345",
   "hasAssignees": false,
   "hasReviewers": false,
@@ -154,7 +164,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getPr(prNo) returns the PR 1`] = `
 Object {
-  "body": "a merge request",
+  "bodyStruct": Object {
+    "hash": "23f41dbec0785a6c77457dd6ebf99ae5970c5fffc9f7a8ad7f66c1b8eeba5b90",
+  },
   "displayNumber": "Merge Request #12345",
   "hasAssignees": false,
   "hasReviewers": false,
@@ -170,7 +182,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getPr(prNo) returns the PR with nonexisting branch 1`] = `
 Object {
-  "body": "a merge request",
+  "bodyStruct": Object {
+    "hash": "23f41dbec0785a6c77457dd6ebf99ae5970c5fffc9f7a8ad7f66c1b8eeba5b90",
+  },
   "displayNumber": "Merge Request #12345",
   "hasAssignees": true,
   "hasReviewers": false,
@@ -186,7 +200,9 @@ Object {
 
 exports[`modules/platform/gitlab/index getPr(prNo) returns the mergeable PR 1`] = `
 Object {
-  "body": "a merge request",
+  "bodyStruct": Object {
+    "hash": "23f41dbec0785a6c77457dd6ebf99ae5970c5fffc9f7a8ad7f66c1b8eeba5b90",
+  },
   "displayNumber": "Merge Request #12345",
   "hasAssignees": true,
   "hasReviewers": false,
diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts
index f0ca2cf8b0fed62be9d8f2d40957a31eadf02f29..6584bcb9152984d1031ef0ac6184af0e3010d233 100644
--- a/lib/modules/platform/gitlab/index.ts
+++ b/lib/modules/platform/gitlab/index.ts
@@ -30,6 +30,7 @@ import {
   getQueryString,
   parseUrl,
 } from '../../../util/url';
+import { getPrBodyStruct } from '../pr-body';
 import type {
   BranchStatusConfig,
   CreatePRConfig,
@@ -607,7 +608,7 @@ export async function getPr(iid: number): Promise<Pr> {
     targetBranch: mr.target_branch,
     number: mr.iid,
     displayNumber: `Merge Request #${mr.iid}`,
-    body: mr.description,
+    bodyStruct: getPrBodyStruct(mr.description),
     state: mr.state === 'opened' ? PrState.Open : mr.state,
     hasAssignees: !!(mr.assignee?.id || mr.assignees?.[0]?.id),
     hasReviewers: !!mr.reviewers?.length,
diff --git a/lib/modules/platform/pr-body.spec.ts b/lib/modules/platform/pr-body.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..485a08376cda68c1f925daa99301e60556281296
--- /dev/null
+++ b/lib/modules/platform/pr-body.spec.ts
@@ -0,0 +1,30 @@
+import { getPrBodyStruct, hashBody } from './pr-body';
+
+describe('modules/platform/pr-body', () => {
+  describe('getPrBodyStruct', () => {
+    it('returns hash for empty inputs', () => {
+      expect(getPrBodyStruct(null)).toEqual({
+        hash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
+      });
+      expect(getPrBodyStruct(undefined)).toEqual({
+        hash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
+      });
+      expect(getPrBodyStruct('')).toEqual({
+        hash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
+      });
+    });
+
+    it('returns rebaseRequested flag', () => {
+      expect(getPrBodyStruct('- [x] <!-- rebase-check -->')).toEqual({
+        hash: '023952693e1e00a52a71b65d9b4804bca6ca9f215c20f6e029dbf420f322d541',
+        rebaseRequested: true,
+      });
+    });
+
+    it('strips reviewable section', () => {
+      expect(getPrBodyStruct('foo<!-- Reviewable:start -->bar')).toEqual({
+        hash: hashBody('foo'),
+      });
+    });
+  });
+});
diff --git a/lib/modules/platform/pr-body.ts b/lib/modules/platform/pr-body.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6427d0ecaa763e5bbcaa7ba624003d8268027903
--- /dev/null
+++ b/lib/modules/platform/pr-body.ts
@@ -0,0 +1,41 @@
+import hasha from 'hasha';
+import { stripEmojis } from '../../util/emoji';
+import { regEx } from '../../util/regex';
+import type { PrBodyStruct } from './types';
+
+function noWhitespaceOrHeadings(input: string): string {
+  return input.replace(regEx(/\r?\n|\r|\s|#/g), '');
+}
+
+const reviewableRegex = regEx(/\s*<!-- Reviewable:start -->/);
+
+export function hashBody(body: string | undefined): string {
+  let result = body?.trim() ?? '';
+  const reviewableIndex = result.search(reviewableRegex);
+  if (reviewableIndex > -1) {
+    result = result.slice(0, reviewableIndex);
+  }
+  result = stripEmojis(result);
+  result = noWhitespaceOrHeadings(result);
+  result = hasha(result, { algorithm: 'sha256' });
+  return result;
+}
+
+export function isRebaseRequested(body: string | undefined): boolean {
+  return !!body?.includes(`- [x] <!-- rebase-check -->`);
+}
+
+export function getPrBodyStruct(
+  input: string | undefined | null
+): PrBodyStruct {
+  const str = input ?? '';
+  const hash = hashBody(str);
+  const result: PrBodyStruct = { hash };
+
+  const rebaseRequested = isRebaseRequested(str);
+  if (rebaseRequested) {
+    result.rebaseRequested = rebaseRequested;
+  }
+
+  return result;
+}
diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts
index e6de7e8852e34ab9ea788b904b9475b0c6cdba8a..aff2f2168876cafa308952b4556db6594b799543 100644
--- a/lib/modules/platform/types.ts
+++ b/lib/modules/platform/types.ts
@@ -43,11 +43,16 @@ export interface RepoParams {
   ignorePrAuthor?: boolean;
 }
 
+export interface PrBodyStruct {
+  hash: string;
+  rebaseRequested?: boolean;
+}
+
 /**
  *
  */
 export interface Pr {
-  body?: string;
+  bodyStruct?: PrBodyStruct;
   sourceBranch: string;
   cannotMergeReason?: string; // for reflecting platform policies which may prevent merging
   createdAt?: string;
diff --git a/lib/workers/repository/onboarding/pr/index.spec.ts b/lib/workers/repository/onboarding/pr/index.spec.ts
index efcd1e96c8f1277144386463f24032b1b02df2b2..6308236390ce6dc3a7e02e3d11cdc3a045a13440 100644
--- a/lib/workers/repository/onboarding/pr/index.spec.ts
+++ b/lib/workers/repository/onboarding/pr/index.spec.ts
@@ -20,6 +20,10 @@ describe('workers/repository/onboarding/pr/index', () => {
     let packageFiles: Record<string, PackageFile[]>;
     let branches: BranchConfig[];
 
+    const bodyStruct = {
+      hash: '8d5d8373c3fc54803f573ea57ded60686a9df8eb0430ad25da281472eed9ce4e',
+    };
+
     beforeEach(() => {
       jest.resetAllMocks();
       config = {
@@ -35,8 +39,6 @@ describe('workers/repository/onboarding/pr/index', () => {
       GlobalConfig.reset();
     });
 
-    let createPrBody: string;
-
     it('returns if onboarded', async () => {
       config.repoIsOnboarded = true;
       await expect(
@@ -47,7 +49,6 @@ describe('workers/repository/onboarding/pr/index', () => {
     it('creates PR', async () => {
       await ensureOnboardingPr(config, packageFiles, branches);
       expect(platform.createPr).toHaveBeenCalledTimes(1);
-      createPrBody = platform.createPr.mock.calls[0][0].prBody;
     });
 
     it('creates PR with labels', async () => {
@@ -123,7 +124,7 @@ describe('workers/repository/onboarding/pr/index', () => {
       platform.getBranchPr.mockResolvedValue(
         partial<Pr>({
           title: 'Configure Renovate',
-          body: createPrBody,
+          bodyStruct,
         })
       );
       await ensureOnboardingPr(config, packageFiles, branches);
@@ -136,7 +137,7 @@ describe('workers/repository/onboarding/pr/index', () => {
       platform.getBranchPr.mockResolvedValueOnce(
         partial<Pr>({
           title: 'Configure Renovate',
-          body: createPrBody,
+          bodyStruct,
         })
       );
       git.isBranchConflicted.mockResolvedValueOnce(true);
@@ -151,7 +152,7 @@ describe('workers/repository/onboarding/pr/index', () => {
       platform.getBranchPr.mockResolvedValueOnce(
         partial<Pr>({
           title: 'Configure Renovate',
-          body: createPrBody,
+          bodyStruct,
         })
       );
       git.isBranchModified.mockResolvedValueOnce(true);
@@ -172,7 +173,7 @@ describe('workers/repository/onboarding/pr/index', () => {
       platform.getBranchPr.mockResolvedValueOnce(
         partial<Pr>({
           title: 'Configure Renovate',
-          body: createPrBody,
+          bodyStruct,
         })
       );
       git.isBranchConflicted.mockResolvedValueOnce(true);
diff --git a/lib/workers/repository/onboarding/pr/index.ts b/lib/workers/repository/onboarding/pr/index.ts
index cc90b3e6408b4b1e707cb1b37ac9dd7c187d5278..4bcd159f9d396c72a4284eb26244935d7feb01c5 100644
--- a/lib/workers/repository/onboarding/pr/index.ts
+++ b/lib/workers/repository/onboarding/pr/index.ts
@@ -4,6 +4,7 @@ import type { RenovateConfig } from '../../../../config/types';
 import { logger } from '../../../../logger';
 import type { PackageFile } from '../../../../modules/manager/types';
 import { platform } from '../../../../modules/platform';
+import { hashBody } from '../../../../modules/platform/pr-body';
 import { emojify } from '../../../../util/emoji';
 import {
   deleteBranch,
@@ -115,9 +116,8 @@ If you need any further assistance then you can also [request help here](${confi
   if (existingPr) {
     logger.debug('Found open onboarding PR');
     // Check if existing PR needs updating
-    if (
-      existingPr.body.trim() === prBody.trim() // Bitbucket strips trailing \n
-    ) {
+    const prBodyHash = hashBody(prBody);
+    if (existingPr.bodyStruct?.hash === prBodyHash) {
       logger.debug(`${existingPr.displayNumber} does not need updating`);
       return;
     }
diff --git a/lib/workers/repository/update/branch/index.spec.ts b/lib/workers/repository/update/branch/index.spec.ts
index 1079c41d859605441bf4d08f1eeab900fed80ad5..1be57ad581c5e42da906448811259c6e6e6c7bbb 100644
--- a/lib/workers/repository/update/branch/index.spec.ts
+++ b/lib/workers/repository/update/branch/index.spec.ts
@@ -14,6 +14,7 @@ import {
 } from '../../../../constants/error-messages';
 import * as _npmPostExtract from '../../../../modules/manager/npm/post-update';
 import type { WriteExistingFilesResult } from '../../../../modules/manager/npm/post-update/types';
+import { hashBody } from '../../../../modules/platform/pr-body';
 import { PrState } from '../../../../types';
 import * as _exec from '../../../../util/exec';
 import type { FileChange, StatusResult } from '../../../../util/git/types';
@@ -903,7 +904,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       schedule.isScheduledNow.mockReturnValueOnce(false);
@@ -936,7 +940,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       schedule.isScheduledNow.mockReturnValueOnce(false);
@@ -971,7 +978,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       schedule.isScheduledNow.mockReturnValueOnce(false);
@@ -1004,7 +1014,7 @@ describe('workers/repository/update/branch/index', () => {
         title: 'rebase!',
         state: PrState.Open,
         labels: ['stop-updating'],
-        body: `- [ ] <!-- rebase-check -->`,
+        bodyStruct: { hash: hashBody(`- [ ] <!-- rebase-check -->`) },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       schedule.isScheduledNow.mockReturnValueOnce(false);
@@ -1039,7 +1049,10 @@ describe('workers/repository/update/branch/index', () => {
         title: 'Update dependency',
         state: PrState.Open,
         labels: ['stop-updating'],
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       schedule.isScheduledNow.mockReturnValueOnce(false);
@@ -1084,7 +1097,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       git.getRepoStatus.mockResolvedValueOnce({
@@ -1170,7 +1186,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as never);
       git.isBranchModified.mockResolvedValueOnce(true);
       git.getRepoStatus.mockResolvedValueOnce({
@@ -1247,7 +1266,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       git.getRepoStatus.mockResolvedValueOnce({
@@ -1327,7 +1349,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       git.getRepoStatus
@@ -1472,7 +1497,10 @@ describe('workers/repository/update/branch/index', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         title: 'rebase!',
         state: PrState.Open,
-        body: `- [x] <!-- rebase-check -->`,
+        bodyStruct: {
+          hash: hashBody(`- [x] <!-- rebase-check -->`),
+          rebaseRequested: true,
+        },
       } as Pr);
       git.isBranchModified.mockResolvedValueOnce(true);
       git.getRepoStatus.mockResolvedValueOnce({
diff --git a/lib/workers/repository/update/branch/index.ts b/lib/workers/repository/update/branch/index.ts
index cedd9bb615976f01350462de9ce7381d4d296ccb..a7fed9f6bbe79be6f002b0c91dd8a664526ef16e 100644
--- a/lib/workers/repository/update/branch/index.ts
+++ b/lib/workers/repository/update/branch/index.ts
@@ -20,6 +20,7 @@ import {
   ensureComment,
   ensureCommentRemoval,
 } from '../../../../modules/platform/comment';
+import { hashBody } from '../../../../modules/platform/pr-body';
 import { BranchStatus, PrState } from '../../../../types';
 import { ExternalHostError } from '../../../../types/errors/external-host-error';
 import { getElapsedDays } from '../../../../util/date';
@@ -37,11 +38,11 @@ import {
   isActiveConfidenceLevel,
   satisfiesConfidenceLevel,
 } from '../../../../util/merge-confidence';
-import { regEx } from '../../../../util/regex';
 import { Limit, isLimitReached } from '../../../global/limits';
 import { BranchConfig, BranchResult, PrBlockedBy } from '../../../types';
 import { ensurePr, getPlatformPrOptions } from '../pr';
 import { checkAutoMerge } from '../pr/automerge';
+import { getPrBody } from '../pr/body';
 import { setArtifactErrorStatus } from './artifacts';
 import { tryBranchAutomerge } from './automerge';
 import { prAlreadyExisted } from './check-existing';
@@ -56,15 +57,11 @@ import { setConfidence, setStability } from './status-checks';
 function rebaseCheck(config: RenovateConfig, branchPr: Pr): boolean {
   const titleRebase = branchPr.title?.startsWith('rebase!');
   const labelRebase = branchPr.labels?.includes(config.rebaseLabel);
-  const prRebaseChecked = branchPr.body?.includes(
-    `- [x] <!-- rebase-check -->`
-  );
+  const prRebaseChecked = !!branchPr.bodyStruct?.rebaseRequested;
 
   return titleRebase || labelRebase || prRebaseChecked;
 }
 
-const rebasingRegex = regEx(/\*\*Rebasing\*\*: .*/);
-
 async function deleteBranchSilently(branchName: string): Promise<void> {
   try {
     await deleteBranch(branchName);
@@ -181,11 +178,12 @@ export async function processBranch(
           if (dependencyDashboardCheck || config.rebaseRequested) {
             logger.debug('Manual rebase has been requested for PR');
           } else {
-            const newBody = branchPr.body?.replace(
-              rebasingRegex,
-              '**Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found.'
-            );
-            if (newBody !== branchPr.body) {
+            const newBody = await getPrBody(branchConfig, {
+              rebasingNotice:
+                'Renovate will not automatically rebase this PR, because other commits have been found.',
+            });
+            const newBodyHash = hashBody(newBody);
+            if (newBodyHash !== branchPr.bodyStruct?.hash) {
               logger.debug(
                 'Updating existing PR to indicate that rebasing is not possible'
               );
@@ -453,9 +451,7 @@ export async function processBranch(
 
     config.stopUpdating = branchPr?.labels?.includes(config.stopUpdatingLabel);
 
-    const prRebaseChecked = branchPr?.body?.includes(
-      `- [x] <!-- rebase-check -->`
-    );
+    const prRebaseChecked = !!branchPr?.bodyStruct?.rebaseRequested;
 
     if (branchExists && dependencyDashboardCheck && config.stopUpdating) {
       if (!prRebaseChecked) {
diff --git a/lib/workers/repository/update/branch/reuse.spec.ts b/lib/workers/repository/update/branch/reuse.spec.ts
index 02e4f9edaf1727827745bb3912f87201249377a5..ceda5f86b94681a4381576b3bd7b2f947f2115fc 100644
--- a/lib/workers/repository/update/branch/reuse.spec.ts
+++ b/lib/workers/repository/update/branch/reuse.spec.ts
@@ -126,7 +126,10 @@ describe('workers/repository/update/branch/reuse', () => {
       platform.getBranchPr.mockResolvedValueOnce({
         ...pr,
         title: 'Update foo to v4',
-        body: 'blah\nblah\n- [x] <!-- rebase-check -->foo\n',
+        bodyStruct: {
+          hash: '123',
+          rebaseRequested: true,
+        },
       });
       const res = await shouldReuseExistingBranch(config);
       expect(res.reuseExistingBranch).toBeFalse();
diff --git a/lib/workers/repository/update/branch/reuse.ts b/lib/workers/repository/update/branch/reuse.ts
index 32ab7abc6cf65d13d1bab5fd4626d8b412e11698..8a6447b036dbf1d842d746686ab0220906972d41 100644
--- a/lib/workers/repository/update/branch/reuse.ts
+++ b/lib/workers/repository/update/branch/reuse.ts
@@ -36,7 +36,7 @@ export async function shouldReuseExistingBranch(
       logger.debug(`Manual rebase requested via PR title for #${pr.number}`);
       return result;
     }
-    if (pr.body?.includes(`- [x] <!-- rebase-check -->`)) {
+    if (pr.bodyStruct?.rebaseRequested) {
       logger.debug(`Manual rebase requested via PR checkbox for #${pr.number}`);
       return result;
     }
diff --git a/lib/workers/repository/update/pr/index.spec.ts b/lib/workers/repository/update/pr/index.spec.ts
index 5b7d10fd6b50629a809d2f302454f192c1427773..eb0d70dcc90219c536352d5b266ad04466f55635 100644
--- a/lib/workers/repository/update/pr/index.spec.ts
+++ b/lib/workers/repository/update/pr/index.spec.ts
@@ -7,6 +7,7 @@ import {
   REPOSITORY_CHANGED,
 } from '../../../../constants/error-messages';
 import * as _comment from '../../../../modules/platform/comment';
+import { getPrBodyStruct } from '../../../../modules/platform/pr-body';
 import type { Pr } from '../../../../modules/platform/types';
 import { BranchStatus, PrState } from '../../../../types';
 import { ExternalHostError } from '../../../../types/errors/external-host-error';
@@ -45,12 +46,13 @@ describe('workers/repository/update/pr/index', () => {
     const sourceBranch = 'renovate-branch';
     const prTitle = 'Some title';
     const body = 'Some body';
+    const bodyStruct = getPrBodyStruct(body);
 
     const pr: Pr = {
       number,
       sourceBranch,
       title: prTitle,
-      body,
+      bodyStruct,
       state: PrState.Open,
     };
 
@@ -255,7 +257,10 @@ describe('workers/repository/update/pr/index', () => {
       });
 
       it('updates PR due to body change', async () => {
-        const changedPr: Pr = { ...pr, body: `${body} updated` };
+        const changedPr: Pr = {
+          ...pr,
+          bodyStruct: getPrBodyStruct(`${body} updated`),
+        };
         platform.getBranchPr.mockResolvedValueOnce(changedPr);
 
         const res = await ensurePr(config);
@@ -265,12 +270,15 @@ describe('workers/repository/update/pr/index', () => {
         expect(platform.createPr).not.toHaveBeenCalled();
       });
 
-      it('ignores eviewable content ', async () => {
+      it('ignores reviewable content ', async () => {
         // See: https://reviewable.io/
 
         const reviewableContent =
           '<!-- Reviewable:start -->something<!-- Reviewable:end -->';
-        const changedPr: Pr = { ...pr, body: `${body}${reviewableContent}` };
+        const changedPr: Pr = {
+          ...pr,
+          bodyStruct: getPrBodyStruct(`${body}${reviewableContent}`),
+        };
         platform.getBranchPr.mockResolvedValueOnce(changedPr);
 
         const res = await ensurePr(config);
diff --git a/lib/workers/repository/update/pr/index.ts b/lib/workers/repository/update/pr/index.ts
index 523f0ed4c1ef328b55a35409c3df6807db6e9b55..ce4f4cf7d2d476eb2a8299e0a8a89f756b34c937 100644
--- a/lib/workers/repository/update/pr/index.ts
+++ b/lib/workers/repository/update/pr/index.ts
@@ -9,12 +9,12 @@ import {
 import { logger } from '../../../../logger';
 import { PlatformPrOptions, Pr, platform } from '../../../../modules/platform';
 import { ensureComment } from '../../../../modules/platform/comment';
+import { hashBody } from '../../../../modules/platform/pr-body';
 import { BranchStatus } from '../../../../types';
 import { ExternalHostError } from '../../../../types/errors/external-host-error';
 import { stripEmojis } from '../../../../util/emoji';
 import { deleteBranch, getBranchLastCommitTime } from '../../../../util/git';
 import { memoize } from '../../../../util/memoize';
-import { regEx } from '../../../../util/regex';
 import { Limit, incLimitedValue, isLimitReached } from '../../../global/limits';
 import type {
   BranchConfig,
@@ -27,10 +27,6 @@ import { ChangeLogError } from './changelog/types';
 import { prepareLabels } from './labels';
 import { addParticipants } from './participants';
 
-function noWhitespaceOrHeadings(input: string): string {
-  return input.replace(regEx(/\r?\n|\r|\s|#/g), '');
-}
-
 export function getPlatformPrOptions(
   config: RenovateConfig & PlatformPrOptions
 ): PlatformPrOptions {
@@ -269,22 +265,13 @@ export async function ensurePr(
         await addParticipants(config, existingPr);
       }
       // Check if existing PR needs updating
-      existingPr.body ??= '';
-      const reviewableIndex = existingPr.body.indexOf(
-        '<!-- Reviewable:start -->'
-      );
-      let existingPrBody = existingPr.body;
-      if (reviewableIndex > -1) {
-        logger.debug('Stripping Reviewable content');
-        existingPrBody = existingPrBody.slice(0, reviewableIndex);
-      }
       const existingPrTitle = stripEmojis(existingPr.title);
+      const existingPrBodyHash = existingPr.bodyStruct?.hash;
       const newPrTitle = stripEmojis(prTitle);
-      existingPrBody = existingPrBody.trim();
+      const newPrBodyHash = hashBody(prBody);
       if (
         existingPrTitle === newPrTitle &&
-        noWhitespaceOrHeadings(stripEmojis(existingPrBody)) ===
-          noWhitespaceOrHeadings(stripEmojis(prBody))
+        existingPrBodyHash === newPrBodyHash
       ) {
         logger.debug(`${existingPr.displayNumber} does not need updating`);
         return { type: 'with-pr', pr: existingPr };