Redirection from http to https seems to work but the opposite doesn't

Hello :wave:

Before diving into the issue, let's start with a little bit of context.
I'm building my homelab with internal services and external services.
Internal services are served under their own subdomain of domain.local (dnsmasq is configured to return my homelab ip for *.domain.local)
External servies are served under thei own subdomain of domain.com
Only :80 and :443 is allowed to coming from outside and are redirected to homelab:81 and homelab:444 respectively.
Thus, I'm sure that internal services are not exposed and safe from hostname spoofing.

In Traefik, I have 4 entrypoints:

  • local (:80)
  • localsecure (:443)
  • web (:81)
  • websecure(:444)

Redirection from http (web) to https (websecure) works well:

web:
    address: :81
    http:
      redirections:
        entryPoint:
          to: :443 # 443 because the redirection is done client-side and thus 443 -> 444 from outside
          scheme: https

But I also have the opposite: from https (localsecure) to https (local) that doesn't work:

localsecure:
    address: :443
    http:
      redirections:
        entryPoint:
          to: :80
          scheme: http

(internal services cannot benefit from ssl certificate as there is no way for let's encrypt to check the challenge).

I tried every lead I have:configuration through entrypoints, routers, middlewares
I've enabled OPTL traces and verbose log but I couldn't find a solution.
Could one of you help me? :pray:
I've must missed something

What’s the error? Browsers usually don’t like to move from https to http, once they had a secure connection.

You can use LetsEncrypt with dnsChallenge to create TLS certs for sub-domains not available on the Internet.

What’s the error?

Sorry, I missed the most important thing... the error: a 404.
https://service.domain.local isn't bound to anything but http://service.domain.local is (hence the need of the redirection).

Browsers usually don’t like to move from https to http, once they had a secure connection.

I checked with curl directly and Traefik send the 404, there is no redirection at all

You can use LetsEncrypt with dnsChallenge to create TLS certs for sub-domains not available on the Internet.

I could use Let's encrypt with dnsChallenge if the domain were existing but it's not (.local tlds are reserved and non-registrable tlds).

You do need a router listening on an entrypoint to apply a middleware.

Just use an alternative domain name like service.local.example.com.

You do need a router listening on an entrypoint to apply a middleware.

Did you mean that I don't need a router? Because if so I'm aware ^^
I just tried every option I had. The code I quoted is the entrypoints configuration.

Just use an alternative domain name like service.local.example.com

Having public dns records targeting private ip addresses is considered as bad practive and even isn't supported by some ISPs... such as mine. ("Free", one of the biggest french ISPs)

Also, even if I could take another route, shouldn't this redirection work, anyway?

You should start by supplying the error, which I asked for before. Until then everyone except you has not enough information.

Enable and check Traefik debug log and Traefik access log in JSON format during requests. Also check browser developer tools network tab during requests.

You don’t need to have sub.local.example.com in your public DNS with a private IP. You probably need to create a local.example.com zone (without IP). Then create *.local.example.com wildcard with LetsEncrypt dnsChallenge. Resolve the local sub-domains to your private IPs with a hosts file or a local DNS resolver (which forwards all other requests to a public one).

You should start by supplying the error, which I asked for before. Until then everyone except you has not enough information.

I did: I get a 404 when accessing https://service.domain.local instead of having a redirection towards http://service.domain.local (redirection from https scheme to http).

Enable and check Traefik debug log and Traefik access log in JSON format during requests. Also check browser developer tools network tab during requests.

I enabled and check both (in common format though) and also enabled tracing (and exploited them with grafana tempo) and found nothing. Traefik seems to behave like there was no redirection.

Also check browser developer tools network tab during requests.

I said I used cURL, here is the output in verbose mode:

* Host service.domain.local :443 was resolved.
* IPv6: (none)
* IPv4: 192.168.1.42
*   Trying 192.168.1.42:443...
* Connected to service.domain.local (192.168.1.42) port 443
* schannel: disabled automatic use of client certificate
* using HTTP/1.x
> GET / HTTP/1.1
> Host: service.domain.local
> User-Agent: curl/8.8.0
> Accept: */*
> Connection: close
>
* Request completely sent off
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
* schannel: server close notification received (close_notify)
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Sun, 29 Dec 2024 10:44:01 GMT
< Content-Length: 19
< Connection: close
<
404 page not found
* Closing connection
* schannel: shutting down SSL/TLS connection with service.domain.local port 443

You don’t need to have sub.local.example.com in your public DNS with a private IP [...]

Yes, this should work.

Share your full Traefik static and dynamic config, and docker-compose.yml if used.

Sure :slight_smile:

docker-compose.yml

services:
  traefik:
    image: "traefik:latest"
    container_name: traefik
    restart: unless-stopped
    networks:
      - web
      - observability_tracing
    ports:
      # external traffic is binded to specific ports
      # 80 -> 81
      # 443 -> 444
      - "81:81"
      - "444:444"
      # internal traffic is supported with normal ports
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "${PWD}/acme2.json:/acme.json"
      - "${PWD}/traefik.yml:/etc/traefik/traefik.yml"
      - "${PWD}/conf:/etc/traefik/conf"
      - "${PWD}/log:/var/log"

networks:
  web:
    external: true
  observability_tracing:
    external: true

traefik.yml


global:
  checkNewVersion: true
  sendAnonymousUsage: false

entryPoints:

  local:
    address: :80
 
  localsecure:
    address: :443

  web:
    address: :81
    http:
      redirections:
        entryPoint:
          to: :443
          scheme: https

  websecure:
    address: :444
      
log:
  level: "ERROR"

accessLog:
   filePath: /var/log/traefik/access.log
   addInternals: true

api:
  dashboard: true
  debug: true


providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    exposedByDefault: false
    network: web

  file:
    directory: /etc/traefik/conf
    watch: true

certificatesResolvers:
  lets-encrypt:
    acme:
      dnsChallenge:
        provider: <redacted>
        delayBeforeCheck: "0"
      email: <redacted>
      storage: /acme.json

tracing:
  otlp:
    http:
      endpoint: http://observability-tempo-1:4318/v1/traces

conf/* (merged)

---
http:
  routers:
    api:
      rule: Host(`traefik.domain.com`)
      entryPoints: 
        - websecure
      service: api@internal
      middlewares:
        - simpleAuth
      tls:
        certResolver: "lets-encrypt"
...
---
http:
  middlewares:
    simpleAuth:
      basicAuth:
        users:
          - <redacted>
    
    publicSimpleAuth:
      basicAuth:
        users:
          - <redacted>
...
---
http:
  middlewares:
    LocalIp:
      ipAllowList:
        sourceRange:
          - "127.0.0.1/16"
          - "192.168.0.0/16"
    
    redirect-to-http:
      redirectScheme:
        scheme: http
        permanent: true

    redirect-to-https:
      redirectScheme:
        scheme: https
        permanent: true
...

This works with a https entrypoint, to redirect to http:8080:

  whoami2:
    image: traefik/whoami:v1.10
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.redir2.entrypoints=websecure2
      - traefik.http.routers.redir2.rule=HostRegexp(`.+`)
      - traefik.http.routers.redir2.middlewares=redir2
      - traefik.http.middlewares.redir2.redirectscheme.scheme=http
      - traefik.http.middlewares.redir2.redirectscheme.port=8080
      - traefik.http.services.redir2.loadbalancer.server.port=80

Thanks for the repro on your side. However I unsuccessfully tried to replicate, on my side with the following:

services:
  whoami:
    image: traefik/whoami:v1.10
    networks:
      - web
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.services.whoami.loadbalancer.server.port=80
      - traefik.http.routers.whoami.rule=Host(`whoami.domain.local`)
      - traefik.http.routers.whoami.entrypoints=localsecure
      - traefik.http.routers.whoami.middlewares=redirect
      - traefik.http.middlewares.redirect.redirectscheme.scheme=http
      - traefik.http.middlewares.redirect.redirectscheme.port=8080

networks:
  web:
    external: true

It doesn't work if I access whoami.domain.local the https scheme, but if I use the http scheme with port 443, it does work.

Adding a TLS section to my entrypoint fixed the issue.

my entrypoints (redirection from https to http is restored):

entryPoints:

  local:
    address: :80
 
  localsecure:
    address: :443
    http:
      tls:
        certResolver: leresolver # added this
      redirections:
        entryPoint:
          to: :80
          scheme: http

and the whoami container:

services:
  whoami:
    image: traefik/whoami:v1.10
    networks:
      - web
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.services.whoami.loadbalancer.server.port=80
      - traefik.http.routers.whoami.rule=Host(`whoami.domain.local`)
      - traefik.http.routers.whoami.entrypoints=local

networks:
  web:
    external: true

I don't understand why the tls section is required. According the doc, it overrides the default tls configuration. But the default tls configuration uses LetsEncrypt provide as well.