How a JavaScript redirect works
The server answers the request normally and sends a complete page. Somewhere in that page is a line of script which, once the browser has loaded and run it, changes the address in the address bar and fetches a different page instead. Nothing about the response says a redirect is happening; the move is a consequence of code that ran afterwards.
This is how single-page applications move between screens, and it is what testing tools, language switchers and “thank you” screens after a form submission often use. It is also the mechanism behind a lot of accidental redirects, where a script written for one situation fires in another.
Because the instruction is inside the page rather than in the response, discovering it requires running the script. That is the whole difference between this and a server redirect, and everything else follows from it.
Why JavaScript redirects matter
Google renders pages and can follow a redirect written in script, so these do work for search in most ordinary cases. But rendering happens on the search engine’s own schedule rather than at the moment the page is fetched, so the redirect is discovered later and with less certainty than one that arrives with the response. Other crawlers, link checkers, social previews and many SEO tools do not run scripts at all, and simply see a page that loaded successfully.
The practical result is inconsistency. The same URL behaves one way for a visitor, another way for a tool, and possibly a third way for a search engine that has not rendered it yet. That makes problems hard to reproduce and audits unreliable — see JavaScript rendering for why the delay exists.
Common mistakes with JavaScript redirects
Using them for a permanent move is the main one. A migration handled in script leaves every old URL answering successfully, so nothing in a crawl report looks wrong while the signal a 301 would have sent is missing entirely.
The second is automatic redirection by location or language. Sending every visitor from one country to a particular version of the site sounds helpful, and it means a crawler arriving from one place can never reach the other versions. Offer the choice with visible links instead, and let people and crawlers pick.
The third is redirecting differently depending on who is asking. Showing one destination to visitors and another to crawlers is cloaking, whatever the reason for it, and it is a risk not worth taking. The fourth, more mundane, is the loop: two scripts each sending the visitor back to the other, which looks like a page that will not load.
What to do about it
For anything permanent, use a server redirect. It is faster, it is visible to every tool, and it needs no rendering to be understood. Keep script-based navigation for what it is good at: moving between views inside an application, where no URL is being retired.
Where one has to stay, check what a search engine actually sees by inspecting the rendered version of the page rather than the source, and make sure the destination is the same for everyone. Then crawl the site for pages that return a successful response but move the visitor anyway, which is the same sweep that finds a technical SEO team its stock of forgotten redirects.