OAuth2 Proxy is a reverse proxy that adds OAuth2 authentication in front of any HTTP service. Unauthenticated users are redirected to the provider (e.g. Google); after sign-in, they can access the upstream app. In this guide you set up OAuth2 Proxy with Google OAuth 2.0 in front of an internal app (we use Gatus as the example; you can use any service on a local port). Traffic flows: NGINX → OAuth2 Proxy (auth) → your app (e.g. Gatus on port 8080).
In this guide you’ll:
- Create Google OAuth 2.0 credentials (client ID and secret) and set the redirect URI
- Install NGINX, Docker, Certbot, and the OAuth2 Proxy binary
- Run an example internal app (Gatus) with Docker Compose, then configure OAuth2 Proxy and a systemd service
- Configure NGINX with
auth_requestto enforce sign-in and obtain a Let’s Encrypt certificate
Related: OAuth2 Proxy for central authentication in Kubernetes
Prerequisites
- A Linux server (this guide uses RHEL/Rocky/Alma with
dnf; adapt for Ubuntu/Debian withapt). - A domain name pointing to the server (e.g.
auth.example.com) for NGINX and Google redirect URI. - A Google Cloud project and access to Google Cloud Console.
Table of contents
- Prerequisites
- Table of contents
- 1. Google OAuth 2.0 setup
- 2. Update server and install packages
- 3. Set up Docker and Docker Compose
- 4. Example: Run an internal app to protect (Gatus)
- 5. Set up OAuth2 Proxy
- 6. Configure NGINX and TLS
- Frequently Asked Questions (FAQ)
- Conclusion
1. Google OAuth 2.0 setup
You need a Google OAuth 2.0 Client ID and Client Secret. Use the same domain you will use for OAuth2 Proxy (e.g. https://auth.example.com).
- Open Google Cloud Console and select (or create) a project.
- Go to APIs & Services → OAuth consent screen. Configure the consent screen if you haven’t (e.g. User type: External, App name, support email).
- Go to APIs & Services → Credentials → Create Credentials → OAuth client ID.
- Application type: Web application. Give it a name (e.g. “OAuth2 Proxy”).
- Authorized JavaScript origins:
https://auth.example.com(replace with your OAuth2 Proxy host). - Authorized redirect URIs:
https://auth.example.com/oauth2/callback(must match exactly). - Create the credential and copy the Client ID and Client secret; you’ll use them in the OAuth2 Proxy config.
2. Update server and install packages
Update the system and install NGINX, Docker, Certbot (for Let’s Encrypt), and unzip:
sudo dnf -y update
sudo dnf install -y nginx docker certbot python3-certbot-nginx unzip
3. Set up Docker and Docker Compose
Start and enable Docker (required for the example Gatus app; skip if you already use another upstream):
sudo systemctl start docker
sudo systemctl enable docker
Confirm docker is running by checking its status.
$ sudo systemctl status docker
â—Ź docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
Active: active (running) since Sun 2025-03-16 09:20:54 UTC; 33s ago
TriggeredBy: â—Ź docker.socket
Docs: https://docs.docker.com
Main PID: 207133 (dockerd)
Tasks: 8
Memory: 31.2M
CPU: 386ms
CGroup: /system.slice/docker.service
└─207133 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --default-ulimit nofile=32768:65536
Mar 16 09:20:53 auth.citizix.com systemd[1]: Starting docker.service - Docker Application Container Engine...
Mar 16 09:20:53 auth.citizix.com dockerd[207133]: time="2025-03-16T09:20:53.818123871Z" level=info msg="Starting up"
Mar 16 09:20:53 auth.citizix.com dockerd[207133]: time="2025-03-16T09:20:53.893991019Z" level=info msg="Loading containers: start."
Mar 16 09:20:54 auth.citizix.com dockerd[207133]: time="2025-03-16T09:20:54.475192203Z" level=info msg="Loading containers: done."
Mar 16 09:20:54 auth.citizix.com dockerd[207133]: time="2025-03-16T09:20:54.506292402Z" level=info msg="Docker daemon" commit=71907ca containerd-snapshotter=false storage-driver=overlay2 version=>
Mar 16 09:20:54 auth.citizix.com dockerd[207133]: time="2025-03-16T09:20:54.506447357Z" level=info msg="Daemon has completed initialization"
Mar 16 09:20:54 auth.citizix.com dockerd[207133]: time="2025-03-16T09:20:54.598889450Z" level=info msg="API listen on /run/docker.sock"
Mar 16 09:20:54 auth.citizix.com systemd[1]: Started docker.service - Docker Application Container Engine.
Add your user to the docker group so you can run Docker without root (replace ec2-user with your username):
sudo usermod -aG docker ec2-user
Install Docker Compose (check releases for the latest version):
sudo curl -L https://github.com/docker/compose/releases/download/v2.33.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose -v
4. Example: Run an internal app to protect (Gatus)
We use Gatus (a health-check dashboard) as the upstream app. OAuth2 Proxy will sit in front of it; you can replace Gatus with any service listening on a local port (e.g. 8080).
Create the config directory and a minimal Gatus config:
sudo mkdir -p /etc/gatus
sudo chown -R $USER:$USER /etc/gatus
Create /etc/gatus/config.yml:
metrics: true
storage:
type: sqlite
path: data.db
endpoints:
- name: example-healthcheck
url: https://example.com/
interval: 1m
conditions:
- "[STATUS] == 200"
Create /etc/gatus/docker-compose.yml:
services:
gatus:
image: twinproduction/gatus:latest
container_name: gatus
restart: always
volumes:
- "/etc/gatus/config.yml:/config/config.yaml"
ports:
- "8080:8080"
Start Gatus:
docker-compose -f /etc/gatus/docker-compose.yml up -d
docker-compose -f /etc/gatus/docker-compose.yml ps
The app listens on localhost:8080. OAuth2 Proxy will use this as the upstream.
5. Set up OAuth2 Proxy
Download the OAuth2 Proxy binary (see releases for newer versions):
curl -L https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.8.1/oauth2-proxy-v7.8.1.linux-amd64.tar.gz -o /tmp/oauth2-proxy.tar.gz
tar -xzvf /tmp/oauth2-proxy.tar.gz -C /tmp
sudo mv /tmp/oauth2-proxy-v7.8.1.linux-amd64/oauth2-proxy /usr/local/bin/oauth2_proxy
Create the config file /etc/oauth2_proxy.cfg. Generate a cookie secret with openssl rand -base64 32 and replace placeholders with your Google Client ID, Client Secret, domain, and upstream (e.g. http://localhost:8080 for Gatus):
provider = "google"
client_id = "YOUR_GOOGLE_CLIENT_ID"
client_secret = "YOUR_GOOGLE_CLIENT_SECRET"
cookie_secret = "GENERATE_WITH_openssl_rand_-base64_32"
redirect_url = "https://auth.example.com/oauth2/callback"
email_domains = ["*"]
cookie_domains = ".example.com"
http_address = "127.0.0.1:4180"
upstreams = ["http://localhost:8080"]
Restrict sign-in to a single domain (e.g. your company): set email_domains = ["example.com"]. Use * to allow any Google account.
Create a systemd unit /etc/systemd/system/oauth2_proxy.service (replace ec2-user with the user that should run the proxy):
[Unit]
Description=OAuth2 Proxy Service
After=network.target
[Service]
ExecStart=/usr/local/bin/oauth2_proxy --config=/etc/oauth2_proxy.cfg
Restart=always
User=ec2-user
Group=ec2-user
[Install]
WantedBy=multi-user.target
Reload systemd, start and enable the service:
sudo systemctl daemon-reload
sudo systemctl start oauth2_proxy
sudo systemctl enable oauth2_proxy
sudo systemctl status oauth2_proxy
You should see active (running) and a log line like mapping path "/" => upstream "http://localhost:8080". For debugging: sudo journalctl -fu oauth2_proxy.
6. Configure NGINX and TLS
NGINX will terminate TLS, use auth_request to enforce OAuth2 Proxy sign-in, and proxy /oauth2/ to OAuth2 Proxy and / to your upstream.
Create /etc/nginx/conf.d/auth.conf (replace auth.example.com with your domain):
server {
listen 80;
server_name auth.example.com;
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
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;
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
proxy_pass http://localhost: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;
}
}
Reload NGINX and obtain a Let’s Encrypt certificate:
sudo systemctl restart nginx
sudo certbot --nginx --non-interactive --agree-tos --email [email protected] -d auth.example.com
Visit https://auth.example.com. You should be redirected to Google to sign in; after authentication you’ll see the upstream app (e.g. Gatus).
Frequently Asked Questions (FAQ)
What is OAuth2 Proxy?
OAuth2 Proxy is a reverse proxy that adds OAuth2/OIDC login in front of HTTP services. Unauthenticated requests get redirected to the provider (e.g. Google); after login, the proxy sets a cookie and forwards requests to the upstream. It supports many providers (Google, GitHub, Azure AD, etc.).
Why use auth_request with NGINX?
auth_request runs a subrequest to /oauth2/auth for each request. If it returns 401, NGINX serves error_page 401 = /oauth2/sign_in, which sends the user to the OAuth2 Proxy sign-in page. This keeps authentication logic in OAuth2 Proxy and NGINX only enforces it.
How do I restrict sign-in to my organization?
Set email_domains in the OAuth2 Proxy config to your domain (e.g. ["example.com"]) so only Google accounts from that domain can sign in. For Google Workspace you can also use google_admin_email and google_group to restrict by group.
Can I use a different upstream than Gatus?
Yes. Change upstreams in /etc/oauth2_proxy.cfg to your app’s URL (e.g. http://127.0.0.1:3000). Ensure the app listens on localhost or an address OAuth2 Proxy can reach.
Conclusion
You set up OAuth2 Proxy with Google OAuth 2.0 in front of an internal app: created Google credentials, installed NGINX and OAuth2 Proxy, ran an example app (Gatus) on port 8080, configured the proxy and systemd, and used NGINX auth_request plus Let’s Encrypt so only signed-in users can access the site. For the same pattern in Kubernetes, see OAuth2 Proxy for central authentication in Kubernetes.