Put Keycloak behind Nginx with a free SSL certificate
Keycloak in production needs HTTPS. This guide walks through an Nginx reverse proxy with a Let's Encrypt cert — so your login page isn't served over plain HTTP.
TL;DR
Run Nginx on the same host as Keycloak, proxy traffic to it on port 8080, and use Certbot to get a free Let's Encrypt certificate. Keycloak needs specific proxy headers to work correctly behind Nginx — get those wrong and you'll see redirect loops.
What you'll need
- A domain name pointing to your server's public IP (DNS A record, propagated)
- A server with Docker and Nginx installed
- Ports 80 and 443 open in your firewall
- Certbot:
sudo apt install certbot python3-certbot-nginx
Step 1 — Run Keycloak bound to localhost
Keycloak should not be publicly accessible directly. Start it bound to the loopback interface only, in production mode:
docker run -d \
--name keycloak \
-p 127.0.0.1:8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=changeme \
-e KC_PROXY=edge \
-e KC_HOSTNAME=auth.yourdomain.com \
quay.io/keycloak/keycloak:latest \
start-dev
Replace auth.yourdomain.com with your actual domain. KC_PROXY=edge tells Keycloak it's behind a reverse proxy that handles TLS.
Step 2 — Configure Nginx
Create a new site config:
sudo nano /etc/nginx/sites-available/keycloak
Paste this, replacing the domain name:
server {
listen 80;
server_name auth.yourdomain.com;
location / {
proxy_pass http://127.0.0.1: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-Proto $scheme;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
}
}
Enable it:
sudo ln -s /etc/nginx/sites-available/keycloak /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 3 — Get the SSL certificate
sudo certbot --nginx -d auth.yourdomain.com
Follow the prompts. Certbot will edit your Nginx config automatically to add the HTTPS server block and redirect HTTP to HTTPS.
Test renewal works:
sudo certbot renew --dry-run
Step 4 — Verify
Open https://auth.yourdomain.com in your browser. You should see the Keycloak welcome page with a valid certificate. The padlock should be green.
The admin console is at https://auth.yourdomain.com/admin.
Common pitfalls
Redirect loop. The most common problem. Make sure KC_PROXY=edge is set on the Keycloak container. Without it, Keycloak doesn't trust the X-Forwarded-Proto header and thinks all requests are HTTP — causing infinite redirects.
KC_HOSTNAME must match your domain exactly. If the value doesn't match the hostname in the browser URL, Keycloak will reject or redirect requests. Include the subdomain.
Buffer size errors. Keycloak's tokens can be large. The proxy_buffer_size and proxy_buffers lines in the Nginx config prevent 502 errors caused by response headers being too big for default buffer sizes.
Certbot fails on DNS not propagated. If the A record is too fresh, Let's Encrypt can't verify the domain. Wait 10–15 minutes and try again.