Mobile app version of vmapp.org
Login or Join
Ravi8258870

: Location header "/foo" with 302 status. Why would address bar append a fragment "#_=_"? Using PHP if that matters, on a site https://www.example.com. I send a Location header of /member with

@Ravi8258870

Posted in: #Url #WebDevelopment

Using PHP if that matters, on a site www.example.com. I send a Location header of /member with a 302 status. The redirect works fine, but the address bar in the browser displays www.example.com/member#_=_.
What mechanism could be adding the #_ =_ fragment?

This happens with both Chrome and Firefox. I've run the code under Xdebug and verified there's no fragment in the string passed to header(), and use Chrome developer tools to verify that the header sent does not contain the fragment. And using Xdebug in the destination code, the fragment is not present in $_SERVER['REQUEST_URI'], though I guess that never contains any fragment anyway.

For more context, which might or might not be helpful, this occurs when logging in to a Zend Framework 1.12 MVC application, when the login succeeds and redirects to the start page for the logged-in member. When the login method uses an email address and password, there is no fragment. When it uses OAuth2 through Facebook, I get the #_ =_ fragment. When using OAuth2 through Google, I just get #. That suggests to me that it's something in my code, but I don't even know how I could make those fragments show up in the address bar if I wanted to! And the code responsible for validating the login and doing the redirection is 90% common across all login methods - the OAuth-based methods just do the OAuth redirections to locate an entry in the user table, and once the entry is found the code is common to finding the entry by looking up an email address in the user table.

Edit: Thanks to a comment from Stephen Ostermiller I now have a JavaScript workaround I can put on the destination page to erase the fragment. And I verified that the fragment is not set by some rogue JavaScript somewhere, because it still shows up with JavaScript disabled. So it must be coming from the browser itself. But why, what would cause that? I can only guess it's related to the multiple redirections that happen with OAuth2, from my site to Facebook and then from Facebook back to my site, and then from my site to a different page on my site. Has anyone heard of such a thing? Interesting that the exact content of the fragment differs depending on which OAuth2 host I connect to, but the fragment is identical using Chrome, Firefox, MS Edge, and Internet Explorer 11. This is driving me crazy!

10.01% popularity Vote Up Vote Down


Login to follow query

More posts by @Ravi8258870

1 Comments

Sorted by latest first Latest Oldest Best

 

@Ravi8258870

Although I had already used Chrome developer tools' Network tab to verify that the redirect to my page did not contain the fragment, the second comment from Stephen Ostermiller prompted me to go back again and check the complete sequence of redirects. And sure enough, when the Facebook API redirects back to my site, it uses a Location header of
www.example.com/newfacebooklogin?code=AQBZ...y6M&state=1497887225-97719e#_=_

So that's where the #_ =_ fragment is coming from, nothing the browser is generating. And it explains why I see different fragments when using different OAuth providers.

Of course now I'm wondering why that fragment is persisting in the URL for the page my site displays. The code that handles the request from Facebook with the fragment does a 302 redirect to a different URL on my site without any fragment, which in turn redirects to another URL on my site with no fragment. The answer appears to be that back in the 2011 timeframe, browsers started persisting fragments through redirects (as described here)

I tried changing the URLs in the redirects I use after getting the request from Facebook to append either a null (#) or blank (#) fragment, but that does not remove the #_ =_ from the address bar. The question of how it got there is answered, but that hasn't led me to an easy solution to the problem I'm experiencing: I don't want any fragment showing up in the address bar to confuse the user, the address bar should look the same when logging in through any method.

The URL containing the fragment originates in a request that has nothing for the browser to render (Facebook just providing the user identity to my site), so there's no place to put JavaScript to clear the fragment. The first page actually rendered on login is variable. E.g. the user specified a page but wasn't logged in - so we redirect to the login screen, then on successful login redirect to the originally requested page. So it seems I need to track server-side whether the page being rendered resulted from logging in via OAuth, and if so, then add JavaScript to it to set location.hash=' '. Since it's possible that a user could explicitly follow a link with a fragment to go to a location on that page, I can't just unconditionally clear location.hash on every page. What a pain. Interestingly, it seems that most questions about fragments seem to be looking to preserve them rather than erase them, e.g. this.

Hmm, I wonder if I can "render" the OAuth request coming from Facebook as nothing but JavaScript that clears the fragment and sets the location to the URL we currently go to using server-side code? That might be tricky under Zend Framework MVC, but I should look into it.

Update:
In fact I followed this path, and it worked out fine. I added a new ZF Controller Action that takes a parameter specifying a URL to redirect to. And the view script for that Action has an empty body with just a few lines of javascript to blank out the fragment (location.hash = ' ') and redirect to the specified URL (location.href = '<?php echo $url ?>'). And then I went back to existing code and changed every redirect to the start page following a login that could have used OAuth2 to instead forward to this new action passing the URL that had previously been specified in the redirect.

10% popularity Vote Up Vote Down


Back to top | Use Dark Theme