Protecting your first app¶
This guide shows you how to protect an internal service with Gatekeeper using nginx.
How it works¶
Nginx uses the auth_request directive to check authentication before serving requests. For each request:
Nginx sends a subrequest to Gatekeeper’s
/api/v1/auth/validateendpointGatekeeper checks the session cookie
If valid, Gatekeeper returns
200 OKand nginx serves the requestIf invalid, Gatekeeper returns
401and nginx redirects to the sign-in page
Prerequisites¶
Gatekeeper running and accessible
nginx installed on your server
An internal app you want to protect
For Ubuntu/Debian, install nginx and certbot together with:
sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx
Step 1: Register the app¶
First, register your app with Gatekeeper:
uv run gk apps add --slug myapp --name "My Internal App"
The slug is a URL-safe identifier used in access control.
Step 2: Grant yourself access¶
Grant your admin user access to the app:
uv run gk apps grant --slug myapp --email you@example.com
Step 3: Configure nginx¶
Add this configuration to nginx. Replace the placeholders with your actual values.
# Gatekeeper auth endpoint (internal only)
location = /_gatekeeper/validate {
internal;
proxy_pass http://localhost:8000/api/v1/auth/validate;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-GK-App "myapp"; # Your app slug
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_set_header Cookie $http_cookie;
}
# Your protected app
server {
listen 443 ssl;
server_name myapp.example.com;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
# SSL configuration...
location / {
auth_request /_gatekeeper/validate;
# On 401, redirect to sign-in
error_page 401 = @signin;
# On 403, show access denied
error_page 403 = @denied;
# Pass the authenticated user to your app
auth_request_set $auth_user $upstream_http_x_auth_user;
proxy_set_header X-Auth-User $auth_user;
# Proxy to your app
proxy_pass http://localhost:3000;
}
location @signin {
return 302 https://auth.example.com/signin?redirect=$scheme://$host$request_uri;
}
location @denied {
return 302 https://auth.example.com/request-access?app=myapp;
}
location = /logout {
return 302 https://auth.example.com/signout?redirect=https://auth.example.com/signin?redirect=https://$host/;
}
location = /signout {
return 302 https://auth.example.com/signout?redirect=https://auth.example.com/signin?redirect=https://$host/;
}
}
For internal apps, keep that X-Robots-Tag header on the app domain too. If you also control the app HTML, add:
<meta name="robots" content="noindex, nofollow, noarchive">
For static docs or other cached internal apps, those Cache-Control: no-store headers and the
logout redirects above avoid a stale page appearing to stay logged in until the next manual
navigation.
Step 4: Reload nginx¶
Test and reload the configuration:
nginx -t
sudo systemctl reload nginx
Step 5: Test it¶
Open your app URL in a browser (e.g.,
https://myapp.example.com)You should be redirected to the Gatekeeper sign-in page
Sign in with your email
After signing in, you’re redirected back to your app
If the user is signed in but lacks access, nginx can send them to a request-access or support flow
Using the authenticated user¶
Gatekeeper passes the user’s email in the X-Auth-User header. Your app can read this to know who’s making the request.
Example in Python/Flask:
from flask import request
@app.route("/")
def index():
user_email = request.headers.get("X-Auth-User")
return f"Hello, {user_email}!"
Troubleshooting¶
Getting 401 even when signed in¶
Check that the
Cookieheader is being passed to GatekeeperVerify
COOKIE_DOMAINmatches your app’s domainCheck browser dev tools for the session cookie
Getting 403 Forbidden¶
Verify the user has access to the app:
gk apps show --slug myappGrant access if missing:
gk apps grant --slug myapp --email user@example.com
Redirect loop¶
Make sure the sign-in page itself isn’t protected by
auth_requestGatekeeper’s own endpoints should not require authentication
Next steps¶
Managing apps — Add more apps and manage access
Managing users — Add users and handle approvals