Keycloak 17.0.1 behind NginX reverse proxy

Hello there!

I am configuring keycloak in compose like that:

 keycloak:
    image: quay.io/keycloak/keycloak:17.0.1
    depends_on:
      - keycloakdb
    networks:
      - net
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloakdb/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: sS3459ol
      KC_HOSTNAME_STRICT: 'false'
      KC_HOSTNAME_STRICT_HTTPS: 'false'
      KC_METRICS_ENABLED: 'true'
      KC_FEATURES: token-exchange
      KC_HTTP_ENABLED: 'true'
    command:
      - "start"
      - "--auto-build"
      - "--hostname=localhost"
      - "--hostname-port=3000"
      - "--hostname-path=/keycloak"
      - "--proxy"
      - "edge"
    container_name: keycloak

I have nginx proxy with such config:

  nginx:
    build: src/nginx
    environment:
      ENVIRONMENT_NAME: Development
    ports:
      - "3000:80"
server {

  listen 80;

  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";

   proxy_http_version 1.1;
   proxy_set_header X-Forwarded-Host $host;
   proxy_set_header X-Forwarded-Server $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 $scheme;
   proxy_set_header Host $http_host;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "Upgrade";
   proxy_pass_request_headers on;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  location /keycloak/ {

    proxy_pass  http://keycloak/;
  }
}

When navigating to http://localhost:3000/keycloak I am redirecting to 80 port… What did I miss?

Thanks in advance.

isn’t "3000:80" mapping 3000 to the HOST_PORT in a docker compose file, which means 3000 will be exposed on the host and mapped to 80 in the container?

Yes, you’re right. So then should I change --hostname-port to 80 because nginx listening 80 in container?

Take a look at the docker-compose documentation. That’s not a Keycloak issue.

I see there is an active bug for this case in 17.0.1

Please post the link.

Keycloak thinks it’s being accessed on port 80, but the browser is contacting port 3000, if I see it correctly.

You also need KC_PROXY=“edge”.
And HTTP_ADDRESS_FORWARDING=true

And “/keycloak” isn’t the standard path for keycloak so you need KC_HTTP_RELATIVE_PATH=/keycloak

There is a guide on how to setup keycloak behind a reverse proxy. Just ignore everything it says about which paths not to expose, because at least at first you need all of them…

Thanks, I’ve tried like that. Now I have “Resource not found” from nginx

My nginx.conf:

server {

  listen 80;

  proxy_http_version 1.1;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

   location /keycloak {
    proxy_pass http://keycloak/keycloak;
   }

  location /api/ {
    proxy_pass http://gateway:8080/;
  }
}

and keycloak setup

  keycloak:
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloakdb/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: secured
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: secured
      KC_HOSTNAME_STRICT: 'false'
      KC_HOSTNAME_STRICT_HTTPS: 'false'
      KC_METRICS_ENABLED: 'true'
      KC_FEATURES: token-exchange
      KC_HTTP_ENABLED: 'true'
      KC_PROXY: edge
      KC_HTTP_RELATIVE_PATH: /keycloak
      HTTP_ADDRESS_FORWARDING: 'true'
    command:
      - "start"
      - "--auto-build"
    container_name: keycloak

Thanks, xgp, I’ve already tried these. There is very low info about configuring nginx reverse proxy instance.

From what you posted, there shouldn’t be anything listening on localhost:8080.

If the docker-compose.yml is the same as above I would expect keycloak at localhost:3000/keycloak/

1 Like

Thanks, I’ll try this.

Now I succed to get to welcome page, but when clicking admin console, getting such weird


Nginx config:

 server {
        listen 8080;

        location /keycloak/ {
            proxy_pass          http://keycloak:8080/;
            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-Host   $host;
            proxy_set_header    X-Forwarded-Server $host;
            proxy_set_header    X-Forwarded-Port   $server_port;
            proxy_set_header    X-Forwarded-Proto  $scheme;
        }
		
		  location /admin/ {
            proxy_pass          http://keycloak:8080/admin;
            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-Host   $host;
            proxy_set_header    X-Forwarded-Server $host;
            proxy_set_header    X-Forwarded-Port   $server_port;
            proxy_set_header    X-Forwarded-Proto  $scheme;
        }
		
				  location /resources/ {
            proxy_pass          http://keycloak:8080/resources;
            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-Host   $host;
            proxy_set_header    X-Forwarded-Server $host;
            proxy_set_header    X-Forwarded-Port   $server_port;
            proxy_set_header    X-Forwarded-Proto  $scheme;
        }

        location /keycloak/auth/ {
            proxy_pass          http://keycloak:8080/keycloak/;
            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-Host   $host;
            proxy_set_header    X-Forwarded-Server $host;
            proxy_set_header    X-Forwarded-Port   $server_port;
            proxy_set_header    X-Forwarded-Proto  $scheme;
        }
    }
  nginx:
    build: src/nginx
    container_name: nginx
    ports:
      - "8080:8080"

What did I miss?

Finally got it worked, now testing. If suceed I write all configs.

1 Like

If you have the link for the guide, vould you please post it here? Is it different than keycloak documentation?

Did it work? Could you mention what did you do?

Yes, it worked. Something was not detailed in up-to-date configuration, especially nginx setup.

Keycloak

keycloak:
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://secured/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: secured
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: secured
      KC_HOSTNAME_STRICT: 'false'
      KC_HOSTNAME_STRICT_HTTPS: 'false'
      KC_METRICS_ENABLED: 'true'
      KC_FEATURES: token-exchange
      KC_HTTP_ENABLED: 'true'
      PROXY_ADDRESS_FORWARDING: 'true'
      KC_HOSTNAME_STRICT_BACKCHANNEL: 'false'
    command:
      - "start"
      - "--auto-build"
      - "--proxy=passthrough" // depends on your load balancer setup, if have TLS termination on upper layer then use "edge" value
    container_name: keycloak

Nginx

services:
  nginx:
    build: src/nginx
    environment:
      ENVIRONMENT_NAME: Development
    ports:
      - "3000:80"

Nginx.conf

server {

    listen 80;


    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_http_version 1.1;
        proxy_set_header X-Correlation-Id $connection-$connection_requests-$msec;
        proxy_pass http://gateway:8080/;
    }

    location /keycloak/ {
        proxy_pass http://keycloak:8080/;
        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-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 3000;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /admin/ {
        proxy_pass http://keycloak:8080/admin/;
        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-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 3000;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /resources/ {
        proxy_pass http://keycloak:8080/resources/;
        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-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 3000;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /js/ {
        proxy_pass http://keycloak:8080/js/;
        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-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 3000;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /realms/ {
        proxy_pass http://keycloak:8080/realms/;
        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-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 3000;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

With this configuration I have an access to keycloak which sits in internal docker network from exposed 3000 nginx port via http://localhost:3000/keycloak

You don’t need the ports configuration in the docker-compose file since you are telling the reverse proxy to access http://keycloak:8080. You only need it to access the port directly from elsewhere on the network.

You still seem to have a mix of port 8080 and port 80 though. And the port definition, if you want to keep it, should almost certainly be “127.0.0.1:3000:80”, else the port is exposed to the whole network unless blocked by a firewall elsewhere.