diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index 01e974dd7c0a1d268f841b3672f86a058957b7e1..4b904edbef5d186b1a6409074a0f01b6f5659b9c 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -287,6 +287,12 @@ If you truly need to configure this then it probably means either:
 - You are hopefully mistaken, and there's a better approach you should use, so open a new "config help" discussion at the [Renovate discussions tab](https://github.com/renovatebot/renovate/discussions) or
 - You have a use case we didn't expect and we should have a feature request from you to add it to the project
 
+## branchNameStrict
+
+By default, Renovate does not use strict-mode when slugifying the branch name. This means that certain special characters such as `.` may end up within the branch name.
+
+By setting this configuration to `true`, all special characters will be removed from the branch name, resulting in a branch name consisting exclusively of alphabetic characters separated by `-`.
+
 ## branchPrefix
 
 You can modify this field if you want to change the prefix used.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 66a5b943950b8e45b8bc03ad577ecf720e77bd7e..c7911955152ee1b25da96d9d7e338d79ee92da90 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -2352,6 +2352,12 @@ const options: RenovateOptions[] = [
     default: false,
     supportedPlatforms: ['github'],
   },
+  {
+    name: 'branchNameStrict',
+    description: `Whether to be strict about the use of special characters within the branch name.`,
+    type: 'boolean',
+    default: false,
+  },
 ];
 
 export function getOptions(): RenovateOptions[] {
diff --git a/lib/config/types.ts b/lib/config/types.ts
index f9f53cae4c6533a8c8af705b51f9a54e2e1b8bc3..4e11fa6e81d24a332156b0b3498a0e182bd8d7c0 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -28,6 +28,7 @@ export interface RenovateSharedConfig {
   branchPrefix?: string;
   branchPrefixOld?: string;
   branchName?: string;
+  branchNameStrict?: boolean;
   manager?: string | null;
   commitMessage?: string;
   commitMessagePrefix?: string;
diff --git a/lib/workers/repository/updates/branch-name.spec.ts b/lib/workers/repository/updates/branch-name.spec.ts
index 93ff3a94ee596ca09b55cf4c9e33bfb12d4fed93..07c368946e8afc169d21e45ea3dc24afb775b7d3 100644
--- a/lib/workers/repository/updates/branch-name.spec.ts
+++ b/lib/workers/repository/updates/branch-name.spec.ts
@@ -146,6 +146,22 @@ describe('workers/repository/updates/branch-name', () => {
       expect(upgrade.branchName).toBe('renovate/jest-42.x');
     });
 
+    it('realistic defaults with strict branch name enabled', () => {
+      const upgrade: RenovateConfig = {
+        branchNameStrict: true,
+        branchName:
+          '{{{branchPrefix}}}{{{additionalBranchPrefix}}}{{{branchTopic}}}',
+        branchTopic:
+          '{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}',
+        branchPrefix: 'renovate/',
+        depNameSanitized: 'jest',
+        newMajor: '42',
+        group: {},
+      };
+      generateBranchName(upgrade);
+      expect(upgrade.branchName).toBe('renovate/jest-42-x');
+    });
+
     it('hashedBranchLength hashing', () => {
       const upgrade: RenovateConfig = {
         branchName:
@@ -293,5 +309,35 @@ describe('workers/repository/updates/branch-name', () => {
         expect(fixture.upgrade.branchName).toEqual(fixture.expectedBranchName);
       });
     });
+
+    it('strict branch name enabled group', () => {
+      const upgrade: RenovateConfig = {
+        branchNameStrict: true,
+        groupName: 'some group name `~#$%^&*()-_=+[]{}|;,./<>? .version',
+        group: {
+          branchName: '{{groupSlug}}-{{branchTopic}}',
+          branchTopic: 'grouptopic',
+        },
+      };
+      generateBranchName(upgrade);
+      expect(upgrade.branchName).toBe(
+        'some-group-name-dollarpercentand-or-lessgreater-version-grouptopic'
+      );
+    });
+
+    it('strict branch name disabled', () => {
+      const upgrade: RenovateConfig = {
+        branchNameStrict: false,
+        groupName: '[some] group name.#$%version',
+        group: {
+          branchName: '{{groupSlug}}-{{branchTopic}}',
+          branchTopic: 'grouptopic',
+        },
+      };
+      generateBranchName(upgrade);
+      expect(upgrade.branchName).toBe(
+        'some-group-name.dollarpercentversion-grouptopic'
+      );
+    });
   });
 });
diff --git a/lib/workers/repository/updates/branch-name.ts b/lib/workers/repository/updates/branch-name.ts
index da71eae75a1d90f3ff1934fa0a677f6e5c3f0e5e..b88ec208b3d8e55f782936156e5b70ffc362c1a9 100644
--- a/lib/workers/repository/updates/branch-name.ts
+++ b/lib/workers/repository/updates/branch-name.ts
@@ -10,6 +10,9 @@ import * as template from '../../../util/template';
 const MIN_HASH_LENGTH = 6;
 
 const RE_MULTIPLE_DASH = regEx(/--+/g);
+
+const RE_SPECIAL_CHARS_STRICT = regEx(/[`~!@#$%^&*()_=+[\]\\|{};':",.<>?]/g);
+
 /**
  * Clean git branch name
  *
@@ -17,15 +20,26 @@ const RE_MULTIPLE_DASH = regEx(/--+/g);
  * - leading dot/leading dot after slash
  * - trailing dot
  * - whitespace
+ * - special characters
+ * - leading or trailing dashes
  * - chained dashes(breaks markdown comments) are replaced by single dash
  */
-function cleanBranchName(branchName: string): string {
+function cleanBranchName(
+  branchName: string,
+  branchNameStrict?: boolean
+): string {
+  let cleanedBranchName = branchName;
+
+  if (branchNameStrict) {
+    cleanedBranchName = cleanedBranchName.replace(RE_SPECIAL_CHARS_STRICT, '-'); // massage out all special characters that slip through slugify
+  }
+
   return cleanGitRef
-    .clean(branchName)
+    .clean(cleanedBranchName)
     .replace(regEx(/^\.|\.$/), '') // leading or trailing dot
     .replace(regEx(/\/\./g), '/') // leading dot after slash
     .replace(regEx(/\s/g), '') // whitespace
-    .replace(regEx(/[[\]?:\\^~]/g), '-') // massage out all these characters: : ? [ \ ^ ~
+    .replace(regEx(/[[\]?:\\^~]/g), '-') // massage out all these characters: [ ] ? : \ ^ ~
     .replace(regEx(/(^|\/)-+/g), '$1') // leading dashes
     .replace(regEx(/-+(\/|$)/g), '$1') // trailing dashes
     .replace(RE_MULTIPLE_DASH, '-'); // chained dashes
@@ -94,5 +108,8 @@ export function generateBranchName(update: RenovateConfig): void {
     update.branchName = template.compile(update.branchName, update);
   }
 
-  update.branchName = cleanBranchName(update.branchName);
+  update.branchName = cleanBranchName(
+    update.branchName,
+    update.branchNameStrict
+  );
 }