Closed Bug 1422334 Opened 2 years ago Closed 3 months ago

reload after history.replaceState after 3xx redirect shows wrong url in URL bar and Location api

Categories

(Core :: DOM: Navigation, defect, P2)

57 Branch
defect

Tracking

()

RESOLVED FIXED
mozilla70
Webcompat Priority P2
Tracking Status
firefox-esr60 --- wontfix
firefox-esr68 --- wontfix
firefox68 --- wontfix
firefox69 --- wontfix
firefox70 --- fixed

People

(Reporter: vg, Assigned: bzbarsky)

References

(Regression)

Details

(Keywords: regression)

Attachments

(3 files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36

Steps to reproduce:

1. Go to the link which will make 3xx redirect with "Location" header.

For example: we enter "http://localhost:2999/test" and server make redirection to "Location: http://localhost:2999/?code=12345"

2. Open console and run command: "history.replaceState(null, '', location.pathname)" which will remove query params: "http://localhost:2999/?code=12345" will become "http://localhost:2999/"

3. Push cmd+r or F5 or click on refresh button. You will see that query params will appear again and our url will become "http://localhost:2999/?code=12345". But if you look at server requests log, you will see that browser actually sends "/" url, not "/?code=12345"


Actual results:

Browser incorrectly show previous url after history.replaceState was called and refresh button was pushed, but sends url that was result of replace by history.replaceState


Expected results:

Browser should show correct url after using history.replaceState after 3xx redirect with "Location" header.
Component: Untriaged → DOM
Product: Firefox → Core
I forget to mention, that this bug also affects "window.location.search" that shows incorrect results.

Actual results after removing query params with "history.replaceState(null, '', location.pathname)" and clicking refresh button:
window.location.search === "?code=12345"

Expected results after removing query params with "history.replaceState(null, '', location.pathname)" and clicking refresh button:
window.location.search === ""
(In reply to Vadim Goncharov from comment #0)
> Created attachment 8933680 [details]
> fif_replaceState_bug.mov
> 
> User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1)
> AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
> 
> Steps to reproduce:
> 
> 1. Go to the link which will make 3xx redirect with "Location" header.
> 
> For example: we enter "http://localhost:2999/test" and server make
> redirection to "Location: http://localhost:2999/?code=12345"
> 
> 2. Open console and run command: "history.replaceState(null, '',
> location.pathname)" which will remove query params:
> "http://localhost:2999/?code=12345" will become "http://localhost:2999/"
> 
> 3. Push cmd+r or F5 or click on refresh button. You will see that query
> params will appear again and our url will become
> "http://localhost:2999/?code=12345". But if you look at server requests log,
> you will see that browser actually sends "/" url, not "/?code=12345"
> 
> 
> Actual results:
> 
> Browser incorrectly show previous url after history.replaceState was called
> and refresh button was pushed, but sends url that was result of replace by
> history.replaceState
> 

Could this confusion come from the fact that F5 or (cmd+r) executes reload from cache and that with our implementation point of view, this is an unfortune expected behaviour?

> 
> Expected results:
> 
> Browser should show correct url after using history.replaceState after 3xx
> redirect with "Location" header.
Flags: needinfo?(sawang)
> Could this confusion come from the fact that F5 or (cmd+r) executes reload
> from cache and that with our implementation point of view, this is an
> unfortune expected behaviour?
Yeah, may be. But in Chrome/Safari everything works as expected.

The main problem is that after using "history.replaceState" and then clicking "cmd+r/f5" we will see that browser sends replaced (correct) url to server, but shows incorrect url both in location.search and browser url bar. And this behaviour continues (if click to "cmd+r/f5") until we click "enter" on browser url bar.
Similar situation

Conditions:
--------------
1. Mozilla Firefox version only >= 56 
2. Single Page Application
3. For routing uses history.replaceState, all parameters not null

Steps:
------
1. Login & redirect to main page base URL
2. Navigate on any application tab & replace URL parameters
3. Press F5 or click Refresh button
4. Ups!.. Again open main page with base URL (but in other browsers we see the selected tab and the correct URL)

Additional: in the journal history of visits (Library) we see all the URLs, which were setting in history.replaceState

Best regards, Dmitry
I'm experiencing a similar situation on 57+.

1. Navigate to page with a querystring.
2. Remove querystring params via a button click in JS.
3. Refresh.
4. Querystring reappears!
I should mention that the refresh in step 3 above is being performed via JS window.location.reload().
We are experiencing the same in our application.

Although I understand this could be a side effect of caching, I can hardly see how this can be considered expected behavior.

At the very least the URL logged in the console should reflect the one actually loaded. But in general, as a user, when I refresh a page, I intend to refresh the current URL in the address bar, not the one that was originally loaded when I entered the application.

Is there anything we can do to speed up confirmation for this one?
We added `nsISHEntry.resultPrincipalURI` in bug 1319111 but `nsDocShell::AddState` only updates `nsISHEntry.URI`:
https://searchfox.org/mozilla-central/rev/409e4e98fe726558fec1a95cb50e467110519ce9/docshell/base/nsDocShell.cpp#11495

Setting resultPrincipalURI at the same time should fix this.

There's another consumer of nsSHEntry::SetURI which I'm not sure if it needs to be updated too:
https://searchfox.org/mozilla-central/rev/409e4e98fe726558fec1a95cb50e467110519ce9/docshell/base/nsDocShell.cpp#5569
Assignee: nobody → sawang
Flags: needinfo?(sawang)
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Priority: -- → P2
Assignee: freesamael → nobody
Status: ASSIGNED → NEW
Is there anyone out there willing to pick this up in Samael's absence?
Experiencing the same problem as timothy and Andrea in our application. After a 302 redirect -> history.replaceState -> refresh, the URL goes back to whatever the 302 redirect was, not the replaceState as expected.

I created a simple test repository to demonstrate:
https://github.com/happypancake/firefox-replace

And here's a video to show it in action:
https://drive.google.com/file/d/1sNy9T5HUPmfQbZznoR_jMgt9OMTgTkJN/view
Firefox on the right, the video starts with this bug, then shows the expected behavior in both Firefox and Chrome.
This bug is really serious and problematic for modern web apps. In our case it raises security concerns. Is there any progress on that?
Having this issue as well, we use tokens for auth which we scrub with replaceState, but when a user reloads, the token appears briefly before replaceState fires again.
I've run into this bug too, and it still exists on Nightly 62.0a1 (2018-05-11).

I'm attaching a python2 script "anomaly_server.py" that runs a toy http server exhibiting the bug.

By default the server is on localhost:8000

1. Visit http://localhost:8000/ in firefox

2. Click on the link to visit page /one

3. /one automatically 302-redirects to /two

4. /two does history.replaceState to change location to /three
So we're viewing page /two, but location is /three

5. Reload will load page /three
So now we're viewing page /three, but location is /two
Reloading more doesn't change anything.

In Chrome and Safari, reload to page /three will show location is /three
Attached file anomaly_server.py
I found a simple workaround for this bug. Before calling history.replaceState, do
  location.hash = location.hash;
Setting hash to itself has no effect, but makes the bug go away.
btw, this bug seems to have been introduced in Firefox 56. Firefox 55 and earlier don't seem to have this bug.
any update? it's been already 9 month that new FF version keep releasing with this silly issue.
Of course there is a "workaround", but I think, it should be fixed.
Or Samael is the only person who can do it? It's sad that Samael is absent for such a long term... ;(
I agree, this is a pretty horrible bug. The browser is requesting a different URL than what is shown in the location bar. Seems like a big deal. Firefox is the only browser with this problem.

Additionally, the workaround isn't ideal because if the URL doesn't already have a hash, location.hash = location.hash adds one, calls popstate, and adds a history entry.
An alternate workaround that is much less simple:

1. Use whatever means available to your backend technology to expose the request URI on the client
2. On page load (before any client routing code), check the URI against window.location
3. If they're different, use replaceState to fix it.

The location will briefly show the incorrect URL on any reload, but at least it'll be fixed and routing can work as expected...
This bug has been reported almost a year ago and still exists in the latest version of Firefox (63.0.1). This prevents our application working correctly in Firefox only, all other browsers behave correctly. The proposed solutions do not work, so we need a fix as soon as possible.
I can confirm that problem still occurs. Can this be prioritized as it's really serious?
I can also confirm that problem still occurs.
While this should obviously be fixed in FF. There is a workaround that seems to not cause any noticable issues in our app:

window.addEventListener('unload', function(event) { location.replace(location) });

This way the state of the js location is flushed to FFs location in cases of refreshes and tab closes (which by the way have the same issue when reopened with e.g. ⌘+⇧+t).
Component: DOM → DOM: Core & HTML

Just want to leave this here for others. Next.js tried using the workaround that Mathis mentioned above and it caused some bad issues for them. Details here: https://github.com/zeit/next.js/pull/6896

We experienced this bug also, where we use a redirect from an admin panel to redirect to our PWA which receives a login token during the redirect. We found that using pushState instead of replaceState creates more appropriate behavior.

+1 to everything said so far. AWS Cognito redirects the user from the AWS-hosted login to my application with the auth tokens in the fragment, which I'd love to be able to scrub using replaceState so users can't log in after logging out by using the back button, which seems kinda important, yeah? Please prioritize this issue.

Felix's hash=hash workaround works, at least.

We encountered this bug recently in a React application. To fully understand the issue and rule out anything application or React-specific, I built a small reproducible example repo with vanilla JS, using nginx for the redirect. I had planned to create a new bug for this, but eventually stumbled upon this existing issue which appears to describe the same scenario.

I will go ahead and share the reproducible example I put together in case anyone finds it helpful in reproducing/resolving the issue: https://github.com/andrew512/firefox-redirect-history-issue-sample

As noted by others, adding a pushState into the history chain appears to mitigate the issue, but this behavior may not always be desirable from an application standpoint.

Component: DOM: Core & HTML → Document Navigation
Webcompat Priority: --- → P2
Summary: history.replaceState after 3xx redirect with "Location" header → reload after history.replaceState after 3xx redirect shows wrong url in URL bar and Location api

The diagnosis in comment 8 is correct.

Assignee: nobody → bzbarsky

If we don't update the resultPrincipalURI, then things that examine it
(e.g. Location APIs and the URL bar) will show the wrong (pre-replaceState) URL.
I believe there is no effective difference between setting the result principal
URI to null and setting it to aNewURI here: the ultimate consumer of it is
NS_GetFinalChannelURI, which will fall back to the originalURI if it's null, and
in this case the originalURI is aNewURI.

Regressed by: 1319111
Pushed by bzbarsky@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/d14199c9c1cc
replaceState should update all the URI state for the entry being replaced.  r=smaug
Status: NEW → RESOLVED
Closed: 3 months ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla70
You need to log in before you can comment on or make changes to this bug.