Hello,
I just migrated from NPM to Traefik since it is more customisable and authentication friendly.
Also a lot more clear in terms of where configs can be made and no mix of UI + config files.
However, I still have a slight issue setting up authentication with OAuth2-Proxy as middleware between Traefik and Keycloak.
The whole setup works, but when a service is being visited when you are not authenticated, the webpage shows a blank page with a clickable text ‘Found’ which directs to the Keycloak login page.
I struggled setting up the whole authentication flow but all is fine now except this last part.
When I click that link and login to Keycloak, I get redirected fine to the service with the correct cookies and states.
So the only missing step, is probably a configuration that should redirect to the login in the first place without manual interaction.
According to the OAuth2-Proxy logs, there is a 401 returned with the forwardAuth (expected) and a 302 redirect but Traefik only sees a 401 with the Location header to the Keycloak login page and a html content representing the blank page with the ‘Found’ text.
Where am I screwing up? ![]()
Note:
I am not running this in docker containers. I am running a Proxmox host with an LXC container for each service.
So
- LXC 1 - Traefik - Linux Service
- LXC 2 - OAuth2-Proxy - Linux Service
- LXC 3 - Keycloak - Linux Service
- LXC 4 - Service 1 - Linux Service
I do not have Docker compose files and all is done with the regular config files.
OAuth2-Proxy Logs:
x.x.x.x - 7bb0fbe7-5f05-4fdd-9e4f-d2dea7685605 - - [2025/10/25 11:08:27] oauthproxy.domain.com GET - "/oauth2/auth" HTTP/1.1 "curl/8.14.1" 401 13 0.000
x.x.x.x - b264f7b7-38cc-4fb7-897d-3548d375dda6 - - [2025/10/25 11:08:27] service.domain.com GET - "/oauth2/sign_in?rd=https%3A%2F%2Fservice.domain.com%2F" HTTP/1.1 "curl/8.14.1" 302 455 0.000
Traefik Logs
{"level":"debug","middlewareName":"oauth-auth@file","middlewareType":"ForwardAuth","time":"2025-10-25T11:16:56+02:00","caller":"github.com/traefik/traefik/v3/pkg/middlewares/auth/forward.go:236","message":"Remote error https://oauthproxy.domain.com/oauth2/auth. StatusCode: 401"}
{"level":"debug","middlewareName":"oauth-errors@file","middlewareType":"CustomError","time":"2025-10-25T11:16:56+02:00","caller":"github.com/traefik/traefik/v3/pkg/middlewares/customerrors/custom_errors.go:121","message":"Caught HTTP Status Code 401, returning error page"}
OAuth2-Proxy Config
################################################################################
# OAuth2-Proxy Configuration (system service in LXC)
# Works with Keycloak (OIDC) and a shared proxy for multiple subdomains
################################################################################
# ---------------------------
# Core HTTP settings
# ---------------------------
http_address = "0.0.0.0:4180" # Port OAuth2-Proxy listens on
reverse_proxy = true # Expect headers from Traefik
#real_client_ip_header = "X-Forwarded-For"
# ---------------------------
# Provider: Keycloak (OIDC)
# ---------------------------
provider = "keycloak-oidc"
oidc_issuer_url = "https://keycloak.domain.com/realms/domain"
login_url = "https://keycloak.domain.com/realms/domain/protocol/openid-connect/auth"
redeem_url = "https://keycloak.domain.com/realms/domain/protocol/openid-connect/token"
validate_url = "https://keycloak.domain.com/realms/domain/protocol/openid-connect/userinfo"
scope = "openid email profile"
code_challenge_method = "S256"
whitelist_domains = [".domain.com", "domain.com", "*.domain.com"]
# Keycloak "Web (OIDC)" application
client_id = "oauth2-proxy"
client_secret = "xxx"
# Where Keycloak redirects users back to after login
redirect_url = "https://oauthproxy.domain.com/oauth2/callback"
proxy_prefix = "/oauth2"
# ---------------------------
# Cookie/session settings
# ---------------------------
cookie_name = "_oauth2_proxy"
cookie_secret = "xxx"
cookie_secure = true
cookie_httponly = true
cookie_expire = "12h"
cookie_refresh = "1h"
cookie_domains = [".domain.com", "domain.com", "*.domain.com"]
cookie_samesite = "lax"
# ---------------------------
# Upstream behavior
# ---------------------------
# We’re not using OAuth2-Proxy to forward to any backend directly;
# Traefik handles the real apps.
upstreams = ["static://202"]
# ---------------------------
# Authorization & access
# ---------------------------
email_domains = ["*"]
pass_access_token = true
pass_user_headers = true
pass_authorization_header = true
set_xauthrequest = true
ssl_insecure_skip_verify = true
skip_provider_button = true
# ---------------------------
# Logging
# ---------------------------
standard_logging = true
request_logging = true
auth_logging = true
logging_max_size = 50
logging_max_age = 30
logging_max_backups = 3
logging_filename = "oauth-proxy_customlogfile"
show_debug_on_error = true
Traefik Config - Middlewares + OAuth"2-Proxy
http:
middlewares:
security-headers:
headers:
frameDeny: true
sslRedirect: true
stsIncludeSubdomains: true
stsSeconds: 63072000
forceSTSHeader: true
auth-headers:
headers:
sslRedirect: true
stsSeconds: 315360000
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
sslHost: domain.com
stsIncludeSubdomains: true
stsPreload: true
frameDeny: true
customRequestHeaders:
X-Forwarded-Proto: "https"
X-Forwarded-Ssl: "on"
# Forward auth middleware that asks oauth2-proxy if request is authenticated
oauth-auth:
forwardAuth:
address: "https://oauthproxy.domain.com/oauth2/auth"
trustForwardHeader: true
# Errors middleware: on 401/403 redirect to oauth2-proxy sign_in (preserves requested URL)
oauth-errors:
errors:
status:
- "401-403"
service: oauthproxy
query: "/oauth2/sign_in?rd={url}"
routers:
oauthproxy:
rule: "Host(`oauthproxy.domain.com`) || PathPrefix(`/oauth2/`)"
entryPoints:
- websecure
service: oauthproxy
middlewares:
- auth-headers
tls:
certResolver: letsencrypt
domains:
- main: "domain.com"
sans:
- "*.domain.com"
services:
oauthproxy:
loadBalancer:
servers:
- url: "http://192.168.0.21:4180"
Traefik Config - Service1
http:
routers:
service1:
rule: "Host(`service1.domain.com`)"
entryPoints:
- websecure
service: service1
middlewares:
- oauth-errors
- oauth-auth
tls:
certResolver: letsencrypt
domains:
- main: "domain.com"
sans:
- "*.domain.com"
services:
service1:
loadBalancer:
servers:
- url: "http://192.168.0.106:80"
When i curl to the service page, I get this response showing a 401 with the location it should redirect to
curl -I https://service1.domain.com =>
HTTP/2 401
date: Sat, 25 Oct 2025 09:36:37 GMT
content-type: text/html; charset=utf-8
location: https://keycloak.domain.com/realms/domain/protocol/openid-connect/auth?approval_prompt=force&client_id=oauth2-proxy&code_challenge=challenge&code_challenge_method=S256&redirect_uri=https%3A%2F%2Foauthproxy.domain.com%2Foauth2%2Fcallback&response_type=code&scope=openid+email+profile&state=state%3Ahttps%3A%2F%2Fservice1.domain.com%2F
cache-control: no-cache, no-store, must-revalidate, max-age=0
expires: Thu, 01 Jan 1970 01:00:00 CET
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
cf-cache-status: DYNAMIC
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=xxx"}]}
server: cloudflare
set-cookie: _oauth2_proxy_csrf=cookie; HttpOnly; SameSite=Lax; Secure; Path=/; Domain=domain.com; Max-Age=900
cf-ray: 9940bb07fd34ba63-BRU
alt-svc: h3=":443"; ma=86400
I am new to this so I am sure I am doing something stupidly small wrong, but hopefully someone can help me out on this ![]()
The default documentation of both Traefik and OAuth2-Proxy did not help me so I am relying on help of the community.
Thank you in advance!
