Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Append trailing slash to URL if missing #14822

Closed
wants to merge 1 commit into from

Conversation

kharenis
Copy link

Closes #5693
Index pages now automatically redirect to a path with a trailing"/". If the site was accessed through a proxy with a modified path, a missing trailing "/" would break the relative paths.

Closes qbittorrent#5693
Index pages now automatically redirect to a path with a trailing
"/". If the site was accessed through a proxy with a modified path,
a missing trailing "/" would break the relative paths.
@Chocobo1 Chocobo1 requested a review from a team April 22, 2021 04:29
@glassez glassez changed the title Automatically Append / to url when missing Append trailing slash to URL if missing Apr 22, 2021
@glassez
Copy link
Member

glassez commented Apr 22, 2021

Does it supersede #14787?

@glassez glassez added the WebUI WebUI-related issues/changes label Apr 22, 2021
@kharenis
Copy link
Author

Does it supersede #14787?

I believe so, I can't think of why a base path would be needed if this fix was to be added.

@glassez
Copy link
Member

glassez commented Apr 22, 2021

Could you explain in detail using some example URL, what exactly happens without and with this patch?

@@ -2,6 +2,14 @@
<html lang="${LANG}">

<head>
<script>
let path = window.location.pathname;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use const:
const path = ...

<script>
let path = window.location.pathname;
if (path) {
let lastChar = path.substr(path.length - 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

substr is not recommended: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr#browser_compatibility
I would suggest: const lastChar = path.slice(-1);

let path = window.location.pathname;
if (path) {
let lastChar = path.substr(path.length - 1);
if (lastChar != '/')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (lastChar !== '/')

if (path) {
let lastChar = path.substr(path.length - 1);
if (lastChar != '/')
window.location.replace(window.location.href + '/');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this redirection break other things? such as the magnet handler?

@@ -2,6 +2,14 @@
<html lang="${LANG}">

<head>
<script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move it to the next line of </noscript>.
The same for private/index.html.

@FranciscoPombal
Copy link
Member

Don't have time to review this right now, but I believe we should give @Piccirello a chance to look at this.

@Piccirello
Copy link
Member

Will review this tonight.

Copy link
Member

@Piccirello Piccirello left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm lacking info right now as to how this solves the linked issue. Could you share some more info @kharenis and maybe an example or two?

My current inclination is that we should probably handle this redirect server-side, rather than client-side.

@kharenis
Copy link
Author

@glassez @Chocobo1 @Piccirello
Hi guys, apologies for going AWOL, I've been having internet problems at home.

Given the following scenario:

Without the patch:
If a user attempts to reach qbt at http://mywebsite.com/qbt, the index page will load, however all of the resources with relative paths fail to load, as they are attempting to reach
[http://mywebsite.com/scripts/login.js, http://mywebsite.com/icons/qbittorrent-tray.svg] etc.

If a user attempts to reach qbt at http://mywebsite.com/qbt/, the relative paths are correctly resolved and everything loads as expected. [http://mywebsite.com/qbt/scripts/login.js, http://mywebsite.com/qbit/icons/qbittorrent-tray.svg] etc.

With the path:
If the user attempts to access http://mywebsite.com/qbt, they will be redirected to http://mywebsite.com/qbt/, and the index page & resources with relative paths all load as expected.

I'm not sure if it can be implemented server-side as qbt receives the path from the reverse proxy's perspective. - In the above scenario, the "/qbt" path from the user's perspective is seen as "/" to qbt.

@glassez
Copy link
Member

glassez commented Apr 27, 2021

I don't see any problems at server side from the example above.

@glassez
Copy link
Member

glassez commented Apr 27, 2021

The bottom line is how relative paths in HTML links are resolved. IIRC, they are resolved against the directory of file containing the link. Seems in case of http://mywebsite.com/qbt/ qbt/ is considered as that directory. But in case of http://mywebsite.com/qbt qbt is considered as filename in root directory so paths are resolved against root directory.
I wonder if it is possible to configure the proxy to redirect qbt to qbt/ itself? Or maybe configure it to replace /qbt/ only so http://mywebsite.com/qbt will not redirected?

@Piccirello
Copy link
Member

Thanks for that explanation. We can definitely nix my earlier server-side suggestion.

It seems that without this fix, users can already employ a nasty regex in their proxy config, which will correctly handle the trailing slash. That's definitely not ideal. At the same time, this fix would now require (some) users to update their proxy config to properly handle double slashes. Is my understanding correct?

It might be helpful if you shared an example proxy config for nginx (or whatever reverse proxy you use) so that we can see what would be needed.

@glassez
Copy link
Member

glassez commented Apr 27, 2021

@Piccirello
Is it correct in HTTP world that both http://mywebsite.com/qbt and http://mywebsite.com/qbt/ points to the same page (if exclude reverse proxy from this scenario)? And how would the relative links on this page behave in this case?

@FranciscoPombal
Copy link
Member

@kharenis @Piccirello

I just refreshed the wiki pages documenting reverse proxy setups with NGINX. I have never had this / problem. I use these setups on my machines pretty much as-is, and it "just works". Will they be impacted by this change?

Links to the relevant wiki pages:

https://github.com/qbittorrent/qBittorrent/wiki/NGINX-Reverse-Proxy-for-Web-UI
https://github.com/qbittorrent/qBittorrent/wiki/Linux-WebUI-HTTPS-with-Let's-Encrypt-certificates-and-NGINX-SSL-reverse-proxy

@kharenis
Copy link
Author

kharenis commented Apr 28, 2021

@Piccirello
I use Haproxy for my reverse proxy and currently have it set to redirect if missing the trailing slash, and then rewrite the path for the forwarded request.
acl qbittorrent-redirect-acl var(txn.txnpath) -m end -i /qbittorrent
http-request set-var(txn.txnpath) path http-request redirect scheme https drop-query append-slash if qbittorrent-redirect-acl http-request set-path %[path,regsub(^/qbittorrent/,/)] if !qbittorrent-redirect-acl

@glassez

Is it correct in HTTP world that both http://mywebsite.com/qbt and http://mywebsite.com/qbt/ points to the same page (if exclude reverse proxy from this scenario)? And how would the relative links on this page behave in this case?

Technically no, whilst they're both a path to a resource, /qbt is treated as the file "qbt" in the "/" directory, and /qbt/ as a directory - with the convention that something representing the directory will be returned (typically an index page).
They're akin to:
http://10.10.10.10:8080/index.html and http://10.10.10.10:8080/

The relative paths get appended to the deepest directory in the path.

I guess what it really comes down to, is it qbt's responsibility to make sure it's being accessed using a directory path as it expects, or should the user have to make their proxy only allow access through a url with a trailing "/".

@FranciscoPombal

I just refreshed the wiki pages documenting reverse proxy setups with NGINX. I have never had this / problem. I use these setups on my machines pretty much as-is, and it "just works". Will they be impacted by this change?

Links to the relevant wiki pages:

https://github.com/qbittorrent/qBittorrent/wiki/NGINX-Reverse-Proxy-for-Web-UI
https://github.com/qbittorrent/qBittorrent/wiki/Linux-WebUI-HTTPS-with-Let's-Encrypt-certificates-and-NGINX-SSL-reverse-proxy

I don't use Nginx as my reverse proxy so haven't got experience with it, but are you able to access qbt without a trailing "/" in the url?
Also, from a quick search of the code, it looks like qbt doesn't make use of these headers;
X-Forwarded-Proto X-Forwarded-For X-Real-IP

I think the best way to handle it is a configurable base path in qbt so it always points things in the right direction without users having to faff around with proxy setups.

@glassez
Copy link
Member

glassez commented Apr 28, 2021

I guess what it really comes down to, is it qbt's responsibility to make sure it's being accessed using a directory path

For me this sounds the same as "it's application's responsibility to make sure it's being run using an executable file with some particular path". Unless you mean web UI rather than qBittorrent itself...

@kharenis
Copy link
Author

I guess what it really comes down to, is it qbt's responsibility to make sure it's being accessed using a directory path

For me this sounds the same as "it's application's responsibility to make sure it's being run using an executable file with some particular path". Unless you mean web UI rather than qBittorrent itself...

Ah yep, I did indeed mean the web UI!

@FranciscoPombal
Copy link
Member

@kharenis

@FranciscoPombal

I don't use Nginx as my reverse proxy so haven't got experience with it, but are you able to access qbt without a trailing "/" in the url?

Yes. What I notice is that a / is automatically appended. So, when I input mywebsite.com/qbt in the browser's address bar and hit return, the page loads successfully, and then the address bar will read mywebsite.com/qbt/ after the page is done loading. This means NGINX is redirecting me automatically.

I tried this with the reference setups documented in the wiki and a fresh browser cache/data every time, and this is reproducible. I opened up Firefox's network monitor with cache disabled, and indeed I found that I'm getting 301'd automatically:

  • request headers:
GET /qbt HTTP/2
Host: mywebsite.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
  • response headers:
HTTP/2 301 Moved Permanently
server: nginx
date: <snip>
content-type: text/html
content-length: 162
location: https://mywebsite.com/qbt/
strict-transport-security: max-age=31536000; includeSubDomains; preload
X-Firefox-Spdy: h2

and the subsequent requests/responses to the path with the trailing / work as normal. In fact, these results are consistent with what the NGINX docs say about such automatic redirections (https://nginx.org/en/docs/http/ngx_http_core_module.html#location):

If a location is defined by a prefix string that ends with the slash character, and requests are processed by one of proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed. In response to a request with URI equal to this string, but without the trailing slash, a permanent redirect with the code 301 will be returned to the requested URI with the slash appended. If this is not desired, an exact match of the URI and location could be defined like this:

location /user/ {
    proxy_pass http://user.example.com;
}

location = /user {
    proxy_pass http://login.example.com;
}

There is probably a way to accomplish this in other webservers as well, even if it is not the default.

Also, from a quick search of the code, it looks like qbt doesn't make use of these headers;
X-Forwarded-Proto X-Forwarded-For X-Real-IP

Yeah, they are there "for the future". It would be very nice to make use of at least X-Forwarded-For to not lock out reverse proxy users with no recourse (for whatever duration that the lock-out is configured for) after too many failed password attempts. This is because without looking at that header or something of the sort, from qBittorrent's perspective, all requests are made from the internal proxy IP address (typically 127.0.0.1), not the user's actual IP address. See also #11908.

I think the best way to handle it is a configurable base path in qbt so it always points things in the right direction without users having to faff around with proxy setups.

Since it currently "just works" with NGINX, wouldn't it be a better idea to simply find out what is needed to make it work with other webservers? Then it would be sufficient to add some text to qBittorrent's "javascript is disabled" error paragraph mentioning the necessary config change. After all, even if you introduce a configurable base path setting, it will still be broken out of the box for some users, and we have to inform them about what needs to be changed to fix the problem.

@github-actions
Copy link

This PR is stale because it has been 60 days with no activity. This PR will be automatically closed within 7 days if there is no further activity.

@github-actions github-actions bot added the Stale label Aug 10, 2021
@github-actions
Copy link

This PR was closed because it has been stalled for some time with no activity.

@github-actions github-actions bot closed this Aug 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Stale WebUI WebUI-related issues/changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Configuration for WebUI base url for use in reverse proxy
5 participants