diff --git a/readme.md b/readme.md
index e1ae5bd20..0d7927ec3 100644
--- a/readme.md
+++ b/readme.md
@@ -368,6 +368,13 @@ Defines if redirect responses should be followed automatically.
Note that if a `303` is sent by the server in response to any request type (`POST`, `DELETE`, etc.), Got will automatically request the resource pointed to in the location header via `GET`. This is in accordance with [the spec](https://tools.ietf.org/html/rfc7231#section-6.4.4).
+###### maxRedirects
+
+Type: `number`
+Default: `10`
+
+If exceeded, the request will be aborted and a `MaxRedirectsError` will be thrown.
+
###### decompress
Type: `boolean`
diff --git a/source/errors.ts b/source/errors.ts
index 95d820723..483ecdae1 100644
--- a/source/errors.ts
+++ b/source/errors.ts
@@ -76,8 +76,8 @@ export class HTTPError extends GotError {
export class MaxRedirectsError extends GotError {
readonly response!: Response;
- constructor(response: Response, options: NormalizedOptions) {
- super('Redirected 10 times. Aborting.', {}, options);
+ constructor(response: Response, maxRedirects: number, options: NormalizedOptions) {
+ super(`Redirected ${maxRedirects} times. Aborting.`, {}, options);
this.name = 'MaxRedirectsError';
Object.defineProperty(this, 'response', {
diff --git a/source/index.ts b/source/index.ts
index 9b0228f8a..160646c54 100644
--- a/source/index.ts
+++ b/source/index.ts
@@ -55,7 +55,8 @@ const defaults: Partial = {
dnsCache: false,
useElectronNet: false,
responseType: 'text',
- resolveBodyOnly: false
+ resolveBodyOnly: false,
+ maxRedirects: 10
},
mutableDefaults: false
};
diff --git a/source/request-as-event-emitter.ts b/source/request-as-event-emitter.ts
index af776b02a..43589c043 100644
--- a/source/request-as-event-emitter.ts
+++ b/source/request-as-event-emitter.ts
@@ -150,8 +150,8 @@ export default (options: NormalizedOptions, input?: TransformStream) => {
options.method = 'GET';
}
- if (redirects.length >= 10) {
- throw new MaxRedirectsError(typedResponse, options);
+ if (redirects.length >= options.maxRedirects) {
+ throw new MaxRedirectsError(typedResponse, options.maxRedirects, options);
}
// Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604
diff --git a/source/utils/types.ts b/source/utils/types.ts
index 219705d4c..50d50b439 100644
--- a/source/utils/types.ts
+++ b/source/utils/types.ts
@@ -161,6 +161,7 @@ export interface Options extends Except;
json?: Record;
context?: {[key: string]: unknown};
+ maxRedirects?: number;
}
export interface NormalizedOptions extends Except, 'timeout' | 'dnsCache' | 'retry' | 'auth' | 'body' | 'port'> {
diff --git a/test/redirects.ts b/test/redirects.ts
index 26b672f56..60edd4c38 100644
--- a/test/redirects.ts
+++ b/test/redirects.ts
@@ -81,6 +81,20 @@ test('throws on endless redirects', withServer, async (t, server, got) => {
t.deepEqual(error.response.redirectUrls, new Array(10).fill(`${server.url}/`));
});
+test('custom `maxRedirects` option', withServer, async (t, server, got) => {
+ server.get('/', (_request, response) => {
+ response.writeHead(302, {
+ location: server.url
+ });
+ response.end();
+ });
+
+ const error = await t.throwsAsync(got('', {maxRedirects: 5}), 'Redirected 5 times. Aborting.');
+
+ // @ts-ignore
+ t.deepEqual(error.response.redirectUrls, new Array(5).fill(`${server.url}/`));
+});
+
test('searchParams are not breaking redirects', withServer, async (t, server, got) => {
server.get('/', reachedHandler);