diff --git a/lib/modules/platform/bitbucket/comments.spec.ts b/lib/modules/platform/bitbucket/comments.spec.ts
index c8cd3edb7a21b80aece18af25feb5a7d33bf5979..7b6ed8f0cd345c43e3161f7f1f9f3b0eeed24765 100644
--- a/lib/modules/platform/bitbucket/comments.spec.ts
+++ b/lib/modules/platform/bitbucket/comments.spec.ts
@@ -47,6 +47,52 @@ describe('modules/platform/bitbucket/comments', () => {
       ).toBeTrue();
     });
 
+    it('finds reopen comment', async () => {
+      const prComment = {
+        content: {
+          raw: 'reopen! comment',
+        },
+        user: {
+          display_name: 'Bob Smith',
+          uuid: '{d2238482-2e9f-48b3-8630-de22ccb9e42f}',
+          account_id: '123',
+        },
+      };
+
+      expect.assertions(1);
+      httpMock
+        .scope(baseUrl)
+        .get('/2.0/repositories/some/repo/pullrequests/5/comments?pagelen=100')
+        .reply(200, {
+          values: [prComment],
+        });
+
+      expect(await comments.reopenComments(config, 5)).toEqual([prComment]);
+    });
+
+    it('finds no reopen comment', async () => {
+      const prComment = {
+        content: {
+          raw: 'comment',
+        },
+        user: {
+          display_name: 'Bob Smith',
+          uuid: '{d2238482-2e9f-48b3-8630-de22ccb9e42f}',
+          account_id: '123',
+        },
+      };
+
+      expect.assertions(1);
+      httpMock
+        .scope(baseUrl)
+        .get('/2.0/repositories/some/repo/pullrequests/5/comments?pagelen=100')
+        .reply(200, {
+          values: [prComment],
+        });
+
+      expect(await comments.reopenComments(config, 5)).toBeEmptyArray();
+    });
+
     it('add updates comment if necessary', async () => {
       expect.assertions(1);
       httpMock
diff --git a/lib/modules/platform/bitbucket/comments.ts b/lib/modules/platform/bitbucket/comments.ts
index 106a638971530b66e59553004f065387cea31609..f601701d891f3738f7c0394d0abd8692aae5ec49 100644
--- a/lib/modules/platform/bitbucket/comments.ts
+++ b/lib/modules/platform/bitbucket/comments.ts
@@ -1,13 +1,16 @@
 import { logger } from '../../../logger';
 import { BitbucketHttp } from '../../../util/http/bitbucket';
 import type { EnsureCommentConfig, EnsureCommentRemovalConfig } from '../types';
-import type { Config, PagedResult } from './types';
+import type { Account, Config, PagedResult } from './types';
+
+export const REOPEN_PR_COMMENT_KEYWORD = 'reopen!';
 
 const bitbucketHttp = new BitbucketHttp();
 
 interface Comment {
   content: { raw: string };
   id: number;
+  user: Account;
 }
 
 export type CommentsConfig = Pick<Config, 'repository'>;
@@ -123,6 +126,19 @@ export async function ensureComment({
   }
 }
 
+export async function reopenComments(
+  config: CommentsConfig,
+  prNo: number
+): Promise<Comment[]> {
+  const comments = await getComments(config, prNo);
+
+  const reopenComments = comments.filter((comment) =>
+    comment.content.raw.startsWith(REOPEN_PR_COMMENT_KEYWORD)
+  );
+
+  return reopenComments;
+}
+
 export async function ensureCommentRemoval(
   config: CommentsConfig,
   deleteConfig: EnsureCommentRemovalConfig
@@ -157,8 +173,13 @@ export async function ensureCommentRemoval(
 }
 
 function sanitizeCommentBody(body: string): string {
-  return body.replace(
-    'checking the rebase/retry box above',
-    'renaming this PR to start with "rebase!"'
-  );
+  return body
+    .replace(
+      'checking the rebase/retry box above',
+      'renaming this PR to start with "rebase!"'
+    )
+    .replace(
+      'rename this PR to get a fresh replacement',
+      'add a comment starting with "reopen!" to get a fresh replacement'
+    );
 }
diff --git a/lib/modules/platform/bitbucket/index.spec.ts b/lib/modules/platform/bitbucket/index.spec.ts
index 44d4cc8afc6b48aff7b4612c88210cde067cf482..603bb2b5e91c861e0d82bcb189514edaa1be8dfc 100644
--- a/lib/modules/platform/bitbucket/index.spec.ts
+++ b/lib/modules/platform/bitbucket/index.spec.ts
@@ -789,6 +789,170 @@ describe('modules/platform/bitbucket/index', () => {
         })
       ).toMatchSnapshot();
     });
+
+    it('finds closed pr with no reopen comments', async () => {
+      const prComment = {
+        content: {
+          raw: 'some comment',
+        },
+        user: {
+          display_name: 'Bob Smith',
+          uuid: '{d2238482-2e9f-48b3-8630-de22ccb9e42f}',
+          account_id: '123',
+        },
+      };
+
+      const scope = await initRepoMock();
+      scope
+        .get(
+          '/2.0/repositories/some/repo/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED&pagelen=50'
+        )
+        .reply(200, {
+          values: [
+            {
+              id: 5,
+              source: { branch: { name: 'branch' } },
+              destination: { branch: { name: 'master' } },
+              title: 'title',
+              state: 'closed',
+            },
+          ],
+        })
+        .get('/2.0/repositories/some/repo/pullrequests/5/comments?pagelen=100')
+        .reply(200, { values: [prComment] });
+
+      const pr = await bitbucket.findPr({
+        branchName: 'branch',
+        prTitle: 'title',
+      });
+      expect(pr?.number).toBe(5);
+    });
+
+    it('finds closed pr with reopen comment on private repository', async () => {
+      const prComment = {
+        content: {
+          raw: 'reopen! comment',
+        },
+        user: {
+          display_name: 'Jane Smith',
+          uuid: '{90b6646d-1724-4a64-9fd9-539515fe94e9}',
+          account_id: '456',
+        },
+      };
+
+      const scope = await initRepoMock({}, { is_private: true });
+      scope
+        .get(
+          '/2.0/repositories/some/repo/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED&pagelen=50'
+        )
+        .reply(200, {
+          values: [
+            {
+              id: 5,
+              source: { branch: { name: 'branch' } },
+              destination: { branch: { name: 'master' } },
+              title: 'title',
+              state: 'closed',
+            },
+          ],
+        })
+        .get('/2.0/repositories/some/repo/pullrequests/5/comments?pagelen=100')
+        .reply(200, { values: [prComment] });
+
+      const pr = await bitbucket.findPr({
+        branchName: 'branch',
+        prTitle: 'title',
+      });
+      expect(pr).toBeNull();
+    });
+
+    it('finds closed pr with reopen comment on public repository from workspace member', async () => {
+      const workspaceMember = {
+        display_name: 'Jane Smith',
+        uuid: '{90b6646d-1724-4a64-9fd9-539515fe94e9}',
+        account_id: '456',
+      };
+
+      const prComment = {
+        content: {
+          raw: 'reopen! comment',
+        },
+        user: workspaceMember,
+      };
+
+      const scope = await initRepoMock({}, { is_private: false });
+      scope
+        .get(
+          '/2.0/repositories/some/repo/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED&pagelen=50'
+        )
+        .reply(200, {
+          values: [
+            {
+              id: 5,
+              source: { branch: { name: 'branch' } },
+              destination: { branch: { name: 'master' } },
+              title: 'title',
+              state: 'closed',
+            },
+          ],
+        })
+        .get('/2.0/repositories/some/repo/pullrequests/5/comments?pagelen=100')
+        .reply(200, { values: [prComment] })
+        .get(
+          '/2.0/workspaces/some/members/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D'
+        )
+        .reply(200, { values: [workspaceMember] });
+
+      const pr = await bitbucket.findPr({
+        branchName: 'branch',
+        prTitle: 'title',
+      });
+      expect(pr).toBeNull();
+    });
+
+    it('finds closed pr with reopen comment on public repository from non-workspace member', async () => {
+      const nonWorkspaceMember = {
+        display_name: 'Jane Smith',
+        uuid: '{90b6646d-1724-4a64-9fd9-539515fe94e9}',
+        account_id: '456',
+      };
+
+      const prComment = {
+        content: {
+          raw: 'reopen! comment',
+        },
+        user: nonWorkspaceMember,
+      };
+
+      const scope = await initRepoMock({}, { is_private: false });
+      scope
+        .get(
+          '/2.0/repositories/some/repo/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED&pagelen=50'
+        )
+        .reply(200, {
+          values: [
+            {
+              id: 5,
+              source: { branch: { name: 'branch' } },
+              destination: { branch: { name: 'master' } },
+              title: 'title',
+              state: 'closed',
+            },
+          ],
+        })
+        .get('/2.0/repositories/some/repo/pullrequests/5/comments?pagelen=100')
+        .reply(200, { values: [prComment] })
+        .get(
+          '/2.0/workspaces/some/members/%7B90b6646d-1724-4a64-9fd9-539515fe94e9%7D'
+        )
+        .reply(404);
+
+      const pr = await bitbucket.findPr({
+        branchName: 'branch',
+        prTitle: 'title',
+      });
+      expect(pr?.number).toBe(5);
+    });
   });
 
   describe('createPr()', () => {
diff --git a/lib/modules/platform/bitbucket/index.ts b/lib/modules/platform/bitbucket/index.ts
index 81bd8c2341f1d417472113fdba6557dc0f8e3c86..26532a19d161b4ff893621788af46f7663fd8593 100644
--- a/lib/modules/platform/bitbucket/index.ts
+++ b/lib/modules/platform/bitbucket/index.ts
@@ -211,6 +211,7 @@ export async function initRepo({
       owner: info.owner,
       mergeMethod: info.mergeMethod,
       has_issues: info.has_issues,
+      is_private: info.is_private,
     };
 
     logger.debug(`${repository} owner = ${config.owner}`);
@@ -309,6 +310,34 @@ export async function findPr({
   if (pr) {
     logger.debug(`Found PR #${pr.number}`);
   }
+
+  /**
+   * Bitbucket doesn't support renaming or reopening declined PRs.
+   * Instead, we have to use comment-driven signals.
+   */
+  if (pr?.state === 'closed') {
+    const reopenComments = await comments.reopenComments(config, pr.number);
+
+    if (is.nonEmptyArray(reopenComments)) {
+      if (config.is_private) {
+        // Only workspace members could have commented on a private repository
+        logger.debug(
+          `Found '${comments.REOPEN_PR_COMMENT_KEYWORD}' comment from workspace member. Renovate will reopen PR ${pr.number} as a new PR`
+        );
+        return null;
+      }
+
+      for (const comment of reopenComments) {
+        if (await isAccountMemberOfWorkspace(comment.user, config.repository)) {
+          logger.debug(
+            `Found '${comments.REOPEN_PR_COMMENT_KEYWORD}' comment from workspace member. Renovate will reopen PR ${pr.number} as a new PR`
+          );
+          return null;
+        }
+      }
+    }
+  }
+
   return pr ?? null;
 }
 
diff --git a/lib/modules/platform/bitbucket/types.ts b/lib/modules/platform/bitbucket/types.ts
index b3b95bb0f16cf736a42fb6270d509bd16d4f45f4..2bd692ca6b26801e64deb2696b30f829a94e7a9f 100644
--- a/lib/modules/platform/bitbucket/types.ts
+++ b/lib/modules/platform/bitbucket/types.ts
@@ -16,6 +16,7 @@ export interface Config {
   prList: Pr[];
   repository: string;
   ignorePrAuthor: boolean;
+  is_private: boolean;
 }
 
 export interface PagedResult<T = any> {
@@ -33,6 +34,7 @@ export interface RepoInfo {
   mergeMethod: string;
   has_issues: boolean;
   uuid: string;
+  is_private: boolean;
 }
 
 export interface RepoBranchingModel {
@@ -64,6 +66,7 @@ export interface RepoInfoBody {
   has_issues: boolean;
   uuid: string;
   full_name: string;
+  is_private: boolean;
 }
 
 export interface PrResponse {
diff --git a/lib/modules/platform/bitbucket/utils.ts b/lib/modules/platform/bitbucket/utils.ts
index a5665bd2f7a9cbd6eaee41b230ad911de2ba7e13..d4a1e6c147c76a2373b9d86f040ca012adc3ed35 100644
--- a/lib/modules/platform/bitbucket/utils.ts
+++ b/lib/modules/platform/bitbucket/utils.ts
@@ -19,6 +19,7 @@ export function repoInfoTransformer(repoInfoBody: RepoInfoBody): RepoInfo {
     mergeMethod: 'merge',
     has_issues: repoInfoBody.has_issues,
     uuid: repoInfoBody.uuid,
+    is_private: repoInfoBody.is_private,
   };
 }