iTranslated by AI
Caution: Fetch API returns response.ok = true by default when a redirect occurs
TL;DR
- When using the JavaScript Fetch API, if a redirect occurs,
Response.ok = trueby default (in some cases).- Furthermore,
Response.statusmay result in 200 even if the original request target was 302.
- Furthermore,
- This is because the Fetch API allows you to specify the behavior when a redirect status is returned from the request destination using the
redirectoption, and the default behavior is to "follow" the redirect.- If the final redirect destination is something like HTTP 404,
reponse.okwould likely befalse(though this might be rare). - Fetch API | MDN
- If the final redirect destination is something like HTTP 404,
- You can either leave the default behavior and determine redirection using
Response.redirected, or control it by specifyingredirect: 'error'orredirect: 'manual'when executing the Fetch API.
Response Object for each redirect option when a redirect occurs
First, let's assume the Fetch API is executed like this:
const response = await fetch("/hoge/fuga", {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postBody),
redirect: "follow"
});
*Note: If the redirect option is omitted when executing the Fetch API, it is treated as redirect: 'follow'.
redirect: 'follow'
Response: {
body: ..., // Depending on the request
bodyUsed: false,
headers: ..., // Depending on the request
ok: true,
redirected: true, // This is true!!
status: 200, // HTTP status of the final redirect destination!!
statusText: "OK", // HTTP status Text of the final redirect destination!!
type: "basic",
url: "https://<URL of the final redirect destination>" // URL of the final redirect destination!!
}
Response.ok Read only
Returns a boolean value indicating whether the response was successful (status in the range 200–299).
As stated in MDN, if the request destination in the Fetch API specifies a redirect (such as HTTP 302), the HTTP status of the "destination URL" after the redirect occurs will be returned.
If it redirects further from the redirect destination, the result of the request to the final redirect destination URL seems to be stored in the response object.
If the final redirect destination is something like HTTP 404, reponse.ok would likely be false (though this might be rare).
redirect: 'error'
The Fetch API (fetch()) itself throws an exception, so a Response object cannot be obtained.
When caught, it shows net::ERR_FAILED 302 (Found).
redirect: 'manual'
Response: {
body: ..., // Depending on the request
bodyUsed: false,
headers: ..., // Depending on the request
ok: false,
redirected: false,
status: 0, // Literally 0
statusText: "", // Literally an empty string
type: "opaqueredirect",
url: "https://84Fetch API request destination>"
}
"manual" – does not follow HTTP redirects, but response.url becomes the new URL and response.redirected becomes true. You can manually redirect to the new URL if necessary.
JavaScript.info says this, but in my experience with Google Chrome v124.0.6367.119, it was redirected: false.
Also,
Response.redirected Read only
Indicates whether or not the response is the result of a redirect (that is, its URL list has more than one entry).
MDN says this, but in Google Chrome v124.0.6367.119, Response.url was a single String and I couldn't find where the URL list was.
Moreover, since Response.url contains the original Fetch API request destination, despite it being called redirect: 'manual', it feels like there isn't even a way to find out the redirect destination and re-fetch it.
I couldn't get the redirect destination even by calling Response.headers.get('Location').
What on earth is this...
Then I found this issue:
Cannot get next URL for redirect="manual"
That is by design
It seems it's by design that redirect: 'manual' cannot know the redirect destination. How disappointing...
Summary of key points
Response.ok |
Response.redirected |
Response.url |
|
|---|---|---|---|
redirect: 'follow' |
true * |
true |
URL of the final redirect destination |
redirect: 'error' |
(Throws an exception) | - | - |
redirect: 'manual' |
false |
false |
Fetch API request destination |
*If the final redirect destination is something like HTTP 404, reponse.ok would likely be false (though this might be rare).
In the end, what is the best way to control redirects?
First of all, you need to keep in mind the main theme of this article:
When using the Fetch API, if a redirect occurs,
response.ok = trueby default.
With that in mind, if the request destination is not expected to cause a redirect, you don't need to worry.
However, if a redirect can occur—for example, due to an authentication session timeout during a POST request—it is better to handle it using the following controls:
- Use
redirect: 'follow'(or omit it)- This is the only case where
Response.redirectedcan betrue. - If
Response.redirectedistrue, it means a redirect has occurred, so you can implement the necessary logic for handling the redirection. - Since
Response.urlcontains the final redirect destination URL, it seems possible to fetch again, get the result'sResponse.text(), and use something likenew DOMParser().parseFromString(Response.text(), "text/html")to forcefully manipulate the redirect destination's DOM. However, it's probably better not to do such unusual things... I think.- If anyone is interested in the details, please leave a comment.
- This is the only case where
- Use
redirect: 'error'- Since a redirect will throw an exception, you can simply handle it using
try-catch. - Basically, Fetch API errors should only occur in cases of communication failure, so be aware that the
catchlogic will handle either a communication failure or a redirect occurrence during Fetch API execution.
- Since a redirect will throw an exception, you can simply handle it using
- Use
redirect: 'manual'- Despite being called "manual," the actual behavior is "don't treat it as an error, but don't follow the redirect either."
- However, unlike
redirect: 'follow',Response.okbecomesfalsewhen a redirect occurs. This is useful because you can handle it uniformly with other non-200 status codes using a condition likeif (!Response.ok).
To summarize even more simply!
- If you want to treat it as an error:
redirect: 'error' - If you want to control it via
Response.ok:redirect: 'manual' - If you want to control it via
Response.redirected:redirect: 'follow'(or unspecified)
How about this as a conclusion?
References
- fetch APIのredirect関連メモ | 銀色うつ時間
- Fetch API fetch() の使い方 | Web Design Leaves
Discussion