diff --git a/lib/util/http/github.spec.ts b/lib/util/http/github.spec.ts
index 68af7bc831436dd44a09592d30f60993389e1718..31292e7b3a0487b2c1ad3bc5d7e8d81c31d3ef96 100644
--- a/lib/util/http/github.spec.ts
+++ b/lib/util/http/github.spec.ts
@@ -1,3 +1,4 @@
+import { Buffer } from 'node:buffer';
 import { codeBlock } from 'common-tags';
 import { DateTime } from 'luxon';
 import * as httpMock from '../../../test/http-mock';
@@ -786,4 +787,166 @@ describe('util/http/github', () => {
       ).rejects.toThrow(EXTERNAL_HOST_ERROR);
     });
   });
+
+  describe('getRawFile()', () => {
+    it('add header and return', async () => {
+      httpMock
+        .scope(githubApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(
+          `${githubApiHost}/foo/bar/contents/lore/ipsum.txt`,
+        ),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support relative path', async () => {
+      httpMock
+        .scope(githubApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(
+          `${githubApiHost}/foo/bar/contents/foo/../lore/ipsum.txt`,
+        ),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support default to api.github.com if no baseURL has been supplied', async () => {
+      httpMock
+        .scope(githubApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support custom host if a baseURL has been supplied', async () => {
+      const customApiHost = 'https://my.comapny.com/api/v3/';
+      httpMock
+        .scope(customApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`, {
+          baseUrl: customApiHost,
+        }),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support default to api.github.com if no baseURL, but repository has been supplied', async () => {
+      httpMock
+        .scope(githubApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(`lore/ipsum.txt`, {
+          repository: 'foo/bar',
+        }),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support custom host if a baseURL and repository has been supplied', async () => {
+      const customApiHost = 'https://my.comapny.com/api/v3/';
+      httpMock
+        .scope(customApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(`lore/ipsum.txt`, {
+          baseUrl: customApiHost,
+          repository: 'foo/bar',
+        }),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support default to api.github.com if content path is used', async () => {
+      httpMock
+        .scope(githubApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'foo');
+      await expect(
+        githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`),
+      ).resolves.toMatchObject({
+        body: 'foo',
+      });
+    });
+
+    it('support custom host if content path is used', async () => {
+      const customApiHost = 'https://my.comapny.com/api/v3/';
+      httpMock
+        .scope(customApiHost)
+        .get('/foo/bar/contents/lore/ipsum.txt')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, 'test');
+      await expect(
+        githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.txt`, {
+          baseUrl: customApiHost,
+        }),
+      ).resolves.toMatchObject({
+        body: 'test',
+      });
+    });
+
+    it('throw error if a ', async () => {
+      httpMock
+        .scope(githubApiHost)
+        .get('/foo/bar/contents/lore/ipsum.bin')
+        .matchHeader(
+          'accept',
+          'application/vnd.github.raw+json, application/vnd.github.v3+json',
+        )
+        .reply(200, Buffer.from('foo', 'binary'));
+      await expect(
+        githubApi.getRawTextFile(`foo/bar/contents/lore/ipsum.bin`, {
+          responseType: 'buffer',
+        }),
+      ).rejects.toThrow();
+    });
+  });
 });
diff --git a/lib/util/http/github.ts b/lib/util/http/github.ts
index 3db9d7b4a741dcf33925108c6e28124147ee1ad4..56db54db38716c77c989888242f97489d607ab88 100644
--- a/lib/util/http/github.ts
+++ b/lib/util/http/github.ts
@@ -493,4 +493,41 @@ export class GithubHttp extends Http<GithubHttpOptions> {
 
     return result;
   }
+
+  /**
+   * Get the raw text file from a URL.
+   * Only use this method to fetch text files.
+   *
+   * @param url Full API URL, contents path or path inside the repository to the file
+   * @param options
+   *
+   * @example url = 'https://api.github.com/repos/renovatebot/renovate/contents/package.json'
+   * @example url = 'renovatebot/renovate/contents/package.json'
+   * @example url = 'package.json' & options.repository = 'renovatebot/renovate'
+   */
+  public async getRawTextFile(
+    url: string,
+    options: InternalHttpOptions & GithubHttpOptions = {},
+  ): Promise<HttpResponse> {
+    const newOptions: InternalHttpOptions & GithubHttpOptions = {
+      ...options,
+      headers: {
+        accept: 'application/vnd.github.raw+json',
+      },
+    };
+
+    let newURL = url;
+    const httpRegex = regEx(/^https?:\/\//);
+    if (options.repository && !httpRegex.test(options.repository)) {
+      newURL = joinUrlParts(options.repository, 'contents', url);
+    }
+
+    const result = await this.get(newURL, newOptions);
+    if (!is.string(result.body)) {
+      throw new Error(
+        `Expected raw text file but received ${typeof result.body}`,
+      );
+    }
+    return result;
+  }
 }