Web UI Not Working Through Nginx

I’m running the hotio/duplicacy Docker image and the web UI is inoperable when I try to access it through my domain with Nginx handing the reverse proxy. Everything is fine through regular LAN access, but when I load it through my domain it appears that none of the CSS or JavaScript is functional.

Instead of this view:
Correct login

I get this view:
Incorrect login

Since the JavaScript isn’t functional, I can’t get past this login page.

I have a few other apps running behind Nginx but this is the only one with this issue.

I guess that was because files under /assets were not loaded. For example, can you open http://host:port/assets/js/jquery.min.js?

Nope, none of the assets are accessible. Trying to access any of them brings me to the admin password page.

Any suggestions on how to get the assets to load through Nginx?

Is it something in your Nginx setup that doesn’t forward these requests to the web UI?

I’m not very experienced with Nginx, but I have Vaultwarden and Emby exposed through it and they’re working fine through the web interfaces. It seems isolated to Duplicacy, but I don’t know what to check for.

Check the log file ~/.duplicacy-web/logs/duplicacy_web.log to see if there are any requests like:

2022/07/12 20:59:43 [::1]:59453 GET /assets/css/bootstrap.min.css
2022/07/12 20:59:43 [::1]:59454 GET /assets/css/paper-dashboard.css
2022/07/12 20:59:43 [::1]:59458 GET /assets/js/jquery.min.js
2022/07/12 20:59:43 [::1]:59457 GET /assets/js/jquery-1.10.2.min.js
2022/07/12 20:59:43 [::1]:59456 GET /assets/css/font-awesome.min.css
2022/07/12 20:59:43 [::1]:59455 GET /assets/css/duplicacy-web.css
2022/07/12 20:59:43 [::1]:59457 GET /assets/js/chartist.min.js
2022/07/12 20:59:43 [::1]:59454 GET /assets/js/bootbox.min.js
2022/07/12 20:59:43 [::1]:59453 GET /assets/js/bootstrap.min.js
2022/07/12 20:59:43 [::1]:59458 GET /assets/js/axios.min.js
2022/07/12 20:59:43 [::1]:59455 GET /assets/js/paper-dashboard.js
2022/07/12 20:59:43 [::1]:59456 GET /assets/js/polyfill.min.js
2022/07/12 20:59:43 [::1]:59456 GET /assets/img/product-icon.png
2022/07/12 20:59:43 [::1]:59456 GET /assets/fonts/fontawesome-webfont.woff2?v=4.7.0

If you don’t see these requests then it means Nginx isn’t forwarding them to the web GUI.

Ugh unrelated to this but I had a power outage that lasted almost 24 hours. Now when I go to restart Duplicacy I get “Failed to load the configuration file /app/.duplicacy-web/duplicacy.json.” I checked the file and it’s 0 bytes, completely empty. I assume the unclean shutdown is to blame.

Is there a way to recover from this or will I need to start over from scratch?

Disregard my last question. I used a new Docker image to get back to a working state.

I just tried accessing via Nginx and it appears to be forwarding to the web GUI and being rejected because the admin token isn’t provided:

2022/07/19 23:13:24 [ip:port] GET /duplicacy/ administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/jquery.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/axios.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/bootstrap.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/css/bootstrap.min.css administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/css/duplicacy-web.css administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/chartist.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/polyfill.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/jquery-1.10.2.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/css/paper-dashboard.css administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/paper-dashboard.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/css/font-awesome.min.css administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/bootbox.min.js administration token not provided
2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/img/product-icon.png administration token not provided

However, if I access through my LAN, only the root access is denied due to the admin token:

2022/07/19 23:16:24 [ip:port] GET / administration token not provided
2022/07/19 23:16:24 [ip:port] GET /assets/css/bootstrap.min.css
2022/07/19 23:16:24 [ip:port] GET /assets/css/paper-dashboard.css
2022/07/19 23:16:24 [ip:port] GET /assets/css/duplicacy-web.css
2022/07/19 23:16:24 [ip:port] GET /assets/css/font-awesome.min.css
2022/07/19 23:16:24 [ip:port] GET /assets/js/jquery-1.10.2.min.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/jquery.min.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/bootstrap.min.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/bootbox.min.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/axios.min.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/chartist.min.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/paper-dashboard.js
2022/07/19 23:16:24 [ip:port] GET /assets/js/polyfill.min.js

Any feedback for my last logs?

It doesn’t appear to be anything to do with Duplicacy. Your reverse proxy setup in Nginx might not be forwarding HTTP cookies thru from the client to Duplicacy Web.

If you’re using Firefox and/or Chrome, pull up the developer tools built into them and check the HTTP headers for a “Cookie:” field. Some ad blocking and/or security web browser extensions filter cookies being sent to public domain names. Is your Nginx server using SSL/TLS?

From the log it appears that Ngnix unnecessarily added a prefix of /duplicacy to all urls, which caused the web GUI to retrieve non-existent pages. You should fix the proxy forward rules.

I checked the HTTP headers and there is no cookie field being sent when I access via the public URL, but there is from the LAN. It should be using SSL because it’s set up with a cert from Let’s Encrypt and it serves over HTTPS. I tried with a clean browser with no extensions at all and it’s the same result with unprocessed JavaScript.

I believe the /duplicacy prefix is because I set up Nginx to use subfolders rather than subdomains. I access the page via https://my.domain/duplicacy. It’s definitely forwarding all the requests correctly, I see all the files under the Sources tab in dev tools. I did, however, just notice that the Console is reporting errors on all the JS files but I don’t know why. They’re all at 1:1, the opening bracket of the doctype tag.

Is this all due to the lack of a cookie in the request header?

Normally in a reverse web proxy setup where the document root of the backend server is mapped to a specific URL, the additional paths added by the proxy aren’t passed through. In other words, while the public-facing domain served by Nginx accepts the following…

https://my.domain/duplicacy/

… the web microservice in Duplicacy sees this…

http://127.0.0.1:3875/

… because as far as Duplicacy is concerned, Nginx is the connecting web browser.

Looking back over the log entries you posted earlier, it’s odd that there was an error about the administration token not being provided because the JavaScript and CSS files required for the login page shouldn’t require a token:

2022/07/19 23:13:24 [ip:port] GET /duplicacy/assets/js/jquery.min.js administration token not provided

It’d be a catch-22… how can a web browser render the login page if the only way to get all of the page components is after successful authentication which isn’t possible because some of the page components aren’t available due to not having logged in yet?

What happens if you try to access one of the CSS files via your public-facing domain? For example:

https://my.domain/duplicacy/assets/css/duplicacy-web.css

On a related note…

I tested a reverse proxy setup on one of my servers and noticed at least a couple of extra things to account for. Duplicacy web edition’s JavaScript functions setPassword() and verifyPassword() aren’t using relative links. Snippets of from the code:

axios.post('/save_password', { old_password: oldPassword, password: password, keyring: keyringEnabled, type: "encrypt" })
axios.post('/verify_password', { password: password, type: "admin" })

Because your Nginx setup expects the document root to be /duplicacy, clicking the [Save] password button will send your web browser to…

https://my.domain/save_password

… and similarly, clicking the [Continue] button on the admin login page goes to:

https://my.domain/verify_password

So even after resolving the HTTP cookie issue, there would still be a login failure. One solution is to add a couple of URL rewrite rules in Nginx to adjust the paths on the fly.

(It’s not a bug in Duplicacy, but an ideal solution would be if Duplicacy switched from absolute paths to relative ones to make it easier for users to run behind a reverse web proxy.)

1 Like

When I try accessing an asset directly via URL it returns the broken admin login page.

I follow what you’re saying about the reverse proxy, and that’s how I set it up in the conf, so I’m not sure why it’s sending the subfolder name with it. This is my config that redirects from /duplicacy:

location /duplicacy {
    return 301 $scheme://$host/duplicacy/;
}

location / {
    include /config/nginx/proxy.conf;
    include /config/nginx/resolver.conf;

    set $upstream_app duplicacy;
    set $upstream_port 3875;
    set $upstream_proto http;
    proxy_pass $upstream_proto://$upstream_app:$upstream_port;

    proxy_set_header Range $http_range;
    proxy_set_header If-Range $http_if_range;
}

Given what you said, I decided to try setting it up as a subdomain proxy instead. Lo and behold, everything works fine and I can log in! However, just out of curiosity, I checked the HTTP headers again and I still don’t see a “cookie” field, so I don’t think that was the root cause.

Ah… now it make sense. :nerd:

There’s more than one way to configure a reverse proxy. It’s likely that your proxy setup was a pass-thru. For a pass-thru, the document path is relayed as-is to the backend server unless the modified document root is stripped by the proxy.

Cool! :sunglasses:

For security reasons, HTTP cookies don’t allow cross-domain access – i.e., the cookie deposited from a login to http://127.0.0.1:3875/ can only be requested by the server at 127.0.0.1, likewise for your LAN address and also for your WAN address.

Because you hadn’t successfully logged on thru your public FQDN, there was no cookie for your web browser to send to Nginx (to be passed onto to Duplicacy’s web microservice), and even if there was still a fresh cookie obtained from successfully logging directly onto Duplicacy via your LAN, it wouldn’t be sent to Nginx (domain name/IP address don’t match).

Also, the Cookie: field is transient. It doesn’t initially exist until a password is set. Then each time the authentication token is refreshed it expires after 24 hours (allowing access without entering the password again during that time).

1 Like

Thanks for the knowledge and help! That makes a lot of sense for the cookies, and I’m still learning how to configure Nginx so that’s helpful to know!

You’re welcome!

Although I’m partial to Apache (HTTP Server), every so often I need to work with Nginx so this was a good opportunity for me to rummage thru that part of my brain. :slightly_smiling_face:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.