X-Forwarded-Proto overwritten to http when using an external Reverse Proxy (NGINX)

I am running ShinyProxy via the ShinyProxy Operator (v2.3.1 / v3.2.2) behind an external NGINX reverse proxy that handles SSL/TLS. Although NGINX is correctly configured to send X-Forwarded-Proto: $scheme (also tried forcing to https), at some point Caddy (I think) changes to HTTP.

This causes internal redirects (like /login) to be generated with the http:// scheme. The main problem is caused when logging in, which leads to shinyproxy.mydomain/auth-success. Then, via JavaScript, a redirection to the landing page (/) is attempted, but as it’s done from an HTTP page, the server causes a SecurityError and aborts the redirection:

Uncaught SecurityError: Failed to execute 'pushState' on 'History': A history state object
with URL 'http://shinyproxy.mydomain/' cannot be created in a document
with origin 'https://shinyproxy.mydomain' and URL 'https://shinyproxy.mydomain/auth-success'.

I’ve configured NGINX as stated in https://www.shinyproxy.io/documentation/security/#https-ssl–tls to redirect to localhost:8082 (operator port changed with #77, working well), with all headers:

server {
    server_name shinyproxy.mydomain;

    location / {
        proxy_pass http://localhost:8082;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_cache_bypass $http_upgrade;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/mydomain/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/mydomain/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = shinyproxy.mydomain) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name shinyproxy.mydomain;
    return 404; # managed by Certbot
}

In shinyproxy.yml I have server.forward-headers-strategy: native (also tried framework) and proxy.openid.enforce-https-redirect-uri: true. Also tried all troubleshooting steps in https://www.shinyproxy.io/documentation/troubleshooting/#invalid-redirect_uri:

spring:
  session:
    store-type: redis
server:
  forward-headers-strategy: native
image: openanalytics/shinyproxy:3.2.2
fqdn: shinyproxy.mydomain
proxy:
  store-mode: Redis
  stop-proxies-on-shutdown: false
  title: My apps
  landing-page: /
  port: 8080
  realm-id: shinyrealm
  authentication: openid
  openid:
    enforce-https-redirect-uri: true
...

I’ve got this with logging.requestdump: true just by loading shinyproxy.mydomain in a browser (before login or any other process).

----------------------------REQUEST---------------------------
               URI=/
 characterEncoding=null
     contentLength=-1
       contentType=null
            cookie=SESSION=NTQ5Z...........
            header=X-Real-Ip=myIP
            header=Accept-Encoding=gzip, deflate, br, zstd
            header=Dnt=1
            header=Sec-Ch-Ua=\"Microsoft Edge\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"
            header=Sec-Fetch-User=?1
            header=Sec-Fetch-Site=none
            header=X-Forwarded-For=172.23.0.1
            header=Cookie=SESSION=NTQ5Z...........
            header=Host=shinyproxy.mydomain
            header=X-Forwarded-Host=shinyproxy.mydomain
            header=Accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
            header=Accept-Language=es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
            header=Sec-Fetch-Mode=navigate
            header=User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
            header=Sec-Ch-Ua-Mobile=?0
            header=Sec-Fetch-Dest=document
            header=X-Forwarded-Proto=http
            header=Sec-Ch-Ua-Platform=\"Windows\"
            header=Upgrade-Insecure-Requests=1
            locale=[es, es_ES, en, en_GB, en_US]
            method=GET
          protocol=HTTP/1.1
       queryString=
        remoteAddr=/172.23.0.11:49858
        remoteHost=sp-caddy.sp-shared-network
            scheme=http
              host=shinyproxy.mydomain
        serverPort=8080
          isSecure=false
--------------------------RESPONSE--------------------------
     contentLength=0
       contentType=null
            header=Expires=0
            header=Cache-Control=no-cache, no-store, max-age=0, must-revalidate
            header=X-XSS-Protection=0
            header=Pragma=no-cache
            header=Location=http://shinyproxy.mydomain/login
            header=Date=Thu, 15 Jan 2026 15:50:43 GMT
            header=Connection=keep-alive
            header=X-Content-Type-Options=nosniff
            header=Content-Length=0
            status=302

==============================================================

----------------------------REQUEST---------------------------
               URI=/login
 characterEncoding=null
     contentLength=-1
       contentType=null
            cookie=SESSION=NTQ5Z...........
            header=X-Real-Ip=myIP
            header=Accept-Encoding=gzip, deflate, br, zstd
            header=Dnt=1
            header=Sec-Ch-Ua=\"Microsoft Edge\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"
            header=Sec-Fetch-User=?1
            header=Sec-Fetch-Site=none
            header=X-Forwarded-For=172.23.0.1
            header=Cookie=SESSION=NTQ5Z...........
            header=Host=shinyproxy.mydomain
            header=X-Forwarded-Host=shinyproxy.mydomain
            header=Accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
            header=Accept-Language=es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
            header=Sec-Fetch-Mode=navigate
            header=User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
            header=Sec-Ch-Ua-Mobile=?0
            header=Sec-Fetch-Dest=document
            header=X-Forwarded-Proto=http
            header=Sec-Ch-Ua-Platform=\"Windows\"
            header=Upgrade-Insecure-Requests=1
            locale=[es, es_ES, en, en_GB, en_US]
            method=GET
          protocol=HTTP/1.1
       queryString=
        remoteAddr=sp-caddy.sp-shared-network/172.23.0.11:49858
        remoteHost=sp-caddy.sp-shared-network
            scheme=http
              host=shinyproxy.mydomain
        serverPort=8080
          isSecure=false
--------------------------RESPONSE--------------------------
     contentLength=0
       contentType=null
            header=Expires=0
            header=Cache-Control=no-cache, no-store, max-age=0, must-revalidate
            header=X-XSS-Protection=0
            header=Pragma=no-cache
            header=Location=http://shinyproxy.mydomain/oauth2/authorization/shinyproxy
            header=Date=Thu, 15 Jan 2026 15:50:43 GMT
            header=Connection=keep-alive
            header=X-Content-Type-Options=nosniff
            header=Content-Length=0
            header=Content-Language=es-
            status=302

==============================================================

----------------------------REQUEST---------------------------
               URI=/oauth2/authorization/shinyproxy
 characterEncoding=null
     contentLength=-1
       contentType=null
            cookie=SESSION=NTQ5Z...........
            header=X-Real-Ip=myIP
            header=Accept-Encoding=gzip, deflate, br, zstd
            header=Dnt=1
            header=Sec-Ch-Ua=\"Microsoft Edge\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"
            header=Sec-Fetch-User=?1
            header=Sec-Fetch-Site=none
            header=X-Forwarded-For=172.23.0.1
            header=Cookie=SESSION=NTQ5Z...........
            header=Host=shinyproxy.mydomain
            header=X-Forwarded-Host=shinyproxy.mydomain
            header=Accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
            header=Accept-Language=es,es-ES;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
            header=Sec-Fetch-Mode=navigate
            header=User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
            header=Sec-Ch-Ua-Mobile=?0
            header=Sec-Fetch-Dest=document
            header=X-Forwarded-Proto=http
            header=Sec-Ch-Ua-Platform=\"Windows\"
            header=Upgrade-Insecure-Requests=1
            locale=[es, es_ES, en, en_GB, en_US]
            method=GET
          protocol=HTTP/1.1
       queryString=
        remoteAddr=sp-caddy.sp-shared-network/172.23.0.11:49858
        remoteHost=sp-caddy.sp-shared-network
            scheme=http
              host=shinyproxy.mydomain
        serverPort=8080
          isSecure=false
--------------------------RESPONSE--------------------------
     contentLength=0
       contentType=null
            header=Expires=0
            header=Cache-Control=no-cache, no-store, max-age=0, must-revalidate
            header=X-XSS-Protection=0
            header=Pragma=no-cache
            header=Location=https://auth.mydomain/auth/realms/shinyproxy/protocol/openid-connect/auth?response_type=code&client_id=shinyproxy-client&scope=openid%20email&state=_usGfwMe2vIClaN6CPw_kcbPAkhegtCWHRahaH9HMhY%3D&redirect_uri=https://shinyproxy.mydomain/login/oauth2/code/shinyproxy&nonce=DMDeMwszeIh5pNFgsN0r3rBnnExQhzD5UQr5imnN6TA
            header=Date=Thu, 15 Jan 2026 15:50:43 GMT
            header=Connection=keep-alive
            header=X-Content-Type-Options=nosniff
            header=Content-Length=0
            status=302

==============================================================

It appears that the internal Caddy instance managed by the Operator is receiving the request from NGINX and, since it’s receiving it on its HTTP port (8082), it overwrites the X-Forwarded-Proto header to http before passing it to the ShinyProxy JAR. Thanks for the help!