How to Set Up OAuth2 Proxy to Protect Internal Sites With Google Auth (Step-by-Step)

Protect internal apps with OAuth2 Proxy and Google OAuth 2.0. Create Google credentials, install NGINX and OAuth2 Proxy, configure the proxy and NGINX auth_request, and secure the site with Let's Encrypt.

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: NGINXOAuth2 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_request to 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 with apt).
  • 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

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).

  1. Open Google Cloud Console and select (or create) a project.
  2. Go to APIs & ServicesOAuth consent screen. Configure the consent screen if you haven’t (e.g. User type: External, App name, support email).
  3. Go to APIs & ServicesCredentialsCreate CredentialsOAuth client ID.
  4. Application type: Web application. Give it a name (e.g. “OAuth2 Proxy”).
  5. Authorized JavaScript origins: https://auth.example.com (replace with your OAuth2 Proxy host).
  6. Authorized redirect URIs: https://auth.example.com/oauth2/callback (must match exactly).
  7. 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:

1
2
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):

1
2
sudo systemctl start docker
sudo systemctl enable docker

Confirm docker is running by checking its status.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ 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):

1
sudo usermod -aG docker ec2-user

Install Docker Compose (check releases for the latest version):

1
2
3
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:

1
2
sudo mkdir -p /etc/gatus
sudo chown -R $USER:$USER /etc/gatus

Create /etc/gatus/config.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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:

1
2
3
4
5
6
7
8
9
services:
  gatus:
    image: twinproduction/gatus:latest
    container_name: gatus
    restart: always
    volumes:
      - "/etc/gatus/config.yml:/config/config.yaml"
    ports:
      - "8080:8080"

Start Gatus:

1
2
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):

1
2
3
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):

1
2
3
4
5
6
7
8
9
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):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[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:

1
2
3
4
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):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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:

1
2
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.

comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy