OAuth2 Proxy

Reverse proxy that adds authentication to any web app

Reverse Proxy & Gateways ★ 15k stars Medium setup MIT

OAuth2 Proxy is a reverse proxy and static file server that adds OAuth and OIDC authentication in front of upstream applications. It protects services that lack their own login.

Key features

  • Adds OAuth/OIDC to any app
  • Many identity providers
  • Forward-auth mode
  • Lightweight Go binary

Pros & cons

Strengths

  • Works with any upstream
  • Broad provider support
  • Kubernetes friendly

Trade-offs

  • Many flags to learn
  • Limited authorization rules

OAuth2 Proxy replaces

Last reviewed Aug 26, 2026 · 829 words

Five settings put a login page in front of an app that has none: a provider, a client ID, a client secret, a cookie secret and a redirect URL. OAuth2 Proxy's other hundred-and-something flags are why the difficulty rating is Medium rather than Easy, and why most people's first attempt ends in a redirect loop. It is a single Go binary, MIT-licensed, needs about 128 MB, has roughly 14,900 GitHub stars, and its real strength is that it will authenticate against almost anything: Google, GitHub, Microsoft, GitLab, Keycloak, Authentik, or any OpenID Connect issuer you point it at.

Two ways to run it, and one is right for a homelab

Mode one is a true proxy: requests hit OAuth2 Proxy, it checks the session cookie, and forwards to the upstream you configured. That works for a single app. Mode two is forward-auth: your reverse proxy asks OAuth2 Proxy "is this request authenticated" on every hit and only the answer travels, so one instance protects 20 apps behind Caddy, Traefik or Nginx. Forward-auth is the pattern for a homelab, and the configuration below is for it.

The configuration that works on the first try

services:
  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    environment:
      OAUTH2_PROXY_PROVIDER: oidc
      OAUTH2_PROXY_OIDC_ISSUER_URL: https://auth.example.com/application/o/proxy/
      OAUTH2_PROXY_CLIENT_ID: oauth2-proxy
      OAUTH2_PROXY_CLIENT_SECRET: paste-from-your-idp
      OAUTH2_PROXY_COOKIE_SECRET: paste-32-random-bytes-base64
      OAUTH2_PROXY_REDIRECT_URL: https://sso.example.com/oauth2/callback
      OAUTH2_PROXY_EMAIL_DOMAINS: "*"
      OAUTH2_PROXY_COOKIE_DOMAINS: .example.com
      OAUTH2_PROXY_WHITELIST_DOMAINS: .example.com
      OAUTH2_PROXY_REVERSE_PROXY: "true"
      OAUTH2_PROXY_SET_XAUTHREQUEST: "true"
      OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180
      OAUTH2_PROXY_UPSTREAMS: static://202
    restart: unless-stopped

Generate the cookie secret with python3 -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'; it must decode to exactly 16, 24 or 32 bytes or the process refuses to start. The issuer URL is whatever your identity provider publishes at /.well-known/openid-configuration; the example is Authentik's shape. COOKIE_DOMAINS and WHITELIST_DOMAINS set to the parent domain are what let one login cover every subdomain and allow the post-login redirect back to the app you started from, and forgetting them is the source of most of the redirect loops. UPSTREAMS: static://202 tells it there is no upstream of its own, only the auth endpoint. REVERSE_PROXY: true makes it trust X-Forwarded-* headers from your proxy, which it must, since it is behind one.

Wiring the reverse proxy

Nginx uses auth_request /oauth2/auth; inside the protected location, plus error_page 401 = /oauth2/sign_in; so an unauthenticated user is sent to log in rather than shown an error, and a location block that proxies /oauth2/ to port 4180. Caddy is the shortest: a forward_auth oauth2-proxy:4180 block with uri /oauth2/auth and copy_headers X-Auth-Request-Email X-Auth-Request-User, and Caddy handles the 401-to-redirect step itself. Traefik's forwardAuth middleware pointed at /oauth2/auth returns the 401 to the browser, so you pair it with an errors middleware that rewrites 401 into a redirect to the sign-in page; the project's docs have that snippet and it is not obvious. In all three, the X-Auth-Request-Email header is how the app behind learns who logged in, and apps that support trusted-header authentication, Grafana and Jellyfin with a plugin among them, will use it to skip their own login.

The authorization gap

Authentication is what OAuth2 Proxy does. Authorization, the rule that says which users may reach which app, is thin: an email domain list, an allowed-emails file, and --allowed-group when the provider sends group claims. There are no per-path rules and no per-app policies in one instance, so "everyone can see the wiki but only admins reach the router" means either a second instance or pushing the decision into the identity provider's application assignments. Authelia has an access-control list with per-domain and per-path rules for exactly this, and Authentik's outposts bundle the proxy with policies; my SSO comparison lays out that choice. Sessions default to the cookie, which gets large if the ID token carries many groups; the Redis session store fixes that when the cookie size warnings appear in the log.

What I'd do

Use OAuth2 Proxy when you want to log in with an account you already have, a GitHub or Google login for a handful of homelab apps, without running an identity provider at all, or when you have an OIDC provider and need one lightweight forward-auth gate behind Caddy. The moment the access rules get more interesting than "is this person one of us", switch the gate to Authelia and keep the identity provider. Either way it belongs in the proxy category for what it is: a very good bouncer, not a security policy, and a free replacement for Cloudflare Access when the apps stay on your own network.

Compare OAuth2 Proxy

3 head-to-head comparisons.

Similar reverse proxy & gateways apps