Is it possible to prevent the browser from following redirects when sending XMLHttpRequest-s (i.e. to get the redirect status code back and handle it myself)?
8 Answers
Not according to the W3C standard for the XMLHttpRequest object (emphasis added):
If the response is an HTTP redirect:
If the origin of the URL conveyed by the Location header is same origin with the XMLHttpRequest origin and the redirect does not violate infinite loop precautions, transparently follow the redirect while observing the same-origin request event rules.
They were considering it for a future release:
This specification does not include the following features which are being considered for a future version of this specification:
- Property to disable following redirects;
but the latest specification no longer mentions this.
4 Comments
The new Fetch API supports different modes of redirect handling: follow, error, and manual, but I can't find a way to view the new URL or the status code when the redirection has been canceled. You just can stop the redirection itself, and then it looks like an error (empty response). If that's all you need, you are good to go. Also you should be aware that the requests made via this API are not cancelable yet. They are now.
As for XMLHttpRequest, you can HEAD the server and inspect whether the URL has changed:
var http = new XMLHttpRequest(); http.open('HEAD', '/the/url'); http.onreadystatechange = function() { if (this.readyState === this.DONE) { console.log(this.responseURL); } }; http.send(); You won't get the status code, but will find the new URL without downloading the whole page from it.
2 Comments
OPTIONS may be a better choice, anyway only works for non general purpose, etc. administrator had configured redirect the whole site / schema, such as HTTP -> HTTPSNo you there isn't any place in the API exposed by XMLHttpRequest that allows you to override its default behaviour of following a 301 or 302 automatically.
If the client is running IE on windows then you can use WinHTTP instead to set an option to prevent that behaviour but thats a very limiting solution.
Comments
You can use responseURL property to get the redirect destination or check whether the response was ultimately fetched from a location you accept.
This of course means the result is fetched anyway, but at least you can get the necessary info about the redirect destination and for example detect conditions when you would like to discard the response.
1 Comment
I extended user's answer to include an abort() call. It seems like this prevents the server from sending too much data when all you want is the redirect url.
var url = 'the url' var http = new XMLHttpRequest(); http.open('GET', url); http.onreadystatechange = function() { if (this.readyState === this.DONE) { console.log(this.responseURL) this.abort() // This seems to stop the response } } http.send() In real life I wrapped the above code in a promise, but it made the code hard to read.
Also, I don't understand why getting the redirect url needs to be this difficult, but that is a question for another time and place.
1 Comment
As according to XMLHttpRequest Living Standard — Last Updated 19 February 2024
is written:
headers received: All redirects (if any) have been followed and all headers of a response have been received.
that means "all redirections is made transparently, and it is no way to control this flow programically".
BUT
You can use fetch() (Fetch API). It has build in "manual" option to controll redirections.
Please notice, that "manual" option does NOT controll the state of connection ! So there is no "done" or "error" answer. You need to add into you server application proper handlers.
BUT - assuming, that you just want to cancel redirected connection, it is no need to expect any answer from server because the connection it self will be cancelled, so there wont be any answer at all.
In practise it goes like this:
fetch(url, { redirect: 'manual', method: "POST", body: data }).then((res) => { // if(res.ok){ <--- this is the main misunderstanding of "manual" option, that programmers await for "ok". But it will never come in "manual" option ! you can continue programm after cancelling redirected connection ... // } }).catch((error) => { this error handler handles errors of first from redirection chain connection } Notice, that first connction from redirections chains will be done and finished (!) This is obvious because only after this first connection will the fetch() mechanism aware that redirection is planned to be established.
Comments
If the server sends a redirect using status code 307 (Temporary Redirect), this can work (at least in Chrome).
let xhr = new XMLHttpRequest(); xhr.open("GET", "http://endpoint-that-can-return-redirect", true); xhr.onreadystatechange = function() { if (this.readyState == XMLHttpRequest.HEADERS_RECEIVED) { // server responded with headers first // 'this.response' is still 'null' here, // but 'this.status' is '200', not '307' as could be expected let redirect_location = this.responseURL; // cancel the request this.abort(); doSomethingWithRedirectLocation(redirect_location); } }; xhr.onload = function() { if (this.status == 200) { // server sends actual data, not a redirect let data = this.response; // or 'this.reponseText' } }; xhr.send(); Comments
It is not possible to handle redirect or 302 status at client side as answered in other comments. However you can prevent redirection. To do that you can set request header "X-Requested-With" with "XMLHttpRequest" xhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); This should be done after open but before send. Example below
let xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { reqObj.success(JSON.parse(this.responseText)) } else if (this.status != 200) { reqObj.error(this.statusText) } }; xhttp.open(reqObj.type, reqObj.url, reqObj.async); xhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xhttp.send(); 1 Comment
status is 0 by design and headers are always empty. This is for security reasons. See: fetch.spec.whatwg.org/#atomic-http-redirect-handling